Search code examples
swiftuigame-physicsgame-development

Restrict movement area of a character using only SwiftUI


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)
        }
    }
}
}

Solution

  • 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
        }