From 45a3a7759c4f0dd261946ce4528e4032312aa339 Mon Sep 17 00:00:00 2001 From: Olivier Biot Date: Tue, 13 Aug 2024 14:38:33 +0800 Subject: [PATCH] new pool system improvements - add the option to use a destroy() method when pushed back into the pool (to mimic the legacy pool implementation) - rename internal import from pool to legacy_pool (internally only) - migrate tween objects to the new pool implementation - pass color arguments directly to the colorPool getter in Camera2d - export createPool directly under pools --- packages/melonjs/src/camera/camera2d.js | 14 +++---- packages/melonjs/src/index.js | 3 +- .../melonjs/src/level/tiled/TMXTileMap.js | 2 +- packages/melonjs/src/particles/emitter.js | 2 +- packages/melonjs/src/physics/body.js | 2 +- packages/melonjs/src/pool.ts | 4 ++ packages/melonjs/src/renderable/container.js | 2 +- packages/melonjs/src/renderable/light2d.js | 2 +- packages/melonjs/src/renderable/renderable.js | 2 +- .../melonjs/src/renderable/text/bitmaptext.js | 2 +- packages/melonjs/src/renderable/text/text.js | 2 +- .../src/system/{pooling.js => legacy_pool.js} | 0 packages/melonjs/src/system/pool.ts | 8 +++- packages/melonjs/src/tweens/tween.ts | 22 ++++++---- packages/melonjs/src/video/texture/atlas.js | 2 +- .../{pool.spec.js => legacy_pool.spec.js} | 0 .../{src/system => tests}/pool.test.ts | 40 +++++++++++++++++-- 17 files changed, 79 insertions(+), 30 deletions(-) rename packages/melonjs/src/system/{pooling.js => legacy_pool.js} (100%) rename packages/melonjs/tests/{pool.spec.js => legacy_pool.spec.js} (100%) rename packages/melonjs/{src/system => tests}/pool.test.ts (70%) diff --git a/packages/melonjs/src/camera/camera2d.js b/packages/melonjs/src/camera/camera2d.js index a61144946..102115efe 100644 --- a/packages/melonjs/src/camera/camera2d.js +++ b/packages/melonjs/src/camera/camera2d.js @@ -4,7 +4,6 @@ import { Matrix2d } from "../math/matrix2d.ts"; import { Matrix3d } from "../math/matrix3d.ts"; import { Rect } from "./../geometries/rectangle.ts"; import { renderer } from "./../video/video.js"; -import pool from "./../system/pooling.js"; import Renderable from "./../renderable/renderable.js"; import { clamp, toBeCloseTo } from "./../math/math.ts"; import { game } from "../index.js"; @@ -17,6 +16,7 @@ import { } from "../system/event.ts"; import { boundsPool } from "./../physics/bounds.ts"; import { colorPool } from "../math/color.ts"; +import { tweenPool } from "../tweens/tween.ts"; /** * @import {Bounds} from "./../physics/bounds.ts"; @@ -520,9 +520,9 @@ export default class Camera2d extends Renderable { * }); */ fadeOut(color, duration = 1000, onComplete) { - this._fadeOut.color = colorPool.get().copy(color); - this._fadeOut.tween = pool - .pull("Tween", this._fadeOut.color) + this._fadeOut.color = colorPool.get(color); + this._fadeOut.tween = tweenPool + .get(this._fadeOut.color) .to({ alpha: 0.0 }, { duration }) .onComplete(onComplete || null); this._fadeOut.tween.isPersistent = true; @@ -540,11 +540,11 @@ export default class Camera2d extends Renderable { * me.game.viewport.fadeIn("#FFFFFF", 75); */ fadeIn(color, duration = 1000, onComplete) { - this._fadeIn.color = colorPool.get().copy(color); + this._fadeIn.color = colorPool.get(color); const _alpha = this._fadeIn.color.alpha; this._fadeIn.color.alpha = 0.0; - this._fadeIn.tween = pool - .pull("Tween", this._fadeIn.color) + this._fadeIn.tween = tweenPool + .get(this._fadeIn.color) .to({ alpha: _alpha }, { duration }) .onComplete(onComplete || null); this._fadeIn.tween.isPersistent = true; diff --git a/packages/melonjs/src/index.js b/packages/melonjs/src/index.js index f6025d164..eec27cb3a 100644 --- a/packages/melonjs/src/index.js +++ b/packages/melonjs/src/index.js @@ -56,7 +56,7 @@ import Application from "./application/application.js"; import { cache as plugins } from "./plugin/plugin.js"; import save from "./system/save.ts"; import timer from "./system/timer.ts"; -import pool from "./system/pooling.js"; +import pool from "./system/legacy_pool.js"; import state from "./state/state.js"; import { BOOT, DOM_READY, eventEmitter } from "./system/event.ts"; import { setNocache } from "./loader/loader.js"; @@ -203,7 +203,6 @@ export function boot() { pool.register("me.Collectable", Collectable); pool.register("me.Trigger", Trigger); pool.register("me.Light2d", Light2d); - pool.register("me.Tween", Tween, true); pool.register("me.Particle", Particle, true); pool.register("me.Sprite", Sprite); pool.register("me.NineSliceSprite", NineSliceSprite); diff --git a/packages/melonjs/src/level/tiled/TMXTileMap.js b/packages/melonjs/src/level/tiled/TMXTileMap.js index a4928322b..ac2cfea97 100644 --- a/packages/melonjs/src/level/tiled/TMXTileMap.js +++ b/packages/melonjs/src/level/tiled/TMXTileMap.js @@ -1,4 +1,4 @@ -import pool from "./../../system/pooling.js"; +import pool from "../../system/legacy_pool.js"; import { game } from "../../index.js"; import { checkVersion } from "./../../utils/utils.ts"; import { collision } from "./../../physics/collision.js"; diff --git a/packages/melonjs/src/particles/emitter.js b/packages/melonjs/src/particles/emitter.js index 4c79085c9..be3cd03b2 100644 --- a/packages/melonjs/src/particles/emitter.js +++ b/packages/melonjs/src/particles/emitter.js @@ -1,4 +1,4 @@ -import pool from "./../system/pooling.js"; +import pool from "../system/legacy_pool.js"; import ParticleEmitterSettings from "./settings.js"; import { randomFloat } from "./../math/math.ts"; import Container from "./../renderable/container.js"; diff --git a/packages/melonjs/src/physics/body.js b/packages/melonjs/src/physics/body.js index 929c0ecfe..d061cee1f 100644 --- a/packages/melonjs/src/physics/body.js +++ b/packages/melonjs/src/physics/body.js @@ -2,7 +2,7 @@ import { Rect } from "./../geometries/rectangle.ts"; import { Ellipse } from "./../geometries/ellipse.ts"; import { Polygon, polygonPool } from "../geometries/polygon.ts"; import { Bounds, boundsPool } from "./bounds.ts"; -import pool from "./../system/pooling.js"; +import pool from "../system/legacy_pool.js"; import { collision } from "./collision.js"; import timer from "./../system/timer.ts"; import { clamp } from "./../math/math.ts"; diff --git a/packages/melonjs/src/pool.ts b/packages/melonjs/src/pool.ts index 8910381dd..212d4d847 100644 --- a/packages/melonjs/src/pool.ts +++ b/packages/melonjs/src/pool.ts @@ -10,6 +10,7 @@ import { matrix3dPool } from "./math/matrix3d"; import { vector2dPool } from "./math/vector2d"; import { vector3dPool } from "./math/vector3d"; import { boundsPool } from "./physics/bounds"; +import { tweenPool } from "./tweens/tween"; const pools = { vector2d: vector2dPool, @@ -24,6 +25,7 @@ const pools = { rectangle: rectPool, roundedRectangle: roundedRectanglePool, ellipse: ellipsePool, + tween: tweenPool, } as const; type PoolKey = keyof typeof pools; @@ -31,3 +33,5 @@ type PoolKey = keyof typeof pools; export const getPool = (key: K): (typeof pools)[K] => { return pools[key]; }; + +export { createPool } from "./system/pool"; diff --git a/packages/melonjs/src/renderable/container.js b/packages/melonjs/src/renderable/container.js index 8478c2a4e..bd383267a 100644 --- a/packages/melonjs/src/renderable/container.js +++ b/packages/melonjs/src/renderable/container.js @@ -2,7 +2,7 @@ import Renderable from "./renderable.js"; import { createGUID } from "../utils/utils"; import { defer } from "../utils/function"; import { game } from "../index.js"; -import pool from "../system/pooling.js"; +import pool from "../system/legacy_pool.js"; import state from "../state/state.js"; import Body from "../physics/body.js"; import { CANVAS_ONRESIZE, eventEmitter } from "../system/event.ts"; diff --git a/packages/melonjs/src/renderable/light2d.js b/packages/melonjs/src/renderable/light2d.js index 39e0e9842..7635859ed 100644 --- a/packages/melonjs/src/renderable/light2d.js +++ b/packages/melonjs/src/renderable/light2d.js @@ -1,6 +1,6 @@ import { ellipsePool } from "./../geometries/ellipse.ts"; import { colorPool } from "./../math/color.ts"; -import pool from "./../system/pooling.js"; +import pool from "../system/legacy_pool.js"; import Renderable from "./renderable.js"; /** diff --git a/packages/melonjs/src/renderable/renderable.js b/packages/melonjs/src/renderable/renderable.js index fbee28e02..48f29c19b 100644 --- a/packages/melonjs/src/renderable/renderable.js +++ b/packages/melonjs/src/renderable/renderable.js @@ -1,5 +1,5 @@ import { Rect } from "./../geometries/rectangle.ts"; -import pool from "./../system/pooling.js"; +import pool from "../system/legacy_pool.js"; import { releaseAllPointerEvents } from "./../input/input.js"; import { clamp } from "./../math/math.ts"; import Body from "./../physics/body.js"; diff --git a/packages/melonjs/src/renderable/text/bitmaptext.js b/packages/melonjs/src/renderable/text/bitmaptext.js index 86c1f990c..4f4f616b6 100644 --- a/packages/melonjs/src/renderable/text/bitmaptext.js +++ b/packages/melonjs/src/renderable/text/bitmaptext.js @@ -1,5 +1,5 @@ import { Color } from "../../math/color.ts"; -import pool from "../../system/pooling.js"; +import pool from "../../system/legacy_pool.js"; import { getImage, getBinary } from "../../loader/loader.js"; import Renderable from "../renderable.js"; import TextMetrics from "./textmetrics.js"; diff --git a/packages/melonjs/src/renderable/text/text.js b/packages/melonjs/src/renderable/text/text.js index 44c4c29ef..88efe8871 100644 --- a/packages/melonjs/src/renderable/text/text.js +++ b/packages/melonjs/src/renderable/text/text.js @@ -1,6 +1,6 @@ import { Color, colorPool } from "../../math/color.ts"; import { renderer as globalRenderer } from "../../video/video.js"; -import pool from "../../system/pooling.js"; +import pool from "../../system/legacy_pool.js"; import Renderable from "../renderable.js"; import { nextPowerOfTwo } from "../../math/math.ts"; import setContextStyle from "./textstyle.js"; diff --git a/packages/melonjs/src/system/pooling.js b/packages/melonjs/src/system/legacy_pool.js similarity index 100% rename from packages/melonjs/src/system/pooling.js rename to packages/melonjs/src/system/legacy_pool.js diff --git a/packages/melonjs/src/system/pool.ts b/packages/melonjs/src/system/pool.ts index 89c507206..f55f1bc6a 100644 --- a/packages/melonjs/src/system/pool.ts +++ b/packages/melonjs/src/system/pool.ts @@ -14,10 +14,12 @@ export interface Pool { } type Reset = ((...args: A) => void) | undefined; +type Destroy = (() => void) | undefined; export interface CreatePoolOptions { instance: T; reset?: Reset; + destroy?: Destroy; } export const createPool = ( @@ -25,6 +27,7 @@ export const createPool = ( ): Pool => { const available = new Set(); const instanceResetMethods = new Map>(); + const instanceDestroyMethods = new Map(); let inUse: number = 0; return { @@ -36,6 +39,8 @@ export const createPool = ( if (available.has(instance)) { throw new Error("Instance is already in pool."); } + const destroy = instanceDestroyMethods.get(instance); + destroy?.(); available.add(instance); inUse--; }, @@ -51,8 +56,9 @@ export const createPool = ( inUse++; return object; } else { - const { instance, reset } = options(...args); + const { instance, reset, destroy } = options(...args); instanceResetMethods.set(instance, reset); + instanceDestroyMethods.set(instance, destroy); inUse++; return instance; } diff --git a/packages/melonjs/src/tweens/tween.ts b/packages/melonjs/src/tweens/tween.ts index 1fd2577f1..4772f9182 100644 --- a/packages/melonjs/src/tweens/tween.ts +++ b/packages/melonjs/src/tweens/tween.ts @@ -1,5 +1,6 @@ import timer from "../system/timer.js"; import { game } from "../index.js"; +import { createPool } from "../system/pool.ts"; import { Easing, EasingFunction } from "./easing.js"; import { Interpolation, InterpolationFunction } from "./interpolation.js"; import { eventEmitter, STATE_RESUME } from "../system/event.js"; @@ -73,14 +74,6 @@ export default class Tween> { this.#boundResumeCallback = this._resumeCallback.bind(this); } - /** - * reset the tween object to default value - * @ignore - */ - onResetEvent(object: T) { - this.setProperties(object); - } - /** * @ignore */ @@ -456,3 +449,16 @@ export default class Tween> { return Interpolation; } } + +export const tweenPool = createPool( + >(object: T) => { + const tween = new Tween(object); + + return { + instance: tween, + reset(object: T) { + tween.setProperties(object); + }, + }; + }, +); diff --git a/packages/melonjs/src/video/texture/atlas.js b/packages/melonjs/src/video/texture/atlas.js index 2d11a8456..311e881eb 100644 --- a/packages/melonjs/src/video/texture/atlas.js +++ b/packages/melonjs/src/video/texture/atlas.js @@ -1,7 +1,7 @@ import { Vector2d } from "../../math/vector2d.ts"; import Sprite from "./../../renderable/sprite.js"; import { renderer } from "./../video.js"; -import pool from "./../../system/pooling.js"; +import pool from "../../system/legacy_pool.js"; import { getImage } from "./../../loader/loader.js"; import { parseTexturePacker } from "./parser/texturepacker.js"; import { parseSpriteSheet } from "./parser/spritesheet.js"; diff --git a/packages/melonjs/tests/pool.spec.js b/packages/melonjs/tests/legacy_pool.spec.js similarity index 100% rename from packages/melonjs/tests/pool.spec.js rename to packages/melonjs/tests/legacy_pool.spec.js diff --git a/packages/melonjs/src/system/pool.test.ts b/packages/melonjs/tests/pool.test.ts similarity index 70% rename from packages/melonjs/src/system/pool.test.ts rename to packages/melonjs/tests/pool.test.ts index 31bf7e356..a140755f6 100644 --- a/packages/melonjs/src/system/pool.test.ts +++ b/packages/melonjs/tests/pool.test.ts @@ -1,8 +1,16 @@ import { expect, test, vi } from "vitest"; -import { createPool } from "./pool"; +import { createPool } from "../src/pool"; -// eslint-disable-next-line @typescript-eslint/no-extraneous-class -class GameObject {} +class GameObject { + destroyCalled = false; + resetCalled = false; + reset() { + this.resetCalled = true; + } + destroy() { + this.destroyCalled = true; + } +} test("can acquire objects from the pool", () => { const pool = createPool(() => { @@ -31,6 +39,32 @@ test("can release objects from the pool", () => { expect(pool.used()).toBe(1); }); +test("object reset and destroy are called when getting & releasing object", () => { + const pool = createPool(() => { + const gameObject = new GameObject(); + return { + instance: gameObject, + reset: () => { + gameObject.reset(); + }, + destroy: () => { + gameObject.destroy(); + }, + }; + }); + const object1 = pool.get(); + + expect(object1).toBeInstanceOf(GameObject); + expect(object1.resetCalled).toEqual(false); + expect(object1.destroyCalled).toEqual(false); + pool.release(object1); + expect(object1.destroyCalled).toEqual(true); + + const object2 = pool.get(); + expect(object2).toBeInstanceOf(GameObject); + expect(object2.resetCalled).toEqual(true); +}); + test("can not release instance that already exist in pool", () => { const pool = createPool(() => { return { instance: new GameObject() };