feat(player): 改进移动方向系统和碰撞处理机制
- 优化玩家移动时的方向计算,现在基于下一个路径点而非最终目标点 - 添加移动过程中动态方向更新功能,提供更流畅的动画过渡 - 重构碰撞处理逻辑,统一管理碰撞器状态避免重复触发 - 调整攻击对齐参数,增加垂直偏移量提升战斗视觉效果 - 增强碰撞检测的健壮性,添加节点有效性验证和状态检查
This commit is contained in:
@@ -57,7 +57,8 @@ export class PlayerController extends Component {
|
|||||||
private lastPosition: Vec3 = new Vec3(); // 上一帧位置
|
private lastPosition: Vec3 = new Vec3(); // 上一帧位置
|
||||||
|
|
||||||
private hasWinTimes = 0
|
private hasWinTimes = 0
|
||||||
private readonly attackAlignGap = 10; // 玩家与怪物对阵时的额外左右间距,单位:像素
|
private readonly attackAlignGap = 20; // 玩家与怪物对阵时的额外左右间距,单位:像素
|
||||||
|
private readonly attackVerticalOffset = 25; // 玩家攻击时相对于怪物的垂直偏移量(玩家更高),单位:像素
|
||||||
|
|
||||||
// 道具列表
|
// 道具列表
|
||||||
private props: Node[] = [];
|
private props: Node[] = [];
|
||||||
@@ -268,12 +269,6 @@ export class PlayerController extends Component {
|
|||||||
this.currentPathIndex = 0;
|
this.currentPathIndex = 0;
|
||||||
this.isMoving = true;
|
this.isMoving = true;
|
||||||
|
|
||||||
// 根据移动方向选择动画
|
|
||||||
const animationName = this.getAnimationNameByDirection(startPos, clampedPos);
|
|
||||||
|
|
||||||
// 切换到对应的动画
|
|
||||||
this.switchAnimation(animationName);
|
|
||||||
|
|
||||||
// 使用平滑路径移动
|
// 使用平滑路径移动
|
||||||
this.startSmoothPathMovement();
|
this.startSmoothPathMovement();
|
||||||
}
|
}
|
||||||
@@ -537,8 +532,15 @@ export class PlayerController extends Component {
|
|||||||
this.lastTargetPosition.set(targetPos);
|
this.lastTargetPosition.set(targetPos);
|
||||||
this.lastPosition.set(currentPos);
|
this.lastPosition.set(currentPos);
|
||||||
|
|
||||||
// 在移动前计算并设置方向(只计算一次)
|
// 计算朝向下一个路径点的方向,而不是当前目标点
|
||||||
this.updateMovementDirectionOnce(currentPos, targetPos);
|
let nextPosForDirection = targetPos;
|
||||||
|
if (this.currentPathIndex < this.currentPath.length - 1) {
|
||||||
|
// 如果还有下一个路径点,使用下一个路径点作为方向参考
|
||||||
|
nextPosForDirection = this.currentPath[this.currentPathIndex + 1];
|
||||||
|
}
|
||||||
|
|
||||||
|
// 在移动前计算并设置方向(基于下一个路径点)
|
||||||
|
this.updateMovementDirectionOnce(currentPos, nextPosForDirection);
|
||||||
|
|
||||||
// 使用缓动移动到目标位置
|
// 使用缓动移动到目标位置
|
||||||
this.moveTween = tween(this.player)
|
this.moveTween = tween(this.player)
|
||||||
@@ -579,10 +581,17 @@ export class PlayerController extends Component {
|
|||||||
|
|
||||||
console.log(`开始平滑路径移动,总距离: ${totalDistance.toFixed(2)}, 总时间: ${totalTime.toFixed(2)}秒`);
|
console.log(`开始平滑路径移动,总距离: ${totalDistance.toFixed(2)}, 总时间: ${totalTime.toFixed(2)}秒`);
|
||||||
|
|
||||||
// 在移动前计算并设置方向(只计算一次)
|
// 初始化时先设置第一个路径点的方向
|
||||||
const startPos = this.player.position.clone();
|
if (this.currentPath.length > 1) {
|
||||||
const finalTargetPos = this.currentPath[this.currentPath.length - 1];
|
const startPos = this.player.position.clone();
|
||||||
this.updateMovementDirectionOnce(startPos, finalTargetPos);
|
const nextPos = this.currentPath[1]; // 第二个路径点
|
||||||
|
this.updateMovementDirectionOnce(startPos, nextPos);
|
||||||
|
} else {
|
||||||
|
// 如果只有一个路径点,直接朝向它
|
||||||
|
const startPos = this.player.position.clone();
|
||||||
|
const finalTargetPos = this.currentPath[0];
|
||||||
|
this.updateMovementDirectionOnce(startPos, finalTargetPos);
|
||||||
|
}
|
||||||
|
|
||||||
// 创建连续的路径移动
|
// 创建连续的路径移动
|
||||||
this.moveTween = tween(this.player)
|
this.moveTween = tween(this.player)
|
||||||
@@ -593,6 +602,9 @@ export class PlayerController extends Component {
|
|||||||
const currentPos = this.getPositionOnPath(ratio);
|
const currentPos = this.getPositionOnPath(ratio);
|
||||||
if (currentPos) {
|
if (currentPos) {
|
||||||
target.position = currentPos;
|
target.position = currentPos;
|
||||||
|
|
||||||
|
// 在移动过程中动态更新方向
|
||||||
|
this.updateDirectionDuringMovement(currentPos, ratio);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
onComplete: () => {
|
onComplete: () => {
|
||||||
@@ -673,6 +685,52 @@ export class PlayerController extends Component {
|
|||||||
this.switchAnimation('walk');
|
this.switchAnimation('walk');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 在移动过程中动态更新方向
|
||||||
|
* 根据当前位置在路径中的位置,计算朝向下一个路径点的方向
|
||||||
|
*/
|
||||||
|
private updateDirectionDuringMovement(currentPos: Vec3, ratio: number) {
|
||||||
|
if (!this.player || this.currentPath.length <= 1) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 计算当前在路径中的大致位置
|
||||||
|
const pathLength = this.currentPath.length;
|
||||||
|
const currentPathIndex = Math.floor(ratio * (pathLength - 1));
|
||||||
|
|
||||||
|
// 确保不超出路径范围
|
||||||
|
const nextIndex = Math.min(currentPathIndex + 1, pathLength - 1);
|
||||||
|
|
||||||
|
// 如果已经到达最后一个路径点,不再更新方向
|
||||||
|
if (currentPathIndex >= pathLength - 1) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获取当前路径点和下一个路径点
|
||||||
|
const currentPathPoint = this.currentPath[currentPathIndex];
|
||||||
|
const nextPathPoint = this.currentPath[nextIndex];
|
||||||
|
|
||||||
|
// 计算到下一个路径点的方向
|
||||||
|
const deltaX = nextPathPoint.x - currentPos.x;
|
||||||
|
const deltaY = nextPathPoint.y - currentPos.y;
|
||||||
|
|
||||||
|
// 如果移动距离很小,不更新动画
|
||||||
|
const distance = Math.sqrt(deltaX * deltaX + deltaY * deltaY);
|
||||||
|
if (distance < 1) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 计算新的方向
|
||||||
|
const newDirection = this.resolveDirectionFromDelta(deltaX, deltaY);
|
||||||
|
|
||||||
|
// 只有当方向发生显著变化时才更新
|
||||||
|
if (newDirection !== this.currentDirection) {
|
||||||
|
this.currentDirection = newDirection;
|
||||||
|
this.updatePlayerScale();
|
||||||
|
console.log(`移动过程中更新方向: ${newDirection}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 停止当前移动
|
* 停止当前移动
|
||||||
*/
|
*/
|
||||||
@@ -694,6 +752,31 @@ export class PlayerController extends Component {
|
|||||||
this.switchAnimation('stand');
|
this.switchAnimation('stand');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 统一处理碰撞回调的启停,确保在逻辑失败时能恢复碰撞器状态
|
||||||
|
*/
|
||||||
|
private async processColliderCollision(otherCollider: Collider2D, handler: () => Promise<boolean | void> | boolean | void) {
|
||||||
|
if (!otherCollider || !otherCollider.node || !otherCollider.node.isValid) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!otherCollider.enabled) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
otherCollider.enabled = false;
|
||||||
|
let shouldKeepDisabled = false;
|
||||||
|
|
||||||
|
try {
|
||||||
|
const keepDisabled = await Promise.resolve(handler());
|
||||||
|
shouldKeepDisabled = keepDisabled === true;
|
||||||
|
} finally {
|
||||||
|
if (!shouldKeepDisabled && otherCollider && otherCollider.isValid && otherCollider.node && otherCollider.node.isValid) {
|
||||||
|
otherCollider.enabled = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
update(deltaTime: number) {
|
update(deltaTime: number) {
|
||||||
// 更新逻辑现在主要由缓动系统处理
|
// 更新逻辑现在主要由缓动系统处理
|
||||||
// 这里可以添加其他需要每帧更新的逻辑
|
// 这里可以添加其他需要每帧更新的逻辑
|
||||||
@@ -703,13 +786,28 @@ export class PlayerController extends Component {
|
|||||||
|
|
||||||
onBeginContact(selfCollider: Collider2D, otherCollider: Collider2D) {
|
onBeginContact(selfCollider: Collider2D, otherCollider: Collider2D) {
|
||||||
console.log('碰撞检测', selfCollider.node.name, otherCollider.node.name);
|
console.log('碰撞检测', selfCollider.node.name, otherCollider.node.name);
|
||||||
// 禁用碰撞器,防止重复触发
|
if (!otherCollider || !otherCollider.node || !otherCollider.node.isValid) {
|
||||||
otherCollider.enabled = false;
|
return;
|
||||||
if (otherCollider.node.name.startsWith('guai_')) {
|
|
||||||
void this.handleAttack(selfCollider, otherCollider);
|
|
||||||
} else if (otherCollider.node.name.startsWith('box_')) {
|
|
||||||
void this.handleBoxCollision(selfCollider, otherCollider);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (this.isAttacking || this.isGameOver || this.isWin) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const nodeName = otherCollider.node.name || '';
|
||||||
|
const isMonster = nodeName.startsWith('guai_');
|
||||||
|
const isBox = nodeName.startsWith('box_');
|
||||||
|
|
||||||
|
if (!isMonster && !isBox) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isMonster) {
|
||||||
|
void this.processColliderCollision(otherCollider, () => this.handleAttack(selfCollider, otherCollider));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
void this.processColliderCollision(otherCollider, () => this.handleBoxCollision(selfCollider, otherCollider));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -751,7 +849,7 @@ export class PlayerController extends Component {
|
|||||||
|
|
||||||
const targetWorldPos = new Vec3(
|
const targetWorldPos = new Vec3(
|
||||||
monsterWorldPos.x + monsterOffsetX + directionMultiplier * totalHalfWidth - playerOffsetX,
|
monsterWorldPos.x + monsterOffsetX + directionMultiplier * totalHalfWidth - playerOffsetX,
|
||||||
monsterWorldPos.y + monsterOffsetY - playerOffsetY,
|
monsterWorldPos.y + monsterOffsetY - playerOffsetY + (this.isUpgraded ? 0 : this.attackVerticalOffset),
|
||||||
playerWorldPos.z
|
playerWorldPos.z
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -922,6 +1020,8 @@ export class PlayerController extends Component {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -1045,9 +1145,6 @@ export class PlayerController extends Component {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 防止重复触发
|
|
||||||
otherCollider.enabled = false;
|
|
||||||
|
|
||||||
this.stopMovement();
|
this.stopMovement();
|
||||||
|
|
||||||
await this.alignPlayerForAttack(selfCollider, otherCollider);
|
await this.alignPlayerForAttack(selfCollider, otherCollider);
|
||||||
@@ -1102,6 +1199,8 @@ export class PlayerController extends Component {
|
|||||||
} else {
|
} else {
|
||||||
finalizeBoxOpen();
|
finalizeBoxOpen();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user