用 Cocos Creator 制作平台跳跃游戏

前言

平台跳跃类游戏如《超级马里奥》《Celeste蔚蓝》等,非常考验玩家的操作和判断,有着非常本真的游戏乐趣。这类游戏乍一看,挺容易做的,但是要做好却不太容易。今天,我将使用 Cocos Creator v2.1.2 演示如何灵活快速地使用 Cocos Creator 来制作这类经典的横版平台跳跃类游戏,主要目的是帮助大家熟悉组件的用法,横版游戏实现方法很多,这里不做讨论!

 

开始游戏制作

 

游戏效果预览

用 Cocos Creator 制作平台跳跃游戏

美术资源来自互联网

 

游戏场景设计

参考《超级玛丽》的世界观,我们先在[节点管理器]构建我们的世界节点树,我们添加了摄像机,游戏背景层,世界根节点,地图节点,角色节点。

用 Cocos Creator 制作平台跳跃游戏

 

1.添加摄像机(Main Camera)

摄像机作为玩家观察游戏世界的窗口,Cocos Creator 默认会自动为场景分配一个[摄像机],我们无需手动添加。

 

2.添加世界根节点(World Root)

添加一个空节点,用于放置游戏内的物体节点。

(1)创建脚本 world.ts 并拖入节点属性面板,用于配置游戏世界参数,比如设置重力加速度 G 的值。

(2)创建脚本 lookat.ts 并拖入节点属性面板,根据 Player 节点的位置同步世界视角。

用 Cocos Creator 制作平台跳跃游戏

全局配置 world.ts 代码:

const {ccclass, property} = cc._decorator;@ccclassexport default class CWorld extends cc.Component {    @property()    WorldFallG: number = 0;    @property()     WorldWalkA: number = 0;    static G: number = 0;        static WalkA: number = 0;         // LIFE-CYCLE CALLBACKS:    onl oad () {        CWorld.G = this.WorldFallG;            CWorld.WalkA = this.WorldWalkA;     }    start () {        // enable Collision System        cc.director.getCollisionManager().enabled = true;        cc.director.getCollisionManager().enabledDebugDraw = true;        cc.director.getCollisionManager().enabledDrawBoundingBox = true;    }    // update (dt) {}}

世界视角控制 lookat.ts 代码:

const { ccclass, property } = cc._decorator;@ccclassexport default class NewClass extends cc.Component {    @property(cc.Node)    target: cc.Node = null;    @property(cc.Node)    map: cc.Node = null;    boundingBox: cc.Rect = null;    screenMiddle: cc.Vec2 = null;    minX: number = 0;    maxX: number = 0;    minY: number = 0;    maxY: number = 0;    isRun: boolean = true;    // LIFE-CYCLE CALLBACKS:    onl oad() {        this.boundingBox = new cc.Rect(0, 0, this.map.width, this.map.height);        let winsize = cc.winSize;        this.screenMiddle = new cc.Vec2(winsize.width / 2, winsize.height / 2);        this.minX = -(this.boundingBox.xMax - winsize.width);        this.maxX = this.boundingBox.xMin;        this.minY = -(this.boundingBox.yMax - winsize.height);        this.maxY = this.boundingBox.yMin;    }    update() {        if (!this.isRun)             return;                    let pos = this.node.convertToWorldSpaceAR(cc.Vec2.ZERO);        let targertPos = this.target.convertToWorldSpaceAR(cc.Vec2.ZERO);        let dis = pos.sub(targertPos);        let dest = this.screenMiddle.add(dis);        dest.x = cc.misc.clampf(dest.x, this.minX, this.maxX);        dest.y = this.minY;        this.node.position = this.node.parent.convertToNodeSpaceAR(dest);    }}

 

3.添加角色(Player)

我们控制的游戏主角节点,作为游戏世界视角的焦点。

 

4.添加地图(Tiled Map)

[Tiled](支持 TiledMap v1.0) 制作好的地图资源 level01,拖入到世界节点下面,自动会生成地图节点,这时候可以查看展开的 TiledMap 地图层级。

用 Cocos Creator 制作平台跳跃游戏

根据 TiledMap 设计的物体类型,需要对物体进行实例化,我们创建 waorldmap.ts 脚本来完成这个工作,下图是我们已配置的地图层级和物体的 Prefab 资源。

用 Cocos Creator 制作平台跳跃游戏

 

地图对象的实例化,分为几步:

  • 实例化类型对应的 Prefab 资源
  • 设置碰撞组
  • 设置物体大小
  • 添加碰撞组件
  • 设置物体的类型标签

在 waorldmap.ts 中,水对象的实例化过程如下:

// get waters layer and traverse all water objects.var waters = this.worldMap.getObjectGroup(this.waterLayerName);for (var i = 1; i < 8; i++) {  var waterName = 'water' + i;  var waterBlock = waters.getObject(waterName);  var waterNode = cc.instantiate(this.ColliderPreName);  // set group name for Collider System.  waterNode.group = 'water';    // set size  waterNode.width = waterBlock.width;  waterNode.height = waterBlock.height;  waterNode.x = waterBlock.x;  waterNode.y = waterBlock.y - waterBlock.height;    // add collider component.  waterNode.addComponent(cc.BoxCollider);  waterNode.getComponent(cc.BoxCollider).size = cc.size(waterNode.width, waterNode.height);  waterNode.getComponent(cc.BoxCollider).offset =     new cc.Vec2(waterNode.width / 2, -waterNode.height / 2);  // set tag for check when collision.   waterNode.getComponent(cc.BoxCollider).tag = 6;  this.node.addChild(waterNode);}

 

5.添加碰撞规则

世界物体包含了角色,地面,方块,金币,甲壳虫,水,蘑菇,创建碰撞组和碰撞组来约束物体彼此之间碰撞规则。

用 Cocos Creator 制作平台跳跃游戏

 

6.游戏物体设计

游戏物体会根据本身的特性去进行分类做成预制体,预制体根据物体特性,添加下面内容:

  • 碰撞特性
  • 动作表现
  • 音效表现
  • 行为控制脚本

 

7.物体 Prefab 制作

例如下面是甲壳虫的资源目录,包含了甲壳虫动画文件 beetle_anim,预制体资源  beetle_node,皮肤文件 beetle_skin,行为控制脚本 beetle_script。

用 Cocos Creator 制作平台跳跃游戏

给甲壳虫预制体添加动作组件

用 Cocos Creator 制作平台跳跃游戏

给甲壳虫预制体添加碰撞组件

用 Cocos Creator 制作平台跳跃游戏

给甲壳虫预制体添加脚本组件,设置了移动速度,缩放系数,音效等属性

用 Cocos Creator 制作平台跳跃游戏

下面是制作甲壳虫脚本,用于碰撞检测和行为控制:

const { ccclass, property } = cc._decorator;@ccclassexport default class enemy extends cc.Component {    @property()    speed: cc.Vec2 = new cc.Vec2(0, 0);    @property    scaleX: number = 1;    @property    canMove: boolean = true;    @property({type: cc.AudioClip})    dieAudio: cc.AudioClip = null;    anim: cc.Animation = null;    // LIFE-CYCLE CALLBACKS:    onl oad() {        this.node.scaleX = 1;        this.anim = this.getComponent(cc.Animation);    }    start() {    }    // onCollisionEnter overrated    onCollisionEnter(other, self) {        if (other.tag == 5) {            this.turn();            this.speed.x = -this.speed.x;        }        var otherAabb = other.world.aabb;        var otherPreAabb = other.world.preAabb.clone();        var selfAabb = self.world.aabb;        var selfPreAabb = self.world.preAabb.clone();        selfPreAabb.y = selfAabb.y;        otherPreAabb.y = otherAabb.y;        if (cc.Intersection.rectRect(selfPreAabb, otherPreAabb)) {            if (selfPreAabb.yMax < otherPreAabb.yMax && other.node.group == 'player') {                this.todie();            }        }    }    todie() {        cc.audioEngine.play(this.dieAudio, false, 1);        this.anim.play('beetled');        this.canMove = false;        this.node.height = this.node.height * 0.3;              this.node.runAction(cc.fadeOut(.5));        this.scheduleOnce(function () {            this.node.removeFromParent();        }, 0.5);    }    update(dt) {        if (this.canMove) {            this.node.x -= this.speed.x * dt;        }    }   turn() {        this.node.scaleX = -this.node.scaleX;    }}

8.角色逻辑设计

作为游戏的核心,角色的行为设计是比较复杂,主要分为控制事件和碰撞事件两部分。

(1)控制事件处理

onLoad() {  cc.systemEvent.on(cc.SystemEvent.EventType.KEY_DOWN, this.onKeyDown, this);  cc.systemEvent.on(cc.SystemEvent.EventType.KEY_UP, this.onKeyUp, this);}onDestroy() {  cc.systemEvent.off(cc.SystemEvent.EventType.KEY_DOWN, this.onKeyDown, this);  cc.systemEvent.off(cc.SystemEvent.EventType.KEY_UP, this.onKeyUp, this);}onKeyDown(event) {  switch (event.keyCode) {    case cc.macro.KEY.a:    case cc.macro.KEY.left:      this.playerLeft();      break;    case cc.macro.KEY.d:    case cc.macro.KEY.right:      this.playerRight();      break;    case cc.macro.KEY.w:    case cc.macro.KEY.up:      this.playerUp();      break;    case cc.macro.KEY.down:    case cc.macro.KEY.s:      this.playerDown();      break;  }}onKeyUp(event) {  switch (event.keyCode) {    case cc.macro.KEY.a:    case cc.macro.KEY.left:    case cc.macro.KEY.d:    case cc.macro.KEY.right:      this.noLRControlPlayer();      break;    case cc.macro.KEY.up:    case cc.macro.KEY.w:      this.noUpControlPlayer();      break;    case cc.macro.KEY.s:    case cc.macro.KEY.down:      this.noDownControlPlayer();      break;  }}

 

(2)碰撞事件处理

物体对象在实例化时候分配了物体类型标签,下面代码根据标签来指派不同的碰撞逻辑。

onCollisionEnter(other, self) {   if (this.touchingNumber == 0) {     if (this.buttonIsPressed)       this.player_walk();     else       this.player_idle();   }   switch (other.tag) {     case 1://coin.tag = 1       this.collisionCoinEnter(other, self);       break;     case 2://bonusblock6.tag = 2     case 3://breakableWall = 3     case 7: //bonusblock6withMushroom.tag = 7       this.collisionBonusWallEnter(other, self);       break;     case 4://enemy.tag = 4       this.collisionEnemyEnter(other, self);       break;     case 5://platform.tag = 5       this.collisionPlatformEnter(other, self);       break;     case 6://water.tag = 6       this.collisionWaterEnter(other, self);       break;     case 8://mushroom.tag = 8       this.collisionMushroomEnter(other, self);       break;   } }

角色与地面的碰撞处理:

collisionPlatformEnter(other, self) {  this.touchingNumber++;  this.jumpCount = 0;  var otherAabb = other.world.aabb;  var otherPreAabb = other.world.preAabb.clone();  var selfAabb = self.world.aabb;  var selfPreAabb = self.world.preAabb.clone();  selfPreAabb.x = selfAabb.x;  otherPreAabb.x = otherAabb.x;  if (cc.Intersection.rectRect(selfPreAabb, otherPreAabb)) {    if (this._speed.x < 0 && (selfPreAabb.xMax > otherPreAabb.xMax)) {      this.node.x += Math.floor(Math.abs(otherAabb.xMax - selfAabb.xMin));      this.collisionX = -1;    }    else if (this._speed.x > 0 && (selfPreAabb.xMin < otherPreAabb.xMin)) {      this.node.x -= Math.floor(Math.abs(otherAabb.xMin - selfAabb.xMax));      this.collisionX = 1;    } else if (this._speed.x == 0 && (selfPreAabb.xMax == otherPreAabb.xMin)) {      this.isFallDown = true;    }    this._speed.x = 0;    other.touchingX = true;    return;  }  selfPreAabb.y = selfAabb.y;  otherPreAabb.y = otherAabb.y;  if (cc.Intersection.rectRect(selfPreAabb, otherPreAabb)) {    if (this._speed.y < 0 && (selfPreAabb.yMax > otherPreAabb.yMax)) {      this.node.y = otherPreAabb.yMax - this.node.parent.y;      this.isJumping = false;      this.collisionY = -1;    }    else if (this._speed.y > 0 && (selfPreAabb.yMin < otherPreAabb.yMin)) {      cc.audioEngine.play(this.hit_block_Audio, false, 1);      this.node.y = otherPreAabb.yMin - selfPreAabb.height - this.node.parent.y;      this.collisionY = 1;    }    this._speed.y = 0;    other.touchingY = true;  }  this.isWallCollisionCount++;}

角色与敌人的碰撞:

collisionEnemyEnter(other, self) {  // 1st step  // get pre aabb, go back before collision  var otherAabb = other.world.aabb;  var otherPreAabb = other.world.preAabb.clone();  var selfAabb = self.world.aabb;  var selfPreAabb = self.world.preAabb.clone();  // 2nd step  // forward x-axis, check whether collision on x-axis  selfPreAabb.x = selfAabb.x;  otherPreAabb.x = otherAabb.x;  if (cc.Intersection.rectRect(selfPreAabb, otherPreAabb)) {    if (this._life == 2) {      cc.audioEngine.play(this.player_decrease_Audio, false, 1);      var actionBy = cc.scaleBy(1, 3 / 5);      this.node.runAction(actionBy);      this._life--;    } else if (this._life == 1) {      this.anim.play("player_die");      this.rabbitDieJump();      this.OverNodeLoad();      return;    }    if (this._speed.x < 0 && (selfPreAabb.xMax > otherPreAabb.xMax)) {      this.node.x += Math.floor(Math.abs(otherAabb.xMax - selfAabb.xMin));      this.collisionX = -1;    }    else if (this._speed.x > 0 && (selfPreAabb.xMin < otherPreAabb.xMin)) {      this.node.x -= Math.floor(Math.abs(otherAabb.xMin - selfAabb.xMax));      this.collisionX = 1;    }    this._speed.x = 0;    other.touchingX = true;    return;  }  // 3rd step  // forward y-axis, check whether collision on y-axis  selfPreAabb.y = selfAabb.y;  otherPreAabb.y = otherAabb.y;  if (cc.Intersection.rectRect(selfPreAabb, otherPreAabb)) {    if (this._speed.y < 0 && (selfPreAabb.yMax > otherPreAabb.yMax)) {      this.rabbitJump();      return;    }        if (this._speed.y > 0 && (selfPreAabb.yMax < otherPreAabb.yMax)) {      if (this._life == 2) {        var actionBy = cc.scaleBy(1, 3 / 5);        this.node.runAction(actionBy);        this._life--;      } else if (this._life == 1) {        this.anim.play("player_die");        this.rabbitDieJump();        this.OverNodeLoad();        return;      }    }        this._speed.y = 0;    other.touchingY = true;  }  this.isWallCollisionCount++;}

 

注意

本教程旨在讲解如何使用 Cocos Creator 编辑器来设计一款横版平台跳跃闯关类游戏,运用 Creator 的组件化思想,减少代码的使用,提供开发效率。所涉及资源来自网络,请勿用于商业目的。

 

源码下载:

https://github.com/xianyinchen/creator_teach

 

在学习和使用 Cocos Creator 过程中有什么问题和建议,欢迎移步至 Cocos 中文社区进行交流!

 

TileMap

https://docs.cocos.com/creator/manual/en/asset-workflow/tiledmap.html

Prefab

https://docs.cocos.com/creator/manual/en/asset-workflow/prefab.html

Keyboard Event

https://docs.cocos.com/creator/manual/en/scripting/player-controls.html#keyboard-events

Atlas

https://docs.cocos.com/creator/manual/en/asset-workflow/atlas.html

 

如果您在使用 Cocos Creator 的过程中,获得了独到的开发心得、见解或是方法,并且乐于分享出来,帮助更多开发者解决技术问题,加速游戏开发效率,期待您与我们联系!

用 Cocos Creator 制作平台跳跃游戏

 

更多精彩

创意小游戏橙皮书发布

Cocos技术派|3D小游戏《快上车》技术分享

用 Cocos Creator 快速制作打地鼠游戏

1周开发,7留35%,小游戏《成语消消对战》团队专访

Cocos Service 全面解析

我的小游戏开发之路|腾讯TGideas周桂华

创意小游戏《荒野日记》Cocos专访:游戏如何讲故事?

5G 云游戏亮相 Chinajoy,大作一键秒玩

Cocos Creator 接入微信小游戏引擎插件指引

Cocos海外开发者专访:遗憾的是没早点开始做游戏

Cocos Creator 重力球游戏制作教程

 

 

上一篇:基于Cocos SDKHub接入华为HMS Game服务—接入SDKHub


下一篇:Cocos2dx-JS绑定C++大致流程