feat: 支持道具以及升级

This commit is contained in:
richarjiang
2025-09-23 16:39:24 +08:00
parent 9972db71be
commit 924862598a
179 changed files with 12173 additions and 309 deletions

View File

@@ -1,9 +1,11 @@
import { _decorator, Component, Node, Vec3, input, Input, EventTouch, Camera, view, tween, Animation, Collider2D, Contact2DType, Label, Color } from 'cc';
import { _decorator, Component, Node, Vec3, input, Input, EventTouch, Camera, view, tween, Animation, Collider2D, Contact2DType, Label, Color, Canvas } from 'cc';
import { TiledMapPathfinder } from './TiledMapPathfinder';
const { ccclass, property } = _decorator;
@ccclass('PlayerController')
export class PlayerController extends Component {
@property(Canvas)
canvas: Canvas | null = null;
@property(Node)
player: Node | null = null; // 玩家节点
@@ -30,6 +32,10 @@ export class PlayerController extends Component {
private originalPosition: Vec3 = new Vec3();
private currentAnimation: string = 'stand'; // 当前播放的动画
private lastTargetPosition: Vec3 = new Vec3(); // 上一个目标位置,用于方向判断
private isUpgraded: boolean = false; // 玩家是否已升级
// 道具列表
private props: Node[] = [];
onLoad() {
// 注册触摸事件
@@ -40,6 +46,8 @@ export class PlayerController extends Component {
// 监听碰撞事件
collider.on(Contact2DType.BEGIN_CONTACT, this.onBeginContact, this);
}
this.initProps();
}
onDestroy() {
@@ -53,6 +61,61 @@ export class PlayerController extends Component {
}
}
initProps() {
if (!this.canvas) {
console.warn('Canvas未设置无法初始化道具');
return;
}
// 查找Canvas下的Props节点
const propsNode = this.canvas.node.getChildByName('Props');
if (!propsNode) {
console.warn('未找到Props节点请确保Canvas下有名为"Props"的节点');
return;
}
// 清空现有的道具列表
this.props.length = 0;
// 获取Props节点下的所有子节点
for (let i = 0; i < propsNode.children.length; i++) {
const child = propsNode.children[i];
this.props.push(child);
console.log(`添加道具: ${child.name}`);
// 为每个道具添加悬浮动画
this.addFloatingAnimation(child);
}
console.log(`初始化道具完成,共找到 ${this.props.length} 个道具`);
}
/**
* 为道具添加悬浮动画
*/
private addFloatingAnimation(propNode: Node) {
if (!propNode) return;
// 保存原始位置
const originalY = propNode.position.y;
const floatHeight = 10; // 悬浮高度(像素)
const floatDuration = 2; // 悬浮周期(秒)
// 创建上下浮动的动画
tween(propNode)
.to(floatDuration / 2, { position: new Vec3(propNode.position.x, originalY + floatHeight, propNode.position.z) }, {
easing: 'sineInOut'
})
.to(floatDuration / 2, { position: new Vec3(propNode.position.x, originalY, propNode.position.z) }, {
easing: 'sineInOut'
})
.union() // 将动画串联起来
.repeatForever() // 无限重复
.start();
console.log(`为道具 ${propNode.name} 添加悬浮动画`);
}
private onTouchStart(event: EventTouch) {
if (!this.player || !this.camera || !this.pathfinder || this.isAttacking) return;
@@ -197,24 +260,30 @@ export class PlayerController extends Component {
return;
}
if (this.currentAnimation === animationName) {
// 如果玩家已升级,在动画名称后添加 "_2" 后缀
let finalAnimationName = animationName;
if (this.isUpgraded && !animationName.endsWith('_2')) {
finalAnimationName = animationName + '_2';
}
if (this.currentAnimation === finalAnimationName) {
return; // 已经是目标动画,不需要切换
}
const animation = this.player.getComponent(Animation);
if (animation) {
// 检查动画是否存在
const state = animation.getState(animationName);
const state = animation.getState(finalAnimationName);
if (!state) {
console.warn(`动画 ${animationName} 不存在,使用默认动画`);
console.warn(`动画 ${finalAnimationName} 不存在,使用默认动画`);
this.currentAnimation = 'stand';
animation.play('stand');
return;
}
this.currentAnimation = animationName;
animation.play(animationName);
console.log(`切换动画: ${animationName}`);
this.currentAnimation = finalAnimationName;
animation.play(finalAnimationName);
console.log(`切换动画: ${finalAnimationName}`);
} else {
console.warn('未找到Animation组件无法播放动画');
}
@@ -331,7 +400,7 @@ export class PlayerController extends Component {
this.switchAnimation('attack');
// 2秒后判定攻击结果
this.scheduleOnce(() => {
this.scheduleOnce(async () => {
// 比较生命值,判断输赢
console.log('判定攻击结果玩家HP:', playerHp, '怪物HP:', monsterHp);
if (playerHp >= monsterHp) {
@@ -350,6 +419,11 @@ export class PlayerController extends Component {
monsterAnimation.play(`${otherCollider.node.name}_die`);
}
// 如果是攻击 guai_2 并且成功,创建道具飞向 player 的动画
if (otherCollider.node.name === 'guai_2') {
await this.createPropsFlyToPlayerAnimation()
}
// 1秒后怪物消失
this.scheduleOnce(() => {
otherCollider.node.destroy();
@@ -430,4 +504,98 @@ export class PlayerController extends Component {
.to(0.1, { color: originalColor })
.start();
}
/**
* 创建道具飞向玩家的动画(同步方法)
*/
private async createPropsFlyToPlayerAnimation(): Promise<void> {
if (!this.player || this.props.length === 0) {
console.warn('玩家或道具不存在,无法创建飞行动画');
return;
}
console.log('创建道具飞向玩家的动画');
// 获取玩家位置
const playerPos = this.player.position.clone();
// 创建所有道具的飞行动画承诺
const flyPromises: Promise<void>[] = [];
// 为每个道具创建飞行动画
this.props.forEach((prop, index) => {
if (!prop || !prop.isValid) return;
// 保存道具原始位置
const originalPos = prop.position.clone();
// 计算飞行时间(根据距离调整)
const distance = Vec3.distance(originalPos, playerPos);
const flyDuration = Math.max(0.5, distance / 500); // 最少0.5秒速度500像素/秒
// 添加延迟,让道具依次飞向玩家
const delay = index * 0.1;
// 停止道具的悬浮动画
tween(prop).stop();
// 创建飞行动画的承诺
const flyPromise = new Promise<void>((resolve) => {
this.scheduleOnce(() => {
tween(prop)
.to(flyDuration, {
position: new Vec3(playerPos.x, playerPos.y, playerPos.z)
}, {
easing: 'quadOut',
onComplete: () => {
// 道具到达玩家位置后消失
prop.destroy();
console.log(`道具 ${prop.name} 已到达玩家位置并消失`);
resolve();
}
})
.start();
}, delay);
});
flyPromises.push(flyPromise);
});
// 等待所有道具飞行动画完成
await Promise.all(flyPromises);
// 所有动画完成后,播放升级动画并设置升级状态
this.playLevelUpAnimation();
this.isUpgraded = true;
console.log('所有道具飞行动画完成,玩家已升级,后续动画将使用升级版本');
}
/**
* 播放玩家升级动画
*/
private playLevelUpAnimation() {
if (!this.player) {
console.warn('玩家节点不存在,无法播放升级动画');
return;
}
// 查找levelUp子节点
const levelUpNode = this.player.getChildByName('levelUp');
if (!levelUpNode) {
console.warn('未找到levelUp子节点');
return;
}
// 获取动画组件
const levelUpAnimation = levelUpNode.getComponent(Animation);
if (!levelUpAnimation) {
console.warn('levelUp节点未找到Animation组件');
return;
}
console.log('播放玩家升级动画');
// 播放升级动画
levelUpAnimation.play('levelUp');
}
}