203 lines
6.3 KiB
TypeScript
203 lines
6.3 KiB
TypeScript
|
|
import { _decorator, Component, Node, Camera, Vec3, input, Input, EventMouse, EventTouch, KeyCode, Quat } from 'cc';
|
||
|
|
|
||
|
|
const { ccclass, property } = _decorator;
|
||
|
|
|
||
|
|
/**
|
||
|
|
* RTS相机控制器
|
||
|
|
* 提供RTS游戏常用的相机控制功能
|
||
|
|
*/
|
||
|
|
@ccclass('RTSCameraController')
|
||
|
|
export class RTSCameraController extends Component {
|
||
|
|
|
||
|
|
@property
|
||
|
|
moveSpeed: number = 10;
|
||
|
|
|
||
|
|
@property
|
||
|
|
rotateSpeed: number = 60;
|
||
|
|
|
||
|
|
@property
|
||
|
|
zoomSpeed: number = 5;
|
||
|
|
|
||
|
|
@property
|
||
|
|
minZoom: number = 5;
|
||
|
|
|
||
|
|
@property
|
||
|
|
maxZoom: number = 30;
|
||
|
|
|
||
|
|
@property
|
||
|
|
boundarySize: number = 50;
|
||
|
|
|
||
|
|
private camera: Camera = null!;
|
||
|
|
private isMouseDown: boolean = false;
|
||
|
|
private lastMousePosition: Vec3 = Vec3.ZERO.clone();
|
||
|
|
private currentZoom: number = 15;
|
||
|
|
|
||
|
|
// 键盘输入状态
|
||
|
|
private keyStates: { [key: string]: boolean } = {};
|
||
|
|
|
||
|
|
onLoad() {
|
||
|
|
this.camera = this.getComponent(Camera)!;
|
||
|
|
this.currentZoom = this.node.position.y;
|
||
|
|
|
||
|
|
// 注册输入事件
|
||
|
|
input.on(Input.EventType.MOUSE_DOWN, this.onMouseDown, this);
|
||
|
|
input.on(Input.EventType.MOUSE_UP, this.onMouseUp, this);
|
||
|
|
input.on(Input.EventType.MOUSE_MOVE, this.onMouseMove, this);
|
||
|
|
input.on(Input.EventType.MOUSE_WHEEL, this.onMouseWheel, this);
|
||
|
|
input.on(Input.EventType.KEY_DOWN, this.onKeyDown, this);
|
||
|
|
input.on(Input.EventType.KEY_UP, this.onKeyUp, this);
|
||
|
|
}
|
||
|
|
|
||
|
|
onDestroy() {
|
||
|
|
// 取消注册输入事件
|
||
|
|
input.off(Input.EventType.MOUSE_DOWN, this.onMouseDown, this);
|
||
|
|
input.off(Input.EventType.MOUSE_UP, this.onMouseUp, this);
|
||
|
|
input.off(Input.EventType.MOUSE_MOVE, this.onMouseMove, this);
|
||
|
|
input.off(Input.EventType.MOUSE_WHEEL, this.onMouseWheel, this);
|
||
|
|
input.off(Input.EventType.KEY_DOWN, this.onKeyDown, this);
|
||
|
|
input.off(Input.EventType.KEY_UP, this.onKeyUp, this);
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* 鼠标按下事件
|
||
|
|
*/
|
||
|
|
private onMouseDown(event: EventMouse) {
|
||
|
|
if (event.getButton() === EventMouse.BUTTON_MIDDLE) {
|
||
|
|
this.isMouseDown = true;
|
||
|
|
this.lastMousePosition.set(event.getLocationX(), event.getLocationY(), 0);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* 鼠标抬起事件
|
||
|
|
*/
|
||
|
|
private onMouseUp(event: EventMouse) {
|
||
|
|
if (event.getButton() === EventMouse.BUTTON_MIDDLE) {
|
||
|
|
this.isMouseDown = false;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* 鼠标移动事件
|
||
|
|
*/
|
||
|
|
private onMouseMove(event: EventMouse) {
|
||
|
|
if (this.isMouseDown) {
|
||
|
|
const deltaX = event.getLocationX() - this.lastMousePosition.x;
|
||
|
|
const deltaY = event.getLocationY() - this.lastMousePosition.y;
|
||
|
|
|
||
|
|
// 相机平移
|
||
|
|
const moveVector = new Vec3(-deltaX * 0.01, 0, deltaY * 0.01);
|
||
|
|
this.node.translate(moveVector);
|
||
|
|
|
||
|
|
this.lastMousePosition.set(event.getLocationX(), event.getLocationY(), 0);
|
||
|
|
|
||
|
|
// 限制相机边界
|
||
|
|
this.clampCameraBounds();
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* 鼠标滚轮事件
|
||
|
|
*/
|
||
|
|
private onMouseWheel(event: EventMouse) {
|
||
|
|
const scrollY = event.getScrollY();
|
||
|
|
this.currentZoom -= scrollY * this.zoomSpeed * 0.1;
|
||
|
|
this.currentZoom = Math.max(this.minZoom, Math.min(this.maxZoom, this.currentZoom));
|
||
|
|
|
||
|
|
const pos = this.node.position;
|
||
|
|
this.node.setPosition(pos.x, this.currentZoom, pos.z);
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* 键盘按下事件
|
||
|
|
*/
|
||
|
|
private onKeyDown(event: any) {
|
||
|
|
this.keyStates[event.keyCode] = true;
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* 键盘抬起事件
|
||
|
|
*/
|
||
|
|
private onKeyUp(event: any) {
|
||
|
|
this.keyStates[event.keyCode] = false;
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* 更新相机移动
|
||
|
|
*/
|
||
|
|
update(deltaTime: number) {
|
||
|
|
this.handleKeyboardMovement(deltaTime);
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* 处理键盘移动
|
||
|
|
*/
|
||
|
|
private handleKeyboardMovement(deltaTime: number) {
|
||
|
|
const moveDistance = this.moveSpeed * deltaTime;
|
||
|
|
let moveVector = Vec3.ZERO.clone();
|
||
|
|
|
||
|
|
// WASD 移动
|
||
|
|
if (this.keyStates[KeyCode.KEY_W] || this.keyStates[KeyCode.ARROW_UP]) {
|
||
|
|
moveVector.z -= moveDistance;
|
||
|
|
}
|
||
|
|
if (this.keyStates[KeyCode.KEY_S] || this.keyStates[KeyCode.ARROW_DOWN]) {
|
||
|
|
moveVector.z += moveDistance;
|
||
|
|
}
|
||
|
|
if (this.keyStates[KeyCode.KEY_A] || this.keyStates[KeyCode.ARROW_LEFT]) {
|
||
|
|
moveVector.x -= moveDistance;
|
||
|
|
}
|
||
|
|
if (this.keyStates[KeyCode.KEY_D] || this.keyStates[KeyCode.ARROW_RIGHT]) {
|
||
|
|
moveVector.x += moveDistance;
|
||
|
|
}
|
||
|
|
|
||
|
|
// 应用移动
|
||
|
|
if (!moveVector.equals(Vec3.ZERO)) {
|
||
|
|
this.node.translate(moveVector);
|
||
|
|
this.clampCameraBounds();
|
||
|
|
}
|
||
|
|
|
||
|
|
// QE 旋转
|
||
|
|
if (this.keyStates[KeyCode.KEY_Q]) {
|
||
|
|
const rotateAngle = this.rotateSpeed * deltaTime * Math.PI / 180;
|
||
|
|
const currentRotation = this.node.rotation.clone();
|
||
|
|
const yRotation = Quat.fromAxisAngle(new Quat(), Vec3.UP, rotateAngle);
|
||
|
|
Quat.multiply(currentRotation, currentRotation, yRotation);
|
||
|
|
this.node.rotation = currentRotation;
|
||
|
|
}
|
||
|
|
if (this.keyStates[KeyCode.KEY_E]) {
|
||
|
|
const rotateAngle = -this.rotateSpeed * deltaTime * Math.PI / 180;
|
||
|
|
const currentRotation = this.node.rotation.clone();
|
||
|
|
const yRotation = Quat.fromAxisAngle(new Quat(), Vec3.UP, rotateAngle);
|
||
|
|
Quat.multiply(currentRotation, currentRotation, yRotation);
|
||
|
|
this.node.rotation = currentRotation;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* 限制相机边界
|
||
|
|
*/
|
||
|
|
private clampCameraBounds() {
|
||
|
|
const pos = this.node.position;
|
||
|
|
const clampedX = Math.max(-this.boundarySize, Math.min(this.boundarySize, pos.x));
|
||
|
|
const clampedZ = Math.max(-this.boundarySize, Math.min(this.boundarySize, pos.z));
|
||
|
|
|
||
|
|
this.node.setPosition(clampedX, pos.y, clampedZ);
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* 设置相机位置
|
||
|
|
*/
|
||
|
|
setCameraPosition(position: Vec3) {
|
||
|
|
this.node.setPosition(position);
|
||
|
|
this.clampCameraBounds();
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* 聚焦到目标位置
|
||
|
|
*/
|
||
|
|
focusOnTarget(target: Vec3, duration: number = 1.0) {
|
||
|
|
const targetPos = new Vec3(target.x, this.currentZoom, target.z);
|
||
|
|
// 这里可以添加缓动动画
|
||
|
|
this.setCameraPosition(targetPos);
|
||
|
|
}
|
||
|
|
}
|