feat: 寻路优化
This commit is contained in:
@@ -41,12 +41,6 @@ export class PlayerController extends Component {
|
||||
@property
|
||||
mapHeight: number = 2560; // 地图高度
|
||||
|
||||
@property({ tooltip: '玩家与怪物进入战斗时的理想距离' })
|
||||
attackPreferredDistance: number = 10;
|
||||
|
||||
@property({ tooltip: '允许的距离误差范围,超出后会进行位置调整' })
|
||||
attackDistanceTolerance: number = 10;
|
||||
|
||||
private isMoving: boolean = false;
|
||||
private isAttacking: boolean = false;
|
||||
private currentPath: Vec3[] = [];
|
||||
@@ -59,10 +53,11 @@ export class PlayerController extends Component {
|
||||
private isWin: boolean = false; // 游戏是否胜利(到达终点)
|
||||
private currentDirection: number = 5; // 当前玩家朝向:3表示左/上,5表示右/下,默认为5
|
||||
|
||||
private hasWinTimes = 0
|
||||
// 平滑移动相关变量
|
||||
private moveTween: any = null; // 当前移动的tween对象
|
||||
private lastPosition: Vec3 = new Vec3(); // 上一帧位置
|
||||
|
||||
private readonly _tempVec3A: Vec3 = new Vec3();
|
||||
private readonly _tempVec3B: Vec3 = new Vec3();
|
||||
private hasWinTimes = 0
|
||||
|
||||
// 道具列表
|
||||
private props: Node[] = [];
|
||||
@@ -225,7 +220,7 @@ export class PlayerController extends Component {
|
||||
if (!this.player || !this.pathfinder) return;
|
||||
|
||||
// 停止当前移动
|
||||
this.stopMovement();
|
||||
// this.stopMovement();
|
||||
|
||||
// 限制目标位置在地图边界内
|
||||
const clampedPos = this.clampPlayerPosition(worldPos);
|
||||
@@ -262,7 +257,8 @@ export class PlayerController extends Component {
|
||||
// 切换到对应的动画
|
||||
this.switchAnimation(animationName);
|
||||
|
||||
this.moveToNextWaypoint();
|
||||
// 使用平滑路径移动
|
||||
this.startSmoothPathMovement();
|
||||
}
|
||||
|
||||
// 限制玩家位置在地图边界内
|
||||
@@ -374,12 +370,15 @@ export class PlayerController extends Component {
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
// 停止当前的移动tween
|
||||
if (this.moveTween) {
|
||||
this.moveTween.stop();
|
||||
this.moveTween = null;
|
||||
}
|
||||
|
||||
const targetPos = this.currentPath[this.currentPathIndex];
|
||||
const currentPos = this.player.position;
|
||||
|
||||
|
||||
// 计算移动距离和时间
|
||||
const distance = Vec3.distance(currentPos, targetPos);
|
||||
const moveTime = distance / this.moveSpeed;
|
||||
@@ -388,10 +387,16 @@ export class PlayerController extends Component {
|
||||
|
||||
// 记录目标位置用于方向判断
|
||||
this.lastTargetPosition.set(targetPos);
|
||||
this.lastPosition.set(currentPos);
|
||||
|
||||
// 使用缓动移动到目标位置
|
||||
tween(this.player)
|
||||
this.moveTween = tween(this.player)
|
||||
.to(moveTime, { position: targetPos }, {
|
||||
easing: 'linear', // 使用线性插值,保持匀速移动
|
||||
onUpdate: (target: Node) => {
|
||||
// 在移动过程中更新动画方向
|
||||
this.updateMovementDirection(target.position);
|
||||
},
|
||||
onComplete: () => {
|
||||
this.currentPathIndex++;
|
||||
this.moveToNextWaypoint();
|
||||
@@ -400,10 +405,153 @@ export class PlayerController extends Component {
|
||||
.start();
|
||||
}
|
||||
|
||||
/**
|
||||
* 开始平滑路径移动
|
||||
*/
|
||||
private startSmoothPathMovement() {
|
||||
if (!this.player || this.currentPath.length === 0) {
|
||||
this.isMoving = false;
|
||||
this.switchAnimation('stand');
|
||||
return;
|
||||
}
|
||||
|
||||
// 停止当前的移动tween
|
||||
if (this.moveTween) {
|
||||
this.moveTween.stop();
|
||||
this.moveTween = null;
|
||||
}
|
||||
|
||||
// 计算总路径长度
|
||||
let totalDistance = 0;
|
||||
for (let i = 0; i < this.currentPath.length - 1; i++) {
|
||||
totalDistance += Vec3.distance(this.currentPath[i], this.currentPath[i + 1]);
|
||||
}
|
||||
|
||||
// 计算总移动时间
|
||||
const totalTime = totalDistance / this.moveSpeed;
|
||||
|
||||
console.log(`开始平滑路径移动,总距离: ${totalDistance.toFixed(2)}, 总时间: ${totalTime.toFixed(2)}秒`);
|
||||
|
||||
// 创建连续的路径移动
|
||||
this.moveTween = tween(this.player)
|
||||
.to(totalTime, { position: this.currentPath[this.currentPath.length - 1] }, {
|
||||
easing: 'linear',
|
||||
onUpdate: (target: Node, ratio: number) => {
|
||||
// 根据进度计算当前位置
|
||||
const currentPos = this.getPositionOnPath(ratio);
|
||||
if (currentPos) {
|
||||
target.position = currentPos;
|
||||
this.updateMovementDirection(currentPos);
|
||||
}
|
||||
},
|
||||
onComplete: () => {
|
||||
this.isMoving = false;
|
||||
this.switchAnimation('stand');
|
||||
console.log('平滑路径移动完成');
|
||||
}
|
||||
})
|
||||
.start();
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据路径进度获取当前位置
|
||||
*/
|
||||
private getPositionOnPath(ratio: number): Vec3 | null {
|
||||
if (!this.player || this.currentPath.length === 0) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// 计算总路径长度
|
||||
const segmentLengths: number[] = [];
|
||||
let totalLength = 0;
|
||||
|
||||
for (let i = 0; i < this.currentPath.length - 1; i++) {
|
||||
const length = Vec3.distance(this.currentPath[i], this.currentPath[i + 1]);
|
||||
segmentLengths.push(length);
|
||||
totalLength += length;
|
||||
}
|
||||
|
||||
// 计算目标距离
|
||||
const targetDistance = totalLength * ratio;
|
||||
|
||||
// 找到对应的路径段
|
||||
let currentDistance = 0;
|
||||
for (let i = 0; i < segmentLengths.length; i++) {
|
||||
if (currentDistance + segmentLengths[i] >= targetDistance) {
|
||||
// 在当前段内
|
||||
const segmentRatio = (targetDistance - currentDistance) / segmentLengths[i];
|
||||
const startPos = this.currentPath[i];
|
||||
const endPos = this.currentPath[i + 1];
|
||||
|
||||
// 线性插值计算当前位置
|
||||
return new Vec3(
|
||||
startPos.x + (endPos.x - startPos.x) * segmentRatio,
|
||||
startPos.y + (endPos.y - startPos.y) * segmentRatio,
|
||||
startPos.z + (endPos.z - startPos.z) * segmentRatio
|
||||
);
|
||||
}
|
||||
currentDistance += segmentLengths[i];
|
||||
}
|
||||
|
||||
// 如果超出范围,返回终点
|
||||
return this.currentPath[this.currentPath.length - 1];
|
||||
}
|
||||
|
||||
/**
|
||||
* 在移动过程中更新动画方向
|
||||
*/
|
||||
private updateMovementDirection(currentPos: Vec3) {
|
||||
if (!this.player || this.currentPath.length === 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
// 计算移动方向(基于下一路径点)
|
||||
let targetPos: Vec3;
|
||||
if (this.currentPathIndex < this.currentPath.length - 1) {
|
||||
targetPos = this.currentPath[this.currentPathIndex + 1];
|
||||
} else {
|
||||
targetPos = this.currentPath[this.currentPath.length - 1];
|
||||
}
|
||||
|
||||
const deltaX = targetPos.x - currentPos.x;
|
||||
const deltaY = targetPos.y - currentPos.y;
|
||||
|
||||
// 如果移动距离很小,不更新动画
|
||||
const distance = Math.sqrt(deltaX * deltaX + deltaY * deltaY);
|
||||
if (distance < 1) {
|
||||
return;
|
||||
}
|
||||
|
||||
// 计算主要移动方向
|
||||
const absX = Math.abs(deltaX);
|
||||
const absY = Math.abs(deltaY);
|
||||
|
||||
// 根据移动方向选择动画
|
||||
let animationName = 'walk';
|
||||
if (absX > absY) {
|
||||
// 水平移动为主
|
||||
this.currentDirection = deltaX < 0 ? 3 : 5;
|
||||
animationName = deltaX < 0 ? 'walk3' : 'walk5';
|
||||
} else {
|
||||
// 垂直移动为主
|
||||
this.currentDirection = deltaY < 0 ? 3 : 5;
|
||||
animationName = deltaY < 0 ? 'walk3' : 'walk5';
|
||||
}
|
||||
|
||||
// 切换到对应的动画
|
||||
this.switchAnimation(animationName);
|
||||
}
|
||||
|
||||
/**
|
||||
* 停止当前移动
|
||||
*/
|
||||
private stopMovement() {
|
||||
// 停止当前的移动tween
|
||||
if (this.moveTween) {
|
||||
this.moveTween.stop();
|
||||
this.moveTween = null;
|
||||
}
|
||||
|
||||
if (this.player) {
|
||||
tween(this.player).stop();
|
||||
}
|
||||
@@ -447,12 +595,6 @@ export class PlayerController extends Component {
|
||||
|
||||
this.stopMovement();
|
||||
|
||||
// this.scheduleOnce(() => {
|
||||
// this.adjustPositionsForAttack(otherCollider.node);
|
||||
// }, 0);
|
||||
|
||||
|
||||
|
||||
// 获取玩家和怪物的生命值
|
||||
const playerHpLabel = this.player.getChildByName('hp');
|
||||
const monsterHpLabel = otherCollider.node.getChildByName('hp');
|
||||
@@ -643,76 +785,6 @@ export class PlayerController extends Component {
|
||||
}
|
||||
}
|
||||
|
||||
private adjustPositionsForAttack(monsterNode: Node) {
|
||||
if (!this.player || !monsterNode) {
|
||||
return;
|
||||
}
|
||||
|
||||
const desiredDistance = Math.max(0, this.attackPreferredDistance);
|
||||
const tolerance = Math.max(0, this.attackDistanceTolerance);
|
||||
|
||||
if (desiredDistance === 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
const playerWorldPos = this._tempVec3A;
|
||||
const monsterWorldPos = this._tempVec3B;
|
||||
this.player.getWorldPosition(playerWorldPos);
|
||||
monsterNode.getWorldPosition(monsterWorldPos);
|
||||
|
||||
const playerTarget = playerWorldPos.clone();
|
||||
const monsterTarget = monsterWorldPos.clone();
|
||||
|
||||
const deltaX = playerWorldPos.x - monsterWorldPos.x;
|
||||
const horizontalDistance = Math.abs(deltaX);
|
||||
const verticalDifference = Math.abs(playerWorldPos.y - monsterWorldPos.y);
|
||||
|
||||
const targetY = (playerWorldPos.y + monsterWorldPos.y) * 0.5;
|
||||
let adjusted = false;
|
||||
if (verticalDifference > 1e-3) {
|
||||
playerTarget.y = targetY;
|
||||
monsterTarget.y = targetY;
|
||||
adjusted = true;
|
||||
}
|
||||
|
||||
const orderCorrect = playerWorldPos.x > monsterWorldPos.x;
|
||||
const distanceOutOfRange = horizontalDistance < desiredDistance - tolerance || horizontalDistance > desiredDistance + tolerance;
|
||||
|
||||
if (!orderCorrect || distanceOutOfRange) {
|
||||
const midX = (playerWorldPos.x + monsterWorldPos.x) * 0.5;
|
||||
const halfDistance = desiredDistance * 0.5;
|
||||
playerTarget.x = midX + halfDistance;
|
||||
monsterTarget.x = midX - halfDistance;
|
||||
adjusted = true;
|
||||
}
|
||||
|
||||
if (!adjusted) {
|
||||
return;
|
||||
}
|
||||
|
||||
// 确保最终目标仍然满足玩家在右侧的要求
|
||||
if (playerTarget.x <= monsterTarget.x) {
|
||||
const swapMidX = (playerTarget.x + monsterTarget.x) * 0.5;
|
||||
const halfDistance = desiredDistance * 0.5;
|
||||
playerTarget.x = swapMidX + halfDistance;
|
||||
monsterTarget.x = swapMidX - halfDistance;
|
||||
}
|
||||
|
||||
const clampedPlayer = this.clampPositionWithinMap(playerTarget);
|
||||
const clampedMonster = this.clampPositionWithinMap(monsterTarget);
|
||||
|
||||
this.setNodeWorldPosition(this.player, clampedPlayer);
|
||||
this.setNodeWorldPosition(monsterNode, clampedMonster);
|
||||
|
||||
this.player.getWorldPosition(playerWorldPos);
|
||||
monsterNode.getWorldPosition(monsterWorldPos);
|
||||
const finalHorizontalDistance = Math.abs(playerWorldPos.x - monsterWorldPos.x);
|
||||
const finalVerticalOffset = Math.abs(playerWorldPos.y - monsterWorldPos.y);
|
||||
console.log(`战斗位置已调整,水平距离: ${finalHorizontalDistance.toFixed(2)}, 垂直误差: ${finalVerticalOffset.toFixed(2)}`);
|
||||
if (playerWorldPos.x >= monsterWorldPos.x) {
|
||||
this.currentDirection = 3;
|
||||
}
|
||||
}
|
||||
|
||||
private setNodeWorldPosition(node: Node, worldPos: Vec3) {
|
||||
const parent = node.parent;
|
||||
|
||||
Reference in New Issue
Block a user