Files
shooter-demo/client-cocos/assets/scripts/Modules/Pinball/GameModes/StandaloneMode.ts
2025-11-28 18:10:10 +08:00

358 lines
11 KiB
TypeScript

import { _decorator, Camera, Component, Node } from 'cc';
import { EventBus } from '../Core/EventBus';
import { PhysicsBodyData, Vector2 } from '../Core/GameData';
import { IPhysicsEngine } from '../Core/IPhysicsEngine';
import { IRenderer } from '../Core/IRenderer';
import { InputManager } from '../Input/InputManager';
import { MouseInputEvent, TouchInputEvent } from '../Input/InputTypes';
import { WasmPhysicsEngine } from '../Physics/WasmPhysicsEngine';
import { PinballRenderer } from '../Renderer/PinballRenderer';
const { ccclass, property } = _decorator;
/**
* Standalone模式 - 单机弹珠物理游戏模式
* 整合物理引擎、渲染器和输入管理器,提供完整的单机游戏体验
*/
@ccclass('StandaloneMode')
export class StandaloneMode extends Component {
@property(Camera)
gameCamera: Camera = null;
@property(Node)
renderNode: Node = null;
@property({ type: Node, tooltip: "用于显示游戏边界的节点" })
boundsNode: Node = null;
// 核心系统
private physicsEngine: IPhysicsEngine = null;
private renderer: IRenderer = null;
private inputManager: InputManager = null;
private eventBus: EventBus = null;
// 游戏配置
@property({ tooltip: "游戏世界宽度" })
worldWidth: number = 800;
@property({ tooltip: "游戏世界高度" })
worldHeight: number = 600;
@property({ tooltip: "重力加速度" })
gravity: number = -9.81;
@property({ tooltip: "弹珠默认半径" })
ballRadius: number = 10;
@property({ tooltip: "弹珠默认密度" })
ballDensity: number = 1.0;
@property({ tooltip: "弹珠默认弹性系数" })
ballRestitution: number = 0.8;
// 游戏状态
private isInitialized: boolean = false;
private ballCount: number = 0;
private activeBalls: Map<number, PhysicsBodyData> = new Map();
async onLoad() {
// 初始化事件总线
this.eventBus = EventBus.getInstance();
// 初始化物理引擎
await this.initializePhysics();
// 初始化渲染器
this.initializeRenderer();
// 初始化输入管理器
this.initializeInput();
// 注册事件监听
this.registerEventHandlers();
console.log('[StandaloneMode] 初始化完成');
}
onEnable() {
if (this.isInitialized) {
this.startGameLoop();
}
}
onDisable() {
this.stopGameLoop();
}
onDestroy() {
this.cleanup();
}
/**
* 初始化物理引擎
*/
private async initializePhysics(): Promise<void> {
this.physicsEngine = new WasmPhysicsEngine();
await this.physicsEngine.initialize({
gravity: { x: 0, y: this.gravity },
timeStep: 1 / 60,
maxBodies: 1000
});
// 创建物理世界
await this.physicsEngine.createWorld({ x: 0, y: this.gravity });
// 创建世界边界
this.createWorldBounds();
console.log('[StandaloneMode] 物理引擎初始化完成');
}
/**
* 初始化渲染器
*/
private initializeRenderer(): void {
if (!this.renderNode) {
console.error('[StandaloneMode] renderNode 未设置');
return;
}
this.renderer = this.renderNode.getComponent(PinballRenderer);
if (!this.renderer) {
this.renderer = this.renderNode.addComponent(PinballRenderer);
}
// 设置渲染器参数
if (this.gameCamera) {
this.renderer.setCamera(this.gameCamera);
}
this.renderer.setWorldBounds(this.worldWidth, this.worldHeight);
console.log('[StandaloneMode] 渲染器初始化完成');
}
/**
* 初始化输入管理器
*/
private initializeInput(): void {
this.inputManager = this.getComponent(InputManager);
if (!this.inputManager) {
this.inputManager = this.addComponent(InputManager);
}
// 设置输入管理器参数
if (this.gameCamera) {
this.inputManager.setCamera(this.gameCamera);
}
console.log('[StandaloneMode] 输入管理器初始化完成');
}
/**
* 注册事件处理器
*/
private registerEventHandlers(): void {
// 监听输入事件
this.eventBus.on('input.mouse.click', (event: MouseInputEvent) => this.onMouseClick(event));
this.eventBus.on('input.touch.start', (event: TouchInputEvent) => this.onTouchStart(event));
// 监听物理事件
this.eventBus.on('physics.collision', (collisionData: any) => this.onPhysicsCollision(collisionData));
console.log('[StandaloneMode] 事件处理器注册完成');
this.isInitialized = true;
}
/**
* 创建世界边界
*/
private createWorldBounds(): void {
const halfWidth = this.worldWidth / 2;
const halfHeight = this.worldHeight / 2;
const wallThickness = 10;
// 创建四面墙壁
const walls = [
// 底部墙
{ x: 0, y: -halfHeight - wallThickness / 2, width: this.worldWidth + wallThickness, height: wallThickness },
// 顶部墙
{ x: 0, y: halfHeight + wallThickness / 2, width: this.worldWidth + wallThickness, height: wallThickness },
// 左侧墙
{ x: -halfWidth - wallThickness / 2, y: 0, width: wallThickness, height: this.worldHeight + wallThickness },
// 右侧墙
{ x: halfWidth + wallThickness / 2, y: 0, width: wallThickness, height: this.worldHeight + wallThickness }
];
for (const wall of walls) {
this.physicsEngine.createBox({
position: { x: wall.x, y: wall.y },
size: { x: wall.width, y: wall.height },
isStatic: true,
restitution: 0.8,
friction: 0.3
});
}
console.log('[StandaloneMode] 世界边界创建完成');
}
/**
* 鼠标点击事件处理
*/
private onMouseClick(event: MouseInputEvent): void {
this.createBallAtPosition(event.position);
}
/**
* 触摸开始事件处理
*/
private onTouchStart(event: TouchInputEvent): void {
this.createBallAtPosition(event.position);
}
/**
* 在指定位置创建弹珠
*/
private createBallAtPosition(position: Vector2): void {
const ballId = this.physicsEngine.createCircle({
position: position,
radius: this.ballRadius,
isStatic: false,
density: this.ballDensity,
restitution: this.ballRestitution,
friction: 0.3
});
// 创建球体数据
const ballData: PhysicsBodyData = {
id: ballId,
position: position,
rotation: 0,
velocity: { x: 0, y: 0 },
angularVelocity: 0,
bodyType: 'circle',
radius: this.ballRadius,
isStatic: false
};
this.activeBalls.set(ballId, ballData);
this.ballCount++;
// 通知渲染器
this.eventBus.emit('ball.created', {
id: ballId,
position: position,
radius: this.ballRadius
});
console.log(`[StandaloneMode] 创建弹珠 #${ballId} 在位置 (${position.x}, ${position.y})`);
}
/**
* 物理碰撞事件处理
*/
private onPhysicsCollision(collisionData: any): void {
// 播放碰撞音效或效果
console.log('[StandaloneMode] 物理碰撞:', collisionData);
}
/**
* 开始游戏循环
*/
private startGameLoop(): void {
// 在update中已经自动运行物理和渲染循环
console.log('[StandaloneMode] 游戏循环已开始');
}
/**
* 停止游戏循环
*/
private stopGameLoop(): void {
console.log('[StandaloneMode] 游戏循环已停止');
}
/**
* 游戏更新循环
*/
update(deltaTime: number) {
if (!this.isInitialized || !this.physicsEngine) {
return;
}
// 步进物理引擎
this.physicsEngine.step(deltaTime);
// 更新所有活动球体的状态
for (const [ballId, ballData] of this.activeBalls) {
const updatedData = this.physicsEngine.getBodyData(ballId);
if (updatedData) {
// 更新本地数据
ballData.position = updatedData.position;
ballData.rotation = updatedData.rotation;
ballData.velocity = updatedData.velocity;
ballData.angularVelocity = updatedData.angularVelocity;
// 发送更新事件给渲染器
this.eventBus.emit('ball.updated', {
id: ballId,
position: ballData.position,
rotation: ballData.rotation
});
}
}
}
/**
* 清理资源
*/
private cleanup(): void {
// 清理事件监听器
this.eventBus.off('input.mouse.click', this.onMouseClick);
this.eventBus.off('input.touch.start', this.onTouchStart);
this.eventBus.off('physics.collision', this.onPhysicsCollision);
// 清理物理引擎
if (this.physicsEngine) {
this.physicsEngine.cleanup();
this.physicsEngine = null;
}
// 清理数据
this.activeBalls.clear();
this.ballCount = 0;
this.isInitialized = false;
console.log('[StandaloneMode] 资源清理完成');
}
/**
* 获取游戏统计信息
*/
public getGameStats() {
return {
ballCount: this.ballCount,
activeBalls: this.activeBalls.size,
worldSize: { width: this.worldWidth, height: this.worldHeight },
isInitialized: this.isInitialized
};
}
/**
* 重置游戏
*/
public resetGame(): void {
// 移除所有球体
for (const ballId of this.activeBalls.keys()) {
this.physicsEngine.removeBody(ballId);
this.eventBus.emit('ball.removed', { id: ballId });
}
this.activeBalls.clear();
this.ballCount = 0;
console.log('[StandaloneMode] 游戏重置完成');
}
}