Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to move enemy towards a moving player?

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()
}
like image 990
JimmyJammed Avatar asked Mar 26 '16 01:03

JimmyJammed


Video Answer


1 Answers

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)
like image 129
Whirlwind Avatar answered Oct 06 '22 08:10

Whirlwind