cocos基础工程
This commit is contained in:
@@ -0,0 +1,358 @@
|
||||
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] 游戏重置完成');
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
{
|
||||
"ver": "4.0.23",
|
||||
"importer": "typescript",
|
||||
"imported": true,
|
||||
"uuid": "f1a5b7ea-5782-43d3-9eea-8b5909a93dbd",
|
||||
"files": [],
|
||||
"subMetas": {},
|
||||
"userData": {}
|
||||
}
|
||||
Reference in New Issue
Block a user