I'm trying to create a mini game using only SwiftUI. I have a map that looks like a Pokémon game's map, divided vertically into different images so that each one fills the screen, and by going left/right the map "shifts" to the next image. However, there are certain areas in which the character cannot go (such as buildings). The current method I'm using is very complicated, is there an easier method (still using only SwiftUI)?
Here's my current code:
@State var index = 0
@State var direction = "Down"
@State var animate = false
var body : some View {
ZStack {
Image(city[index])
.resizable()
.frame(width: UIScreen.main.bounds.width, height: UIScreen.main.bounds.height)
.ignoresSafeArea()
MainCharacter(X: $positionX, Y: $positionY, direction: $direction)
.position(x: positionX, y: positionY)
VStack {
Spacer()
HStack {
GamePad(direction: $direction, animate: $animate)
Spacer()
}
.padding(30)
}
}
.onChange(of: animate) { newValue in
if newValue == true {
move()
}
}
}
func move() {
if canMove() == true {
if direction == "Left" {
if positionX >= UIScreen.main.bounds.width*0.57 {
withAnimation(Animation.linear(duration: 0.01)) {
positionX -= 1
}
move()
}
}
else if direction == "Right" {
withAnimation(Animation.linear(duration: 0.01)) {
positionX += 1
}
move()
}
//And so on
}
}
func canMove() -> Bool {
var result = true
if index == 0 {
if direction == "Up" {
if positionY <= UIScreen.main.bounds.height*0.17 {
result = false
}
if positionX > UIScreen.main.bounds.width*1.13 && positionY >= UIScreen.main.bounds.height*0.38 && positionY <= UIScreen.main.bounds.height*0.42 {
result = false
}
if positionX >= UIScreen.main.bounds.width*1.33 && positionY >= UIScreen.main.bounds.height*0.47 && positionY <= UIScreen.main.bounds.height*0.85 {
result = false
}
if positionX >= UIScreen.main.bounds.width*0.85 && positionX <= UIScreen.main.bounds.width*1.3 && positionY <= UIScreen.main.bounds.height*0.58 && positionY >= UIScreen.main.bounds.height*0.47 {
result = false
}
}
if direction == "Down" {
//Conditions
}
if direction == "Left" {
//Conditions
}
if direction == "Right" {
//Conditions
}
}
else if index == 1 {
//And so on
}
return result
}
The Game Pad:
struct GamePad: View {
@Binding var direction : String
@Binding var animate : Bool
var body: some View {
ZStack {
ZStack {
VStack(spacing: 40) {
Rectangle()
.stroke(.black, lineWidth: 6)
.frame(width: 40, height: 43)
Rectangle()
.stroke(.black, lineWidth: 6)
.frame(width: 40, height: 43)
}
HStack(spacing: 40) {
Rectangle()
.stroke(.black, lineWidth: 6)
.frame(width: 43, height: 40)
Rectangle()
.stroke(.black, lineWidth: 6)
.frame(width: 43, height: 40)
}
Rectangle()
.foregroundColor(.white)
.frame(width: 40, height: 40)
}
ZStack {
VStack(spacing: 40) {
Rectangle()
.frame(width: 40, height: 43)
.foregroundColor(.white)
.overlay(
Button {
} label: {
VStack {
Image(systemName: "arrowtriangle.up.fill")
.foregroundColor(.black)
Spacer()
}
.padding(.top, 10)
.gesture(DragGesture(minimumDistance: 0.0, coordinateSpace: .global)
.onChanged { _ in
direction = "Up"
animate = true
}
.onEnded { _ in
direction = ""
animate = false
}
)
}
)
Rectangle()
.frame(width: 40, height: 43)
.foregroundColor(.white)
.overlay(
Button {
} label: {
VStack {
Spacer()
Image(systemName: "arrowtriangle.down.fill")
.foregroundColor(.black)
}
.padding(.bottom, 10)
.gesture(DragGesture(minimumDistance: 0.0, coordinateSpace: .global)
.onChanged { _ in
direction = "Down"
animate = true
}
.onEnded { _ in
direction = ""
animate = false
}
)
}
)
}
HStack(spacing: 40) {
Rectangle()
.frame(width: 43, height: 40)
.foregroundColor(.white)
.overlay(
Button {
} label: {
VStack {
Spacer()
Image(systemName: "arrowtriangle.left.fill")
.foregroundColor(.black)
Spacer()
}
.gesture(DragGesture(minimumDistance: 0.0, coordinateSpace: .global)
.onChanged { _ in
direction = "Left"
animate = true
}
.onEnded { _ in
direction = ""
animate = false
}
)
}
)
Rectangle()
.frame(width: 43, height: 40)
.foregroundColor(.white)
.overlay(
Button {
} label: {
VStack {
Spacer()
Image(systemName: "arrowtriangle.right.fill")
.foregroundColor(.black)
Spacer()
}
.gesture(DragGesture(minimumDistance: 0.0, coordinateSpace: .global)
.onChanged { _ in
direction = "Right"
animate = true
}
.onEnded { _ in
direction = ""
animate = false
}
)
}
)
}
Rectangle()
.foregroundColor(.white)
.frame(width: 40, height: 40)
}
}
}
}
I have found a solution that it's probably not the best one nor well written, but it's all in SwiftUI, which is what I needed. I recreated every building as a CGRect:
let hospital = CGRect(x: UIScreen.main.bounds.width*0.47, y: UIScreen.main.bounds.height*0.56, width: UIScreen.main.bounds.width*0.25, height: UIScreen.main.bounds.height*0.33)
Then, in the view I display all of those buildings using a Rectangle:
ZStack {
Rectangle().path(in: hospital)
.foregroundColor(.clear)
}
Then I have my character
Character()
.position(x: posX, y: posY)
And when he is moving, I make a check with the position using the posX and posY variables that I have assigned to my character:
func canMove(x: CGFloat, y: CGFloat) -> Bool {
let buildings = [hospital, house, mountain]
var canMove = true
for shape in buildings {
if shape.contains(CGPoint(x: x, y: y)) {
canMove = false
}
}
return canMove
}