241 lines
7.5 KiB
TypeScript
241 lines
7.5 KiB
TypeScript
import { _decorator, Component, Node, Camera, Vec3, tween, Quat, easing, TweenEasing, EventTouch, input, Input } from 'cc';
|
|
const { ccclass, property } = _decorator;
|
|
|
|
// 定义镜头配置接口
|
|
interface CameraShot {
|
|
position: Vec3; // 相机位置
|
|
lookAt: Vec3; // 看向的位置
|
|
duration?: number; // 过渡时间
|
|
delay?: number; // 停留时间
|
|
easing?: TweenEasing; // 改为 TweenEasing 类型
|
|
}
|
|
|
|
@ccclass('CameraManager')
|
|
export class CameraManager extends Component {
|
|
@property(Camera)
|
|
camera: Camera = null;
|
|
|
|
@property
|
|
defaultTransitionTime: number = 2.0; // 默认过渡时间
|
|
|
|
@property
|
|
defaultStayTime: number = 3.0; // 默认停留时间
|
|
|
|
@property(Node)
|
|
public defaultCamera: Node = null;
|
|
|
|
@property(Node)
|
|
public deadCamera: Node = null;
|
|
|
|
private isPlaying: boolean = false;
|
|
private currentShotIndex: number = 0;
|
|
private shots: CameraShot[] = [];
|
|
|
|
private readonly MIN_DEAD_ROTATION = -230;
|
|
private readonly MAX_DEAD_ROTATION = -180;
|
|
private deadCameraRotation: number = -180;
|
|
private lastTouchX: number = 0;
|
|
private readonly DEAD_CAMERA_ROTATION_SPEED = 0.3;
|
|
private isTouchingDeadCamera: boolean = false;
|
|
|
|
// 预设一些常用的镜头
|
|
readonly SHOTS = {
|
|
OVERVIEW: {
|
|
position: new Vec3(0, 30, 30),
|
|
lookAt: new Vec3(0, 0, 0),
|
|
duration: 2,
|
|
delay: 3
|
|
},
|
|
SIDE_VIEW: {
|
|
position: new Vec3(20, 5, 0),
|
|
lookAt: new Vec3(0, 2, 0),
|
|
duration: 2,
|
|
delay: 3
|
|
},
|
|
PLAYER_VIEW: {
|
|
position: new Vec3(0, 2, 60),
|
|
lookAt: new Vec3(0, 2, 0),
|
|
duration: 2,
|
|
delay: 3
|
|
},
|
|
DOLL_VIEW: {
|
|
position: new Vec3(0, 2, -10),
|
|
lookAt: new Vec3(0, 2, -5),
|
|
duration: 2,
|
|
delay: 3
|
|
}
|
|
};
|
|
|
|
start() {
|
|
if (!this.camera) {
|
|
this.camera = this.getComponent(Camera);
|
|
}
|
|
|
|
// 确保死亡摄像机初始隐藏
|
|
if (this.deadCamera) {
|
|
this.deadCamera.active = false;
|
|
}
|
|
|
|
// 确保默认摄像机初始显示
|
|
if (this.defaultCamera) {
|
|
this.defaultCamera.active = true;
|
|
}
|
|
}
|
|
|
|
// 添加一个镜头到序列
|
|
addShot(shot: CameraShot) {
|
|
this.shots.push({
|
|
position: shot.position,
|
|
lookAt: shot.lookAt,
|
|
duration: shot.duration || this.defaultTransitionTime,
|
|
delay: shot.delay || this.defaultStayTime,
|
|
easing: shot.easing || 'quadOut' as TweenEasing // 使用类型断言
|
|
});
|
|
}
|
|
|
|
// 清空镜头序列
|
|
clearShots() {
|
|
this.shots = [];
|
|
this.currentShotIndex = 0;
|
|
}
|
|
|
|
// 开始播放镜头序列
|
|
async playShotsSequence(loop: boolean = false) {
|
|
if (this.isPlaying || this.shots.length === 0) return;
|
|
|
|
this.isPlaying = true;
|
|
this.currentShotIndex = 0;
|
|
|
|
while (this.isPlaying) {
|
|
await this.playShot(this.shots[this.currentShotIndex]);
|
|
|
|
this.currentShotIndex++;
|
|
if (this.currentShotIndex >= this.shots.length) {
|
|
if (loop) {
|
|
this.currentShotIndex = 0;
|
|
} else {
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
this.isPlaying = false;
|
|
}
|
|
|
|
// 停止播放
|
|
stopSequence() {
|
|
this.isPlaying = false;
|
|
}
|
|
|
|
// 立即切换到指定镜头
|
|
setCameraShot(shot: CameraShot) {
|
|
this.camera.node.setPosition(shot.position);
|
|
this.camera.node.lookAt(shot.lookAt);
|
|
}
|
|
|
|
// 播放单个镜头
|
|
private playShot(shot: CameraShot): Promise<void> {
|
|
return new Promise((resolve) => {
|
|
// 计算目标四元数
|
|
const targetQuat = new Quat();
|
|
const targetPos = shot.position.clone();
|
|
const up = new Vec3(0, 1, 0);
|
|
|
|
// 计算朝向
|
|
const forward = Vec3.subtract(new Vec3(), shot.lookAt, shot.position).normalize();
|
|
const right = Vec3.cross(new Vec3(), forward, up).normalize();
|
|
Vec3.cross(up, right, forward);
|
|
|
|
Quat.fromAxes(targetQuat, right, up, forward.multiplyScalar(-1));
|
|
|
|
// 创建动画
|
|
tween(this.camera.node)
|
|
.parallel(
|
|
tween().to(shot.duration, { position: targetPos }, { easing: shot.easing }),
|
|
tween().to(shot.duration, { rotation: targetQuat }, { easing: shot.easing })
|
|
)
|
|
.delay(shot.delay)
|
|
.call(() => resolve())
|
|
.start();
|
|
});
|
|
}
|
|
|
|
// 示例:创建开场镜头序列
|
|
createIntroSequence() {
|
|
this.clearShots();
|
|
this.addShot(this.SHOTS.OVERVIEW);
|
|
this.addShot(this.SHOTS.SIDE_VIEW);
|
|
this.addShot(this.SHOTS.PLAYER_VIEW);
|
|
this.playShotsSequence(false);
|
|
}
|
|
|
|
// 示例:创建游戏结束镜头序列
|
|
createEndingSequence() {
|
|
this.clearShots();
|
|
this.addShot(this.SHOTS.DOLL_VIEW);
|
|
this.addShot(this.SHOTS.OVERVIEW);
|
|
this.playShotsSequence(false);
|
|
}
|
|
|
|
switchToDeadCamera(playerPos: Vec3) {
|
|
if (!this.deadCamera || !this.defaultCamera) {
|
|
console.error('Camera references not set in CameraManager');
|
|
return;
|
|
}
|
|
|
|
// 设置死亡摄像机的初始位置和朝向
|
|
this.deadCamera.setWorldPosition(new Vec3(
|
|
playerPos.x,
|
|
playerPos.y + 2,
|
|
playerPos.z + 5
|
|
));
|
|
|
|
// 初始朝向
|
|
this.deadCameraRotation = -180;
|
|
this.deadCamera.eulerAngles = new Vec3(-15, this.deadCameraRotation, 0);
|
|
|
|
// 切换摄像机
|
|
this.defaultCamera.active = false;
|
|
this.deadCamera.active = true;
|
|
|
|
// 添加触摸事件监听
|
|
this.enableDeadCameraControl();
|
|
}
|
|
|
|
private enableDeadCameraControl() {
|
|
input.on(Input.EventType.TOUCH_START, this.onDeadCameraTouchStart, this);
|
|
input.on(Input.EventType.TOUCH_MOVE, this.onDeadCameraTouchMove, this);
|
|
input.on(Input.EventType.TOUCH_END, this.onDeadCameraTouchEnd, this);
|
|
input.on(Input.EventType.TOUCH_CANCEL, this.onDeadCameraTouchEnd, this);
|
|
}
|
|
|
|
private onDeadCameraTouchStart(event: EventTouch) {
|
|
this.isTouchingDeadCamera = true;
|
|
this.lastTouchX = event.getLocationX();
|
|
}
|
|
|
|
private onDeadCameraTouchMove(event: EventTouch) {
|
|
if (!this.isTouchingDeadCamera || !this.deadCamera) return;
|
|
|
|
const currentTouchX = event.getLocationX();
|
|
const deltaX = currentTouchX - this.lastTouchX;
|
|
|
|
this.deadCameraRotation -= deltaX * this.DEAD_CAMERA_ROTATION_SPEED;
|
|
this.deadCameraRotation = Math.max(this.MIN_DEAD_ROTATION, Math.min(this.MAX_DEAD_ROTATION, this.deadCameraRotation));
|
|
|
|
this.deadCamera.eulerAngles = new Vec3(-15, this.deadCameraRotation, 0);
|
|
|
|
this.lastTouchX = currentTouchX;
|
|
}
|
|
|
|
private onDeadCameraTouchEnd() {
|
|
this.isTouchingDeadCamera = false;
|
|
}
|
|
|
|
onDestroy() {
|
|
input.off(Input.EventType.TOUCH_START, this.onDeadCameraTouchStart, this);
|
|
input.off(Input.EventType.TOUCH_MOVE, this.onDeadCameraTouchMove, this);
|
|
input.off(Input.EventType.TOUCH_END, this.onDeadCameraTouchEnd, this);
|
|
input.off(Input.EventType.TOUCH_CANCEL, this.onDeadCameraTouchEnd, this);
|
|
}
|
|
}
|