通过《Cocos 2.x-Hello World 飞机大战游戏》的简单实践,算是入门了Cocos游戏开发,查看了官方手册3.8的入门游戏,了解了一些游戏的概念,通过2.4来实现这个小游戏,这里记录一下实践过程。

1. 环境配置

编辑器版本V2.4.11,设计分辨率960x640,固定高度:

官方手册文档地址:

https://docs.cocos.com/creator/manual/zh/getting-started/first-game-2d/

2. 创建主角

在Canvas创建一个空节点Player作为主角节点,并在Player节点下面创建一个精灵节点(渲染节点Sprite,单色)作为玩家的身体部分,取名Body,将Body的Position.y设置成40,大小也调整为40x40,并调整其颜色为红色。

即Player可能有多个部分组成,如:

身体:Body(渲染节点Sprite)
昵称:Label
血条:自定义UI

创建Typescript脚本组件(JumpPlayer)并挂在Player节点中。

3. 创建地图

跟创建Player的Body一样,在Canvas下面创建一个单色Sprite节点,并制作成预制体:

如图:红色代表玩家,白色代表地图

4. 让主角动起来

这里利用鼠标单击让主角跳起来,当鼠标单击左键时,跳一步,当鼠标单击右键时,跳两步。

新增JumpPlayerController脚本组件,并挂在Canvas上面,实现鼠标单击的监听:

import JumpPlayer from "./JumpPlayer";

const { ccclass, property } = cc._decorator;

@ccclass
export default class JumpPlayerController extends cc.Component {
  @property(cc.Node)
  player: cc.Node = null;

  playerComponent: JumpPlayer = null;
  onLoad() {
    this.playerComponent = this.player.getComponent(JumpPlayer);
  }

  start() {
    this.node.on(
      cc.Node.EventType.MOUSE_UP,
      (e: cc.Event.EventMouse) => {
        if (e.getButton() == 0) {
          this.playerComponent.jump(1);
        } else if (e.getButton() == 2) {
          this.playerComponent.jump(2);
        }
      },
      this
    );
  }
}

这里的player是一个cc.Node,挂载的是Player节点,用于控制Player节点的移动,具体的移动代码实现如下:

const { ccclass, property } = cc._decorator;
const BLOCK_SIZE = 40;

@ccclass
export default class JumpPlayer extends cc.Component {
  jumping: boolean = false;
  step: number = 1;
  // 移动时长
  duration: number = 0.1;
  speed: number = 0;
  // 移动了多久
  jumpTime: number = 0;
  // 位置
  current: cc.Vec2 = new cc.Vec2();
  temp: cc.Vec2 = new cc.Vec2();
  dist: cc.Vec2 = new cc.Vec2();
  // onLoad () {}
  start() {}

  update(deltaTime) {
    if (this.jumping) {
      this.jumpTime += deltaTime;
      if (this.jumpTime > this.duration) {
        this.node.setPosition(this.dist);
        this.jumping = false;
      } else {
        this.node.setPosition(this.current);
        this.temp.x = this.speed * deltaTime;
        cc.Vec2.add(this.current, this.current, this.temp);
        this.node.setPosition(this.current);
      }
    }
  }

  // P_1 = P_0 + v*t
  jump(step: number) {
    if (this.jumping) {
      return;
    }
    this.jumping = true;
    this.step = step;
    // 计算速度
    const distance = this.step * BLOCK_SIZE;
    this.speed = distance / this.duration;
    this.jumpTime = 0;
    this.node.getPosition(this.current);
    cc.Vec2.add(this.dist, this.current, new cc.Vec2(distance, 0));
  }
}

这里的位置计算使用的是向量,简单理解为有方向的距离(可以参考野生程序君的解释),代码逻辑参考Cocos3.8的官方手册实例代码,最终的移动效果:

5. 制作动画

给Player的Body节点新增一个动画组件(Animation),并在assets/jump/animation中创建AnimationClip资源挂载在Animaction组件的Clip字段上。

在动画编辑器中,添加position属性,选中Body设置position.y=40(可能需要改变一下数值才能生成帧),然后将动画帧拖到0.10的地方,将Body的position.y设置成120,继续将动画帧拖动到0.20的地方,将Body的position.y重新设置成40,这样点击播放按钮就可以看到动画了:

用同样的方法制作step2,移动两步的动画帧(间隔设置成0.2,即0,0.2,0.4)

调整Player脚本,计算动画时长调整移动时长,使得动画播放更加平滑:

  // 需要将Body拖到该属性上
  @property(cc.Animation)
  bodyAnimation: cc.Animation = null;

  // P_1 = P_0 + v*t
  jump(step: number) {
    if (this.jumping) {
      return;
    }
    this.jumping = true;
    this.step = step;

    const clip = `step${step}`;
    this.duration = this.bodyAnimation.getAnimationState(clip).duration;

    // 计算速度
    const distance = this.step * BLOCK_SIZE;
    this.speed = distance / this.duration;
    this.jumpTime = 0;
    this.node.getPosition(this.current);
    cc.Vec2.add(this.dist, this.current, new cc.Vec2(distance, 0));

    this.bodyAnimation.play(clip);
  }

动画相关的API文档:

https://docs.cocos.com/creator/2.4/api/zh/classes/Animation.html

6. 游戏管理器

关于Manager,官方文档是这样说的:

一般情况为了保存游戏的数据,我们需要创建一些类来辅助这类工作。

创建一个GameManager节点,并创建对应的JumpGameManager脚本组件挂载在它上面,用于生成地图块:

const { ccclass, property } = cc._decorator;
import { BLOCK_SIZE } from "./JumpPlayer";
enum Block {
  NONE,
  STONE,
}
@ccclass
export default class JumpGameManager extends cc.Component {
  // Box预制体
  @property(cc.Prefab)
  box: cc.Prefab = null;
  distance: number = 50;
  blockArray: Block[] = [];
  // onLoad () {}

  start() {
    this.renderBlock();
  }

  // update (dt) {}

  renderBlock() {
    this.node.removeAllChildren();
    this.blockArray = [];

    // 第一个必须是STONE
    this.blockArray.push(Block.STONE);

    for (let i = 1; i < this.distance; i++) {
      if (this.blockArray[i - 1] == Block.NONE) {
        // 不能出现连续的坑
        this.blockArray.push(Block.STONE);
      } else {
        // 随机生成0或1,0=NONE、1=STONE
        this.blockArray.push(Math.floor(Math.random() * 2));
      }
    }

    for (let i = 0; i < this.blockArray.length; i++) {
      const block = this.spawnBlock(this.blockArray[i]);
      if (block) {
        this.node.addChild(block);
        block.setPosition(i * BLOCK_SIZE, 0);
      }
    }
  }

  spawnBlock(block: Block) {
    if (this.box && block == Block.STONE) {
      return cc.instantiate(this.box);
    }
    return null;
  }
}

地图生成效果:

7. 相机和卷轴

2.x的Canvas跟3.x稍有不同,这里的做法跟3.x的案例不太一样,2.4的摄像机文档地址:

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

使用两个摄像机、也就是给玩家添加一个特写摄像机,并且取消主摄像机的Align with screen的配置

这样玩家的摄像机就可以跟玩家一起移动:

posted on 2023-11-05 14:52  ql1710  阅读(46)  评论(0编辑  收藏  举报