6

I am creating a simple sprite kit game that will position a player on the left side of the screen, while enemies approach from the right. Since the player can be moved up and down, I want the enemies to "smartly" adjust their path towards the player.

I tried removing and re-adding the SKAction sequence whenever the player moves, but the below code causes the enemies to not show at all, probably because its just adding and removing each action on every frame update, so they never have a chance to move.

Hoping to get a little feedback about the best practice of creating "smart" enemies that will move towards a player's position at any time.

Here is my code:

    func moveEnemy(enemy: Enemy) {
    let moveEnemyAction = SKAction.moveTo(CGPoint(x:self.player.position.x, y:self.player.position.y), duration: 1.0)
    moveEnemyAction.speed = 0.2
    let removeEnemyAction = SKAction.removeFromParent()
    enemy.runAction(SKAction.sequence([moveEnemyAction,removeEnemyAction]), withKey: "moveEnemyAction")
}

func updateEnemyPath() {
    for enemy in self.enemies {
        if let action = enemy.actionForKey("moveEnemyAction") {
            enemy.removeAllActions()
            self.moveEnemy(enemy)
        }
    }
}

override func update(currentTime: NSTimeInterval) {
    self. updateEnemyPath()
}
Whirlwind
  • 14,286
  • 11
  • 68
  • 157
JimmyJammed
  • 9,598
  • 19
  • 79
  • 146
  • "Doesn't work right" is not a technical term. We need complete information on your expectations and what it is doing improperly. – Rob Mar 26 '16 at 01:32
  • @Rob Sorry for the vagueness, right now the above code causes no enemies to appear. Probably because its just adding and removing each action on every frame update, so they never have a chance to move. Thanks! – JimmyJammed Mar 26 '16 at 04:16
  • 1
    Although you already got an answer that you marked as correct I would strongly recommend that you look into GameplayKit (ios 9 API). It provides tools for more flexible game design including pathfinding and enemy seeking which makes things such as you want much nicer and easier. There is some good tutorials on the web about this. https://www.raywenderlich.com/119959/gameplaykit-tutorial-entity-component-system-agents-goals-behaviors and http://code.tutsplus.com/tutorials/an-introduction-to-gameplaykit-part-1--cms-24483 – crashoverride777 Mar 26 '16 at 17:49

1 Answers1

17

You have to update enemy position and zRotation property in each update: method call.

Seeker and a Target

Okay, so lets add some nodes to the scene. We need a seeker and a target. Seeker would be a missile, and target would be a touch location. I said you should do this inside of a update: method, but I will use touchesMoved method to make a better example. Here is how you should setup the scene:

import SpriteKit

class GameScene: SKScene, SKPhysicsContactDelegate {

    let missile = SKSpriteNode(imageNamed: "seeking_missile")

    let missileSpeed:CGFloat = 3.0

    override func didMoveToView(view: SKView) {

        missile.position = CGPoint(x: frame.midX, y: frame.midY)

        addChild(missile)
    }
}

Aiming

To implement the aiming you have to calculate the how much you have to rotate a sprite based on its target. In this example I will use a missile and make it point towards the touch location. To accomplish this, you should use atan2 function, like this ( inside touchesMoved: method):

if let touch = touches.first {

    let location = touch.locationInNode(self)

    //Aim
    let dx = location.x - missile.position.x
    let dy = location.y - missile.position.y
    let angle = atan2(dy, dx)

    missile.zRotation = angle

}

Note that atan2 accepts parameters in y,x order, rather than x,y.

So right now, we have an angle in which missile should go. Now lets update its position based on that angle (add this inside touchesMoved: method right below the aiming part):

//Seek
 let vx = cos(angle) * missileSpeed
 let vy = sin(angle) * missileSpeed

 missile.position.x += vx
 missile.position.y += vy

And that would be it. Here is the result:

seeking missile

Note that in Sprite-Kit the angle of 0 radians specifies the positive x axis. And the positive angle is in the counterclockwise direction:

polar coords

Read more here.

This means that you should orient your missile to the right missile nose points to the right rather than upwards enter image description here. You can use the upwards oriented image as well, but you will have to do something like this:

missile.zRotation = angle - CGFloat(M_PI_2)
Whirlwind
  • 14,286
  • 11
  • 68
  • 157