Floyd Resler
Floyd Resler

Reputation: 1786

Swift GameKit Moving Sprite In Arc

I need some help in making the player in my platform game jump. The problem I'm having is that he jumps through a pillar which he's not supposed to do (I do have the physics set up correctly). I suspect it is because of my horribly inefficient code below. I know there's a better way to move him along a path but I'm not sure how to do it.

var xStep = CGFloat(18)
var yStep = CGFloat(30)

var x = CGFloat(playerJump.position.x)
var y = CGFloat(playerJump.position.y)

let follow = SKAction.move(to: CGPoint(x: x, y:y), duration: 0.1)
x += xStep
y += yStep
let follow2 = SKAction.move(to: CGPoint(x: x, y:y), duration: 0.1)
x += xStep
y += yStep
let follow3 = SKAction.move(to: CGPoint(x: x, y:y), duration: 0.1)
x += xStep
y += yStep
let follow4 = SKAction.move(to: CGPoint(x: x, y:y), duration: 0.1)
x += xStep
y += yStep
let follow5 = SKAction.move(to: CGPoint(x: x, y:y), duration: 0.1)
x += xStep
y -= yStep
let follow6 = SKAction.move(to: CGPoint(x: x, y:y), duration: 0.1)
x += xStep
y -= yStep
let follow7 = SKAction.move(to: CGPoint(x: x, y:y), duration: 0.1)
x += xStep
y -= yStep
let follow8 = SKAction.move(to: CGPoint(x: x, y:y), duration: 0.1)
x += xStep
y -= yStep
let follow9 = SKAction.move(to: CGPoint(x: x, y:y), duration: 0.1)

let group = SKAction.group([
    SKAction.repeat(jumpAnimation!, count: 1),
           SKAction.sequence([follow,follow2,follow3,follow4,follow5,follow6,follow7,follow8,follow9])
])
if playerJump.parent == nil {
    addChild(playerJump)
}
playerJump.run(SKAction.sequence([group]), completion: {
    self.player.position=self.playerJump.position
    self.playerJump.removeAllActions()
    self.playerJump.removeFromParent()
    self.addChild(self.player)
})

Thanks in advance for any assistance.

UPDATE The problem started when I increased the value in xStep. The total distance the character jumps would put it past the pillar. That's why I thought my code was an issue. Here's a video of what's happening. Jumping Video

UPDATE 2 Here's my newest code which still puts the player on the other side of the pillar.

@objc func jumpTapped() {
    var xStep = CGFloat(18)
    let yStep = CGFloat(12)

    jumping=true
    xStep = player.xScale * xStep
    var x = player.position.x
    var y = player.position.y


    var moveAnimation = [SKAction]()

    for i in 0...8 {
        x += xStep
        if i < 5 {
            y += yStep
        } else {
            y -= yStep
        }
        let move = SKAction.move(to: CGPoint(x: x, y: y), duration: 0.1)
        moveAnimation.append(move)
    }

    let group = SKAction.group([jumpAnimation!, SKAction.sequence(moveAnimation)])

    player.physicsBody?.affectedByGravity = false
    player.removeAllActions()
    player.run(group) {
        self.endJump()
    }
}

func endJump() {
    player.removeAllActions()
    player.physicsBody?.affectedByGravity = true
    player.run(SKAction.repeatForever(self.playerAnimation!))
    jumping=false
}

Upvotes: 0

Views: 220

Answers (2)

Floyd Resler
Floyd Resler

Reputation: 1786

I figured out what I was doing wrong. My mistake was moving the sprite within the SKAction. I should have been moving the sprite in the update method. So, I added this to update():

    if jumping {
            if jumpCount > 3 {
                player.position.x += xStep // Jump straight up for a couple of frames to clear steps
            }
            if jumpCount < maxJump/2 {
                player.position.y += yStep  //Move sprite up for first half of jump
            } else {
                player.position.y -= yStep //Move sprite down for last half of jump
            }
            jumpCount += 1
            if jumpCount >= maxJump {
                endJump()
            }
        }

All the collision detection works properly and the player doesn't jump through pillars.

Upvotes: 0

Ron Myschuk
Ron Myschuk

Reputation: 6061

It looks like you are setting player.position to self.playerJump.position

That is probably your problem, it is jumping beyond where the pillar is. You might as well have said player.position.x = player.position.x + 500 which will put you on the other side of the pillar as well ignoring any physics all together.

Why are you adding a sprite and running the actions on it? why not just run the animation and the actions on the player sprite?

As far as cleaning up your code goes you can try this. there are other things you can do as well like have the character jump along a Bezier path but this might be fine for what you are doing. But I highly doubt this will have any impact on your issue of the player jumping through the pillar.

Some small tidbits for you.

Sprite positions are already a CGFloat no need to cast them

short hand completion code syntax is object.run(someAction) { //do something when complete }

running an action with repeat of 1 is pointless

running a sequence of a single action (SKAction.sequence([group])) is also pointless

var xStep = CGFloat(18)
var yStep = CGFloat(30)

var x = playerJump.position.x
var y = playerJump.position.y
var moveAnimation: [SKAction]!

for _ in 0...9 {
    x += xStep
    y += yStep
    moveAnimation.append(SKAction.move(to: CGPoint(x: x, y: y), duration: 0.1))
}

let group = SKAction.group([jumpAnimation, SKAction.sequence(moveAnimation)])

guard playerJump.parent else { addChild(playerJump)}

playerJump.run(group) {
    self.player.position = self.playerJump.position
    self.playerJump.removeAllActions()
    self.playerJump.removeFromParent()
    self.addChild(self.player)
}

Edit try adding the below function to your code

func didBegin(_ contact: SKPhysicsContact) {

    let contactAName = contact.bodyA.node?.name
    let contactBName = contact.bodyB.node?.name

    if (contactAName == "pillar") || (contactBName == "pillar") {
        //assuming only player can hit pillar so don't need to check if one of the contacts is player
        endJump()
    }
}

Upvotes: 1

Related Questions