Cocos Creator經典游戲制作之:信使(The Messenger)


版權申明:

  • 本文原創首發於以下網站:
  1. 博客園『優夢創客』的空間:https://www.cnblogs.com/raymondking123
  2. 優夢創客的官方博客:https://91make.top
  3. 優夢創客的游戲講堂:https://91make.ke.qq.com
  4. 『優夢創客』的微信公眾號:umaketop
  • 您可以自由轉載,但必須加入完整的版權聲明!

游戲原圖


游戲介紹

  • 《信使(The Messenger)》是一款橫版過關冒險游戲,此游戲在IGN上獲得了8.0分的不錯成績,IGN編輯MITCHELL SALTZMAN認為《信使》擁有很多想帶給玩家的內容。它是一款2D動作平台游戲,擁有華麗的美學,從8位風格無縫過渡到16位,游戲擁有一些搞笑的幽默場景,巧妙的對白,以及一個最佳的復古"芯片風”音樂。
  • 《信使(The Messenger)》無論從哪一方面來看都是受到了FC時代《忍者龍劍傳》的影響,這款游戲的發行商Devolver Digital還邀請到了FC《忍者龍劍傳》初代的制作人吉沢秀雄進行體驗。

游戲規則介紹

  • 游戲的操作與FC上的忍龍基本一致,左右移動、跳、落(從平台上落下)、攻擊

游戲開發過程

搭建游戲UI界面

  • 在assets上創建一個文件夾名為Scence,這個文件夾用來存放場景,新建一個場景Scence改名為Game
  • 在Canvas上建一個Background節點,在這個節點上插入Sprite(圖片精靈)組件,把背景圖片拖進去,調整大小,這個就是游戲的背景
  • 在Canvas上新建節點Ground、Platform、Wall、Pitfall、Save、Lame,分別用來放置地面、平台、牆、陷阱、保存點、燈

創建主角

  • 將一張忍者的圖片拉至Canvas節點下,並重命名為Player

制作動畫特效

  • 在資源管理器的assets文件夾下創建一個子文件夾Animation,用來存放各種動畫
  • 選擇Player,在動畫編輯器中點擊按鈕《添加Animation組件》為Player節點添加動畫組件
  • 在點擊按鈕《新建Clip文件》為Player創建動畫
  • 單擊動畫編輯器左上角的書寫圖標按鈕開始編輯動畫
  • 將主角的往右跑的4張圖片組合成主角右跑動畫(序列幀動畫)
  • 再創建主角左跑動畫、攻擊動畫、爬牆動畫······

寫腳本

Player腳本

創建Player腳本
  • 在資源管理器的assets文件夾下創建一個子文件夾Script,用來存放各種腳本
  • 在資源管理器的Script文件夾下創建一個JavaScript腳本,重命名為Player
  • 將腳本Player掛在Player對象上
編輯Player腳本
定義玩家屬性
properties: {
    camera: { //攝像機
        type: cc.Node, //屬性類型
        default: null, //默認值
    },
    jumpHeight: 200, //跳躍高度
    playerState: 2, //玩家狀態 0站立 1跳 2落 3爬牆
    isRun: false,  //是否跑動
    playerDirection: 1, //玩家方向 0左 1右     
    mayJump: true, //能否跳躍
    speed: 0, //速度
    hp: 5, //生命值
    time: 0, //時間
    shinState: 0, //攀爬狀態 0為不動 1為上爬 2為下滑     
    isAttack: false, //是攻擊狀態
    whetherSpringback: true, //是否回彈
},
給玩家創建各種能力(函數)
  • 攻擊
attack() {
    this.isAttack = true; //是攻擊狀態 
    if (this.playerDirection == 0) { //如果玩家方向為左
        this.getComponent(cc.Animation).play("主角左攻動畫");
    } else { //否則
        this.getComponent(cc.Animation).play("主角右攻動畫");
    }
    this.node.getComponent(cc.BoxCollider).size = cc.size(140, 87); //獲取主角的碰撞器,並設置包圍盒大小(擴大包圍盒,因為攻擊時人物的包圍盒要把刀包圍進去)
},
  • 左移
leftMove() {
    this.node.x -= 10
},
  • 右移
rightMove() {
    this.node.x += 10;
},
  • 攀爬,觸碰牆壁進入爬牆狀態后可使用爬牆方法
shin() {
    if (this.shinState == 1) { //如果人物攀爬狀態為上爬
        this.node.y += 3;
    } else if (this.shinState == 2) { //否則,就是人物狀態為下滑
        this.node.y -= 9;
    }
},
  • 跳躍,在平台、地面、空中時可執行
jumpAction: null,
jump() {
    this.jumpAction = cc.moveBy(0.5, cc.v2(0, this.jumpHeight)).easing(cc.easeCubicActionOut());
    this.node.runAction(this.jumpAction);
},
  • 爬牆跳,此方法只有攀爬在牆壁上時可使用
jumpDistance: null,
wallJump: null,
WallJump() {
    if (this.playerDirection == 0) {
        this.jumpDistance = 200;
    } else {
        this.jumpDistance = -200;
    }
    this.wallJump = cc.moveBy(0.5, cc.v2(this.jumpDistance, this.jumpHeight)).easing(cc.easeCubicActionOut());
    this.node.runAction(this.wallJump);
},
  • 翻跳上地面,當與牆壁結束碰撞時執行,用於結束攀爬進入站立狀態
WallGroundJump() {
    if (this.playerDirection == 0) {
        this.jumpDistance = -100;
        this.getComponent(cc.Animation).play("主角左跳動畫");
    } else {
        this.jumpDistance = 100;
        this.getComponent(cc.Animation).play("主角右跳動畫");
    }
    var WallJump = cc.moveBy(0.25, cc.v2(this.jumpDistance, this.jumpHeight / 2));
    this.node.runAction(WallJump);
},
  • 落下,這是一個被動方法,只要玩家在空中且沒有上升力,此方法就一直執行
drop(speed) {
    this.node.y += speed;
},
主角的碰撞處理
  • 碰撞開始
onCollisionEnter: function (other, self) { //處理碰撞的方法
    if (other.node.group == "Ground" || other.node.group == "Platform") { //如果玩家碰到的是節點分組中的地面,或平台
        this.otherObject = other; //獲取另一個對象(玩家碰撞的對象)
        if (this.speed < -30) { //如果速度過快
            this.hp = 0; //摔死
        }
        if (self.node.getComponent("Player").playerState == 2 && other.node.y < self.node.y) { //如果玩家狀態為下落
            self.node.getComponent("Player").playerState = 0; //玩家狀態變為站立
            self.node.y = other.node.y + other.node.height / 2 + 42; //回彈,防止人物腳陷入地面
            if (this.isRun) { //如果是跑動狀態
                if (this.playerDirection == 0) { //如果玩家方向為左
                    this.getComponent(cc.Animation).play("主角左跑動畫");
                } else { //否則,就是玩家方向為右
                    this.getComponent(cc.Animation).play("主角右跑動畫");
                }
            } else { //否則就是玩家不是跑動狀態
                if (this.playerDirection == 0) { //如果玩家方向為左
                    this.getComponent(cc.Animation).play("主角左立動畫");
                } else { //否則,就是玩家方向為右
                    this.getComponent(cc.Animation).play("主角右立動畫");
                }
            }
        } else if (self.node.getComponent("Player").shinState == 2) {
            self.node.getComponent("Player").playerState = 0; //玩家狀態變為站立
            if (self.node.getComponent("Player").playerDirection == 0) {
                self.node.x += 20;
                this.getComponent(cc.Animation).play("主角左立動畫");
            } else {
                self.node.x -= 20;
                this.getComponent(cc.Animation).play("主角右立動畫");
            }
        }
    } else if (other.node.group == "Wall") { //如果玩家碰到的是節點分組中的牆壁
        if (this.isAttack) { //如果是攻擊狀態
            this.isRun = false; //停止跑動
            if (this.playerDirection == 0) { //如果人物方向為左
                this.node.x += 50; //攻擊到牆壁往右回彈
            } else { //否則(人物方向為右)
                this.node.x -= 50; //攻擊到牆壁往左回彈
            }
        } else {
            if (self.node.getComponent("Player").playerState == 0) {
                self.node.y += 20;
            }
            this.node.stopAction(this.wallJump);
            self.node.getComponent("Player").playerState = 3; //玩家狀態變為爬牆
            this.isRun = false;
            this.shinState = 0; //攀爬狀態為不動
            if (this.playerDirection == 0) { //如果人物方向為左
                self.node.x = other.node.x + other.node.width / 2 + 25; //回彈,防止人物陷入牆壁
                this.getComponent(cc.Animation).play("主角左貼動畫");
            } else { //否則,就是人物方向為右
                self.node.x = other.node.x - other.node.width / 2 - 25; //回彈,防止人物陷入牆壁
                this.getComponent(cc.Animation).play("主角右貼動畫");
            }
        }
    } else if (other.node.group == "Pitfall") { //如果玩家碰到的是節點分組中的陷阱
        this.hp = 0;
        this.time = 0;
    } else if (other.node.group == "Save") { //如果玩家碰到的是節點分組中的保存點
        this.playerX = this.node.x;
        this.playerY = this.node.y;
    }
},
  • 碰撞過程中
onCollisionStay: function (other, self) {
    if (other.node.group == "Lamp") {
        if (this.isAttack) {
            this.mayJump = true;
            this.node.getComponent(cc.BoxCollider).size = cc.size(55, 87); //獲取主角的碰撞器,並設置包圍盒大小(縮小包圍盒,因為攻擊到燈后結束攻擊人物收刀后碰撞范圍變小)
            this.isAttack = false;
            this.time = 0;
        }
    }
},
  • 碰撞結束時
onCollisionExit: function (other, self) {
    if (this.hp > 0) {
        if (other.node.group == "Ground" || other.node.group == "Platform") { //如果玩家與地面或平台碰撞結束
            if (self.node.getComponent("Player").playerState == 0) { //如果玩家狀態為站立
                self.node.getComponent("Player").playerState = 2; //玩家狀態變為下落
                if (this.playerDirection == 0) { //如果玩家方向為左
                    this.getComponent(cc.Animation).play("主角左落動畫");
                } else { //否則,就是玩家方向為右
                    this.getComponent(cc.Animation).play("主角右落動畫");
                }
            }
        } else if (other.node.group == "Wall" && self.node.getComponent("Player").playerState == 3) {
            this.playerState = 1; //玩家狀態設為跳
            this.WallGroundJump(); //玩家執行翻跳上地面
            this.speed = 100; //剛起跳時速度快
            this.mayJump = false; //能跳設為false
        }
    }
},
在update中刷新玩家
update(dt) {
    if (this.hp == 0) { //如果玩家生命值為0,就是死了     
        if (this.time == 0) {
            this.getComponent(cc.Animation).play("主角爆炸動畫");
        }
        if (this.time < 0.4) {
            this.time += dt;
        } else if (this.time >= 0.4) {
            this.node.x = this.playerX;
            this.node.y = this.playerY + 30;
            this.speed = 0;
            this.playerState = 2;
            this.time = 0;
            this.isRun = false;
            this.hp = 5;
        }
    } else { //否則,就是活着
        //this.camera.x = this.node.x; 
        //if (this.camera.x > this.node.x + 100) {
            this.camera.x = this.node.x //+ 100;
        //} else if (this.camera.x < this.node.x - 100) {
            this.camera.x = this.node.x //- 100;
        //}
        if (this.camera.y > this.node.y + 100) {
            this.camera.y = this.node.y + 100;
        } else if (this.camera.y < this.node.y - 100) {
            this.camera.y = this.node.y - 100;
        if (this.isAttack) { //是否是攻擊狀態
            this.time += dt; //計時
            if (this.time > 0.3) { //當時間大於0.3秒
                this.node.getComponent(cc.BoxCollider).size = cc.size(55, 87); //獲取主角的碰撞器,並設置包圍盒大小(縮小包圍盒,因為攻擊結束人物收刀后碰撞范圍變小)
                this.isAttack = false; //將攻擊狀態變為false
                this.time = 0; //時間歸零
                if (this.playerState == 0) { //如果玩家是站立狀態
                    if (this.isRun) { //如果玩家是跑動的的
                        if (this.playerDirection == 0) { //如果玩家方向為左
                            this.getComponent(cc.Animation).play("主角左跑動畫");
                        } else { //否則(就是玩家方向為右)
                            this.getComponent(cc.Animation).play("主角右跑動畫");
                        }
                    } else { //否則(就是站在不動)
                        if (this.playerDirection == 0) { //如果玩家方向為左
                            this.getComponent(cc.Animation).play("主角左立動畫");
                        } else { //否則(就是玩家方向為右)
                            this.getComponent(cc.Animation).play("主角右立動畫");
                        }
                    }
                } else { //否則(就是在空中)
                    if (this.playerDirection == 0) { //如果玩家方向為左
                        this.getComponent(cc.Animation).play("主角左落動畫");
                    } else { //否則(就是玩家方向為右)
                        this.getComponent(cc.Animation).play("主角右落動畫");
                    }
                }
            }
        if (this.isRun) { //如果能跑動
            if (this.playerDirection == 0) { //如果玩家方向是左
                this.leftMove(); //向左跑
            } else { //否則
                this.rightMove(); //向右跑
            }
        if (this.playerState == 0) { //如果玩家狀態為站立
            this.speed = 0; //速度歸零
            this.mayJump = true; //能跳躍
        } else if (this.playerState == 1) { //如果玩家狀態為跳
            this.speed -= dt * 400; //速度越來越慢
            if (this.speed <= 0) { //如果速度減少到小於等於0
                this.speed = 0; //速度歸零
                this.node.stopAction(this.jumpAction); //停止跳
                this.playerState = 2; //玩家狀態變為下落
            }
        } else if (this.playerState == 2) { //如果玩家狀態為下落
            this.speed -= dt * 30; //下落狀態下速度隨時間變得越來越快
            this.drop(this.speed); //執行下落
        } else if (this.playerState == 3) { //如果玩家狀態為爬牆
            this.speed = 0; //速度歸零
            this.mayJump = true; //能跳躍
            this.shin(); //攀爬
        }
    }
},

Game腳本

創建Game腳本
  • 在資源管理器的Script文件夾下創建一個JavaScript腳本,重命名為Game
  • 將腳本Game掛在Canvas上
編輯Game腳本
在onLoad中開啟碰撞監聽、監聽系統事件
onLoad() {
    var manager = cc.director.getCollisionManager(); //獲取碰撞組件
    manager.enabled = true; //開啟碰撞監聽
    //manager.enabledDebugDraw = true; //開啟碰撞組件Debug
    this.playerJS = this.Player.getComponent("Player"), //獲取玩家腳本
    //系統事件,當鍵被按下時調用keyDown回調函數處理
    cc.systemEvent.on(cc.SystemEvent.EventType.KEY_DOWN, this.keyDown, this);
    //系統事件,當鍵彈起時調用keyUp回調函數處理
    cc.systemEvent.on(cc.SystemEvent.EventType.KEY_UP, this.keyUp, this);
},
創建keyDown於keyUp方法用來控制玩家
  • keyDown方法
playerJS: null, //玩家腳本
keyDown(event) {
    if (this.playerJS.hp > 0) { //如果玩家hp大於0,就是玩家活着
        switch (event.keyCode) {
            case cc.macro.KEY.a:
                if (this.playerJS.isRun == false) { //如果能跑動是錯誤的
                    if (this.playerJS.playerState != 3) { //如果玩家不為爬牆
                        this.playerJS.isRun = true; //能跑動設為true
                        this.playerJS.playerDirection = 0; //玩家方向設為左
                        if (this.playerJS.playerState == 0) { //如果玩家狀態為站立
                            this.Player.getComponent(cc.Animation).play("主角左跑動畫");
                        }
                    } else if (this.playerJS.playerDirection == 1) {
                        this.playerJS.playerState = 1; //玩家狀態設為跳
                        this.playerJS.WallJump(); //玩家執行爬牆跳
                        this.playerJS.playerDirection = 0; //玩家方向設為左
                        this.playerJS.speed = 200; //剛起跳時速度快
                        this.playerJS.mayJump = false; //能跳設為false
                        this.Player.getComponent(cc.Animation).play("主角左跳動畫");
                    }
                }
                break;
            case cc.macro.KEY.d:
                if (this.playerJS.isRun == false) { //如果能跑動是錯誤的
                    if (this.playerJS.playerState != 3) { //如果玩家不為爬牆
                        this.playerJS.isRun = true; //能跑動設為true
                        this.playerJS.playerDirection = 1; //玩家方向設為右
                        if (this.playerJS.playerState == 0) { //如果玩家狀態為站立
                            this.Player.getComponent(cc.Animation).play("主角右跑動畫");
                        }
                    } else if (this.playerJS.playerDirection == 0) {
                        this.playerJS.playerState = 1; //玩家狀態設為跳
                        this.playerJS.WallJump(); //玩家執行爬牆跳
                        this.playerJS.playerDirection = 1; //玩家方向設為右
                        this.playerJS.speed = 200; //剛起跳時速度快
                        this.playerJS.mayJump = false; //能跳設為false
                        this.Player.getComponent(cc.Animation).play("主角右跳動畫");
                    }
                }
                break;
            case cc.macro.KEY.w:
                if (this.playerJS.playerState != 3) { //如果玩家狀態不為爬牆
                    if (this.playerJS.mayJump) { //如果能跳                   
                        this.playerJS.playerState = 1; //玩家狀態設為跳
                        this.playerJS.jump(); //玩家執行跳
                        this.playerJS.speed = 200; //剛起跳時速度快
                        this.playerJS.mayJump = false; //能跳設為false
                        if (this.playerJS.playerDirection == 0) { //如果玩家方向為左
                            this.Player.getComponent(cc.Animation).play("主角左跳動畫");
                        } else { //否則,就是玩家方向為右
                            this.Player.getComponent(cc.Animation).play("主角右跳動畫");
                        }
                    }
                } else { //否則,就是玩家為爬牆狀態
                    this.playerJS.shinState = 1; //攀爬狀態設為上爬
                    if (this.playerJS.playerDirection == 0) { //如果玩家方向為左
                        this.Player.getComponent(cc.Animation).play("主角左爬動畫");
                    } else { //否則,就是玩家方向為右
                        this.Player.getComponent(cc.Animation).play("主角右爬動畫");
                    }
                }
                break;
            case cc.macro.KEY.s:
                if (this.playerJS.playerState == 0) { //如果玩家狀態為站立
                    if (this.playerJS.otherObject.node.group == "Ground") { //如果玩家依附在地面上
                        //蹲下
                    } else if (this.playerJS.otherObject.node.group == "Platform") { //如果玩家依附在平台上
                        //this.Player.y -= 30; //玩家y坐標減,為了快速從平台上落下(此語句可有可無)
                        this.playerJS.playerState = 2; //玩家狀態變為下落狀態
                        if (this.playerJS.playerDirection == 0) { //如果玩家方向為左
                            this.Player.getComponent(cc.Animation).play("主角左落動畫");
                        } else { //否則,就是玩家方向為右
                            this.Player.getComponent(cc.Animation).play("主角右落動畫");
                        }
                    }
                } else if (this.playerJS.playerState == 3) { //如果玩家狀態為爬牆
                    this.playerJS.shinState = 2 //攀爬狀態設為下滑
                    if (this.playerJS.playerDirection == 0) { //如果玩家方向為左
                        this.Player.getComponent(cc.Animation).play("主角左爬動畫");
                    } else { //否則,就是玩家方向為右
                        this.Player.getComponent(cc.Animation).play("主角右爬動畫");
                    }
                }
                break;
            case cc.macro.KEY.j:
                if (this.playerJS.isAttack == false) { //如果玩家攻擊狀態為false(已經是攻擊狀態不能再攻擊,必須等攻擊結束才能再次攻擊)
                    if (this.playerJS.playerState != 3) //如果玩家不為爬牆狀態(不能在爬牆時攻擊)
                    {
                        this.playerJS.attack(); //玩家執行攻擊
                    }
                }
                break;
        }
    }
},
  • keyUp方法
keyUp(event) {
    if (this.playerJS.hp > 0) { //如果玩家hp大於0,就是玩家活着
        switch (event.keyCode) {
            case cc.macro.KEY.a:
                if (this.playerJS.isRun && this.playerJS.playerDirection == 0) { //如果在跑動,並且玩家方向朝左
                    this.playerJS.isRun = false; //能跑動設為false
                    if (this.playerJS.playerState == 0) { //如果玩家狀態為站立
                        this.Player.getComponent(cc.Animation).play("主角左立動畫");
                    }
                }
                break;
            case cc.macro.KEY.d:
                if (this.playerJS.isRun && this.playerJS.playerDirection == 1) { //如果在跑動,並且玩家方向朝右
                    this.playerJS.isRun = false; //能跑動設為false
                    if (this.playerJS.playerState == 0) { //如果玩家狀態為站立
                        this.Player.getComponent(cc.Animation).play("主角右立動畫");
                    }
                }
                break;
            case cc.macro.KEY.w:
                if (this.playerJS.playerState != 3) { //如果玩家狀態不為爬牆
                    if (this.playerJS.playerState == 1) { //如果玩家狀態為跳,並且速度大於100
                        if (this.playerJS.speed > 100) {
                            this.playerJS.speed = 50; //玩家速度變慢
                        }
                    }
                } else { //否則,就是玩家為爬牆狀態
                    this.playerJS.shinState = 0; //攀爬狀態設為不動
                    if (this.playerJS.playerDirection == 0) { //如果玩家方向為左
                        this.Player.getComponent(cc.Animation).play("主角左貼動畫");
                    } else { //否則,就是玩家方向為右
                        this.Player.getComponent(cc.Animation).play("主角右貼動畫");
                    }
                }
                break;
            case cc.macro.KEY.s:
                if (this.playerJS.playerState == 3) {
                    this.playerJS.shinState = 0; //攀爬狀態設為不動
                    if (this.playerJS.playerDirection == 0) { //如果玩家方向為左
                        this.Player.getComponent(cc.Animation).play("主角左貼動畫");
                    } else { //否則,就是玩家方向為右
                        this.Player.getComponent(cc.Animation).play("主角右貼動畫");
                    }
                }
                break;
        }
    }
},

項目鏈接

https://github.com/VRVVR/ccz.git


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM