187 lines
5.4 KiB
TypeScript
187 lines
5.4 KiB
TypeScript
import { _decorator, Component, Node, Vec3, input, Input, EventTouch, Camera, view, tween } from 'cc';
|
||
import { TiledMapPathfinder } from './TiledMapPathfinder';
|
||
const { ccclass, property } = _decorator;
|
||
|
||
@ccclass('PlayerController')
|
||
export class PlayerController extends Component {
|
||
|
||
@property(Node)
|
||
player: Node | null = null; // 玩家节点
|
||
|
||
@property(Camera)
|
||
camera: Camera | null = null; // 主摄像机
|
||
|
||
@property(TiledMapPathfinder)
|
||
pathfinder: TiledMapPathfinder | null = null; // 寻路组件
|
||
|
||
@property({ range: [1, 300] })
|
||
moveSpeed: number = 300; // 移动速度(像素/秒)
|
||
|
||
@property
|
||
mapWidth: number = 1080; // 地图宽度
|
||
|
||
@property
|
||
mapHeight: number = 2560; // 地图高度
|
||
|
||
private isMoving: boolean = false;
|
||
private currentPath: Vec3[] = [];
|
||
private currentPathIndex: number = 0;
|
||
private originalPosition: Vec3 = new Vec3();
|
||
|
||
onLoad() {
|
||
// 注册触摸事件
|
||
input.on(Input.EventType.TOUCH_START, this.onTouchStart, this);
|
||
}
|
||
|
||
onDestroy() {
|
||
// 移除触摸事件
|
||
input.off(Input.EventType.TOUCH_START, this.onTouchStart, this);
|
||
}
|
||
|
||
start() {
|
||
if (this.player) {
|
||
this.originalPosition.set(this.player.position);
|
||
}
|
||
}
|
||
|
||
private onTouchStart(event: EventTouch) {
|
||
if (!this.player || !this.camera || !this.pathfinder) return;
|
||
|
||
// 获取触摸点的UI坐标
|
||
const touchLocation = event.getUILocation();
|
||
|
||
// 将UI坐标转换为世界坐标
|
||
const worldPos = this.screenToWorldPoint(touchLocation);
|
||
|
||
console.log(`触摸UI坐标: (${touchLocation.x}, ${touchLocation.y})`);
|
||
console.log(`转换后世界坐标: (${worldPos.x.toFixed(2)}, ${worldPos.y.toFixed(2)})`);
|
||
|
||
this.moveToPositionWithPathfinding(worldPos);
|
||
}
|
||
|
||
private screenToWorldPoint(screenPos: { x: number, y: number }): Vec3 {
|
||
if (!this.camera) {
|
||
console.error('Camera未设置,无法进行坐标转换');
|
||
return new Vec3(screenPos.x, screenPos.y, 0);
|
||
}
|
||
|
||
// 获取可见区域大小
|
||
const visibleSize = view.getVisibleSize();
|
||
|
||
// 计算屏幕中心点
|
||
const centerX = visibleSize.width * 0.5;
|
||
const centerY = visibleSize.height * 0.5;
|
||
|
||
// 将屏幕坐标转换为以屏幕中心为原点的坐标
|
||
const normalizedX = screenPos.x - centerX;
|
||
const normalizedY = screenPos.y - centerY;
|
||
|
||
// 考虑相机的位置偏移
|
||
const cameraPos = this.camera.node.position;
|
||
|
||
// 计算世界坐标
|
||
const worldX = normalizedX + cameraPos.x;
|
||
const worldY = normalizedY + cameraPos.y;
|
||
|
||
return new Vec3(worldX, worldY, 0);
|
||
}
|
||
|
||
|
||
private moveToPositionWithPathfinding(worldPos: Vec3) {
|
||
if (!this.player || !this.pathfinder) return;
|
||
|
||
// 停止当前移动
|
||
this.stopMovement();
|
||
|
||
// 限制目标位置在地图边界内
|
||
const clampedPos = this.clampPlayerPosition(worldPos);
|
||
|
||
// 检查目标位置是否可行走
|
||
if (!this.pathfinder.isWorldPositionWalkable(clampedPos)) {
|
||
console.log('目标位置不可行走,寻找最近的可行走位置');
|
||
const closestWalkable = this.pathfinder.getClosestWalkablePosition(clampedPos);
|
||
if (!closestWalkable) {
|
||
console.warn('找不到可行走的位置');
|
||
return;
|
||
}
|
||
clampedPos.set(closestWalkable);
|
||
}
|
||
|
||
// 使用寻路算法计算路径
|
||
const startPos = this.player.position;
|
||
this.currentPath = this.pathfinder.findPath(startPos, clampedPos);
|
||
|
||
if (this.currentPath.length === 0) {
|
||
console.warn('无法找到路径');
|
||
return;
|
||
}
|
||
|
||
console.log(`找到路径,包含${this.currentPath.length}个点`);
|
||
|
||
// 开始沿路径移动
|
||
this.currentPathIndex = 0;
|
||
this.isMoving = true;
|
||
this.moveToNextWaypoint();
|
||
}
|
||
|
||
// 限制玩家位置在地图边界内
|
||
private clampPlayerPosition(position: Vec3): Vec3 {
|
||
// 计算地图边界(地图锚点为0.5,0.5,所以范围是-mapWidth/2到+mapWidth/2)
|
||
const mapHalfWidth = this.mapWidth * 0.5;
|
||
const mapHalfHeight = this.mapHeight * 0.5;
|
||
|
||
// 限制玩家位置
|
||
const clampedPosition = position.clone();
|
||
clampedPosition.x = Math.max(-mapHalfWidth, Math.min(mapHalfWidth, position.x));
|
||
clampedPosition.y = Math.max(-mapHalfHeight, Math.min(mapHalfHeight, position.y));
|
||
|
||
return clampedPosition;
|
||
}
|
||
|
||
/**
|
||
* 移动到路径中的下一个路径点
|
||
*/
|
||
private moveToNextWaypoint() {
|
||
if (!this.player || this.currentPath.length === 0 || this.currentPathIndex >= this.currentPath.length) {
|
||
this.isMoving = false;
|
||
console.log('路径移动完成');
|
||
return;
|
||
}
|
||
|
||
const targetPos = this.currentPath[this.currentPathIndex];
|
||
const currentPos = this.player.position;
|
||
|
||
// 计算移动距离和时间
|
||
const distance = Vec3.distance(currentPos, targetPos);
|
||
const moveTime = distance / this.moveSpeed;
|
||
|
||
console.log(`移动到路径点${this.currentPathIndex}: (${targetPos.x.toFixed(2)}, ${targetPos.y.toFixed(2)})`);
|
||
|
||
// 使用缓动移动到目标位置
|
||
tween(this.player)
|
||
.to(moveTime, { position: targetPos }, {
|
||
onComplete: () => {
|
||
this.currentPathIndex++;
|
||
this.moveToNextWaypoint();
|
||
}
|
||
})
|
||
.start();
|
||
}
|
||
|
||
/**
|
||
* 停止当前移动
|
||
*/
|
||
private stopMovement() {
|
||
if (this.player) {
|
||
tween(this.player).stop();
|
||
}
|
||
this.isMoving = false;
|
||
this.currentPath = [];
|
||
this.currentPathIndex = 0;
|
||
}
|
||
|
||
update(deltaTime: number) {
|
||
// 更新逻辑现在主要由缓动系统处理
|
||
// 这里可以添加其他需要每帧更新的逻辑
|
||
}
|
||
} |