/// /// namespace gdjs { /** * The PIXI.js renderer for the Tile map runtime object. * * @class TileMapRuntimeObjectPixiRenderer * @category Renderers > Tile Map */ export class TileMapRuntimeObjectPixiRenderer { private _object: | gdjs.TileMapRuntimeObject | gdjs.SimpleTileMapRuntimeObject; private _pixiObject: PIXI.tilemap.CompositeTilemap; private _lastCullingLeftBound = 0; private _lastCullingRightBound = 0; private _lastCullingTopBound = 0; private _lastCullingBottomBound = 0; /** * @param runtimeObject The object to render * @param instanceContainer The gdjs.RuntimeScene in which the object is */ constructor( runtimeObject: | gdjs.TileMapRuntimeObject | gdjs.SimpleTileMapRuntimeObject, instanceContainer: gdjs.RuntimeInstanceContainer ) { this._object = runtimeObject; // This setting allows tile maps with more than 16K tiles. PIXI.tilemap.settings.use32bitIndex = true; // Load (or reset) this._pixiObject = new PIXI.tilemap.CompositeTilemap(); this._pixiObject.tileAnim = [0, 0]; instanceContainer .getLayer('') .getRenderer() .addRendererObject(this._pixiObject, runtimeObject.getZOrder()); this.updateAngle(); this.updateOpacity(); this.updatePosition(); } getRendererObject() { return this._pixiObject; } incrementAnimationFrameX(instanceContainer: gdjs.RuntimeInstanceContainer) { this._pixiObject.tileAnim[0] += 1; } updatePosition(): void { this._pixiObject.pivot.x = this._object.getOriginalWidth() / 2; this._pixiObject.pivot.y = this._object.getOriginalHeight() / 2; this._pixiObject.position.x = this._object.x + this.getWidth() / 2; this._pixiObject.position.y = this._object.y + this.getHeight() / 2; } updateAngle(): void { this._pixiObject.rotation = gdjs.toRad(this._object.angle); } updateOpacity(): void { const newAlpha = this._object._opacity / 255; if (this._pixiObject.alpha === newAlpha) { return; } this._pixiObject.alpha = newAlpha; const tileMap = this._object.getTileMap(); if (!tileMap) return; for (const layer of tileMap.getLayers()) { const isLayerHidden = (this._object.getDisplayMode() === 'index' && this._object.getDisplayedLayerIndex() !== layer.id) || (this._object.getDisplayMode() === 'visible' && !layer.isVisible()); // Only set alpha on editable layers that are not hidden, // as others are not rendered. if (isLayerHidden) continue; if (layer instanceof TileMapHelper.EditableTileMapLayer) { layer.setAlpha(this._pixiObject.alpha); } } // Changing the alpha requires a full re-render of the tile map. this._object.updateTileMap(true); } setWidth(width: float): void { this._pixiObject.scale.x = width / this._object.getOriginalWidth(); this._pixiObject.position.x = this._object.x + width / 2; } setHeight(height: float): void { this._pixiObject.scale.y = height / this._object.getOriginalHeight(); this._pixiObject.position.y = this._object.y + height / 2; } setScaleX(scaleX: float): void { this._pixiObject.scale.x = scaleX; const width = scaleX * this._object.getOriginalWidth(); this._pixiObject.position.x = this._object.x + width / 2; } setScaleY(scaleY: float): void { this._pixiObject.scale.y = scaleY; const height = scaleY * this._object.getOriginalHeight(); this._pixiObject.position.y = this._object.y + height / 2; } getWidth(): float { return this._object.getOriginalWidth() * this._pixiObject.scale.x; } getHeight(): float { return this._object.getOriginalHeight() * this._pixiObject.scale.y; } getScaleX(): float { return this._pixiObject.scale.x; } getScaleY(): float { return this._pixiObject.scale.y; } refreshPixiTileMap( textureCache: TileMapHelper.TileTextureCache, forceRefresh: boolean ) { const object = this._object; const tileMap = object.getTileMap(); if (!tileMap) return; const dimX = tileMap.getDimensionX(); const dimY = tileMap.getDimensionY(); let leftBound = 0; let rightBound = dimX; let topBound = 0; let bottomBound = dimY; const instanceContainer = this._object.getInstanceContainer(); const scene = instanceContainer.getScene(); const layerName = this._object.getLayer(); const layer = this._object.getInstanceContainer().getLayer(layerName); if ( // Don't cull small maps or chunks. dimX + dimY > 100 && // TODO Handle culling for TileMapRuntimeObject. isSimpleTileMap(object) && instanceContainer === scene && (!gdjs.scene3d || (gdjs.scene3d.camera.getCameraRotationX(scene, layerName, 0) === 0 && gdjs.scene3d.camera.getCameraRotationY(scene, layerName, 0) === 0)) ) { const cameraX = layer.getCameraX(); const cameraY = layer.getCameraY(); let cameraHalfWidth = layer.getCameraWidth() / 2; let cameraHalfHeight = layer.getCameraHeight() / 2; if (layer.getCameraRotation() !== 0) { const hypot = cameraHalfWidth + cameraHalfHeight; cameraHalfWidth = hypot; cameraHalfHeight = hypot; } const [cameraLeftTile, cameraTopTile] = object.getGridCoordinatesFromSceneCoordinates( cameraX - cameraHalfWidth, cameraY - cameraHalfHeight ); const [cameraRightTile, cameraBottomTile] = object.getGridCoordinatesFromSceneCoordinates( cameraX + cameraHalfWidth, cameraY + cameraHalfHeight ); leftBound = Math.min(cameraLeftTile, cameraRightTile); rightBound = Math.max(cameraLeftTile, cameraRightTile) + 1; topBound = Math.min(cameraTopTile, cameraBottomTile); bottomBound = Math.max(cameraTopTile, cameraBottomTile) + 1; } if ( forceRefresh || this._lastCullingLeftBound !== leftBound || this._lastCullingRightBound !== rightBound || this._lastCullingTopBound !== topBound || this._lastCullingBottomBound !== bottomBound ) { this._lastCullingLeftBound = leftBound; this._lastCullingRightBound = rightBound; this._lastCullingTopBound = topBound; this._lastCullingBottomBound = bottomBound; TileMapHelper.PixiTileMapHelper.updatePixiTileMap( this._pixiObject, tileMap, textureCache, // @ts-ignore this._object.getDisplayMode(), this._object.getDisplayedLayerIndex(), leftBound, rightBound, topBound, bottomBound ); } } destroy(): void { // Keep textures because they are shared by all tile maps. this._pixiObject.destroy(false); } } function isSimpleTileMap( object: gdjs.SimpleTileMapRuntimeObject | gdjs.TileMapRuntimeObject ): object is gdjs.SimpleTileMapRuntimeObject { //@ts-ignore We are checking if the methods are present. return object.getGridCoordinatesFromSceneCoordinates; } /** * @category Renderers > Tile Map */ export const TileMapRuntimeObjectRenderer = gdjs.TileMapRuntimeObjectPixiRenderer; /** * @category Renderers > Tile Map */ export type TileMapRuntimeObjectRenderer = gdjs.TileMapRuntimeObjectPixiRenderer; }