This commit is contained in:
YHH
2025-06-24 19:34:37 +08:00
parent 68a615bc7b
commit 0f18a1979e
62 changed files with 10606 additions and 2113 deletions

View File

@@ -0,0 +1,203 @@
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);
}
}

View File

@@ -0,0 +1,9 @@
{
"ver": "4.0.24",
"importer": "typescript",
"imported": true,
"uuid": "dabc6540-6e9f-450a-9d22-b9af94c20d6d",
"files": [],
"subMetas": {},
"userData": {}
}

View File

@@ -0,0 +1,225 @@
import { _decorator, Component, Node, Vec3, Label, Button, EventHandler } from 'cc';
const { ccclass, property } = _decorator;
/**
* UI控制器 - 管理RTS演示的用户界面
*/
@ccclass('UIController')
export class UIController extends Component {
@property(Label)
selectedUnitsLabel: Label = null!;
@property(Button)
moveButton: Button = null!;
@property(Button)
attackButton: Button = null!;
@property(Button)
gatherButton: Button = null!;
@property(Button)
patrolButton: Button = null!;
@property(Label)
infoLabel: Label = null!;
// 回调函数
public onUnitSelected: ((units: Node[]) => void) | null = null;
public onCommandIssued: ((command: string, target?: Vec3 | Node) => void) | null = null;
private selectedUnitsCount: number = 0;
onLoad() {
this.setupUI();
this.updateSelectedUnitsDisplay();
this.updateInfoDisplay('RTS 行为树演示 - 点击单位选择,然后发布命令');
}
/**
* 设置UI事件
*/
private setupUI() {
// 移动命令按钮
if (this.moveButton) {
const moveHandler = new EventHandler();
moveHandler.target = this.node;
moveHandler.component = 'UIController';
moveHandler.handler = 'onMoveCommand';
this.moveButton.clickEvents.push(moveHandler);
}
// 攻击命令按钮
if (this.attackButton) {
const attackHandler = new EventHandler();
attackHandler.target = this.node;
attackHandler.component = 'UIController';
attackHandler.handler = 'onAttackCommand';
this.attackButton.clickEvents.push(attackHandler);
}
// 收集命令按钮
if (this.gatherButton) {
const gatherHandler = new EventHandler();
gatherHandler.target = this.node;
gatherHandler.component = 'UIController';
gatherHandler.handler = 'onGatherCommand';
this.gatherButton.clickEvents.push(gatherHandler);
}
// 巡逻命令按钮
if (this.patrolButton) {
const patrolHandler = new EventHandler();
patrolHandler.target = this.node;
patrolHandler.component = 'UIController';
patrolHandler.handler = 'onPatrolCommand';
this.patrolButton.clickEvents.push(patrolHandler);
}
}
/**
* 设置选中单位数量
*/
setSelectedUnitsCount(count: number) {
this.selectedUnitsCount = count;
this.updateSelectedUnitsDisplay();
this.updateButtonStates();
}
/**
* 更新选中单位显示
*/
private updateSelectedUnitsDisplay() {
if (this.selectedUnitsLabel) {
this.selectedUnitsLabel.string = `选中单位: ${this.selectedUnitsCount}`;
}
}
/**
* 更新按钮状态
*/
private updateButtonStates() {
const hasSelection = this.selectedUnitsCount > 0;
if (this.moveButton) {
this.moveButton.interactable = hasSelection;
}
if (this.attackButton) {
this.attackButton.interactable = hasSelection;
}
if (this.gatherButton) {
this.gatherButton.interactable = hasSelection;
}
if (this.patrolButton) {
this.patrolButton.interactable = hasSelection;
}
}
/**
* 更新信息显示
*/
updateInfoDisplay(message: string) {
if (this.infoLabel) {
this.infoLabel.string = message;
}
console.log(`[UI] ${message}`);
}
/**
* 移动命令
*/
onMoveCommand() {
if (this.selectedUnitsCount === 0) {
this.updateInfoDisplay('请先选择单位');
return;
}
// 生成随机目标位置
const targetPos = new Vec3(
(Math.random() - 0.5) * 20,
0,
(Math.random() - 0.5) * 20
);
this.onCommandIssued?.('move', targetPos);
this.updateInfoDisplay(`发布移动命令到位置 (${targetPos.x.toFixed(1)}, ${targetPos.z.toFixed(1)})`);
}
/**
* 攻击命令
*/
onAttackCommand() {
if (this.selectedUnitsCount === 0) {
this.updateInfoDisplay('请先选择单位');
return;
}
// 生成随机攻击位置
const targetPos = new Vec3(
(Math.random() - 0.5) * 15,
0,
(Math.random() - 0.5) * 15
);
this.onCommandIssued?.('attack', targetPos);
this.updateInfoDisplay(`发布攻击命令到位置 (${targetPos.x.toFixed(1)}, ${targetPos.z.toFixed(1)})`);
}
/**
* 收集命令
*/
onGatherCommand() {
if (this.selectedUnitsCount === 0) {
this.updateInfoDisplay('请先选择单位');
return;
}
this.onCommandIssued?.('gather');
this.updateInfoDisplay('发布收集资源命令');
}
/**
* 巡逻命令
*/
onPatrolCommand() {
if (this.selectedUnitsCount === 0) {
this.updateInfoDisplay('请先选择单位');
return;
}
this.onCommandIssued?.('patrol');
this.updateInfoDisplay('发布巡逻命令');
}
/**
* 显示单位信息
*/
showUnitInfo(unitName: string, unitType: string, health: number, maxHealth: number) {
const healthPercent = Math.round((health / maxHealth) * 100);
this.updateInfoDisplay(`${unitName} (${unitType}) - 生命值: ${health}/${maxHealth} (${healthPercent}%)`);
}
/**
* 显示行为树状态
*/
showBehaviorTreeStatus(unitName: string, currentBehavior: string) {
this.updateInfoDisplay(`${unitName} 当前行为: ${currentBehavior}`);
}
/**
* 显示错误信息
*/
showError(message: string) {
this.updateInfoDisplay(`错误: ${message}`);
console.error(`[UI Error] ${message}`);
}
/**
* 显示成功信息
*/
showSuccess(message: string) {
this.updateInfoDisplay(`成功: ${message}`);
}
}

View File

@@ -0,0 +1,9 @@
{
"ver": "4.0.24",
"importer": "typescript",
"imported": true,
"uuid": "00e982f3-dcf3-44b0-9b63-5e2877c1971e",
"files": [],
"subMetas": {},
"userData": {}
}