zoukankan      html  css  js  c++  java
  • swift语言开发的一个游戏------熊猫跑酷(KongfuPanda)

    项目地址:https://github.com/jakciehoo/KongfuPanda

    欢迎加QQ群:260558552。大家一起交流iOS开发,我们可以一起学习,我很想集结一些志同道合的朋友,一起把iOS开发学好,学精,相互学习相互鼓励。

    1.首先创建一个游戏项目:

    2.将图片资源导入

    将我项目里的

    atlas整个目录

    sound组里的音乐(background.mp3,fly.mp3,hit_platform.mp3,apple.mp3,hit.mp3,jump_from_platform.mp3,lose.mp3)

    background组(background_f0.png,background_f1.png)

    Images.xcassets的图片一个一个拷贝到相应的Images.xcassets.

    3.再添加以下几个swift类

    熊猫类

    熊猫是我们游戏的主角,我给它添加了4个动作,跑,跳,翻滚,二次条,为了增加起跳时逼真性还添加了跑动增效动作。

    //
    //  Panda.swift
    //  KongfuPanda
    //
    //  Created by HooJackie on 15/7/1.
    //  Copyright (c) 2015年 jackie. All rights reserved.
    //
    
    import SpriteKit
    enum Status :Int{
        case run = 1, jump, jump2,roll
    }
    
    class Panda:SKSpriteNode {
        //定义跑,跳,滚动等动作动画
        let runAtlas = SKTextureAtlas(named: "run.atlas")
        var runFrames = [SKTexture]()
        let jumpAtlas = SKTextureAtlas(named: "jump.atlas")
        var jumpFrames = [SKTexture]()
        let rollAtlas = SKTextureAtlas(named: "roll.atlas")
        var rollFrames = [SKTexture]()
        //增加跳起的逼真效果动画
        let jumpEffectAtlas = SKTextureAtlas(named: "jump_effect.atlas")
        var jumpEffectFrames = [SKTexture]()
        var jumpEffect = SKSpriteNode()
        
        var status = Status.run
        var jumpStart:CGFloat = 0.0
        var jumpEnd:CGFloat = 0.0
        
        
        init(){
    
            let texture = runAtlas.textureNamed("panda_run_01")
            let size = texture.size()
            super.init(texture: texture, color: SKColor.whiteColor(), size: size)
            //
            for var i = 1; i<=runAtlas.textureNames.count; i++ {
                let tempName = String(format: "panda_run_%.2d", i)
                let runTexture = runAtlas.textureNamed(tempName)
                
                if (runTexture != nil) {
                    runFrames.append(runTexture)
                }
            }
            //
            for var i = 1; i<=jumpAtlas.textureNames.count; i++ {
                let tempName = String(format: "panda_jump_%.2d", i)
                let jumpTexture = jumpAtlas.textureNamed(tempName)
                
                if (jumpTexture != nil) {
                    jumpFrames.append(jumpTexture)
                }
            }
            //
            for var i = 1; i<=rollAtlas.textureNames.count; i++ {
                let tempName = String(format: "panda_roll_%.2d", i)
                let rollTexture = rollAtlas.textureNamed(tempName)
                
                if (rollTexture != nil) {
                    rollFrames.append(rollTexture)
                }
            }
            // 跳的时候的点缀效果
            for var i=1 ; i <= jumpEffectAtlas.textureNames.count ; i++ {
                let tempName = String(format: "jump_effect_%.2d", i)
                let effectexture = jumpEffectAtlas.textureNamed(tempName)
                if (effectexture != nil) {
                    jumpEffectFrames.append(effectexture)
                }
            }
            jumpEffect = SKSpriteNode(texture: jumpEffectFrames[0])
            jumpEffect.position = CGPointMake(-80, -30)
            jumpEffect.hidden = true
            self.addChild(jumpEffect)
            
            self.physicsBody = SKPhysicsBody(rectangleOfSize: size)
            self.physicsBody?.dynamic = true
            self.physicsBody?.allowsRotation = false
            self.physicsBody?.restitution = 0.1 //反弹力
            self.physicsBody?.categoryBitMask = BitMaskType.panda
            self.physicsBody?.contactTestBitMask = BitMaskType.scene | BitMaskType.platform | BitMaskType.apple
            self.physicsBody?.collisionBitMask = BitMaskType.platform
            self.zPosition = 20
            run()
            
        }
    
        required init?(coder aDecoder: NSCoder) {
            fatalError("init(coder:) has not been implemented")
        }
        
        
        func run(){
            //清楚所有动作
            self.removeAllActions()
            self.status = .run
            //重复跑动动作
            self.runAction(SKAction.repeatActionForever(SKAction.animateWithTextures(runFrames, timePerFrame: 0.05)))
        }
        func jump(){
            self.removeAllActions()
            if status != .jump2 {
                //Adds an action to the list of actions executed by the node.
                //Creates an action that animates changes to a sprite’s texture.
                
                self.runAction(SKAction.animateWithTextures(jumpFrames, timePerFrame: 0.05),withKey:"jump")
                //The physics body’s velocity vector, measured in meters per second.
                self.physicsBody?.velocity = CGVectorMake(0, 450)
                if status == Status.jump {
                    self.runAction(SKAction.animateWithTextures(rollFrames, timePerFrame: 0.05))
                    status = Status.jump2
                    self.jumpStart = self.position.y
                }else {
                    showJumpEffect()
                    status = .jump
                }
            }
            
            
        }
        func roll(){
            self.removeAllActions()
            self.status = .roll
            self.runAction(SKAction.animateWithTextures(rollFrames, timePerFrame: 0.05),completion:{
                self.run()
                })
        }
        
        func showJumpEffect(){
            jumpEffect.hidden = false
            var ectAct = SKAction.animateWithTextures( jumpEffectFrames, timePerFrame: 0.05)
            var removeAct = SKAction.runBlock({() in
                self.jumpEffect.hidden = true
            })
            // 执行两个动作,先显示,后隐藏
            jumpEffect.runAction(SKAction.sequence([ectAct,removeAct]))
        }
        
        
        
    }
    Panda.swift

    平台类

    //
    //  Platform.swift
    //  KongfuPanda
    //
    //  Created by HooJackie on 15/7/1.
    //  Copyright (c) 2015年 jackie. All rights reserved.
    //
    
    import SpriteKit
    
    class Platform:SKNode {
        var CGFloat = 0.0
        var height:CGFloat = 10.0
        var isDown = false
        var isShock = false
        //创建平台
        func onCreate(arrSprite:[SKSpriteNode]){
            for platform in arrSprite {
                platform.position.x = self.width
                self.addChild(platform)
                self.width += platform.size.width
            }
            //短到只有三小块的平台会下落
            if arrSprite.count <= 3 {
                isDown = true
            }else {
                //随机振动
                let random = arc4random() % 10
                if random > 6 {
                    isShock = true
                }
            }
             self.height = 10.0
    
            self.physicsBody = SKPhysicsBody(rectangleOfSize: CGSizeMake(self.width, self.height),center:CGPointMake(self.width/2, 0))
            self.physicsBody?.categoryBitMask = BitMaskType.platform
            self.physicsBody?.dynamic = false
            self.physicsBody?.allowsRotation = false
            self.physicsBody?.restitution = 0
            self.zPosition = 20
            
        }
        
    }
    Platform.swift

    平台生成类

    难点1:在理解平台如何生成。可以查看资源图platform_l ,platform_m,platform_r.将他们拼接到一起,根据Platfom_m的数量不同,产生不同的长度的平台。

    难点2:游戏中的熊猫移动我们眼睛看以为是熊猫在跑,其实移动的是平台,通过平台向左移动,给我们的错觉是熊猫在向右跑。

    //
    //  PlatformFactory.swift
    //  KongfuPanda
    //
    //  Created by HooJackie on 15/7/1.
    //  Copyright (c) 2015年 jackie. All rights reserved.
    //
    
    import SpriteKit
    
    class PlatformFactory:SKNode {
        let textureLeft = SKTexture(imageNamed: "platform_l")
        let textureMid = SKTexture(imageNamed: "platform_m")
        let textureRight = SKTexture(imageNamed: "platform_r")
        
        var platforms = [Platform]()
        var screenWdith:CGFloat = 0.0
        var delegate:ProtocolMainscreen?
        
        // 随机生成一定长度的平台
        func createPlatformRandom(){
            let midNum = arc4random()%4 + 1
            let gap:CGFloat = CGFloat(arc4random()%8 + 1)
            let x = self.screenWdith + CGFloat(midNum*50) + gap + 100
            let y = CGFloat(arc4random()%200 + 200)
            
            createPlatform(midNum, x: x, y: y)
        }
        //生成平台方法
        func createPlatform(midNum:UInt32,x:CGFloat,y:CGFloat){
            let platform = Platform()
            let platform_left = SKSpriteNode(texture: textureLeft)
            platform_left.anchorPoint = CGPointMake(0, 0.9)
            
            let platform_right = SKSpriteNode(texture: textureRight)
            platform_right.anchorPoint = CGPointMake(0, 0.9)
            
            var arrPlatform = [SKSpriteNode]()
            
            arrPlatform.append(platform_left)
            platform.position = CGPointMake(x, y)
            
            for i in 1...midNum {
                let platform_mid = SKSpriteNode(texture: textureMid)
                platform_mid.anchorPoint = CGPointMake(0, 0.9)
                arrPlatform.append(platform_mid)
            }
            arrPlatform.append(platform_right)
            platform.onCreate(arrPlatform)
            platform.name = "platform"
            self.addChild(platform)
            
            platforms.append(platform)
            self.delegate?.onGetData(platform.width + x - screenWdith,theY:y)
            
        }
        //平台向左移动的方法
        func move(speed:CGFloat){
            for p in platforms {
                let position = p.position
                p.position = CGPointMake(position.x - speed, position.y)
            }
            if platforms[0].position.x < -platforms[0].width{
                platforms[0].removeFromParent()
                platforms.removeAtIndex(0)
            }
        }
        //清楚所有的Node
        func reset(){
            
            self.removeAllChildren()
            platforms.removeAll(keepCapacity: false)
        }
    }
    PlatformFactory.swift

    背景音乐类

      生成各种音效。

    //
    //  BackGround.swift
    //场景的动态背景图类
    //  KongfuPanda
    //
    //  Created by HooJackie on 15/7/1.
    //  Copyright (c) 2015年 jackie. All rights reserved.
    //
    
    import SpriteKit
    
    class Background:SKNode {
        //近处的背景
        var arrBG = [SKSpriteNode]()
        //远处的背景
        var arrFar = [SKSpriteNode]()
        
        override init() {
            super.init()
            var farTexture = SKTexture(imageNamed: "background_f1")
            var farBg0 = SKSpriteNode(texture: farTexture)
            farBg0.position.y = 150
            farBg0.zPosition = 9
            farBg0.anchorPoint = CGPointMake(0, 0)
            
    
            var farBg1 = SKSpriteNode(texture: farTexture)
            farBg1.position.y = 150
            farBg1.zPosition = 9
            farBg1.anchorPoint = CGPointMake(0, 0)
            farBg1.position.x = farBg1.frame.width
            
            var farBg2 = SKSpriteNode(texture: farTexture)
            farBg2.position.y = 150
            farBg2.zPosition = 9
            farBg2.anchorPoint = CGPointMake(0, 0)
            farBg2.position.x = farBg2.frame.width*2
            
            self.addChild(farBg0)
            self.addChild(farBg1)
            self.addChild(farBg2)
            arrFar.append(farBg0)
             arrFar.append(farBg1)
             arrFar.append(farBg2)
            
            var texture = SKTexture(imageNamed: "background_f0")
            var bg0 = SKSpriteNode(texture: texture)
            bg0.anchorPoint = CGPointMake(0, 0)
            bg0.position.y = 70
            bg0.zPosition = 10
            
            var bg1 = SKSpriteNode(texture: texture)
            bg1.anchorPoint = CGPointMake(0, 0)
            bg1.position.y = 70
            bg1.zPosition = 10
            bg1.position.x = bg0.frame.size.width
            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){
            //近景
            for bg in arrBG {
                bg.position.x -= speed
            }
            if arrBG[0].position.x + arrBG[0].frame.size.width < speed {
                arrBG[0].position.x = 0
                arrBG[1].position.x = arrBG[0].frame.size.width
            }
            //远景
            for far in arrFar {
                far.position.x -= speed/4
                
            }
            if arrFar[0].position.x + arrFar[0].frame.size.width < speed/4 {
                arrFar[0].position.x = 0
                arrFar[1].position.x = arrFar[0].frame.size.width
                arrFar[2].position.x = arrFar[0].frame.size.width * 2
            }
            
        }
        
    }
    BackGround.swift

    位运算标识类

    //
    //  BitMaskType.swift
    //  KongfuPanda
    //
    //  Created by HooJackie on 15/7/1.
    //  Copyright (c) 2015年 jackie. All rights reserved.
    //
    
    
    class BitMaskType {
        class var panda:UInt32 {
            return 1<<0
        }
        class var platform:UInt32 {
            return 1<<1
        }
        class var apple:UInt32 {
            return 1<<2
        }
        class var scene:UInt32{
            return 1<<3
        }
    }
    BitMaskType.swift

    苹果生成类

    import SpriteKit
    
    class AppleFactory:SKNode{
        let appleTexture = SKTexture(imageNamed: "apple")
        var sceneWidth:CGFloat = 0.0
        var arrApple = [SKSpriteNode]()
        var timer = NSTimer()
        var theY:CGFloat = 0.0
        
        override init() {
            super.init()
        }
        
        required init?(coder aDecoder: NSCoder) {
            fatalError("init(coder:) has not been implemented")
        }
        func onInit(CGFloat, y:CGFloat) {
            
            self.sceneWidth = width
            self.theY = y
            timer = NSTimer.scheduledTimerWithTimeInterval( 0.2, target: self, selector: "createApple", userInfo: nil, repeats: true)
        }
        func createApple(){
            var random = arc4random() % 10
            if random > 8 {
                var apple = SKSpriteNode(texture: appleTexture)
                apple.physicsBody = SKPhysicsBody(rectangleOfSize: apple.size)
                apple.physicsBody!.restitution = 0
                apple.physicsBody!.categoryBitMask = BitMaskType.apple
                apple.physicsBody!.dynamic = false
                apple.anchorPoint = CGPointMake(0, 0)
                apple.zPosition = 40
                apple.position  = CGPointMake(sceneWidth+apple.frame.width , theY + 150)
                arrApple.append(apple)
                self.addChild(apple)
            }
            
        }
        func move(speed:CGFloat){
            for apple in arrApple {
                apple.position.x -= speed
            }
            if arrApple.count > 0 && arrApple[0].position.x < -20{
                
                arrApple[0].removeFromParent()
                arrApple.removeAtIndex(0)
                
            }
            
        }
        func reSet(){
            self.removeAllChildren()
            arrApple.removeAll(keepCapacity: false)
        }
    }
    AppleFactory

    游戏主界面类

    import SpriteKit
    
    class GameScene: SKScene,SKPhysicsContactDelegate , ProtocolMainscreen{
        lazy var panda  = Panda()
        lazy var platformFactory = PlatformFactory()
        lazy var sound = SoundManager()
        lazy var bg = Background()
        lazy var appleFactory = AppleFactory()
        let scoreLab = SKLabelNode(fontNamed:"Chalkduster")
        let appLab = SKLabelNode(fontNamed:"Chalkduster")
        let myLabel = SKLabelNode(fontNamed:"Chalkduster")
        var appleNum = 0
        
        
        var moveSpeed :CGFloat = 15.0
        var maxSpeed :CGFloat = 50.0
        var distance:CGFloat = 0.0
        var lastDis:CGFloat = 0.0
        var theY:CGFloat = 0.0
        var isLose = false
        override func didMoveToView(view: SKView) {
            
            let skyColor = SKColor(red:113.0/255.0, green:197.0/255.0, blue:207.0/255.0, alpha:1.0)
            self.backgroundColor = skyColor
            scoreLab.horizontalAlignmentMode = SKLabelHorizontalAlignmentMode.Left
            scoreLab.position = CGPointMake(20, self.frame.size.height-150)
            scoreLab.text = "run: 0 km"
            self.addChild(scoreLab)
            
            appLab.horizontalAlignmentMode = SKLabelHorizontalAlignmentMode.Left
            appLab.position = CGPointMake(400, self.frame.size.height-150)
            appLab.text = "eat: (appleNum) apple"
            self.addChild(appLab)
            
            myLabel.text = "";
            myLabel.fontSize = 65;
            myLabel.zPosition = 100
            myLabel.position = CGPoint(x:CGRectGetMidX(self.frame), y:CGRectGetMidY(self.frame));
            self.addChild(myLabel)
            
            self.physicsWorld.contactDelegate = self
            self.physicsWorld.gravity = CGVectorMake(0, -5)
            self.physicsBody = SKPhysicsBody(edgeLoopFromRect: self.frame)
            self.physicsBody!.categoryBitMask = BitMaskType.scene
            self.physicsBody!.dynamic = false
            
            panda.position = CGPointMake(200, 400)
            self.addChild(panda)
            self.addChild(platformFactory)
            platformFactory.screenWdith = self.frame.width
            platformFactory.delegate = self
            platformFactory.createPlatform(3, x: 0, y: 200)
            
            self.addChild(bg)
            
            self.addChild(sound)
            sound.playBackgroundMusic()
            
            appleFactory.onInit(self.frame.width, y: theY)
            self.addChild( appleFactory )
            
        }
        func didBeginContact(contact: SKPhysicsContact){
            
            //熊猫和苹果碰撞
            if (contact.bodyA.categoryBitMask | contact.bodyB.categoryBitMask) == (BitMaskType.apple | BitMaskType.panda){
                sound.playEat()
                self.appleNum++
                if contact.bodyA.categoryBitMask == BitMaskType.apple {
                    contact.bodyA.node!.hidden = true
                }else{
                    contact.bodyB.node!.hidden = true
                }
                
                
            }
            
            //熊猫和台子碰撞
            if (contact.bodyA.categoryBitMask | contact.bodyB.categoryBitMask) == (BitMaskType.platform | BitMaskType.panda){
                var isDown = false
                var canRun = false
                if contact.bodyA.categoryBitMask == BitMaskType.platform {
                    if (contact.bodyA.node as! Platform).isDown {
                        isDown = true
                        contact.bodyA.node!.physicsBody!.dynamic = true
                        contact.bodyA.node!.physicsBody!.collisionBitMask = 0
                    }else if (contact.bodyA.node as! Platform).isShock {
                        (contact.bodyA.node as! Platform).isShock = false
                        downAndUp(contact.bodyA.node!, down: -50, downTime: 0.2, up: 100, upTime: 1, isRepeat: true)
                    }
                    if contact.bodyB.node!.position.y > contact.bodyA.node!.position.y {
                        canRun=true
                    }
                    
                }else if contact.bodyB.categoryBitMask == BitMaskType.platform  {
                    if (contact.bodyB.node as! Platform).isDown {
                        contact.bodyB.node!.physicsBody!.dynamic = true
                        contact.bodyB.node!.physicsBody!.collisionBitMask = 0
                        isDown = true
                    }else if (contact.bodyB.node as! Platform).isShock {
                        (contact.bodyB.node as! Platform).isShock = false
                        downAndUp(contact.bodyB.node!, down: -50, downTime: 0.2, up: 100, upTime: 1, isRepeat: true)
                    }
                    if contact.bodyA.node!.position.y > contact.bodyB.node!.position.y {
                        canRun=true
                    }
                    
                }
                
                panda.jumpEnd = panda.position.y
                if panda.jumpEnd-panda.jumpStart <= -70 {
                    panda.roll()
                    sound.playRoll()
                    
                    if !isDown {
                        downAndUp(contact.bodyA.node!)
                        downAndUp(contact.bodyB.node!)
                    }
                    
                }else{
                    if canRun {
                        panda.run()
                    }
                    
                }
            }
            
            if (contact.bodyA.categoryBitMask | contact.bodyB.categoryBitMask) == (BitMaskType.scene | BitMaskType.panda) {
                println("game over")
                myLabel.text = "game over";
                sound.playDead()
                isLose = true
                sound.stopBackgroundMusic()
                
    
            }
            
            //落地后jumpstart数据要设为当前位置,防止自由落地计算出错
            panda.jumpStart = panda.position.y
        }
        func didEndContact(contact: SKPhysicsContact){
            panda.jumpStart = panda.position.y
            
        }
        func downAndUp(node :SKNode,down:CGFloat = -50,downTime:CGFloat=0.05,up:CGFloat=50,upTime:CGFloat=0.1,isRepeat:Bool=false){
            let downAct = SKAction.moveByX(0, y: down, duration: Double(downTime))
            //moveByX(CGFloat(0), y: down, duration: downTime)
            let upAct = SKAction.moveByX(0, y: up, duration: Double(upTime))
            let downUpAct = SKAction.sequence([downAct,upAct])
            if isRepeat {
                node.runAction(SKAction.repeatActionForever(downUpAct))
            }else {
                node.runAction(downUpAct)
            }
            
            
        }
        
        
        override func touchesBegan(touches: Set<NSObject>, withEvent event: UIEvent) {
            if isLose {
                reSet()
            }else{
                if panda.status != Status.jump2 {
                    sound.playJump()
                }
                panda.jump()
            }
            
            
        }
        //重新开始游戏
        func reSet(){
            isLose = false
            panda.position = CGPointMake(200, 400)
            myLabel.text = ""
            moveSpeed  = 15.0
            distance = 0.0
            lastDis = 0.0
            self.appleNum = 0
            platformFactory.reset()
            appleFactory.reSet()
            platformFactory.createPlatform(3, x: 0, y: 200)
            sound.playBackgroundMusic()
        }
        override func update(currentTime: CFTimeInterval) {
            if isLose {
                
            }else{
                if panda.position.x < 200 {
                    var x = panda.position.x + 1
                    panda.position = CGPointMake(x, panda.position.y)
                }
                distance += moveSpeed
                lastDis -= moveSpeed
                var tempSpeed = CGFloat(5 + Int(distance/2000))
                if tempSpeed > maxSpeed {
                    tempSpeed = maxSpeed
                }
                if moveSpeed < tempSpeed {
                    moveSpeed = tempSpeed
                }
                
                if lastDis < 0 {
                    platformFactory.createPlatformRandom()
                }
                distance += moveSpeed
                scoreLab.text = "run: (Int(distance/1000*10)/10) km"
                appLab.text = "eat: (appleNum) apple"
                platformFactory.move(moveSpeed)
                bg.move(moveSpeed/5)
                appleFactory.move(moveSpeed)
            }
            
        }
        
        func onGetData(dist:CGFloat,theY:CGFloat){
            
            self.lastDis = dist
            self.theY = theY
            appleFactory.theY = theY
        }
        
    }
    
    protocol ProtocolMainscreen {
        func onGetData(dist:CGFloat,theY:CGFloat)
    }
    GameScene.swift

    4.最后的游戏的效果图:

  • 相关阅读:
    Bootstrap 2.2.2 的新特性
    Apache POI 3.9 发布,性能显著提升
    SQL Relay 0.48 发布,数据库中继器
    ProjectForge 4.2.0 发布,项目管理系统
    红帽企业 Linux 发布 6.4 Beta 版本
    红薯 快速的 MySQL 本地和远程密码破解
    MariaDB 宣布成立基金会
    Percona XtraBackup 2.0.4 发布
    Rocks 6.1 发布,光盘机群解决方案
    精通Servlet研究,HttpServlet的实现追究
  • 原文地址:https://www.cnblogs.com/JackieHoo/p/4619759.html
Copyright © 2011-2022 走看看