Cocos Creator 2D 实战:触摸移动、碰撞检测与计分

用 Cocos Creator 完成一个可触控的 2D 收集游戏:绑定触摸事件、配置碰撞器并实现最小计分系统。

Cocos Creator 2D 实战:触摸移动、碰撞检测与计分

一个最小可玩的 2D 游戏,通常至少要有输入、目标、碰撞反馈和分数。本篇在前一篇场景的基础上,做一个拖动 Player 收集 Coin 的小例子。

本文目标

完成后,玩家可以在手机或浏览器中触摸移动角色;角色碰到金币时金币消失,界面上的分数加一。

1. 准备场景节点

建议先建立以下结构:

text
Canvas
├─ Player
├─ CoinContainer
│  ├─ Coin_01
│  ├─ Coin_02
│  └─ Coin_03
└─ UI
   └─ ScoreLabel

Player 和每一个 Coin 都需要有可见的 Sprite。触摸事件依赖 UITransform,通常在 UI 节点上已经存在;如果你创建的是普通节点,记得补上 UITransform 组件。

2. 配置 2D 碰撞器

选中 Player 和 Coin,分别添加 BoxCollider2D 或 CircleCollider2D。为了让碰撞事件能正常产生,至少让参与碰撞的一方拥有 RigidBody2D。

对于金币这类可收集物,建议在 Collider2D 上开启 Sensor。Sensor 仍会产生碰撞回调,但不会把角色硬生生弹开;它很适合道具、触发区和传送门。

还要检查 Physics2D 的分组碰撞矩阵:Player 所在分组必须允许和 Coin 所在分组相互碰撞。

3. 用触摸事件移动角色

将下面的脚本挂到 Player 节点:

text
import { _decorator, Component, EventTouch, Node } from 'cc';

const { ccclass, property } = _decorator;

@ccclass('PlayerTouchController')
export class PlayerTouchController extends Component {
  @property(Node)
  target: Node | null = null;

  onEnable() {
    this.node.on(Node.EventType.TOUCH_MOVE, this.onTouchMove, this);
  }

  onDisable() {
    this.node.off(Node.EventType.TOUCH_MOVE, this.onTouchMove, this);
  }

  private onTouchMove(event: EventTouch) {
    const point = event.getUILocation();
    this.node.setWorldPosition(point.x, point.y, 0);
  }
}

这个版本刻意保持简单,适合先确认输入链路是通的。正式项目可在此基础上增加边界限制、插值移动、手势判断和拖拽状态。

4. 处理开始碰撞事件

再创建 CollectScore.ts 并挂到 Player:

text
import { _decorator, Collider2D, Component, Contact2DType, Label } from 'cc';

const { ccclass, property } = _decorator;

@ccclass('CollectScore')
export class CollectScore extends Component {
  @property(Label)
  scoreLabel: Label | null = null;

  private score = 0;
  private collider: Collider2D | null = null;

  onEnable() {
    this.collider = this.getComponent(Collider2D);
    this.collider?.on(Contact2DType.BEGIN_CONTACT, this.onBeginContact, this);
  }

  onDisable() {
    this.collider?.off(Contact2DType.BEGIN_CONTACT, this.onBeginContact, this);
  }

  private onBeginContact(_self: Collider2D, other: Collider2D) {
    if (!other.node.name.startsWith('Coin')) return;
    other.node.destroy();
    this.score += 1;
    if (this.scoreLabel) {
      this.scoreLabel.string = '得分:' + this.score;
    }
  }
}

在 Inspector 中把 ScoreLabel 拖到 scoreLabel 属性上。预览时碰到 Coin,分数就会更新。

5. 调试清单

  1. 没有碰撞回调:确认两边都有 Collider2D,至少一边有 RigidBody2D。
  2. 碰到金币被弹开:将 Coin 的 Collider2D 设为 Sensor。
  3. 分数没有更新:确认 Label 已经拖到脚本属性上。
  4. 手机上的触摸无效:确认事件绑在带 UITransform 的节点上,并用真机或移动模拟器测试。

可以继续完善的方向

不要长期依赖节点名称判断 Coin。后续可给金币挂一个 Coin 组件,或使用分组、标签和对象池来管理大量道具。玩法验证阶段先让链路跑通,比一开始追求复杂架构更重要。

参考