292 lines
8.6 KiB
TypeScript
292 lines
8.6 KiB
TypeScript
/**
|
|
* Pinball 渲染器实现
|
|
* 负责渲染物理对象、粒子效果等
|
|
*/
|
|
|
|
import { _decorator, Camera, Color, Component, Graphics, Node, Prefab, Sprite, tween, Vec3 } from 'cc';
|
|
import { PhysicsBodyData, Vector2 } from '../Core/GameData';
|
|
import { IRenderer, ParticleEffect, RenderObject } from '../Core/IRenderer';
|
|
|
|
const { ccclass, property } = _decorator;
|
|
|
|
|
|
@ccclass
|
|
export class PinballRenderer extends Component implements IRenderer {
|
|
|
|
@property(Node)
|
|
private renderContainer: Node = null!;
|
|
|
|
@property(Prefab)
|
|
private ballPrefab: Prefab = null!;
|
|
|
|
private renderObjects: Map<string, Node> = new Map();
|
|
private particlePool: Node[] = [];
|
|
private camera: Camera = null!;
|
|
|
|
/**
|
|
* 设置相机
|
|
*/
|
|
setCamera(camera: Camera): void {
|
|
this.camera = camera;
|
|
console.log('Camera set for PinballRenderer');
|
|
}
|
|
|
|
/**
|
|
* 设置世界边界
|
|
*/
|
|
setWorldBounds(width: number, height: number): void {
|
|
console.log(`World bounds set to ${width} x ${height}`);
|
|
// 可以在此处设置渲染边界或背景
|
|
}
|
|
|
|
/**
|
|
* 初始化渲染器
|
|
*/
|
|
async initialize(parentNode: any): Promise<void> {
|
|
console.log('Initializing PinballRenderer...');
|
|
|
|
// 设置渲染容器
|
|
if (parentNode) {
|
|
this.renderContainer = parentNode;
|
|
} else {
|
|
this.renderContainer = this.node;
|
|
}
|
|
|
|
// 获取相机 - 在 Cocos Creator 3.x 中需要通过场景查找
|
|
const cameraNode = this.renderContainer.scene?.getChildByName('Main Camera');
|
|
if (cameraNode) {
|
|
this.camera = cameraNode.getComponent(Camera);
|
|
}
|
|
|
|
if (!this.camera) {
|
|
console.warn('Main camera not found, creating default camera');
|
|
const cameraNode = new Node('PinballCamera');
|
|
this.camera = cameraNode.addComponent(Camera);
|
|
this.renderContainer.addChild(cameraNode);
|
|
}
|
|
|
|
// 创建默认球体预制件(如果没有提供)
|
|
if (!this.ballPrefab) {
|
|
await this.createDefaultBallPrefab();
|
|
}
|
|
|
|
console.log('PinballRenderer initialized successfully');
|
|
}
|
|
|
|
/**
|
|
* 创建默认的球体预制件
|
|
*/
|
|
private async createDefaultBallPrefab(): Promise<void> {
|
|
// 创建一个简单的圆形节点作为默认球体
|
|
const ballNode = new Node('DefaultBall');
|
|
|
|
// 添加 Sprite 组件
|
|
const sprite = ballNode.addComponent(Sprite);
|
|
|
|
// 创建圆形材质
|
|
const graphics = ballNode.addComponent(Graphics);
|
|
graphics.fillColor = Color.WHITE;
|
|
graphics.circle(0, 0, 25); // 半径 25 像素
|
|
graphics.fill();
|
|
|
|
console.log('Created default ball prefab');
|
|
}
|
|
|
|
/**
|
|
* 渲染物理体
|
|
*/
|
|
renderBodies(bodies: PhysicsBodyData[]): void {
|
|
// 清理不存在的渲染对象
|
|
const currentBodyIds = new Set(bodies.map(body => body.id.toString()));
|
|
for (const [id, node] of this.renderObjects) {
|
|
if (!currentBodyIds.has(id)) {
|
|
this.removeRenderObject(id);
|
|
}
|
|
}
|
|
|
|
// 更新或创建渲染对象
|
|
for (const body of bodies) {
|
|
const bodyIdStr = body.id.toString();
|
|
let renderNode = this.renderObjects.get(bodyIdStr);
|
|
|
|
if (!renderNode) {
|
|
// 创建新的渲染对象
|
|
const renderObject = this.createRenderObject(body);
|
|
renderNode = this.createRenderNode(renderObject);
|
|
this.renderObjects.set(bodyIdStr, renderNode);
|
|
this.renderContainer.addChild(renderNode);
|
|
}
|
|
|
|
// 更新位置
|
|
renderNode.setPosition(body.position.x * 100, body.position.y * 100); // 放大显示
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 创建渲染对象数据
|
|
*/
|
|
createRenderObject(body: PhysicsBodyData): RenderObject {
|
|
return {
|
|
id: body.id.toString(),
|
|
position: body.position,
|
|
radius: body.radius,
|
|
color: body.isStatic ?
|
|
{ r: 128, g: 128, b: 128, a: 255 } : // 静态物体:灰色
|
|
{ r: 255, g: 100, b: 100, a: 255 }, // 动态物体:红色
|
|
layer: 0
|
|
};
|
|
}
|
|
|
|
/**
|
|
* 创建渲染节点
|
|
*/
|
|
private createRenderNode(renderObject: RenderObject): Node {
|
|
const node = new Node(`Ball_${renderObject.id}`);
|
|
|
|
// 添加 Graphics 组件用于绘制圆形
|
|
const graphics = node.addComponent(Graphics);
|
|
graphics.fillColor = new Color(
|
|
renderObject.color.r,
|
|
renderObject.color.g,
|
|
renderObject.color.b,
|
|
renderObject.color.a
|
|
);
|
|
|
|
const radius = Math.max(renderObject.radius * 100, 10); // 最小半径 10 像素
|
|
graphics.circle(0, 0, radius);
|
|
graphics.fill();
|
|
|
|
// 添加边框
|
|
graphics.strokeColor = Color.BLACK;
|
|
graphics.lineWidth = 2;
|
|
graphics.circle(0, 0, radius);
|
|
graphics.stroke();
|
|
|
|
return node;
|
|
}
|
|
|
|
/**
|
|
* 更新渲染对象
|
|
*/
|
|
updateRenderObject(renderObject: RenderObject, body: PhysicsBodyData): void {
|
|
const node = this.renderObjects.get(renderObject.id);
|
|
if (node) {
|
|
// 更新位置
|
|
node.setPosition(body.position.x * 100, body.position.y * 100);
|
|
|
|
// 可以在此添加更多属性的更新,如颜色、大小等
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 移除渲染对象
|
|
*/
|
|
removeRenderObject(bodyId: string): void {
|
|
const node = this.renderObjects.get(bodyId);
|
|
if (node) {
|
|
node.removeFromParent();
|
|
this.renderObjects.delete(bodyId);
|
|
console.log(`Removed render object ${bodyId}`);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 播放粒子效果
|
|
*/
|
|
playParticleEffect(effect: ParticleEffect): void {
|
|
// 创建简单的粒子效果节点
|
|
const particleNode = this.getParticleNode();
|
|
particleNode.setPosition(effect.position.x * 100, effect.position.y * 100);
|
|
|
|
// 使用 Cocos Creator 3.x 的 tween 系统
|
|
tween(particleNode)
|
|
.parallel(
|
|
tween().to(effect.lifetime, { opacity: 0 }),
|
|
tween().by(effect.lifetime, {
|
|
position: new Vec3(effect.velocity.x * 50, effect.velocity.y * 50, 0)
|
|
})
|
|
)
|
|
.call(() => {
|
|
this.recycleParticleNode(particleNode);
|
|
})
|
|
.start();
|
|
|
|
this.renderContainer.addChild(particleNode);
|
|
}
|
|
|
|
/**
|
|
* 获取粒子节点(对象池)
|
|
*/
|
|
private getParticleNode(): Node {
|
|
if (this.particlePool.length > 0) {
|
|
return this.particlePool.pop()!;
|
|
}
|
|
|
|
const node = new Node('Particle');
|
|
const graphics = node.addComponent(Graphics);
|
|
graphics.fillColor = Color.YELLOW;
|
|
graphics.circle(0, 0, 3);
|
|
graphics.fill();
|
|
|
|
return node;
|
|
}
|
|
|
|
/**
|
|
* 回收粒子节点
|
|
*/
|
|
private recycleParticleNode(node: Node): void {
|
|
node.removeFromParent();
|
|
// 重置透明度 - 在 Cocos Creator 3.x 中通过 UIOpacity 组件
|
|
const uiOpacity = node.getComponent('cc.UIOpacity') as any;
|
|
if (uiOpacity) {
|
|
uiOpacity.opacity = 255;
|
|
}
|
|
node.setPosition(0, 0, 0); // 重置位置
|
|
this.particlePool.push(node);
|
|
}
|
|
|
|
/**
|
|
* 清除所有渲染对象
|
|
*/
|
|
clear(): void {
|
|
for (const [id, node] of this.renderObjects) {
|
|
node.removeFromParent();
|
|
}
|
|
this.renderObjects.clear();
|
|
|
|
// 清理粒子池
|
|
for (const particle of this.particlePool) {
|
|
particle.removeFromParent();
|
|
}
|
|
this.particlePool = [];
|
|
|
|
console.log('Cleared all render objects');
|
|
}
|
|
|
|
/**
|
|
* 设置相机位置
|
|
*/
|
|
setCameraPosition(position: Vector2): void {
|
|
if (this.camera) {
|
|
this.camera.node.setPosition(position.x * 100, position.y * 100);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 设置相机缩放
|
|
*/
|
|
setCameraZoom(zoom: number): void {
|
|
if (this.camera) {
|
|
// 在 Cocos Creator 3.x 中使用 orthoHeight 控制缩放
|
|
this.camera.orthoHeight = 600 / zoom; // 基础高度 600
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 销毁渲染器
|
|
*/
|
|
dispose(): void {
|
|
this.clear();
|
|
console.log('PinballRenderer disposed');
|
|
}
|
|
} |