ワンクリックで
galacean-object-pool
此 Skill 包含了 Galacean Engine 中对象池(Object Pool)的实现模式,用于高效管理子弹、敌人等频繁创建销毁的游戏对象
Codex または Claude でインストール この Prompt をコピーして Codex、Claude、または他のアシスタントに貼り付けると、Skill ページを確認してインストールできます。
メニュー
此 Skill 包含了 Galacean Engine 中对象池(Object Pool)的实现模式,用于高效管理子弹、敌人等频繁创建销毁的游戏对象
Codex または Claude でインストール この Prompt をコピーして Codex、Claude、または他のアシスタントに貼り付けると、Skill ページを確認してインストールできます。
SOC 職業分類に基づく
此 Skill 约定了 Galacean Engine 中自定义着色器的编写方式,包含 Shader.create、BaseMaterial 参数绑定、动画驱动、透明混合与常见排错。
此 Skill 包含了使用 Galacean Engine 物理引擎的注意事项、距离检测替代方案以及常见问题解决方案
此 Skill 包含了在 Galacean Engine 中通过 Script 脚本处理物体交互(如点击)的核心模式,基于 onPointerClick 实现
此 Skill 包含了在 Galacean Engine 中创建物理碰撞体的核心模式,涵盖动态碰撞体 (DynamicCollider) 和 静态碰撞体 (StaticCollider) 的实现
使用 Galacean Engine 开发2D游戏的核心模式,包括真2D(Sprite)和伪2D(3D正交投影)实现
此 Skill 包含了 Galacean Engine 相机的配置方法,包括透视/正交投影设置、俯视视角配置、窗口适配等
| name | galacean-object-pool |
| description | 此 Skill 包含了 Galacean Engine 中对象池(Object Pool)的实现模式,用于高效管理子弹、敌人等频繁创建销毁的游戏对象 |
在游戏中,频繁创建和销毁对象(如子弹、敌人)会导致:
对象池解决方案:预先创建对象,重复利用,避免频繁创建销毁。
class ObjectPool<T extends Entity> {
private pool: T[] = [];
private createFn: () => T;
private resetFn: (obj: T) => void;
private initialSize: number;
private initialized: boolean = false;
constructor(
createFn: () => T, // 创建对象的函数
resetFn: (obj: T) => void, // 重置对象的函数
initialSize: number = 10
) {
this.createFn = createFn;
this.resetFn = resetFn;
this.initialSize = initialSize;
}
// 延迟初始化,避免构造函数中的循环依赖问题
private initialize(): void {
if (this.initialized) return;
this.initialized = true;
for (let i = 0; i < this.initialSize; i++) {
const obj = this.createFn();
obj.enabled = false;
this.pool.push(obj);
}
}
// 获取对象
get(): T {
this.initialize();
let obj: T;
if (this.pool.length > 0) {
obj = this.pool.pop()!;
} else {
obj = this.createFn();
}
// 重置对象状态(设置 pool 引用、重置属性)
this.resetFn(obj);
// 启用对象
obj.enabled = true;
return obj;
}
// 归还对象到池中
return(obj: T): void {
// 禁用对象
obj.enabled = false;
// 移出视野(避免干扰)
obj.transform.position.set(0, -1000, 0);
this.pool.push(obj);
}
// 清空对象池
clear(): void {
this.pool.forEach(obj => obj.destroy());
this.pool = [];
}
}
class BulletScript extends Script {
speed: number = 15;
direction: Vector3 = new Vector3();
lifeTime: number = 3;
private currentLife: number = 0;
private pool: ObjectPool<Entity> | null = null;
// 关键:对象池通过此方法设置引用
setPool(pool: ObjectPool<Entity>): void {
this.pool = pool;
}
onUpdate(deltaTime: number): void {
this.currentLife += deltaTime;
if (this.currentLife >= this.lifeTime) {
this.recycle();
return;
}
// 移动
const moveDistance = this.speed * deltaTime;
this.entity.transform.translate(
this.direction.x * moveDistance,
0,
this.direction.z * moveDistance,
false
);
// 超出边界检查
const pos = this.entity.transform.position;
if (pos.x < -15 || pos.x > 15 || pos.z < -15 || pos.z > 15) {
this.recycle();
}
}
// 重置对象状态(对象池调用)
reset(): void {
this.currentLife = 0;
this.direction.set(0, 0, 0);
}
// 回收对象
private recycle(): void {
if (this.pool) {
this.pool.return(this.entity);
}
}
}
class GameManager extends Script {
private bulletPool: ObjectPool<Entity> | null = null;
onAwake(): void {
this.initPools();
}
private initPools(): void {
this.bulletPool = new ObjectPool<Entity>(
() => this.createBullet(), // createFn
(bullet) => this.resetBullet(bullet), // resetFn
30 // 初始池大小
);
}
// 创建子弹(只创建基础组件)
private createBullet(): Entity {
const bullet = this.entity.createChild('bullet');
// 视觉组件
const meshRenderer = bullet.addComponent(MeshRenderer);
meshRenderer.mesh = PrimitiveMesh.createSphere(this.engine, 0.3);
const material = new BlinnPhongMaterial(this.engine);
material.baseColor.set(1, 0.8, 0, 1);
meshRenderer.setMaterial(material);
// 脚本组件(不设置 pool,在 reset 时设置)
bullet.addComponent(BulletScript);
return bullet;
}
// 重置子弹(对象池获取时调用)
private resetBullet(bullet: Entity): void {
const script = bullet.getComponent(BulletScript);
if (script) {
// 关键:设置 pool 引用
script.setPool(this.bulletPool!);
script.reset();
}
}
// 发射子弹
private shoot(): void {
if (!this.bulletPool) return;
const bullet = this.bulletPool.get();
bullet.transform.position.set(0, 0.5, 0);
const script = bullet.getComponent(BulletScript);
if (script) {
script.direction = new Vector3(0, 0, 1); // 设置方向
}
}
}
问题:createFn 中引用 this.bulletPool 时,pool 还未赋值。
// ❌ 错误:循环依赖
private initPools(): void {
this.bulletPool = new ObjectPool<Entity>(
() => {
const bullet = this.createBullet();
bullet.getComponent(BulletScript).setPool(this.bulletPool!); // 此时为 null!
return bullet;
},
...
);
}
解决:在 resetFn 中设置 pool 引用。
// ✅ 正确:延迟设置 pool
private initPools(): void {
this.bulletPool = new ObjectPool<Entity>(
() => this.createBullet(), // 不设置 pool
(bullet) => {
const script = bullet.getComponent(BulletScript);
if (script) {
script.setPool(this.bulletPool!); // 在 reset 时设置
script.reset();
}
},
30
);
}
创建 (createFn)
↓
初始化状态 (resetFn) ← 获取对象时调用
↓
使用 (enabled = true)
↓
回收 (return) → enabled = false, 移出视野
↓
复用 (回到对象池等待下次 get)
script.setPool(this.bulletPool!)isDead = falseclass GameManager extends Script {
private pools: Map<string, ObjectPool<Entity>> = new Map();
createPool<T extends Entity>(
name: string,
createFn: () => T,
resetFn: (obj: T) => void,
initialSize: number = 10
): void {
this.pools.set(name, new ObjectPool(createFn, resetFn, initialSize));
}
get(name: string): Entity | null {
const pool = this.pools.get(name);
return pool ? pool.get() : null;
}
return(name: string, obj: Entity): void {
const pool = this.pools.get(name);
if (pool) pool.return(obj);
}
}
class GameManager extends Script {
private bulletPool!: ObjectPool<Entity>;
private enemyPool!: ObjectPool<Entity>;
onAwake(): void {
this.initPools();
}
private initPools(): void {
// 子弹池
this.bulletPool = new ObjectPool<Entity>(
() => this.createBulletBase(),
(bullet) => {
const script = bullet.getComponent(BulletScript);
if (script) {
script.setPool(this.bulletPool);
script.reset();
}
},
30
);
// 敌人池
this.enemyPool = new ObjectPool<Entity>(
() => this.createEnemyBase(),
(enemy) => {
const script = enemy.getComponent(EnemyScript);
if (script) {
script.setPool(this.enemyPool);
script.reset();
}
},
20
);
}
// 发射子弹
shoot(position: Vector3, direction: Vector3): void {
const bullet = this.bulletPool.get();
bullet.transform.position = position;
const script = bullet.getComponent(BulletScript);
if (script) {
script.direction = direction;
}
}
// 生成敌人
spawnEnemy(position: Vector3): void {
const enemy = this.enemyPool.get();
enemy.transform.position = position;
}
}
A: 确保 return() 中设置了 obj.enabled = false 并移出视野。
A: 检查是否在 resetFn 中正确调用了 setPool(),且 get() 时调用了 resetFn()。
A: 预创建足够数量的对象,避免运行时再创建。
问题: 每帧为每个格子创建纹理导致浏览器崩溃(12000个/秒)
解决: 用 Map 缓存纹理,每种颜色只创建一次
private textureCache = new Map<string, {texture: Texture2D, sprite: Sprite}>();
getOrCreateSprite(color: Color): Sprite {
const key = `${color.r}_${color.g}_${color.b}`;
if (!this.textureCache.has(key)) {
this.textureCache.set(key, { texture: create(), sprite: new Sprite() });
}
return this.textureCache.get(key)!.sprite;
}
问题: 回收的Entity仍然可见或可交互
解决: 回收时必须设置 isActive=false 并移出视野
return(entity: Entity) {
entity.isActive = false; // 必须!
entity.transform.setPosition(0, -1000, 0); // 移出视野
this.pool.push(entity);
}
问题: 棋盘存储颜色,�问题: 棋盘存储颜色,�问题: 棋盘存储颜色,�问题: 棋盘存�cri问题: �法回收 private board: (Cprivate board: (Cprivate board: (Cprivate board: (Cprivate board: (Cprivate board: (Cromino() { this.board[y][x] = entity; } clearLines() { this.pool.return(this.board[y][x]); }