2018-01-12 通过向量概念控制Sprite的移动实践

  1. 定义类变量
 //Sprite僵尸主角
    let zombie = SKSpriteNode(imageNamed: "zombie1")
    
    //上一帧更新的时间,用于跟currentTime比对
    var lastUpdateTime : TimeInterval = 0
    
    //相邻两帧的时间差,dt = currentTime - lastUpdateTime
    var dt : TimeInterval = 0
    
    //僵尸每秒钟的移动点数
    let zombieMovePointsPerSec:CGFloat = 480.0
    
    //移动向量
    var velocity = CGPoint.zero
    
    //最后点击的位置
    var lastTouchLocation = CGPoint.zero

  1. 初始化场景 - 固定套路
 override func viewDidLoad() {
        super.viewDidLoad()
        
        //Initialize scene
        let scene = GameScene(size: CGSize(width: 1920, height: 1080))
        
        //Initialize skView
        let skView = self.view as! SKView
        
        //Set FPS & node count present in the scene
        skView.showsFPS = true
        
        skView.showsNodeCount = true
        
        //A Boolean value that indicates whether parent-child and sibling relationships affect the rendering order of nodes in the scene.
        skView.ignoresSiblingOrder = true
        
        //Set the scaleMode
        scene.scaleMode = .aspectFill
        
        //Present scene in skView
        skView.presentScene(scene)
       
        
    }
  1. 初始化bg并添加到场景中、设置僵尸的初始位置并添加到场景中
override func didMove(to view: SKView) {
        
        //初始化bg并添加到场景中
        let bg = SKSpriteNode(imageNamed: "background1")
        
        bg.scale(to: CGSize(width: 1920, height: 1080))
        
        bg.position = CGPoint(x: self.size.width/2, y: self.size.height/2)
        
        bg.zPosition = -1
        
        addChild(bg)
        
        //设置僵尸初始位置并添加到场景中

        zombie.position = CGPoint(x: size.width/4, y: size.height/4)
        
        addChild(zombie)

    }
  1. 通过touchesBegan或touchesMoved确定最后点击的位置

5.在触摸事件中调用moveZombieForward(location: touchLocation)

 override func touchesBegan(_ touches: Set, with event: UIEvent?) {
        
        guard let touch  = touches.first else {
            
            return
            
        }
        
        let touchLocation = touch.location(in: self)
        
        lastTouchLocation = touchLocation
        
            moveZombieForward(location: touchLocation)

        
        
    }
    
    override func touchesMoved(_ touches: Set, with event: UIEvent?) {
        
        guard let touch = touches.first else {
            
            return
        }
        
        let touchLocation = touch.location(in: self)
        
        lastTouchLocation = touchLocation

        
        moveZombieForward(location: touchLocation)
    }

6.设置僵尸移动的向量、运动直线距离、方向以及速度

func moveZombieForward(location:CGPoint) {
        
        
        //触摸点与当前僵尸位置的偏移量
        let offset = CGPoint(x: location.x - zombie.position.x, y: location.y - zombie.position.y)
        
        //根据偏移量算触摸点与当前僵尸位置的直线距离
        let length = sqrt(Double(offset.x * offset.x + offset.y * offset.y))
        
        //根据偏移量及直线距离,算出从当前僵尸的位置到触摸点方向的向量,其实Y/X就是直线的斜率
        let direction = CGPoint(x: offset.x/CGFloat(length), y: offset.y/CGFloat(length))
        
        //速度的表达式,高中物理学过,速度和速率是有区别的,速度不仅仅表现物体运动的快慢,还表现了物体运动的方向。而速率只表现了物体运动的快慢
        velocity = CGPoint(x: direction.x * zombieMovePointsPerSec, y: direction.y * zombieMovePointsPerSec)
        
    }


  1. 得到了僵尸运动的速度,现在就可以写僵尸移动的方法,然后在update()函数中去调用。
    注:dt = currentTime - lastUpdateTime 也就是两帧之间的时间差。将dt * velocity 就是每一帧僵尸实际运动的距离

'''
func move(sprite:SKSpriteNode, velocity:CGPoint) {

    let amountToMove = CGPoint(x: velocity.x * CGFloat(dt), y: velocity.y * CGFloat(dt))
    
    sprite.position = CGPoint(x: sprite.position.x + amountToMove.x, y: sprite.position.y + amountToMove.y)
    
}

'''

8.这里还有一个rotate方法,是在每次点击屏幕后,僵尸都会旋转到正朝点击位置的方向。这里我就没有按照教程直接设置zombie.zRotation了,而是直接使用了SKAction.rotate的方法,这样能让僵尸转向得更自然一些,没那么生硬。
三角函数就不同说了吧,Y/X一般都是等于斜率角度的正切值,使用atan2(Double,Double)反正切函数,计算出旋转角度,僵尸就乖乖地旋转过去啦。

   func rotate(sprite:SKSpriteNode, direction:CGPoint){
        
//        sprite.zRotation = CGFloat(atan2(direction.y, direction.x))
        
        sprite.run(SKAction.rotate(toAngle: CGFloat(atan2(direction.y, direction.x)), duration: 0.2))
        
    }
  1. 然后就是能让僵尸相应点击事件的update函数了,其实学了下一章SKAction,就不必依赖update函数来执行移动操作了,因为这样我感觉效率会很低,而且sprite不容易控制。但是还是先按照教程这样写吧?
  override func update(_ currentTime: TimeInterval) {
        
        //计算相邻两帧的时间差
        if lastUpdateTime > 0 {
            dt = currentTime - lastUpdateTime
        } else {
            dt = 0 }
        
        //然后将当前时间赋值到上次更新的时间,周而复始
        lastUpdateTime = currentTime
        
        //根据不断变化的速度velocity将移动事件作用到zombie这个sprite上,让他进行移动。
        move(sprite: zombie, velocity: velocity)
        
        //该旋转时就旋转
        rotate(sprite: zombie, direction: velocity)
        
        //边界碰撞检测
        boundsCheckZombie()
        
        //僵尸距离点击的点还有6个点时就停止
        if abs(zombie.position.x - lastTouchLocation.x) <= 6 || abs(zombie.position.y - lastTouchLocation.y) <= 6 {
            
            zombie.position = lastTouchLocation
            
            velocity = CGPoint.zero
        
       
        }
    }

10.到这里,僵尸就已经可以乖乖的听你话,指哪走哪了,但是我们还是不希望僵尸出边框对吧?因为还没学到物理引擎检测碰撞那里,我们还是用几何知识来解决这个问题,检测sprite到边界的距离,如果越界了,首先让sprite的位置回到边界的位置,然后让其朝反方向移动,也就相当于弹回来了。

    let bottomLeft = CGPoint.zero
    
    let topRight = CGPoint(x: size.width, y: size.height)
    
    if zombie.position.x - zombie.size.width/2 <= bottomLeft.x {
        
        zombie.position.x = bottomLeft.x + zombie.size.width/2
        
        velocity.x = -velocity.x
        
    }
    
    if zombie.position.x + zombie.size.width/2 >= topRight.x {
        
        zombie.position.x = topRight.x - zombie.size.width/2
        
        velocity.x = -velocity.x
        
    }
    
    if zombie.position.y - zombie.size.height/2 <= bottomLeft.y {
        
        zombie.position.y = bottomLeft.y + zombie.size.height/2
        
        velocity.y = -velocity.y
    }
    
    if zombie.position.y + zombie.size.height/2 >= topRight.y {
        
        zombie.position.y = topRight.y - zombie.size.height/2
        
        velocity.y = -velocity.y
    }

好了,第一章已经学完了,下周开始学第二章SKAction。

To be continued...

你可能感兴趣的:(2018-01-12 通过向量概念控制Sprite的移动实践)