Files
rougelike-demo/client/assets/scripts/App/Game/CameraController.ts

264 lines
8.2 KiB
TypeScript
Raw Normal View History

2025-12-18 16:04:56 +08:00
import { _decorator, Camera, Component, find, Node, Vec3 } from 'cc';
const { ccclass, property } = _decorator;
/**
* CameraController
2025-12-18 16:04:56 +08:00
*
* UI摄像机的概念
*/
@ccclass('CameraController')
export class CameraController extends Component {
/** 跟随目标 */
private target: Node = null;
2025-12-18 16:04:56 +08:00
/** 世界摄像机节点 (Main Camera) */
private worldCameraNode: Node = null;
2025-12-18 16:04:56 +08:00
/** 世界摄像机组件 */
private worldCamera: Camera = null;
/** UI摄像机节点 (Canvas/Camera) */
private uiCameraNode: Node = null;
/** UI摄像机组件 */
private uiCamera: Camera = null;
/** 相对偏移量 */
private offset: Vec3 = new Vec3(0, 10, 8);
/** 跟随速度 */
@property({ displayName: '跟随速度' })
public followSpeed: number = 5;
/** 是否平滑跟随 */
@property({ displayName: '平滑跟随' })
public smoothFollow: boolean = true;
onLoad() {
2025-12-18 16:04:56 +08:00
this.findCameras();
}
/**
*
*/
private findCameras(): void {
this.findWorldCamera();
this.findUICamera();
}
/**
2025-12-18 16:04:56 +08:00
* (Main Camera)
*/
2025-12-18 16:04:56 +08:00
private findWorldCamera(): void {
// 查找名为 "Main Camera" 的世界摄像机
const mainCameraNode = find('Main Camera', this.node.scene);
if (mainCameraNode) {
2025-12-18 16:04:56 +08:00
this.worldCameraNode = mainCameraNode;
this.worldCamera = mainCameraNode.getComponent(Camera);
console.log('[CameraController] 找到世界摄像机:', mainCameraNode.name);
} else {
console.error('[CameraController] 未找到世界摄像机(Main Camera)');
}
}
/**
* UI摄像机 (Canvas/Camera)
*/
private findUICamera(): void {
// 查找Canvas下的Camera作为UI摄像机
const canvasNode = find('Canvas', this.node.scene);
if (canvasNode) {
const uiCameraNode = canvasNode.getChildByName('Camera');
if (uiCameraNode) {
this.uiCameraNode = uiCameraNode;
this.uiCamera = uiCameraNode.getComponent(Camera);
console.log('[CameraController] 找到UI摄像机:', uiCameraNode.name);
} else {
console.error('[CameraController] 未找到Canvas下的Camera节点');
}
} else {
2025-12-18 16:04:56 +08:00
console.error('[CameraController] 未找到Canvas节点');
}
}
/**
*
* @param target
*/
public setTarget(target: Node): void {
this.target = target;
2025-12-18 16:04:56 +08:00
if (this.target && this.worldCameraNode) {
// 立即设置初始位置
this.updateCameraPosition(false);
console.log('[CameraController] 设置跟随目标:', target.name);
}
}
/**
*
* @param offset
*/
public setOffset(offset: Vec3): void {
this.offset.set(offset);
}
/**
*
*/
public getTarget(): Node {
return this.target;
}
/**
2025-12-18 16:04:56 +08:00
*
*/
public getWorldCameraNode(): Node {
return this.worldCameraNode;
}
/**
*
*/
public getWorldCamera(): Camera {
return this.worldCamera;
}
/**
* UI摄像机节点
*/
public getUICameraNode(): Node {
return this.uiCameraNode;
}
/**
* UI摄像机组件
*/
2025-12-18 16:04:56 +08:00
public getUICamera(): Camera {
return this.uiCamera;
}
update(deltaTime: number) {
2025-12-18 16:04:56 +08:00
if (this.target && this.worldCameraNode) {
this.updateCameraPosition(this.smoothFollow, deltaTime);
}
}
/**
*
* @param smooth 使
* @param deltaTime (smooth为true时需要)
*/
private updateCameraPosition(smooth: boolean = true, deltaTime: number = 0): void {
2025-12-18 16:04:56 +08:00
if (!this.target || !this.worldCameraNode) {
return;
}
// 计算目标位置
const targetPosition = new Vec3();
Vec3.add(targetPosition, this.target.worldPosition, this.offset);
if (smooth && deltaTime > 0) {
// 平滑跟随
2025-12-18 16:04:56 +08:00
const currentPosition = this.worldCameraNode.worldPosition;
const newPosition = new Vec3();
Vec3.lerp(newPosition, currentPosition, targetPosition, this.followSpeed * deltaTime);
2025-12-18 16:04:56 +08:00
this.worldCameraNode.setWorldPosition(newPosition);
} else {
// 立即跟随
2025-12-18 16:04:56 +08:00
this.worldCameraNode.setWorldPosition(targetPosition);
}
// 让摄像机看向目标
2025-12-18 16:04:56 +08:00
this.worldCameraNode.lookAt(this.target.worldPosition);
}
/**
*
*/
public stopFollow(): void {
this.target = null;
console.log('[CameraController] 停止跟随');
}
2025-12-18 16:04:56 +08:00
/**
*
* @param worldPosition Vec3
* @param camera
* @returns Vec3z为深度
*/
public worldToCameraPosition(worldPosition: Vec3, camera: Camera): Vec3 {
if (!camera) {
console.warn('[CameraController] 摄像机参数为空,无法进行坐标转换');
return new Vec3(0, 0, 0);
}
// 使用摄像机的worldToScreen方法将世界坐标转换为屏幕坐标
const screenPos = new Vec3();
camera.worldToScreen(worldPosition, screenPos);
return screenPos;
}
/**
* Node的本地坐标
* -> -> Node本地坐标
* @param screenPosition
* @param sourceCamera
* @param targetNode Node节点
* @returns Node的本地坐标
*/
public CameraToNode(screenPosition: Vec3, sourceCamera: Camera, targetNode: Node): Vec3 {
if (!sourceCamera || !targetNode) {
console.warn('[CameraController] 摄像机或目标节点参数为空,无法进行坐标转换');
return new Vec3(0, 0, 0);
}
// 第一步:摄像机屏幕坐标转世界坐标
const worldPosition = new Vec3();
sourceCamera.screenToWorld(screenPosition, worldPosition);
// 第二步世界坐标转Node本地坐标
const localPosition = new Vec3();
targetNode.inverseTransformPoint(localPosition, worldPosition);
return localPosition;
}
/**
* UI摄像机节点的本地坐标
* @param worldPosition
* @returns UI摄像机节点的本地坐标
*/
public worldToUICamera(worldPosition: Vec3, node: Node): Vec3 {
if (!this.worldCamera || !this.uiCameraNode) {
console.warn('[CameraController] 世界摄像机或UI摄像机节点未初始化');
return new Vec3(0, 0, 0);
}
// 第一步:世界坐标转世界摄像机屏幕坐标
const worldCameraPos = this.worldToCameraPosition(worldPosition, this.worldCamera);
// 第二步世界摄像机屏幕坐标转UI摄像机节点本地坐标
const uiCameraLocalPos = this.CameraToNode(worldCameraPos, this.uiCamera, node);
return uiCameraLocalPos;
// // 世界坐标转Node本地坐标
// const localPosition = new Vec3();
// node.inverseTransformPoint(localPosition, worldPosition);
return worldPosition;
}
/**
*
*/
onDestroy(): void {
this.target = null;
2025-12-18 16:04:56 +08:00
this.worldCameraNode = null;
this.worldCamera = null;
this.uiCameraNode = null;
this.uiCamera = null;
console.log('[CameraController] 摄像机控制器已销毁');
}
}