121 lines
3.9 KiB
TypeScript
121 lines
3.9 KiB
TypeScript
|
// CameraOrbit.ts
|
|||
|
import { _decorator, Component, Node, Vec3, EventTouch, input, Input, v3 } from 'cc';
|
|||
|
const { ccclass, property } = _decorator;
|
|||
|
|
|||
|
@ccclass('CameraOrbit')
|
|||
|
export class CameraOrbit extends Component {
|
|||
|
@property(Node)
|
|||
|
public targetNode: Node | null = null; // 需通过脚本动态设置目标节点
|
|||
|
|
|||
|
private lastTouchX: number = 0;
|
|||
|
private readonly rotationSpeed: number = 0.6;
|
|||
|
private isTouching: boolean = false;
|
|||
|
private currentAngle: number = 0;
|
|||
|
|
|||
|
// 动态计算参数
|
|||
|
private radius: number = 5; // 初始默认值,会自动修正
|
|||
|
private heightOffset: number = 2; // 初始默认值,会自动修正
|
|||
|
private centerOffset = v3(0, 1, 0);// 中心点Y偏移(角色腰部高度)
|
|||
|
|
|||
|
// 初始化时自动校准参数
|
|||
|
private isInitialized: boolean = false;
|
|||
|
|
|||
|
start() {
|
|||
|
this.tryInitialize();
|
|||
|
}
|
|||
|
|
|||
|
// 每帧更新摄像机位置
|
|||
|
lateUpdate() {
|
|||
|
if (!this.isInitialized) return;
|
|||
|
this.updateCameraPosition(true); // true表示保持当前角度
|
|||
|
}
|
|||
|
|
|||
|
// 动态设置目标节点
|
|||
|
public setTarget(node: Node) {
|
|||
|
this.targetNode = node;
|
|||
|
this.tryInitialize();
|
|||
|
}
|
|||
|
|
|||
|
private tryInitialize() {
|
|||
|
if (!this.targetNode || this.isInitialized) return;
|
|||
|
|
|||
|
// 计算机型参数与目标的相对关系
|
|||
|
const targetWorldPos = this.targetNode.worldPosition;
|
|||
|
const cameraWorldPos = this.node.worldPosition;
|
|||
|
|
|||
|
// 计算水平距离(半径)
|
|||
|
const dx = cameraWorldPos.x - targetWorldPos.x;
|
|||
|
const dz = cameraWorldPos.z - targetWorldPos.z;
|
|||
|
this.radius = Math.sqrt(dx * dx + dz * dz);
|
|||
|
|
|||
|
// 计算起始角度(世界坐标系)
|
|||
|
this.currentAngle = Math.atan2(dx, dz) * 180 / Math.PI;
|
|||
|
|
|||
|
// 计算高度差
|
|||
|
this.heightOffset = cameraWorldPos.y - targetWorldPos.y;
|
|||
|
|
|||
|
this.isInitialized = true;
|
|||
|
}
|
|||
|
|
|||
|
private updateCameraPosition(keepAngle: boolean = false) {
|
|||
|
if (!this.isInitialized) return;
|
|||
|
|
|||
|
const targetWorldPos = this.targetNode!.worldPosition.clone()
|
|||
|
.add(this.centerOffset);
|
|||
|
|
|||
|
// 计算水平坐标
|
|||
|
const angleRad = this.currentAngle * Math.PI / 180;
|
|||
|
const x = targetWorldPos.x + Math.sin(angleRad) * this.radius;
|
|||
|
const z = targetWorldPos.z + Math.cos(angleRad) * this.radius;
|
|||
|
|
|||
|
// 设置摄像机世界坐标
|
|||
|
this.node.worldPosition = new Vec3(
|
|||
|
x,
|
|||
|
targetWorldPos.y + this.heightOffset,
|
|||
|
z
|
|||
|
);
|
|||
|
|
|||
|
// 确保准确朝向目标中心点
|
|||
|
this.node.lookAt(targetWorldPos);
|
|||
|
}
|
|||
|
|
|||
|
// 触摸事件处理(保持不变)
|
|||
|
onEnable() {
|
|||
|
input.on(Input.EventType.TOUCH_START, this.onTouchStart, this);
|
|||
|
input.on(Input.EventType.TOUCH_MOVE, this.onTouchMove, this);
|
|||
|
input.on(Input.EventType.TOUCH_END, this.onTouchEnd, this);
|
|||
|
input.on(Input.EventType.TOUCH_CANCEL, this.onTouchEnd, this);
|
|||
|
}
|
|||
|
|
|||
|
onDisable() {
|
|||
|
input.off(Input.EventType.TOUCH_START, this.onTouchStart, this);
|
|||
|
input.off(Input.EventType.TOUCH_MOVE, this.onTouchMove, this);
|
|||
|
input.off(Input.EventType.TOUCH_END, this.onTouchEnd, this);
|
|||
|
input.off(Input.EventType.TOUCH_CANCEL, this.onTouchEnd, this);
|
|||
|
}
|
|||
|
|
|||
|
private onTouchStart(event: EventTouch) {
|
|||
|
this.isTouching = true;
|
|||
|
this.lastTouchX = event.getLocationX();
|
|||
|
}
|
|||
|
|
|||
|
private onTouchMove(event: EventTouch) {
|
|||
|
if (!this.isTouching) return;
|
|||
|
|
|||
|
const currentTouchX = event.getLocationX();
|
|||
|
const deltaX = currentTouchX - this.lastTouchX;
|
|||
|
|
|||
|
// 更新角度
|
|||
|
this.currentAngle -= deltaX * this.rotationSpeed;
|
|||
|
this.currentAngle = (this.currentAngle + 360) % 360;
|
|||
|
|
|||
|
// 立即更新摄像机位置
|
|||
|
this.updateCameraPosition();
|
|||
|
this.lastTouchX = currentTouchX;
|
|||
|
}
|
|||
|
|
|||
|
private onTouchEnd() {
|
|||
|
this.isTouching = false;
|
|||
|
}
|
|||
|
}
|