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<UITouch>, 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<UITouch>, 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...

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,684评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 87,143评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,214评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,788评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,796评论 5 368
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,665评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,027评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,679评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 41,346评论 1 299
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,664评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,766评论 1 331
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,412评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,015评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,974评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,203评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,073评论 2 350
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,501评论 2 343