前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >用 Cocos Creator 制作平台跳跃游戏

用 Cocos Creator 制作平台跳跃游戏

作者头像
一枚小工
发布2020-03-02 13:46:30
2.2K0
发布2020-03-02 13:46:30
举报
文章被收录于专栏:Cocos Creator开发

前言

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

为了方便大家阅读,蓝色文字为参考链接,统一放在文末,敬请留意!

开始游戏制作

游戏效果预览

美术资源来自互联网

游戏场景设计

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

1.添加摄像机(Main Camera)

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

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

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

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

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

全局配置 world.ts 代码:

代码语言:javascript
复制
const {ccclass, property} = cc._decorator;

@ccclass
export 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:

    onLoad () {
        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 代码:

代码语言:javascript
复制
const { ccclass, property } = cc._decorator;

@ccclass
export 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:

    onLoad() {
        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 地图层级。

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

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

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

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

代码语言:javascript
复制
// 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.添加碰撞规则

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

6.游戏物体设计

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

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

7.物体 Prefab 制作

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

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

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

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

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

代码语言:javascript
复制
const { ccclass, property } = cc._decorator;

@ccclass
export 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:

    onLoad() {
        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)控制事件处理

代码语言:javascript
复制
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)碰撞事件处理

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

代码语言:javascript
复制
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;
   }
 }

角色与地面的碰撞处理:

代码语言:javascript
复制
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++;
}

角色与敌人的碰撞:

代码语言:javascript
复制
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 中文社区进行交流!

参考链接

复制链接至浏览器中即可打开

Cocos 中文社区:

https://forum.cocos.com/t/creator/47112

Cocos Creator 下载:

https://www.cocos.com/creator

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

节点管理

https://docs.cocos.com/creator/manual/en/content-workflow/node-tree.html

摄像机

https://docs.cocos.com/creator/manual/en/render/camera.html

Tiled

https://www.mapeditor.org/

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

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2019-09-12,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 一枚小工 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档