2026-04-30 00:13:37 +08:00
|
|
|
|
import {
|
|
|
|
|
|
_decorator,
|
|
|
|
|
|
Component,
|
|
|
|
|
|
input,
|
|
|
|
|
|
EventTouch,
|
|
|
|
|
|
Node,
|
|
|
|
|
|
Input,
|
|
|
|
|
|
Vec3,
|
|
|
|
|
|
Collider2D,
|
|
|
|
|
|
Contact2DType,
|
|
|
|
|
|
SpriteFrame,
|
|
|
|
|
|
Sprite,
|
|
|
|
|
|
tween,
|
|
|
|
|
|
Prefab,
|
|
|
|
|
|
instantiate,
|
|
|
|
|
|
director,
|
2026-05-12 06:46:31 +08:00
|
|
|
|
Color,
|
2026-04-30 00:13:37 +08:00
|
|
|
|
} from "cc";
|
|
|
|
|
|
import { NoteControl } from "./NoteControl";
|
|
|
|
|
|
import { BombEffect } from "../effects/BombEffectControl";
|
|
|
|
|
|
import { EventDispatcher } from "../libs/EventDispatcher";
|
|
|
|
|
|
import { GlobalData } from "../config/GlobalData";
|
|
|
|
|
|
import { GameState } from "../config/Config";
|
|
|
|
|
|
const { ccclass, property } = _decorator;
|
|
|
|
|
|
|
|
|
|
|
|
@ccclass("DinoControl")
|
|
|
|
|
|
export class DinoControl extends Component {
|
|
|
|
|
|
// 小恐龙脖子图片
|
|
|
|
|
|
@property(SpriteFrame)
|
|
|
|
|
|
neckSpriteFrame: SpriteFrame = null;
|
|
|
|
|
|
|
|
|
|
|
|
// 小恐龙脖子的高度
|
|
|
|
|
|
private neckHeight: number = 62;
|
|
|
|
|
|
|
|
|
|
|
|
// x 轴的边界
|
|
|
|
|
|
private xMaxDistance: number = 180;
|
|
|
|
|
|
|
|
|
|
|
|
// 爆炸特效预制体
|
|
|
|
|
|
@property(Prefab)
|
|
|
|
|
|
bombEffectPrefab: Prefab = null;
|
|
|
|
|
|
|
|
|
|
|
|
// 获取线节点
|
|
|
|
|
|
@property(Node)
|
|
|
|
|
|
lineNode: Node = null;
|
|
|
|
|
|
|
|
|
|
|
|
onLoad() {
|
2026-05-12 06:46:31 +08:00
|
|
|
|
// 设置小恐龙颜色
|
|
|
|
|
|
this.setDinoColor();
|
2026-04-30 00:13:37 +08:00
|
|
|
|
// 监听继续游戏事件
|
|
|
|
|
|
EventDispatcher.getTarget().on(
|
|
|
|
|
|
EventDispatcher.GAME_RESUME,
|
|
|
|
|
|
this.continueGame,
|
|
|
|
|
|
this,
|
|
|
|
|
|
);
|
|
|
|
|
|
// 监听小恐龙移动到线的位置
|
|
|
|
|
|
EventDispatcher.getTarget().on(
|
|
|
|
|
|
EventDispatcher.MOVE_DINO_TO_LINE,
|
|
|
|
|
|
this.moveDinoToLine,
|
|
|
|
|
|
this,
|
|
|
|
|
|
);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
start() {
|
|
|
|
|
|
// 监听手指滑动事件
|
|
|
|
|
|
input.on(Input.EventType.TOUCH_MOVE, this.onTouchMove, this);
|
|
|
|
|
|
// 监听碰撞事件
|
|
|
|
|
|
this.node
|
|
|
|
|
|
.getComponent(Collider2D)
|
|
|
|
|
|
.on(Contact2DType.BEGIN_CONTACT, this.onCollisionEnter, this);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 滑动事件
|
|
|
|
|
|
onTouchMove(event: EventTouch) {
|
|
|
|
|
|
// 获取小恐龙的位置
|
|
|
|
|
|
const dinoPos = this.node.position;
|
|
|
|
|
|
// 获取手指x移动距离
|
|
|
|
|
|
const deltaX = event.getDeltaX();
|
|
|
|
|
|
// 更新小恐龙的位置
|
|
|
|
|
|
// 目标位置
|
|
|
|
|
|
const targetPos = new Vec3(dinoPos.x + deltaX, dinoPos.y, dinoPos.z);
|
|
|
|
|
|
if (targetPos.x < -this.xMaxDistance) {
|
|
|
|
|
|
targetPos.x = -this.xMaxDistance;
|
|
|
|
|
|
} else if (targetPos.x > this.xMaxDistance) {
|
|
|
|
|
|
targetPos.x = this.xMaxDistance;
|
|
|
|
|
|
}
|
|
|
|
|
|
this.node.setPosition(targetPos);
|
|
|
|
|
|
|
|
|
|
|
|
// 移动距离大于0时,小恐龙向右移动,否则向左移动
|
|
|
|
|
|
if (deltaX > 0) {
|
|
|
|
|
|
this.node.scale = new Vec3(-1, 1, 1);
|
|
|
|
|
|
} else {
|
|
|
|
|
|
this.node.scale = new Vec3(1, 1, 1);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
onCollisionEnter(selfCollider: Collider2D, otherCollider: Collider2D) {
|
|
|
|
|
|
// 如果游戏状态不是进行中,直接返回
|
|
|
|
|
|
if (GlobalData.gameState != GameState.PLAYING) {
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if (otherCollider.tag == 2) {
|
|
|
|
|
|
// 得分操作
|
|
|
|
|
|
GlobalData.curRoundTotalScore += GlobalData.addScore;
|
|
|
|
|
|
|
|
|
|
|
|
// 触发小恐龙吃到音符事件
|
|
|
|
|
|
EventDispatcher.getTarget().emit(EventDispatcher.DINO_EAT_NOTE);
|
|
|
|
|
|
// 小恐龙碰到了音符
|
|
|
|
|
|
otherCollider.node.getComponent(NoteControl).noteBeEaten();
|
|
|
|
|
|
// 增加脖子长度
|
|
|
|
|
|
this.addDinoNeckLength();
|
|
|
|
|
|
// 执行吃掉音符动画
|
|
|
|
|
|
this.playEatNoteAnimation();
|
|
|
|
|
|
// 播放爆炸特效
|
|
|
|
|
|
this.playBombEffect();
|
|
|
|
|
|
|
|
|
|
|
|
// 检测是否闯关成功
|
|
|
|
|
|
const isPassLevel = this.checkIsPassLevel();
|
|
|
|
|
|
|
|
|
|
|
|
if (isPassLevel) {
|
|
|
|
|
|
EventDispatcher.getTarget().emit(EventDispatcher.PASS_LEVLE);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 增加脖子长度
|
|
|
|
|
|
addDinoNeckLength() {
|
|
|
|
|
|
// 添加脖子空节点
|
|
|
|
|
|
let neckChild = new Node();
|
|
|
|
|
|
// 给这个节点添加精灵组件
|
|
|
|
|
|
neckChild.addComponent(Sprite).spriteFrame = this.neckSpriteFrame;
|
|
|
|
|
|
|
|
|
|
|
|
// 设置这个空节点的父节点
|
|
|
|
|
|
neckChild.parent = this.node.getChildByName("dino_neck_box");
|
|
|
|
|
|
|
2026-05-12 06:46:31 +08:00
|
|
|
|
// 设置脖子的颜色
|
|
|
|
|
|
neckChild.getComponent(Sprite).color = new Color(
|
|
|
|
|
|
GlobalData.currentDinoInfo.dinoColor,
|
|
|
|
|
|
);
|
2026-04-30 00:13:37 +08:00
|
|
|
|
// 获取小恐龙身体
|
|
|
|
|
|
const bodyNode = this.node.getChildByName("dino_body");
|
|
|
|
|
|
|
|
|
|
|
|
// 获取小恐龙位置
|
|
|
|
|
|
const bodyPos = bodyNode.getPosition();
|
|
|
|
|
|
// 设置小恐龙新的位置(往下移动一个脖子的距离)
|
|
|
|
|
|
bodyNode.setPosition(bodyPos.x, bodyPos.y - this.neckHeight, 0);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 执行吃掉音符动画
|
|
|
|
|
|
playEatNoteAnimation() {
|
|
|
|
|
|
// 获取当前所有脖子列表
|
|
|
|
|
|
const neckList: Node[] = this.node
|
|
|
|
|
|
.getChildByName("dino_neck_box")
|
|
|
|
|
|
.children.slice(0, 16) as Node[];
|
|
|
|
|
|
|
|
|
|
|
|
// 定义存储动画的 promise
|
|
|
|
|
|
let animationPromises = Promise.resolve();
|
|
|
|
|
|
// 遍历所有脖子
|
|
|
|
|
|
for (let i = 0; i < neckList.length; i++) {
|
|
|
|
|
|
let neckNode = neckList[i];
|
|
|
|
|
|
// 播放吃掉音符动画
|
|
|
|
|
|
animationPromises = animationPromises.then(() => {
|
|
|
|
|
|
return new Promise((resolve) => {
|
|
|
|
|
|
tween(neckNode)
|
|
|
|
|
|
.to(0.1, { scale: new Vec3(1.2, 1, 1) })
|
|
|
|
|
|
.to(0.1, { scale: new Vec3(1, 1, 1) })
|
|
|
|
|
|
.call(() => resolve())
|
|
|
|
|
|
.start();
|
|
|
|
|
|
});
|
|
|
|
|
|
});
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 播放爆炸特效
|
|
|
|
|
|
playBombEffect() {
|
|
|
|
|
|
// 实例化爆炸特效
|
|
|
|
|
|
let bombEffectNode = instantiate(this.bombEffectPrefab);
|
|
|
|
|
|
// 设置爆炸特效节点的位置
|
|
|
|
|
|
let bombPos = new Vec3(
|
|
|
|
|
|
this.node.position.x,
|
|
|
|
|
|
this.node.position.y + 100,
|
|
|
|
|
|
this.node.position.z,
|
|
|
|
|
|
);
|
|
|
|
|
|
// 设置位置
|
|
|
|
|
|
bombEffectNode.setPosition(bombPos);
|
|
|
|
|
|
// 设置爆炸特效节点的父节点
|
|
|
|
|
|
bombEffectNode.setParent(this.node.parent);
|
|
|
|
|
|
// 播放爆炸特效
|
|
|
|
|
|
bombEffectNode.getComponent(BombEffect).playBombEffect();
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 移动小恐龙到线的位置
|
|
|
|
|
|
moveDinoToLine() {
|
|
|
|
|
|
// 获取线的世界坐标
|
|
|
|
|
|
const linePos = this.lineNode.getWorldPosition();
|
|
|
|
|
|
// 获取小恐龙身体的世界坐标位置
|
|
|
|
|
|
const bodyPos = this.node.getChildByName("dino_body").getWorldPosition();
|
|
|
|
|
|
// 计算两个坐标 y 轴的差值
|
|
|
|
|
|
const deltaY = linePos.y - bodyPos.y;
|
|
|
|
|
|
// 移动小恐龙到线的位置动画
|
|
|
|
|
|
tween(this.node)
|
|
|
|
|
|
.by(1, { position: new Vec3(0, deltaY - 44, 0) })
|
|
|
|
|
|
.start();
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 检测是否通过本关
|
|
|
|
|
|
checkIsPassLevel(): boolean {
|
|
|
|
|
|
// 获取 note_manager 节点
|
|
|
|
|
|
const noteManager = director
|
|
|
|
|
|
.getScene()
|
|
|
|
|
|
.getChildByName("Canvas")
|
|
|
|
|
|
.getChildByName("note_manager");
|
|
|
|
|
|
if (noteManager?.children?.length === 0) return true;
|
|
|
|
|
|
return false;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 继续游戏
|
|
|
|
|
|
continueGame() {
|
|
|
|
|
|
const isPassLevel = this.checkIsPassLevel();
|
|
|
|
|
|
if (isPassLevel) {
|
|
|
|
|
|
// 发送闯关成功事件
|
|
|
|
|
|
EventDispatcher.getTarget().emit(EventDispatcher.PASS_LEVLE);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-05-12 06:46:31 +08:00
|
|
|
|
// 设置小恐龙颜色
|
|
|
|
|
|
setDinoColor() {
|
|
|
|
|
|
// 获取当前小恐龙信息
|
|
|
|
|
|
const dinoInfo = GlobalData.currentDinoInfo;
|
|
|
|
|
|
// 获取小恐龙颜色
|
|
|
|
|
|
const dinoColor = new Color(dinoInfo.dinoColor);
|
|
|
|
|
|
// 设置小恐龙身体颜色
|
|
|
|
|
|
this.node.getChildByName("dino_body").getComponent(Sprite).color =
|
|
|
|
|
|
dinoColor;
|
|
|
|
|
|
// 设置小恐龙头部颜色
|
|
|
|
|
|
this.node.getChildByName("dino_head").getComponent(Sprite).color =
|
|
|
|
|
|
dinoColor;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-04-30 00:13:37 +08:00
|
|
|
|
update(deltaTime: number) {}
|
|
|
|
|
|
}
|