Files
esengine/extensions/cocos/cocos-ecs/assets/scripts/controllers/RTSCameraController.ts

203 lines
6.3 KiB
TypeScript
Raw Normal View History

2025-06-24 19:34:37 +08:00
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);
}
}