• swift语言实战晋级-第9章 游戏实战-跑酷熊猫-9-10 移除平台与视差滚动


    9.9 移除场景之外的平台

     

        用为平台是源源不断的产生的,如果不注意销毁,平台就将越积越多,虽然在游戏场景中看不到。几十个还看不出问题,那几万个呢?几百万个呢?

        所以我们来看看怎么移除平台,那什么样的平台需要移除呢?又如何移除呢?之前我们也说过,当平台完全移除游戏场景的时候就可以进行移除操作了。需要做两个操作,1从平台工厂类中移除,2从平台数组中移除。

    而且,因为平台是一个接一个出现的,所以我们不需要每次都遍历平台数组去对每个平台都做判断。只需要对最左边也就是第一个平台做这种检测就可以了。这种检测放在move方法中是最适合不过的了。

     

    func move(speed:CGFloat){
    //遍历所有
    for p in platforms{
    //x坐标的变化长生水平移动的动画
    p.position.x -= speed
    }
    //移除平台
    if platforms[0].position.x < -platforms[0].width {
    platforms[0].removeFromParent()
    platforms.removeAtIndex(0)
    }
    }
    

    9.10 视差滚动背景

     

        什么是视差滚动?它指的是让多层背景以不同的速度移动,形成立体的运动效果,从而带来非常出色的视觉体验。

     

        而我们的游戏中也运用到了这个技术。如下图:

        在跑酷熊猫这个游戏中,我们的背景分为两层。第一层更靠近游戏窗口的色彩更鲜艳,移动速度也更快一些。第二层由于要模拟远处的场景,所以颜色更淡一些,对比度更弱一些,移动速度也就更慢一些。

        于是,在我们的编码中,我们可以利用GameScene中定义的移动速度moveSpeed,给第一层背景1/5的moveSpeed的值,给第二层背景1/20的moveSeed的值。这样就形成了视察滚动。

       

        视差滚动讲完,我们还要理解一种循环滚动的算法。因为我们不可能像产生平台那样,源源不断的生成新的背景。我们要用一种循环滚动的算法来实现。

        首先,我们的背景图片是可以收尾无缝衔接的。那么该需要多少张无缝衔接图来组成背景呢?判断标准是:当第一张图移除屏幕外,剩下的图在x轴方向上还能够填满游戏屏幕就够了。看下图:

     

     

        像上图这样的情况,只需要两张背景图就够了。那么我们就用两张无缝衔接的背景图来做说明什么是循环滚动。下图是初始情况:

     

    当移动到再上一张图9-17的位置时,直接跳回图9-18的位置,再继续向左移动就形成了循环滚动。我们从下图能看到,回跳的时候,在游戏场景中,背景在视觉上是没有变化的,但实际上已经开始了新的滚动。

     

    原理讲完,我们来看看具体的编码。我们新建一个背景类,类名是BackGround,继承自SKNode。

        在背景类中我们定义两个数组,一个用来存放近景,另一个用来存放远景:

    //近处背景数组
    ar arrBG = [SKSpriteNode]()
    //远处背景数组
    var arrFar = [SKSpriteNode]()
    

        我们在构造方法init内些生成背景的代码。由于远景图片比较小,所以要完成循环滚动要3张图。

        循环滚动的算法卸写在了move方法中,move方法和平台工厂类的move方法一样,接收一个表示速度的参数,这个参数是GameScene中moveSpeed的1/5。因为远景速度移动要更慢,所以它的x坐标的变化值是近景的四分之一。

        看看完整代码:

    import SpriteKit
    
    class BackGround :SKNode {
        //近处背景数组
        var arrBG = [SKSpriteNode]()
        //远处背景数组
        var arrFar = [SKSpriteNode]()
        
        override init() {
            super.init()
            //获取远处背景的纹理
            var farTexture = SKTexture(imageNamed: "background_f1")
            //远处背景由3章无缝图衔接而成
            var farBg0 = SKSpriteNode(texture: farTexture)
            farBg0.anchorPoint = CGPointMake(0, 0)
            farBg0.zPosition = 9
            farBg0.position.y = 150
            
            var farBg1 = SKSpriteNode(texture: farTexture)
            farBg1.anchorPoint = CGPointMake(0, 0)
            farBg1.zPosition = 9
            farBg1.position.x = farBg0.frame.width
            farBg1.position.y = farBg0.position.y
            
            var farBg2 = SKSpriteNode(texture: farTexture)
            farBg2.anchorPoint = CGPointMake(0, 0)
            farBg2.zPosition = 9
            farBg2.position.x = farBg0.frame.width * 2
            farBg2.position.y = farBg0.position.y
            
            self.addChild(farBg0)
            self.addChild(farBg1)
            self.addChild(farBg2)
            arrFar.append(farBg0)
            arrFar.append(farBg1)
            arrFar.append(farBg2)
            
            
            //近处背景纹理
            var texture = SKTexture(imageNamed: "background_f0")
            //近处背景由2章无缝衔接图组成
            var bg0 = SKSpriteNode(texture: texture)
            bg0.anchorPoint = CGPointMake(0, 0)
            var bg1 = SKSpriteNode(texture: texture)
            bg1.anchorPoint = CGPointMake(0, 0)
            bg1.position.x = bg0.frame.width
            bg0.zPosition = 10
            bg1.zPosition = 10
            bg0.position.y = 70
            bg1.position.y = bg0.position.y
            self.addChild(bg0)
            self.addChild(bg1)
            arrBG.append(bg0)
            arrBG.append(bg1)
        }
        
        required init(coder aDecoder: NSCoder) {
            fatalError("init(coder:) has not been implemented")
        }
        //移动方法
        func move(speed:CGFloat){
            //通过遍历获取背景,然后做x方向的改变
            for bg in arrBG {
                bg.position.x -= speed
            }
            //循环滚动算法
            if arrBG[0].position.x + arrBG[0].frame.width < speed {
                arrBG[0].position.x = 0
                arrBG[1].position.x = arrBG[0].frame.width
            }
            //远景同上
            
            for far in arrFar {
                far.position.x -= speed/4
            }
            if arrFar[0].position.x + arrFar[0].frame.width < speed/4 {
                arrFar[0].position.x = 0
                arrFar[1].position.x = arrFar[0].frame.width
                arrFar[2].position.x = arrFar[0].frame.width * 2
            }
        }
    }
    

        和平台工厂类与熊猫类一样,我们需要在GameScene里申明一个bg变量。

    lazy var bg = BackGround()

        然后在didMoveToView方法中加载,要比熊猫panda早一些。

    self.addChild(bg)

        背景的滚动也需要在GameScene的update中实现

    bg.move(moveSpeed/5)

     

        此时GameScene已经发生了很大的改变,让我们看看完整的代码:

    import SpriteKit
    
    class GameScene: SKScene,ProtocolMainScene{
        lazy var panda = Panda()
        lazy var platformFactory = PlatformFactory()
        lazy var bg = BackGround()
        
        //移动速度
        var moveSpeed:CGFloat = 15
        //判断最后一个平台还有多远完全进入游戏场景
        var lastDis:CGFloat = 0.0
        override func didMoveToView(view: SKView) {
            //场景的背景颜色
            let skyColor = SKColor(red:113/255,green:197/255,blue:207/255,alpha:1)
            self.backgroundColor = skyColor
            
            //设置背景
            self.addChild(bg)
            
            
            //给熊猫定一个初始位置
            panda.position = CGPointMake(200, 400)
            //将熊猫显示在场景中
            self.addChild(panda)
            //将平台工厂加入视图
            self.addChild(platformFactory)
            //将屏幕的宽度传到平台工厂类中
            platformFactory.sceneWidth = self.frame.width
            //设置代理
            platformFactory.delegate = self
           
            
        }
        override func touchesBegan(touches: NSSet, withEvent event: UIEvent) {
            //当熊猫状态为跑的时候播放跳的动作
            if panda.status == Status.run {
                panda.jump()
            }else if panda.status == Status.jump {
                //当状态为跳的时候,执行打滚动画
                panda.roll()
            }
            
        }
        override func update(currentTime: CFTimeInterval) {
            lastDis -= moveSpeed
            if lastDis <= 0 {
                println("生成新平台")
                platformFactory.createPlatformRandom()
            }
            platformFactory.move(self.moveSpeed)
            bg.move(moveSpeed/5)
        }
        
        func onGetData(dist:CGFloat){
            self.lastDis = dist
            
        }
    
    }
    

        最后我们运行一下,看看效果。嗯,一切都非常完美。wait,平台好像有点不对劲,跑到背景后面去了。

    好吧,通过检查代码我们发现,背景都设置了zPosition,数值在9,10。这个属性决定了渲染顺序。越小越先渲染,也就是越小越在后面。那么解决这个bug的方法也很简单。只要把平台的在Position设的大一些,例如20,就能解决。

    所以我们切换到Platform类,在onCreate方法的最后一行添加一句:

    self.zPosition = 20

     

        再运行一下,这回正常了。

     

    我的微信公众号

    我写的破书:《Swift语言实战晋级》http://item.jd.com/11641501.html

     

  • 相关阅读:
    C# SQLiteHelper
    C# 自定义等待窗口
    C# Work PPT to PDF
    SQL 分隔字符串
    SQL 客户端查看
    SQL 自定义四舍五入
    SQL 并联更新
    C# 委托简单例子
    每天一个Linux命令(52)telnet命令
    每天一个Linux命令(51)ss命令
  • 原文地址:https://www.cnblogs.com/sandal1980/p/4290026.html
Copyright © 2020-2023  润新知