first commit

This commit is contained in:
zzc
1970-01-01 08:26:29 +08:00
commit ed6cca82db
91 changed files with 8400 additions and 0 deletions

View File

@@ -0,0 +1,140 @@
import { _decorator, Component, Node, AudioSource, AudioClip, director } from 'cc';
const { ccclass, property } = _decorator;
// 背景音乐类型
export enum BGMType {
Home,
Level,
Boss,
Play
}
// 音效类型
export enum SFXType {
Merge,
Success,
Fail,
Item,
Victory,
Magic,
Shuffle,
Hint
}
@ccclass('AudioManager')
export class AudioManager extends Component {
private static _instance: AudioManager | null = null;
public static get instance(): AudioManager {
console.log(33333, !this._instance)
if (!this._instance) {
const node = new Node('AudioManager');
this._instance = node.addComponent(AudioManager);
director.addPersistRootNode(node);
}
return this._instance;
}
// AudioSources
private bgmSource!: AudioSource;
private sfxSource!: AudioSource;
// 开关
public bgmEnabled: boolean = true;
public sfxEnabled: boolean = true;
// 音乐 / 音效 Map
private bgmClips: Map<BGMType, AudioClip> = new Map();
private sfxClips: Map<SFXType, AudioClip> = new Map();
private currentBGM: BGMType | null = null;
onLoad() {
// 初始化单例
AudioManager._instance = this;
// 跨场景不销毁
// director.addPersistRootNode(this.node);
// 读取本地开关
this.bgmEnabled = localStorage.getItem('bgmEnabled') !== '0';
this.sfxEnabled = localStorage.getItem('sfxEnabled') !== '0';
// 自动挂载或创建 AudioSource
const sources = this.node.getComponents(AudioSource);
if (sources.length >= 2) {
this.bgmSource = sources[0];
this.sfxSource = sources[1];
} else {
this.bgmSource = this.node.addComponent(AudioSource);
this.bgmSource.loop = true;
this.sfxSource = this.node.addComponent(AudioSource);
}
}
/** 注册 BGM */
public registerBGM(type: BGMType, clip: AudioClip) {
this.bgmClips.set(type, clip);
}
/** 注册 SFX */
public registerSFX(type: SFXType, clip: AudioClip) {
this.sfxClips.set(type, clip);
}
/** 播放背景音乐 */
public playBGM(type: BGMType) {
// if (!this.bgmEnabled) return;
// if (this.currentBGM === type) return;
const clip = this.bgmClips.get(type);
if (!clip) return;
this.currentBGM = type;
this.bgmSource.clip = clip;
this.bgmSource.loop = true;
this.bgmSource.volume = 0.5;
this.bgmSource.play();
}
/** 播放音效 */
public playSFX(type: SFXType) {
// if (!this.sfxEnabled) return;
const clip = this.sfxClips.get(type);
if (!clip) return;
this.sfxSource.playOneShot(clip, 1.0);
}
/** 设置 BGM 开关 */
public setBGMEnabled(enabled: boolean) {
this.bgmEnabled = enabled;
localStorage.setItem('bgmEnabled', enabled ? '1' : '0');
if (!this.bgmSource) return;
if (enabled) {
// 开启播放当前 BGM
if (this.currentBGM !== null || this.currentBGM !== undefined) {
const clip = this.bgmClips.get(this.currentBGM);
if (clip) {
this.bgmSource.clip = clip;
this.bgmSource.play();
}
}
} else {
// 关闭停止播放
this.bgmSource.stop();
}
}
/** 设置 SFX 开关 */
public setSFXEnabled(enabled: boolean) {
this.sfxEnabled = enabled;
localStorage.setItem('sfxEnabled', enabled ? '1' : '0');
// 关闭音效后playSFX 会直接返回,不会播放
}
/** 设置 SFX 开关 */
public setCurBgm(type: number) {
this.currentBGM = type;
}
}

View File

@@ -0,0 +1,9 @@
{
"ver": "4.0.24",
"importer": "typescript",
"imported": true,
"uuid": "4764f1a4-4e46-4c19-b03c-8f4b21b25cb8",
"files": [],
"subMetas": {},
"userData": {}
}

91
assets/script/Fruit.ts Normal file
View File

@@ -0,0 +1,91 @@
import {
_decorator,
Component,
Sprite,
SpriteFrame,
UITransform,
Size,
CircleCollider2D,
Contact2DType,
IPhysics2DContact,
RigidBody2D,
Vec2,
Node,
} from "cc";
import { FRUITS, MAX_LEVEL } from "./FruitConfig";
import { GameManager } from "./GameManager";
const { ccclass, property } = _decorator;
@ccclass("Fruit")
export class Fruit extends Component {
@property
level = 1; // 1..MAX_LEVEL
@property(Sprite)
sprite!: Sprite;
game!: GameManager;
collider!: CircleCollider2D;
body!: RigidBody2D;
// 防止同一对重复合成
merging = false;
removed = false;
init(level: number, spriteFrame: SpriteFrame, game: GameManager) {
this.level = level;
this.game = game;
if (!this.sprite) this.sprite = this.getComponent(Sprite)!;
this.sprite.spriteFrame = spriteFrame;
const def = FRUITS[this.level - 1];
const ui = this.getComponent(UITransform)!;
ui.setContentSize(new Size(def.radius * 2, def.radius * 2));
this.collider = this.getComponent(CircleCollider2D)!;
this.collider.radius = def.radius;
this.body = this.getComponent(RigidBody2D)!;
this.body.enabled = true;
this.merging = false;
this.removed = false;
}
onEnable() {
const col = this.getComponent(CircleCollider2D);
if (col) col.on(Contact2DType.BEGIN_CONTACT, this.onBeginContact, this);
}
onDisable() {
const col = this.getComponent(CircleCollider2D);
if (col) col.off(Contact2DType.BEGIN_CONTACT, this.onBeginContact, this);
}
private onBeginContact(
selfCol: CircleCollider2D,
otherCol: CircleCollider2D,
_contact: IPhysics2DContact | null
) {
if (this.removed || this.merging) return;
const otherNode = otherCol.node as Node;
const otherFruit = otherNode.getComponent(Fruit);
if (!otherFruit || otherFruit.removed || otherFruit.merging) return;
// 仅同级可合成,且不能超过最大级
if (otherFruit.level !== this.level || this.level >= MAX_LEVEL) return;
// 为避免两者都合成,使用 uuid 决定触发者(只让“较小”的触发)
if (this.node.uuid > otherNode.uuid) return;
this.merging = true;
otherFruit.merging = true;
// 合成到下一级
const nextLevel = this.level + 1;
const wpA = this.node.worldPosition;
const wpB = otherNode.worldPosition;
const mid = wpA.clone().add(wpB).multiplyScalar(0.5);
this.game.spawnMerged(nextLevel, mid, this, otherFruit);
}
}

View File

@@ -0,0 +1,9 @@
{
"ver": "4.0.24",
"importer": "typescript",
"imported": true,
"uuid": "bb6094b3-338a-441c-a827-32edc145dc63",
"files": [],
"subMetas": {},
"userData": {}
}

View File

@@ -0,0 +1,59 @@
// assets/scripts/FruitConfig.ts
import { SpriteFrame } from "cc";
export interface FruitDef {
level: number; // 1..MAX
name: string;
radius: number; // 物理半径(像素)
score: number; // 合成到该等级获得的分数
sprite?: SpriteFrame; // 运行时由 GameManager 注入
}
export const MAX_LEVEL = 11; // 西瓜
// 半径与分数可按手感微调(半径越大越难堆)
export const FRUITS: FruitDef[] = [
{ level: 1, name: "樱桃", radius: 26, score: 1 },
{ level: 2, name: "草莓", radius: 30, score: 2 },
{ level: 3, name: "葡萄", radius: 34, score: 4 },
{ level: 4, name: "橘子", radius: 38, score: 8 },
{ level: 5, name: "柠檬", radius: 44, score: 16 },
{ level: 6, name: "猕猴桃", radius: 50, score: 24 },
{ level: 7, name: "番茄", radius: 58, score: 36 },
{ level: 8, name: "桃子", radius: 66, score: 48 },
{ level: 9, name: "菠萝", radius: 74, score: 64 },
{ level: 10, name: "哈密瓜", radius: 84, score: 96 },
{ level: 11, name: "西瓜", radius: 96, score: 128 },
];
export function clampLevel(lv: number) {
if (lv < 1) return 1;
if (lv > MAX_LEVEL) return MAX_LEVEL;
return lv;
}
// export function randomStartLevel(min=1, max=5) {
// const a = Math.max(1, Math.min(MAX_LEVEL-1, min));
// const b = Math.max(a, Math.min(MAX_LEVEL-1, max));
// return Math.floor(Math.random() * (b - a + 1)) + a;
// }
export function randomStartLevel(
minLevel: number,
maxLevel: number,
score: number
): number {
let level = Math.floor(Math.random() * (maxLevel - minLevel + 1)) + minLevel;
// 高分奖励机制:比如 score >= 50 时,有 20% 概率出现 maxLevel+1 的水果
if (score >= 800 && Math.random() < 0.2) {
level = Math.min(level + 1, FRUITS.length);
}
// 高分再加概率,可根据需求增加更高级水果
if (score >= 1500 && Math.random() < 0.1) {
level = Math.min(level + 2, FRUITS.length);
}
return level;
}

View File

@@ -0,0 +1,9 @@
{
"ver": "4.0.24",
"importer": "typescript",
"imported": true,
"uuid": "0597dd34-c555-4e0c-8c81-f662a8e41a20",
"files": [],
"subMetas": {},
"userData": {}
}

View File

@@ -0,0 +1,468 @@
import {
_decorator,
Component,
Node,
Prefab,
SpriteFrame,
input,
Input,
NodePool,
Script,
Sprite,
UITransform,
v3,
Vec3,
math,
Vec2,
instantiate,
RigidBody2D,
tween,
ERigidBody2DType,
CircleCollider2D,
PhysicsSystem2D,
sys,
Label,
UIOpacity,
director,
ParticleSystem2D,
Color,
EventTouch,
view,
AudioClip,
} from "cc";
import { AudioManager, BGMType, SFXType } from "./AudioManager";
import { Fruit } from "./Fruit";
const { ccclass, property } = _decorator;
import { FRUITS, clampLevel, randomStartLevel } from "./FruitConfig";
@ccclass("GameManager")
export class GameManager extends Component {
@property(Prefab)
fruitPrefab!: Prefab; // 统一水果预制
@property([SpriteFrame])
fruitSprites: SpriteFrame[] = []; // 长度需 >= FRUITS.length按等级顺序
@property(Node)
fruitRoot!: Node;
@property(Node)
dropLine!: Node;
@property(Node)
preview!: Node; // 顶部预览点,挂 Sprite
@property(Node)
nextPreview!: Node;
@property(Node)
topSensor!: Node;
@property(Node)
resultNode: Node = null;
@property(Label)
resultLabel: Label = null;
@property(Label)
scoreLabel!: Label;
@property(Label)
bestLabel!: Label;
@property(Prefab)
explosionPrefab: Prefab = null!;
@property(AudioClip)
mergeSFX: AudioClip = null!;
@property(AudioClip)
failSFX: AudioClip = null!;
@property(AudioClip)
victorySFX: AudioClip = null!;
@property(AudioClip)
playBgm: AudioClip = null;
// —— 可调参数 ——
@property
previewY = 560; // 预览/下落的 Y根据分辨率调整
@property
randomStartMaxLevel = 5; // 随机初级水果最大等级
@property
previewClampPadding = 40; // 贴边留白
@property
spawnCooldown = 1; // 连续投掷冷却
dangerOn = false;
// —— 运行时状态 ——
nextLevel = 1; // 将要投的等级
queuedLevel = 1; // 下一颗的预告
canDrop = true;
isGameOver = false;
score = 0;
best = 0;
private pool = new NodePool();
onLoad() {
AudioManager.instance.registerBGM(BGMType.Play, this.playBgm);
AudioManager.instance.registerSFX(SFXType.Merge, this.mergeSFX);
AudioManager.instance.registerSFX(SFXType.Fail, this.failSFX);
AudioManager.instance.registerSFX(SFXType.Victory, this.victorySFX);
AudioManager.instance.setCurBgm(BGMType.Play);
AudioManager.instance.playBGM(BGMType.Play);
for (let i = 0; i < FRUITS.length; i++) {
FRUITS[i].sprite = this.fruitSprites[i] || FRUITS[i].sprite;
}
this.best = Number(sys.localStorage.getItem("best") || 0);
if (this.bestLabel) this.bestLabel.string = `${this.best}`;
if (this.scoreLabel) this.scoreLabel.string = `0`;
}
protected onDestroy() {
this.unbindInput();
}
start() {
this.bindInput();
this.reset();
PhysicsSystem2D.instance.gravity = new Vec2(0, -800);
}
bindInput() {
input.on(Input.EventType.TOUCH_MOVE, this.onMove, this);
input.on(Input.EventType.TOUCH_START, this.onMove, this); // 触摸开始也更新预览位置
input.on(Input.EventType.TOUCH_END, this.onDrop, this);
}
unbindInput() {
input.off(Input.EventType.TOUCH_MOVE, this.onMove, this);
input.off(Input.EventType.TOUCH_START, this.onMove, this);
input.off(Input.EventType.TOUCH_END, this.onDrop, this);
}
reset() {
this.isGameOver = false;
this.canDrop = true;
// TODO 分数
this.updateScore(0);
// 清场(放回对象池而非直接删)
const children = [...this.fruitRoot.children];
for (const c of children) this.recycleFruit(c);
// 初始化两个预报等级
this.nextLevel = randomStartLevel(1, this.randomStartMaxLevel, this.score);
this.queuedLevel = randomStartLevel(
1,
this.randomStartMaxLevel,
this.score
);
this.refreshPreview();
this.refreshNextPreview();
this.setDanger(false);
}
setDanger(on: boolean) {
this.dangerOn = on;
}
recycleFruit(node: Node) {
const fr = node.getComponent(Fruit);
if (fr) fr.removed = true;
node.removeFromParent();
this.pool.put(node);
}
refreshPreview() {
const sp = this.preview.getComponent(Sprite);
const def = FRUITS[this.nextLevel - 1];
sp.spriteFrame = def.sprite!;
const ui = this.preview.getComponent(UITransform)!;
ui.setContentSize(def.radius * 2, def.radius * 2);
// 初始位置居中(仅设置 XY 固定在预览线)
const p = this.preview.position;
this.preview.active = true;
this.preview.setPosition(v3(0, this.previewY, p.z));
}
refreshNextPreview() {
const sp = this.nextPreview.getComponent(Sprite);
const def = FRUITS[this.queuedLevel - 1];
sp.spriteFrame = def.sprite!;
const ui = this.nextPreview.getComponent(UITransform)!;
ui.setContentSize(def.radius * 2, def.radius * 2);
// 初始位置居中(仅设置 XY 固定在预览线)
const p = this.nextPreview.position;
this.nextPreview.active = true;
// this.nextPreview.setPosition(v3(100, this.previewY, p.z));
}
onMove(e: EventTouch) {
if (this.isGameOver) return;
const screenPos = e.getUILocation();
const canvasUI = this.node.getComponent(UITransform)!;
const localPos = canvasUI.convertToNodeSpaceAR(
new Vec3(screenPos.x, screenPos.y, 0)
);
// 获取预览水果世界宽度
const fruitWorldWidth =
this.preview.getComponent(UITransform)!.contentSize.width *
this.preview.worldScale.x;
// 获取 Canvas 世界半宽
const canvasWorldHalfWidth = view.getVisibleSize().width / 2;
const clampedX = math.clamp(
localPos.x,
-canvasWorldHalfWidth + fruitWorldWidth / 2,
canvasWorldHalfWidth - fruitWorldWidth / 2
);
this.preview.setPosition(
v3(clampedX, this.previewY, this.preview.position.z)
);
}
onDrop() {
if (this.isGameOver || !this.canDrop) return;
// 禁止再次投掷
this.canDrop = false;
// 当前水果下落位置
const dropPos = this.preview.worldPosition;
// 1⃣ 生成当前水果
this.spawnFruit(this.nextLevel, dropPos);
// 2⃣ 隐藏预览,防止连续操作
this.preview.active = false;
// 3⃣ 延迟 1 秒刷新队列和显示预览
this.scheduleOnce(() => {
// 队列前移
this.nextLevel = this.queuedLevel;
this.queuedLevel = randomStartLevel(
1,
this.randomStartMaxLevel,
this.score
);
// 刷新预览
this.refreshPreview();
this.refreshNextPreview();
this.preview.active = true;
// 恢复投掷
this.canDrop = true;
}, 0.6); // 延迟 1 秒
}
spawnFruit(level: number, worldPos: Vec3, inheritVel?: Vec2) {
// 1⃣ 获取节点:从对象池或者实例化
const node =
this.pool.size() > 0 ? this.pool.get()! : instantiate(this.fruitPrefab);
node.parent = this.fruitRoot;
node.active = true;
// 2⃣ 获取水果半宽(世界显示宽度)
const fruitRadius = FRUITS[level - 1].radius;
const fruitUI = node.getComponent(UITransform)!;
const fruitHalfWidth = (fruitUI.contentSize.width * node.worldScale.x) / 2;
// 3⃣ 获取 Canvas / Root 容器缩放比例
const rootUI = this.fruitRoot.getComponent(UITransform)!;
const canvasScaleX = view.getVisibleSize().width / rootUI.contentSize.width;
const halfRootW = (rootUI.contentSize.width * canvasScaleX) / 2;
// 4⃣ 转换到 FruitRoot 坐标系
const local = rootUI.convertToNodeSpaceAR(worldPos.clone());
// 5⃣ 限制 X 坐标不超出屏幕边界
local.x = math.clamp(
local.x,
-halfRootW + fruitHalfWidth,
halfRootW - fruitHalfWidth
);
// 6⃣ 设置初始位置、缩放、旋转
node.setPosition(local);
node.setScale(1, 1, 1);
node.setRotationFromEuler(0, 0, 0);
// 7⃣ 初始化组件
const fruit = node.getComponent(Fruit)!;
const index = Math.max(0, Math.min(level - 1, FRUITS.length - 1));
const sf = FRUITS[index].sprite!;
fruit.init(level, sf, this);
// 8⃣ 物理恢复
const rb = node.getComponent(RigidBody2D)!;
rb.enabled = true;
rb.linearVelocity = inheritVel || new Vec2(0, -10);
rb.angularVelocity = 0;
const col = node.getComponent(CircleCollider2D)!;
if (col) col.enabled = true;
// 9⃣ 出场缩放动画
node.setScale(0.9, 0.9, 1);
tween(node)
.to(0.1, { scale: v3(1, 1, 1) })
.start();
return fruit;
}
updateScore(v: number) {
this.score = v;
if (this.scoreLabel) this.scoreLabel.string = `${this.score}`;
if (this.score > this.best) {
this.best = this.score;
sys.localStorage.setItem("best", String(this.best));
}
if (this.bestLabel) this.bestLabel.string = `${this.best}`;
}
// 被 Fruit 调用:把两颗同级水果合成到 nextLevel
spawnMerged(nextLevel: number, worldMid: Vec3, a: Fruit, b: Fruit) {
if (this.isGameOver) return;
// 读两者速度,合成后给一个小的向上速度,避免“坍塌”
const va = a.getComponent(RigidBody2D)!.linearVelocity;
const vb = b.getComponent(RigidBody2D)!.linearVelocity;
// const inherit = new Vec2(
// (va.x + vb.x) * 0.25,
// Math.max(120, (va.y + vb.y) * 0.25)
// );
const inherit = new Vec2(
(va.x + vb.x) * 0.25,
Math.max(3, (va.y + vb.y) * 0.25) // 至少往上轻推一点
);
// 回收/删除旧的
this.recycleFruit(a.node);
this.recycleFruit(b.node);
// 生成新的
const f = this.spawnFruit(clampLevel(nextLevel), worldMid, inherit);
// 计分:按目标等级给分
this.updateScore(this.score + FRUITS[clampLevel(nextLevel) - 1].score);
const explosion = instantiate(this.explosionPrefab);
const canvas = director.getScene().getChildByName("Canvas")!;
canvas.addChild(explosion);
explosion.setWorldPosition(worldMid);
explosion.setSiblingIndex(canvas.children.length - 1);
const ps = explosion.getComponent(ParticleSystem2D);
if (ps) {
ps.resetSystem();
const brightColors = [
new Color(255, 255, 0), // 黄色
new Color(255, 128, 0), // 橙色
new Color(255, 0, 0), // 红色
new Color(0, 255, 255), // 青色
new Color(128, 0, 255), // 紫色
];
// 随机给粒子选择颜色
ps.startColor =
brightColors[Math.floor(Math.random() * brightColors.length)];
// 可选:结束颜色渐变为透明
ps.endColor = new Color(
ps.startColor.r,
ps.startColor.g,
ps.startColor.b,
0
);
}
this.scheduleOnce(() => {
explosion.destroy();
}, 1.5); // 粒子持续时间
const circle = new Node();
const sp = circle.addComponent(Sprite);
sp.spriteFrame = this.fruitSprites[nextLevel - 1]; // 你可以换一张发光圈贴图
circle.parent = this.fruitRoot;
circle.setPosition(worldMid);
circle.setScale(0, 0, 1);
// 添加透明度组件
const uiOpacity = circle.addComponent(UIOpacity);
uiOpacity.opacity = 255;
// 并行动画:缩放 + 渐隐
tween(circle)
.to(0.3, { scale: v3(2, 2, 1) })
.call(() => circle.destroy())
.start();
tween(uiOpacity).to(0.3, { opacity: 0 }).start();
// 合成特效:放大闪烁
const n = f.node;
const s0 = n.scale.clone();
tween(n)
.to(0.06, { scale: v3(s0.x * 1.15, s0.y * 1.15, 1) })
.to(0.08, { scale: v3(1, 1, 1) })
.start();
AudioManager.instance.playSFX(SFXType.Merge);
// const nextFruit = FRUITS[nextLevel+1].name
// if(nextFruit === '西瓜') this.gameOver(true)
// 音效:合成
// this.playSfx(0.2);
}
gameOver(success = false) {
if (this.isGameOver) return;
this.isGameOver = true;
this.canDrop = false;
this.setDanger(true);
// 所有水果轻微变暗/缩放(可选)
// this.fruitRoot.children.forEach((n) =>
// tween(n).to(0.2, { scale: v3(0.95, 0.95, 1) }).start()
// );
// AudioManager.instance.playSFX(SFXType.Fail);
this.resultShow(success);
// 失败音
// this.playSfx(0.35);
}
resultShow(success: boolean) {
this.resultNode.active = true;
if (!success) {
AudioManager.instance.playSFX(SFXType.Fail);
this.resultLabel.string = "哎萌巴巴";
} else {
AudioManager.instance.playSFX(SFXType.Victory);
this.resultLabel.string = "恭喜哎萌";
}
}
startNewGame() {
director.loadScene("game");
}
}

View File

@@ -0,0 +1,9 @@
{
"ver": "4.0.24",
"importer": "typescript",
"imported": true,
"uuid": "15d78db8-b272-49f3-af02-b806dfda026d",
"files": [],
"subMetas": {},
"userData": {}
}

View File

@@ -0,0 +1,58 @@
import { _decorator, Component, BoxCollider2D, Contact2DType, IPhysics2DContact, Node, Color, find, director } from 'cc';
import { Fruit } from './Fruit';
import { GameManager } from './GameManager';
const { ccclass, property } = _decorator;
@ccclass('TopSensor')
export class TopSensor extends Component {
game!: GameManager;
@property
holdSeconds = 2.0; // 触顶持续秒数
private touching = new Map<Node, number>(); // node -> enterTime
onLoad(){
const canvas = director.getScene().getChildByName("Canvas");
// 2. 获取 Game 节点
const gameNode = canvas.getChildByName("Game");
// 3. 获取 GameManager 脚本
this.game = gameNode.getComponent(GameManager);
}
onEnable() {
const col = this.getComponent(BoxCollider2D)!;
col.sensor = true;
col.on(Contact2DType.BEGIN_CONTACT, this.onBegin, this);
col.on(Contact2DType.END_CONTACT, this.onEnd, this);
}
onDisable() {
const col = this.getComponent(BoxCollider2D)!;
col.off(Contact2DType.BEGIN_CONTACT, this.onBegin, this);
col.off(Contact2DType.END_CONTACT, this.onEnd, this);
}
private onBegin(_self: BoxCollider2D, other: any, _c: IPhysics2DContact | null) {
const f = (other.node as Node).getComponent(Fruit);
if (f) this.touching.set(other.node, performance.now() / 1000);
}
private onEnd(_self: BoxCollider2D, other: any) {
this.touching.delete(other.node);
}
update() {
if (!this.game || this.game.isGameOver) return;
const now = performance.now() / 1000;
for (const [node, t] of this.touching) {
if (now - t >= this.holdSeconds) {
this.game.gameOver();
break;
}
}
// 可选:把状态交给 GameManager 显示红线闪烁
// this.game.setDanger(this.touching.size > 0);
}
}

View File

@@ -0,0 +1,9 @@
{
"ver": "4.0.24",
"importer": "typescript",
"imported": true,
"uuid": "176fd316-a8b3-4dda-8b7d-36a3cf09f4e7",
"files": [],
"subMetas": {},
"userData": {}
}