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.最后的游戏的效果图:

  • 相关阅读:
    shift
    start
    exit
    call
    goto
    Activity生命周期(二)
    color 和 mode
    pause 和 title
    day 4 飞机大战-面向对象
    day 3 创建窗口,移动-函数版
  • 原文地址:https://www.cnblogs.com/JackieHoo/p/4619759.html
Copyright © 2011-2022 走看看