UNPKG

phaser-plugin-inspector

Version:
1,277 lines (1,003 loc) 61.9 kB
import Phaser from 'phaser'; import { Pane } from 'tweakpane'; const FXMap = { 4: 'GLOW', 5: 'SHADOW', 6: 'PIXELATE', 7: 'VIGNETTE', 8: 'SHINE', 9: 'BLUR', 12: 'GRADIENT', 13: 'BLOOM', 14: 'COLOR_MATRIX', 15: 'CIRCLE', 16: 'BARREL', 17: 'DISPLACEMENT', 18: 'WIPE', 19: 'BOKEH' }; const TAU = 2 * Math.PI; const CacheNames = ['audio', 'binary', 'bitmapFont', 'html', 'json', 'obj', 'physics', 'shader', 'text', 'tilemap', 'video', 'xml']; const CameraEvents = Phaser.Cameras.Scene2D.Events; const SceneEvents = Phaser.Scenes.Events; const GameObjectEvents = Phaser.GameObjects.Events; const { BlendModes } = Phaser; const { GetFirst } = Phaser.Utils.Array; function animToPrint ({ key, delay, duration, frameRate, repeat, repeatDelay }) { return { key, delay, duration, frameRate, repeat, repeatDelay }; } function sceneToPrint (scene) { const { key, status, active, visible } = scene.sys.settings; return { key, status, active, visible }; } function soundToPrint ({ key, isPaused, isPlaying, seek, totalDuration, volume }) { return { key, isPaused, isPlaying, seek, totalDuration, volume }; } function textureToPrint ({ key, firstFrame, frameTotal }) { return { key, firstFrame, frameTotal }; } function copyToSafeObj (obj) { const out = {}; for (const key in obj) { const val = obj[key]; const typ = typeof val; if (!val || typ === 'boolean' || typ === 'number' || typ === 'string') { out[key] = val; } } return out; } function copyToSafeTable (obj) { const out = {}; for (const key in obj) { const val = obj[key]; out[key] = typeof val === 'object' ? copyToSafeObj(obj[key]) : val; } return out; } function printCaches (manager) { for (const name of CacheNames) { printCache(name, manager[name]); } for (const name in manager.custom) { printCache(name, manager.custom[name]); } } function printCache (name, cache) { const { size } = cache.entries; if (size > 0) { console.info(`${name} cache (${size})`); console.table(copyToSafeTable(cache.entries.entries)); } else { console.info(`${name} cache is empty`); } } function printDevice (device) { for (const key in device) { console.info(key); console.table(copyToSafeObj(device[key])); } } function snapshot (img) { img.style.width = '256px'; img.style.height = 'auto'; document.body.appendChild(img); } function AddPointer (pointer, pane) { const folder = pane.addFolder({ title: `Pointer ${pointer.id}`, expanded: false }); folder.addMonitor(pointer, 'active'); folder.addMonitor(pointer, 'angle'); folder.addMonitor(pointer, 'buttons'); folder.addMonitor(pointer, 'deltaX'); folder.addMonitor(pointer, 'deltaY'); folder.addMonitor(pointer, 'deltaZ'); folder.addMonitor(pointer, 'distance'); folder.addMonitor(pointer, 'downX'); folder.addMonitor(pointer, 'downY'); folder.addMonitor(pointer, 'id'); folder.addMonitor(pointer, 'isDown'); folder.addMonitor(pointer, 'locked'); folder.addMonitor(pointer, 'movementX'); folder.addMonitor(pointer, 'movementY'); folder.addMonitor(pointer, 'primaryDown'); folder.addMonitor(pointer, 'upX'); folder.addMonitor(pointer, 'upY'); folder.addMonitor(pointer.velocity, 'x', { label: 'velocity x' }); folder.addMonitor(pointer.velocity, 'y', { label: 'velocity y' }); folder.addMonitor(pointer, 'worldX'); folder.addMonitor(pointer, 'worldY'); folder.addMonitor(pointer, 'x'); folder.addMonitor(pointer, 'y'); return folder; } function AddSound (sound, pane) { const folder = pane.addFolder({ title: `Sound “${sound.key}”`, expanded: false }); sound.once('destroy', () => { folder.dispose(); }); if (sound.currentMarker) { folder.addMonitor(sound.currentMarker, 'name'); } folder.addMonitor(sound, 'duration'); folder.addMonitor(sound, 'isPaused'); folder.addMonitor(sound, 'isPlaying'); folder.addMonitor(sound, 'seek'); folder.addMonitor(sound, 'totalDuration'); folder.addMonitor(sound, 'loop'); folder.addInput(sound, 'mute'); folder.addInput(sound, 'volume', { min: 0, max: 1 }); folder.addButton({ title: 'Play' }).on('click', () => { console.info('Play sound “%s”', sound.key); sound.play(); }); folder.addButton({ title: 'Pause' }).on('click', () => { console.info('Pause sound “%s”', sound.key); sound.pause(); }); folder.addButton({ title: 'Resume' }).on('click', () => { console.info('Resume sound “%s”', sound.key); sound.resume(); }); folder.addButton({ title: 'Stop' }).on('click', () => { console.info('Stop sound “%s”', sound.key); sound.stop(); }); folder.addButton({ title: 'Remove' }).on('click', () => { console.info('Remove sound “%s”', sound.key); sound.destroy(); }); for (const name in sound.markers) { folder.addButton({ title: `Play “${name}”` }).on('click', () => { console.info('Play sound “%s” marker “%s”', sound.key, name); sound.play(name); }); } return folder; } function displayListItemToPrint ({ name, type, x, y, visible, depth }) { return { name, type, x, y, visible, depth }; } function updateListItemToPrint ({ name, type, active }) { return { name, type, active }; } function tweenToPrint ({ duration, elapsed, paused, progress, state, totalElapsed, totalProgress }) { return { duration, elapsed, paused, progress, state, totalElapsed, totalProgress }; } function timerEventToPrint ({ delay, elapsed, loop, paused, repeat, repeatCount }) { return { delay, elapsed, loop, paused, repeat, repeatCount }; } function keyToPrint ({ duration, emitOnRepeat, enabled, isDown, isUp, location, repeats, timeDown, timeUp }) { return { duration, emitOnRepeat, enabled, isDown, isUp, location, repeats, timeDown, timeUp }; } function cameraToPrint ({ name, id, x, y, width, height, visible, alpha }) { return { name, id, x, y, width, height, visible, alpha }; } function lightToPrint ({ x, y, radius, color, intensity, visible }) { return { x, y, radius, color: `rgb(${color.r.toFixed(2)}, ${color.g.toFixed(2)}, ${color.b.toFixed(2)})`, intensity, visible }; } function AddCamera (camera, pane) { const defaultCamera = camera.cameraManager.default; const w = defaultCamera.width; const h = defaultCamera.height; const folder = pane.addFolder({ title: `Camera ${camera.id} ${camera.name || ''}`, expanded: false }); folder.addMonitor(camera, 'name'); folder.addInput(camera, 'alpha', { min: 0, max: 1, step: 0.05 }); folder.addInput(camera, 'backgroundColor'); folder.addInput(camera, 'x', { min: -w, max: w, step: 10 }); folder.addInput(camera, 'y', { min: -h, max: h, step: 10 }); folder.addInput(camera, 'width', { min: 0, max: w, step: 10 }); folder.addInput(camera, 'height', { min: 0, max: h, step: 10 }); folder.addInput(camera, 'scrollX', { min: -w, max: w, step: 10 }); folder.addInput(camera, 'scrollY', { min: -h, max: h, step: 10 }); folder.addInput(camera, 'originX', { min: 0, max: 1, step: 0.05 }); folder.addInput(camera, 'originY', { min: 0, max: 1, step: 0.05 }); folder.addInput(camera, 'rotation', { min: 0, max: TAU }); folder.addInput(camera, 'zoom', { min: 0.1, max: 10, step: 0.05 }); folder.addInput(camera, 'followOffset'); folder.addInput(camera, 'disableCull'); folder.addInput(camera, 'inputEnabled'); folder.addInput(camera, 'roundPixels'); folder.addInput(camera, 'useBounds'); folder.addInput(camera, 'visible'); folder.addMonitor(camera, 'centerX'); folder.addMonitor(camera, 'centerY'); folder.addMonitor(camera.midPoint, 'x', { label: 'midPoint x' }); folder.addMonitor(camera.midPoint, 'y', { label: 'midPoint y' }); folder.addMonitor(camera.worldView, 'x', { label: 'world x' }); folder.addMonitor(camera.worldView, 'y', { label: 'world y' }); folder.addMonitor(camera.worldView, 'width', { label: 'world width' }); folder.addMonitor(camera.worldView, 'height', { label: 'world height' }); // TODO const { deadzone } = camera; if (deadzone) { folder.addMonitor(deadzone, 'x', { label: 'deadzone x' }); folder.addMonitor(deadzone, 'y', { label: 'deadzone y' }); folder.addMonitor(deadzone, 'width', { label: 'deadzone width' }); folder.addMonitor(deadzone, 'height', { label: 'deadzone height' }); } if (camera.hasPostPipeline) { AddPipelines(camera.postPipelines, folder, { title: 'Post Pipelines' }); } folder.addButton({ title: 'Fade in' }).on('click', () => { camera.fadeIn(); }); folder.addButton({ title: 'Fade out' }).on('click', () => { camera.fadeOut(); }); folder.addButton({ title: 'Flash' }).on('click', () => { camera.flash(); }); folder.addButton({ title: 'Shake' }).on('click', () => { camera.shake(); }); folder.addButton({ title: 'Reset effects' }).on('click', () => { camera.resetFX(); }); folder.addButton({ title: 'Reset post pipeline' }).on('click', () => { camera.resetPostPipeline(); }); camera.on(CameraEvents.DESTROY, () => { folder.dispose(); }); return folder; } function AddArcadePhysicsWorld (world, pane) { const { arcadePhysics, events } = world.scene.sys; const folder = pane.addFolder({ title: 'Arcade Physics', expanded: false }); folder.addMonitor(world.bodies, 'size', { label: 'bodies.size' }); folder.addInput(world, 'fixedStep'); folder.addInput(world, 'forceX'); folder.addInput(world, 'fps', { min: 5, max: 300, step: 5 }).on('change', ({ value }) => { world.setFPS(value); }); folder.addMonitor(world, '_frameTimeMS'); folder.addInput(world, 'gravity', { x: { min: -1000, max: 1000 }, y: { min: -1000, max: 1000 } }); folder.addInput(world, 'isPaused'); folder.addInput(world, 'OVERLAP_BIAS', { label: 'OVERLAP_BIAS', min: 0, max: 32, step: 1 }); folder.addMonitor(world.staticBodies, 'size', { label: 'staticBodies.size' }); folder.addInput(world, 'TILE_BIAS', { label: 'TILE_BIAS', min: 0, max: 32, step: 1 }); folder.addInput(world, 'timeScale', { min: 0.1, max: 10, step: 0.1 }); folder.addInput(world, 'useTree'); if (world.debugGraphic) { folder.addInput(world.debugGraphic, 'visible', { label: 'debugGraphic.visible' }); } folder.addButton({ title: 'Enable update' }).on('click', () => { arcadePhysics.enableUpdate(); }); folder.addButton({ title: 'Disable update' }).on('click', () => { arcadePhysics.disableUpdate(); }); folder.addButton({ title: 'Update' }).on('click', () => { world.update(0, world._frameTimeMS || (1000 / 60)); }); folder.addButton({ title: 'Print colliders' }).on('click', () => { console.info('Colliders', world.colliders.getActive()); }); events.once(SceneEvents.SHUTDOWN, () => { folder.dispose(); }); return folder; } function AddMatterPhysicsWorld (world, pane) { const { events } = world.scene.sys; const folder = pane.addFolder({ title: 'Matter Physics', expanded: false }); folder.addInput(world, 'autoUpdate'); folder.addInput(world, 'enabled'); folder.addInput(world.localWorld, 'gravity'); folder.addInput(world.localWorld.gravity, 'scale', { label: 'gravity scale', min: 0, max: 0.1, step: 0.001 }); if (world.debugGraphic) { folder.addInput(world.debugGraphic, 'visible', { label: 'debugGraphic.visible' }); } folder.addButton({ title: 'Pause' }).on('click', () => { world.pause(); }); folder.addButton({ title: 'Resume' }).on('click', () => { world.resume(); }); folder.addButton({ title: 'Step' }).on('click', () => { world.step(); }); events.once(SceneEvents.SHUTDOWN, () => { folder.dispose(); }); return folder; } function AddGameObject (obj, pane, options = { title: `${obj.type} “${obj.name}”` }) { const folder = pane.addFolder(options); folder.addInput(obj, 'active'); folder.addMonitor(obj, 'cameraFilter'); folder.addMonitor(obj, 'state'); if ('texture' in obj && obj.texture && obj.texture.key) { const proxy = { get 'texture.key' () { return obj.texture.key; }, get 'frame.name' () { return obj.frame.name; } }; folder.addMonitor(proxy, 'texture.key'); folder.addMonitor(proxy, 'frame.name', { format: String }); } if ('displayTexture' in obj) { const proxy = { get 'displayTexture.key' () { return obj.displayTexture.key; }, get 'displayFrame.name' () { return obj.displayFrame.name; } }; folder.addMonitor(proxy, 'displayTexture.key'); folder.addMonitor(proxy, 'displayFrame.name', { format: String }); } if ('alpha' in obj) { folder.addInput(obj, 'alpha', { min: 0, max: 1, step: 0.05 }); } if ('blendMode' in obj) { folder.addInput(obj, 'blendMode', { options: BlendModes }); } if ('depth' in obj) { folder.addMonitor(obj, 'depth'); } if ('width' in obj) { folder.addMonitor(obj, 'width'); folder.addMonitor(obj, 'height'); } if ('displayWidth' in obj) { folder.addMonitor(obj, 'displayWidth'); folder.addMonitor(obj, 'displayHeight'); } if ('originX' in obj) { folder.addMonitor(obj, 'originX'); folder.addMonitor(obj, 'originY'); } if ('displayOriginX' in obj) { folder.addMonitor(obj, 'displayOriginX'); folder.addMonitor(obj, 'displayOriginY'); } if ('scaleX' in obj) { folder.addMonitor(obj, 'scaleX'); folder.addMonitor(obj, 'scaleY'); folder.addInput(obj, 'scale', { min: 0.1, max: 10, step: 0.1 }); } if ('flipX' in obj) { folder.addInput(obj, 'flipX'); folder.addInput(obj, 'flipY'); } if ('angle' in obj) { folder.addMonitor(obj, 'angle'); } if ('rotation' in obj) { folder.addInput(obj, 'rotation', { min: 0, max: TAU, step: 0.01 * TAU }); } if ('visible' in obj) { folder.addInput(obj, 'visible'); } if ('x' in obj) { folder.addMonitor(obj, 'x'); folder.addMonitor(obj, 'y'); folder.addMonitor(obj, 'z'); folder.addMonitor(obj, 'w'); } if ('modelPosition' in obj) { folder.addInput(obj, 'modelPosition'); folder.addInput(obj, 'modelScale'); folder.addInput(obj, 'modelRotation'); } if ('fov' in obj) { folder.addMonitor(obj, 'fov'); } if ('faces' in obj && 'length' in obj.faces) { folder.addMonitor(obj.faces, 'length', { label: 'faces.length', format: FormatLength }); } if ('vertices' in obj && 'length' in obj.vertices) { folder.addMonitor(obj.vertices, 'length', { label: 'vertices.length', format: FormatLength }); } if ('totalRendered' in obj) { folder.addMonitor(obj, 'totalRendered', { format: FormatLength }); } if ('tilesDrawn' in obj) { folder.addMonitor(obj, 'tilesDrawn', { format: FormatLength }); } if ('tilesTotal' in obj) { folder.addMonitor(obj, 'tilesTotal', { format: FormatLength }); } if (obj.pipeline) { // WebGL only if ('getPipelineName' in obj) { const proxy = { get 'getPipelineName()' () { return obj.getPipelineName(); } }; folder.addMonitor(proxy, 'getPipelineName()', { label: 'getPipelineName()' }); } if ('hasPostPipeline' in obj) { folder.addMonitor(obj, 'hasPostPipeline'); if (obj.hasPostPipeline) { AddPipelines(obj.postPipelines, folder, { title: 'Post Pipelines' }); } } if ('resetPipeline' in obj) { folder.addButton({ title: 'Reset pipeline' }).on('click', () => { console.info('Reset pipeline', obj.type, obj.name); obj.resetPipeline(); }); } if ('resetPostPipeline' in obj) { folder.addButton({ title: 'Reset post pipeline' }).on('click', () => { console.info('Reset post pipeline', obj.type, obj.name); obj.resetPostPipeline(); }); } } if ('preFX' in obj && obj.preFX && obj.preFX.list.length > 0) { AddFXComponent(obj.preFX, folder); } // The `postFX` controller doesn't seem to show any relevant state. if ('children' in obj && 'length' in obj.children) { folder.addMonitor(obj.children, 'length', { label: 'children.length', format: FormatLength }); } if ('displayList' in obj) { const { displayList } = obj; folder.addButton({ title: 'Bring to top' }).on('click', () => { console.info('Bring to top', obj.type, obj.name); displayList.bringToTop(obj); }); folder.addButton({ title: 'Move down' }).on('click', () => { console.info('Move down', obj.type, obj.name); displayList.moveDown(obj); }); folder.addButton({ title: 'Move to …' }).on('click', () => { const idx = prompt(`Move to index (0 to ${displayList.length - 1}):`); if (!idx) { return; } console.info('Move to index', idx, obj.type, obj.name); displayList.moveTo(obj, idx); }); folder.addButton({ title: 'Move up' }).on('click', () => { console.info('Move up', obj.type, obj.name); displayList.moveUp(obj); }); folder.addButton({ title: 'Send to back' }).on('click', () => { console.info('Send to back', obj.type, obj.name); displayList.sendToBack(obj); }); } folder.addButton({ title: 'Destroy' }).on('click', () => { console.info('Destroy', obj.type, obj.name); obj.destroy(); }); obj.once(GameObjectEvents.DESTROY, () => { folder.dispose(); }); return folder; } function AddGroup (group, pane, options = { title: `${group.type} “${group.name}”` }) { const folder = pane.addFolder(options); const graphOptions = { view: 'graph', min: 0, max: group.maxSize === -1 ? 100 : group.maxSize }; folder.addMonitor(group.getChildren(), 'length', graphOptions); const proxy = { get active () { return group.countActive(true); }, get inactive () { return group.countActive(false); }, get free () { return group.getTotalFree(); }, get full () { return group.isFull(); } }; folder.addMonitor(proxy, 'active', graphOptions); folder.addMonitor(proxy, 'inactive', graphOptions); if (group.maxSize > -1) { folder.addMonitor(proxy, 'free', graphOptions); } folder.addMonitor(group, 'maxSize'); folder.addMonitor(proxy, 'full'); folder.addButton({ title: 'Clear' }).on('click', () => { console.info('Clear group', group.name); group.clear(); }); folder.addButton({ title: 'Destroy' }).on('click', () => { console.info('Destroy group', group.name); group.destroy(); }); folder.addButton({ title: 'Destroy group members' }).on('click', () => { console.info('Destroy group members', group.name); group.clear(true, true); }); group.once(GameObjectEvents.DESTROY, () => { folder.dispose(); }); return folder; } function AddLight (light, pane, options = { title: 'Light' }) { const folder = pane.addFolder(options); folder.addInput(light, 'color', { color: { type: 'float' } }); folder.addInput(light, 'intensity', { min: 0, max: 10, step: 0.1 }); folder.addInput(light, 'radius', { min: 0, max: 1024, step: 8 }); folder.addInput(light, 'visible'); folder.addMonitor(light, 'x'); folder.addMonitor(light, 'y'); return folder; } function AddParticleEmitter (emitter, pane, options = { title: `Particle Emitter “${emitter.name}”` }) { const folder = pane.addFolder(options); const max = emitter.maxParticles || 100; const proxy = { get 'atLimit()' () { return emitter.atLimit(); }, get 'getParticleCount()' () { return emitter.getParticleCount(); } }; folder.addMonitor(emitter, 'active'); folder.addMonitor(emitter, 'animQuantity'); folder.addMonitor(proxy, 'atLimit()'); folder.addMonitor(emitter, 'delay'); folder.addMonitor(emitter, 'duration'); folder.addMonitor(emitter, 'emitting'); folder.addMonitor(emitter, 'frameQuantity'); folder.addMonitor(emitter, 'maxAliveParticles'); folder.addMonitor(emitter, 'maxParticles'); folder.addMonitor(emitter, 'quantity'); folder.addMonitor(emitter, 'stopAfter'); folder.addInput(emitter, 'blendMode', { options: BlendModes }); folder.addInput(emitter, 'frequency', { min: -1, max: 1000 }); folder.addInput(emitter, 'moveTo'); folder.addInput(emitter, 'particleBringToTop'); folder.addInput(emitter, 'radial'); folder.addInput(emitter, 'randomFrame'); folder.addInput(emitter, 'timeScale', { min: 0.1, max: 10, step: 0.1 }); folder.addInput(emitter, 'visible'); const opsFolder = folder.addFolder({ title: 'Ops', expanded: false }); for (const op of Object.values(emitter.ops)) { const { propertyKey, start, end, current, method } = op; const opFolder = opsFolder.addFolder({ title: propertyKey, expanded: false }); if (typeof current === 'number') { if (method === 1) { opFolder.addInput(op, 'current'); } else { opFolder.addMonitor(op, 'current'); } } if (method === 4 || method === 5 || method === 6) { opFolder.addInput(op, 'start'); opFolder.addInput(op, 'end'); } if (method === 4) { opFolder.addInput(op, 'steps', { step: 1 }); opFolder.addInput(op, 'yoyo'); opFolder.addInput(op, 'direction', { min: 0, max: 1, step: 1 }); opFolder.addMonitor(op, 'counter', { min: start, max: end, view: 'graph' }); } } const graphsFolder = folder.addFolder({ title: 'Counters', expanded: false }); graphsFolder.addMonitor(emitter.alive, 'length', { view: 'graph', min: 0, max: max, label: 'getAliveParticleCount()', format: FormatLength }); graphsFolder.addMonitor(emitter.dead, 'length', { view: 'graph', min: 0, max: max, label: 'getDeadParticleCount()', format: FormatLength }); graphsFolder.addMonitor(proxy, 'getParticleCount()', { view: 'graph', min: 0, max: max, format: FormatLength }); if (emitter.frequency > 0) { graphsFolder.addMonitor(emitter, 'flowCounter', { view: 'graph', min: 0, max: emitter.frequency }); } if (emitter.frameQuantity > 1) { graphsFolder.addMonitor(emitter, 'frameCounter', { view: 'graph', min: 0, max: emitter.frameQuantity }); } if (emitter.animQuantity > 1) { graphsFolder.addMonitor(emitter, 'animCounter', { view: 'graph', min: 0, max: emitter.animQuantity }); } if (emitter.duration > 0) { graphsFolder.addMonitor(emitter, 'elapsed', { view: 'graph', min: 0, max: emitter.duration }); } if (emitter.stopAfter > 0) { graphsFolder.addMonitor(emitter, 'stopCounter', { view: 'graph', min: 0, max: emitter.stopAfter }); } if (emitter.emitZones.length > 1) { graphsFolder.addMonitor(emitter, 'zoneIndex', { view: 'graph', min: 0, max: emitter.emitZones.length }); } if (emitter.frames.length > 1) { graphsFolder.addMonitor(emitter, 'currentFrame', { view: 'graph', min: 0, max: emitter.frames.length }); } if (emitter.anims.length > 1) { graphsFolder.addMonitor(emitter, 'currentAnim', { view: 'graph', min: 0, max: emitter.anims.length }); } const { processors } = emitter; if (processors.length > 0) { const processorsFolder = folder.addFolder({ title: 'Processors' }); for (const processor of processors.list) { processorsFolder.addInput(processor, 'active', { label: `${processor.name || 'Processor'} active` }); } } folder.addButton({ title: 'Start' }).on('click', () => { emitter.start(); }); folder.addButton({ title: 'Stop' }).on('click', () => { emitter.stop(); }); folder.addButton({ title: 'Pause' }).on('click', () => { emitter.pause(); }); folder.addButton({ title: 'Resume' }).on('click', () => { emitter.resume(); }); folder.addButton({ title: 'Kill all' }).on('click', () => { emitter.killAll(); }); folder.addButton({ title: 'Print JSON' }).on('click', () => { console.log(JSON.stringify(emitter.toJSON())); }); emitter.once(GameObjectEvents.DESTROY, () => { folder.dispose(); }); return folder; } function AddVideo (video, pane, options = { title: `Video “${video.name}”` }) { const folder = pane.addFolder(options); const videoProxy = { get 'getCurrentTime()' () { return video.getCurrentTime(); }, get 'getDuration()' () { return video.getDuration(); }, get 'getLoop()' () { return video.getLoop(); }, get 'getPlaybackRate()' () { return video.getPlaybackRate(); }, get 'getProgress()' () { return video.getProgress(); }, get 'getVolume()' () { return video.getVolume(); }, get 'isMuted()' () { return video.isMuted(); }, get 'isPaused()' () { return video.isPaused(); }, get 'isPlaying()' () { return video.isPlaying(); }, get 'seekTo()' () { return video.getProgress(); }, set 'seekTo()' (v) { video.seekTo(v); }, get 'setPlaybackRate()' () { return video.getPlaybackRate(); }, set 'setPlaybackRate()' (v) { video.setPlaybackRate(v); }, get 'setVolume()' () { return video.getVolume(); }, set 'setVolume()' (v) { video.setVolume(v); } }; folder.addMonitor(video, 'failedPlayAttempts'); folder.addMonitor(video, 'frameReady'); folder.addMonitor(videoProxy, 'getCurrentTime()'); folder.addMonitor(videoProxy, 'getDuration()'); folder.addMonitor(videoProxy, 'getLoop()'); folder.addMonitor(videoProxy, 'getPlaybackRate()'); folder.addMonitor(videoProxy, 'getProgress()'); folder.addMonitor(videoProxy, 'getVolume()'); folder.addMonitor(videoProxy, 'isMuted()'); folder.addMonitor(videoProxy, 'isPaused()'); folder.addMonitor(videoProxy, 'isPlaying()'); folder.addMonitor(video, 'isSeeking'); folder.addMonitor(video, 'isStalled'); folder.addMonitor(video, 'retry'); folder.addInput(videoProxy, 'seekTo()', { min: 0, max: 1 }); folder.addInput(videoProxy, 'setPlaybackRate()', { min: 0.25, max: 4, step: 0.25 }); folder.addInput(videoProxy, 'setVolume()', { min: 0, max: 1 }); folder.addMonitor(video, 'touchLocked'); folder.addButton({ title: 'Play' }).on('click', () => { console.info('Play video'); video.play(); }); folder.addButton({ title: 'Stop' }).on('click', () => { console.info('Stop video'); video.stop(); }); folder.addButton({ title: 'Pause' }).on('click', () => { console.info('Pause video'); video.pause(); }); folder.addButton({ title: 'Resume' }).on('click', () => { console.info('Resume video'); video.resume(); }); folder.addButton({ title: 'Change source …' }).on('click', () => { const src = prompt(`Change source (from '${video.cacheKey}')`); if (src) video.changeSource(src); }); folder.addButton({ title: 'Set current time …' }).on('click', () => { video.setCurrentTime(prompt(`Set current time (0 to ${video.getDuration()})`)); }); folder.addButton({ title: 'Set loop true' }).on('click', () => { video.setLoop(true); }); folder.addButton({ title: 'Set loop false' }).on('click', () => { video.setLoop(false); }); folder.addButton({ title: 'Set mute true' }).on('click', () => { video.setMute(true); }); folder.addButton({ title: 'Set mute false' }).on('click', () => { video.setMute(false); }); video.once(GameObjectEvents.DESTROY, () => { folder.dispose(); }); return folder; } function AddTween (tween, pane, options = { title: 'Tween' }) { const folder = pane.addFolder(options); // > When creating a Tween, you can no longer pass a function for the following properties: // > duration, hold, repeat and repeatDelay. // > These should be numbers only. You can, however, still provide a function for delay, to keep it compatible with the StaggerBuilder. folder.addMonitor(tween, 'countdown'); folder.addMonitor(tween, 'duration'); folder.addMonitor(tween, 'elapsed'); folder.addMonitor(tween, 'loop'); folder.addMonitor(tween, 'loopCounter'); folder.addMonitor(tween, 'state'); folder.addInput(tween, 'timeScale', { min: 0.1, max: 10, step: 0.1 }); folder.addMonitor(tween, 'totalData'); folder.addMonitor(tween, 'totalDuration'); folder.addMonitor(tween, 'totalElapsed'); folder.addMonitor(tween, 'totalProgress', { view: 'graph', min: 0, max: 1 }); for (const dat of tween.data) { folder.addMonitor(dat, 'progress', { view: 'graph', min: 0, max: 1, label: `${dat.key} progress` }); } for (const dat of tween.data) { folder.addMonitor(dat, 'current', { label: `${dat.key} current` }); } folder.addButton({ title: 'Play' }).on('click', () => { console.info('Play tween'); tween.play(); }); folder.addButton({ title: 'Pause' }).on('click', () => { console.info('Pause tween'); tween.pause(); }); folder.addButton({ title: 'Resume' }).on('click', () => { console.info('Resume tween'); tween.resume(); }); folder.addButton({ title: 'Stop' }).on('click', () => { console.info('Stop tween'); tween.stop(); }); folder.addButton({ title: 'Restart' }).on('click', () => { console.info('Restart tween'); tween.restart(); }); folder.addButton({ title: 'Remove' }).on('click', () => { console.info('Remove tween'); tween.remove(); }); return folder; } function AddChain (chain, pane, options = { title: 'Tween Chain' }) { const folder = pane.addFolder(options); folder.addMonitor(chain, 'currentIndex', { min: 0, max: chain.totalData, view: 'graph' }); folder.addMonitor(chain, 'hasStarted'); folder.addMonitor(chain, 'loop'); folder.addMonitor(chain, 'loopCounter'); folder.addMonitor(chain, 'state'); folder.addInput(chain, 'timeScale', { min: 0.1, max: 10, step: 0.1 }); folder.addMonitor(chain, 'totalData'); folder.addButton({ title: 'Play' }).on('click', () => { console.info('Play chain'); chain.play(); }); folder.addButton({ title: 'Stop' }).on('click', () => { console.info('Stop chain'); chain.stop(); }); folder.addButton({ title: 'Restart' }).on('click', () => { console.info('Restart chain'); chain.restart(); }); folder.addButton({ title: 'Pause' }).on('click', () => { console.info('Pause chain'); chain.pause(); }); folder.addButton({ title: 'Resume' }).on('click', () => { console.info('Resume chain'); chain.resume(); }); folder.addButton({ title: 'Destroy' }).on('click', () => { console.info('Destroy chain'); chain.destroy(); }); return folder; } function AddTimeline (timeline, pane, options = { title: 'Timeline' }) { const folder = pane.addFolder(options); const proxy = { get 'getProgress()' () { return timeline.getProgress(); }, get 'isPlaying()' () { return timeline.isPlaying(); } }; folder.addMonitor(timeline, 'complete'); folder.addMonitor(timeline, 'elapsed'); folder.addMonitor(proxy, 'getProgress()', { min: 0, max: 1, view: 'graph' }); folder.addMonitor(proxy, 'isPlaying()'); folder.addMonitor(timeline, 'paused'); folder.addMonitor(timeline, 'totalComplete'); folder.addButton({ title: 'Clear' }).on('click', () => { console.info('Clear timeline'); timeline.clear(); }); folder.addButton({ title: 'Destroy' }).on('click', () => { console.info('Destroy timeline'); timeline.destroy(); }); folder.addButton({ title: 'Pause' }).on('click', () => { console.info('Pause timeline'); timeline.pause(); }); folder.addButton({ title: 'Play' }).on('click', () => { console.info('Play timeline'); timeline.play(); }); folder.addButton({ title: 'Reset' }).on('click', () => { console.info('Reset timeline'); timeline.reset(); }); folder.addButton({ title: 'Resume' }).on('click', () => { console.info('Resume timeline'); timeline.resume(); }); folder.addButton({ title: 'Stop' }).on('click', () => { console.info('Stop timeline'); timeline.stop(); }); return folder; } function AddTimerEvent (timer, pane, options = { title: 'Timer Event' }) { const folder = pane.addFolder(options); const proxy = { get 'getOverallProgress()' () { return timer.getOverallProgress(); }, get 'getProgress()' () { return timer.getProgress(); }, get 'getOverallRemaining()' () { return timer.getOverallRemaining(); }, get 'getRemaining()' () { return timer.getRemaining(); } }; folder.addMonitor(timer, 'elapsed'); folder.addMonitor(timer, 'hasDispatched'); folder.addMonitor(proxy, 'getOverallProgress()', { min: 0, max: 1, view: 'graph' }); folder.addMonitor(proxy, 'getProgress()', { min: 0, max: 1, view: 'graph' }); folder.addMonitor(proxy, 'getOverallRemaining()'); folder.addMonitor(proxy, 'getRemaining()'); folder.addMonitor(timer, 'loop'); folder.addMonitor(timer, 'paused'); folder.addInput(timer, 'paused'); folder.addMonitor(timer, 'repeat'); folder.addMonitor(timer, 'repeatCount'); folder.addButton({ title: 'Dispatch and remove' }).on('click', () => { console.info('Dispatch and remove timer'); timer.remove(true); }); folder.addButton({ title: 'Remove' }).on('click', () => { console.info('Remove timer'); timer.remove(); }); folder.addButton({ title: 'Reset with current config' }).on('click', () => { console.info('Reset timer with current config', timer); timer.reset(timer); }); return folder; } function AddInput (input, pane, options = { title: `Input (${input.gameObject.type} “${input.gameObject.name}”)` }) { const folder = pane.addFolder(options); const { gameObject } = input; const inputPlugin = gameObject.scene.sys.input; folder.addMonitor(input, 'cursor'); folder.addMonitor(input, 'customHitArea'); folder.addMonitor(input, 'draggable'); folder.addMonitor(input, 'dragStartX'); folder.addMonitor(input, 'dragStartXGlobal'); folder.addMonitor(input, 'dragStartY'); folder.addMonitor(input, 'dragStartYGlobal'); folder.addMonitor(input, 'dragState'); folder.addMonitor(input, 'dragX'); folder.addMonitor(input, 'dragY'); folder.addMonitor(input, 'dropZone'); folder.addMonitor(input, 'enabled'); folder.addMonitor(input, 'localX'); folder.addMonitor(input, 'localY'); folder.addButton({ title: 'Enable debug' }).on('click', () => { inputPlugin.enableDebug(gameObject); }); folder.addButton({ title: 'Remove debug' }).on('click', () => { inputPlugin.removeDebug(gameObject); }); input.gameObject.once(GameObjectEvents.DESTROY, () => { folder.dispose(); }); return folder; } function AddArcadeBody (body, pane, options = { title: `Body (${body.gameObject.type} “${body.gameObject.name}”)` }) { const folder = pane.addFolder(options); // body.physicsType === Phaser.Physics.Arcade.DYNAMIC_BODY folder.addMonitor(body, 'enable'); folder.addInput(body, 'enable'); folder.addInput(body, 'debugShowBody'); folder.addInput(body, 'debugShowVelocity'); folder.addInput(body, 'debugBodyColor', { view: 'color' }); folder.addMonitor(body.velocity, 'x', { label: 'velocity x' }); folder.addMonitor(body.velocity, 'y', { label: 'velocity y' }); folder.addMonitor(body, 'speed'); folder.addMonitor(body, 'angle'); folder.addMonitor(body, '_dx', { label: 'deltaX()' }); folder.addMonitor(body, '_dy', { label: 'deltaY()' }); folder.addMonitor(body, '_tx', { label: 'deltaXFinal()' }); folder.addMonitor(body, '_ty', { label: 'deltaYFinal()' }); folder.addMonitor(body, 'left'); folder.addMonitor(body, 'top'); folder.addMonitor(body, 'right'); folder.addMonitor(body, 'bottom'); folder.addMonitor(body.center, 'x', { label: 'center.x' }); folder.addMonitor(body.center, 'y', { label: 'center.y' }); body.gameObject.once(GameObjectEvents.DESTROY, () => { folder.dispose(); }); return folder; } function AddAnimationState (state, pane, options = { title: `Animation (${state.parent.type} “${state.parent.name}”)` }) { const folder = pane.addFolder(options); const proxy = { get 'getName()' () { return state.getName(); }, get 'getFrameName()' () { return state.getFrameName(); }, get nextAnim () { return state.nextAnim ? (state.nextAnim.key || state.nextAnim) : ''; } }; folder.addMonitor(proxy, 'getName()'); folder.addMonitor(proxy, 'getFrameName()'); folder.addMonitor(state, 'delay'); folder.addMonitor(state, 'delayCounter'); folder.addMonitor(state, 'duration'); folder.addMonitor(state, 'forward'); folder.addMonitor(state, 'frameRate'); folder.addMonitor(state, 'hasStarted'); folder.addMonitor(state, 'isPaused'); folder.addMonitor(state, 'isPlaying'); folder.addMonitor(state, 'msPerFrame'); folder.addMonitor(proxy, 'nextAnim', { label: 'nextAnim (key)' }); folder.addMonitor(state.nextAnimsQueue, 'length', { label: 'nextAnimsQueue.length', format: FormatLength }); folder.addMonitor(state, 'repeat'); folder.addMonitor(state, 'repeatCounter'); folder.addMonitor(state, 'repeatDelay'); folder.addInput(state, 'skipMissedFrames'); folder.addInput(state, 'timeScale', { min: 0.1, max: 10, step: 0.1 }); folder.addMonitor(state, 'yoyo'); folder.addButton({ title: 'Stop' }).on('click', () => { console.info('Stop animation'); state.stop(); }); folder.addButton({ title: 'Pause' }).on('click', () => { console.info('Pause animation'); state.pause(); }); folder.addButton({ title: 'Resume' }).on('click', () => { console.info('Resume animation'); state.resume(); }); folder.addButton({ title: 'Restart' }).on('click', () => { console.info('Restart animation'); state.restart(); }); folder.addButton({ title: 'Reverse' }).on('click', () => { console.info('Reverse animation'); state.reverse(); }); folder.addButton({ title: 'Next frame' }).on('click', () => { console.info('Next animation frame'); state.nextFrame(); }); folder.addButton({ title: 'Previous frame' }).on('click', () => { console.info('Previous animation frame'); state.previousFrame(); }); state.parent.once(GameObjectEvents.DESTROY, () => { folder.dispose(); }); return folder; } function AddKey (key, pane, options = { title: `Key (${key.keyCode})` }) { const folder = pane.addFolder(options); folder.addMonitor(key, 'duration'); folder.addInput(key, 'emitOnRepeat'); folder.addInput(key, 'enabled'); folder.addMonitor(key, 'isDown'); folder.addMonitor(key, 'isUp'); folder.addMonitor(key, 'location'); folder.addMonitor(key, 'repeats'); folder.addMonitor(key, 'timeDown'); folder.addMonitor(key, 'timeUp'); folder.addButton({ title: 'Destroy' }).on('click', () => { console.info('Destroy key'); key.destroy(); folder.dispose(); }); return folder; } function AddKeys (keys, pane, options = { title: 'Keys' }) { const folder = pane.addFolder(options); for (const name in keys) { const key = keys[name]; folder.addMonitor(key, 'isDown', { label: `${name} isDown` }); } return folder; } function AddFXComponent (comp, pane, options = { title: `${comp.isPost ? 'Post' : 'Pre'} FX` }) { const folder = pane.addFolder(options); folder.addMonitor(comp, 'enabled'); folder.addInput(comp, 'padding', { min: 0, max: 32, step: 1 }); folder.addButton({ title: 'Clear' }).on('click', () => { comp.clear(); }); folder.addButton({ title: 'Disable' }).on('click', () => { comp.disable(); }); folder.addButton({ title: 'Enable' }).on('click', () => { comp.enable(); }); for (const ctrl of comp.list) { AddFXController(ctrl, folder); } return folder; } function AddFXController (ctrl, pane, options = { title: `FX ${FXMap[ctrl.type]}` }) { const folder = pane.addFolder(options); for (const key in ctrl) { if (key.startsWith('_')) continue; if (key === 'type') continue; const val = ctrl[key]; const typ = typeof val; if (typ !== 'number' && typ !== 'boolean') continue; if (key === 'alpha') { folder.addInput(ctrl, key, { min: 0, max: 1 }); continue; } if (key === 'axis' || key === 'direction') { folder.addInput(ctrl, key, { min: 0, max: 1, step: 1 }); continue; } if (key === 'color' || key === 'color1' || key === 'color2' || key === 'backgroundColor') { folder.addInput(ctrl, key, { view: 'color' }); continue; } if (key === 'progress') { folder.addInput(ctrl, key, { min: 0, max: 1 }); continue; } if (key === 'quality') { folder.addInput(ctrl, key, { options: { low: 0, medium: 1, high: 2 } }); continue; } if (key === 'samples') { folder.addInput(ctrl, key, { min: 1, max: 12, step: 1 }); continue; } if (key === 'steps') { folder.addInput(ctrl, key, { min: 1, max: 10, step: 1 }); continue; } folder.addInput(ctrl, key); } return folder; } function AddPipelines (pipelines, pane, options = { title: 'Pipelines' }) { const folder = pane.addFolder(options); for (const pipeline of pipelines) { folder.addInput(pipeline, 'active', { label: `${pipeline.name} active` }); } return folder; } function AddPipeline (pipeline, pane, options = { title: `${pipeline.isPost ? 'Post Pipeline' : 'Pipeline'} “${pipeline.name}”` }) { const folder = pane.addFolder(options); folder.addInput(pipeline, 'active'); // What else? return folder; } function AddActive (items, pane, options = { title: 'Active' }) { const folder = pane.addFolder(options); for (const item of items) { folder.addInput(item, 'active', { min: 0, max: 1, label: item.name || item.type || '(Unnamed)' }); } return folder; } function AddAlpha (items, pane, options = { title: 'Alpha' }) { const folder = pane.addFolder(options); for (const item of items) { folder.addInput(item, 'alpha', { min: 0, max: 1, label: item.name || item.type || '(Unnamed)' }); } return folder; } function AddVisible (items, pane, options = { title: 'Visible' }) { const folder = pane.addFolder(options); for (const item of items) { folder.addInput(item, 'visible', { label: item.name || item.type || '(Unnamed)' }); } return folder; } function AddScenes (scenes, pane, options = { title: 'Scenes Visible' }) { const folder = pane.addFolder(options); for (const scene of scenes) { folder.addInput(scene.sys.settings, 'visible', { label: scene.sys.settings.key }); } return folder; } function FormatLength (len) { return len.toFixed(0); } function InspectByName (name, gameObjects, pane) { if (name === null) return; const gameObject = GetFirst(gameObjects, 'name', name); if (!gameObject) { console.info('No game object found with name "%s"', name); return; } const newPane = AddGameObject(gameObject, pane); console.info('Added folder %s to folder %s', newPane.title, pane.title); } function InspectByType (type, gameObjects, pane) { if (!type) return; const gameObject = GetFirst(gameObjects, 'type', type); if (!gameObject) { console.info('No game object found with type "%s"', type); return; } const newPane = AddGameObject(gameObject, pane); console.info('Added folder %s to folder %s', newPane.title, pane.title); } function InspectByIndex (index, gameObjects, pane) { if (index === null || index < 0) return; index = Number(index); const gameObject = gameObjects[index]; if (!gameObject) { console.info('No game object found at index %s', index); return; } const newPane = AddGameObject(gameObject, pane); console.info('Added folder %s to folder %s', newPane.title, pane.title); } class InspectorGlobalPlugin extends Phaser.Plugins.BasePlugin { constructor (pluginManager) { if (Phaser.VERSION.split('.')[1] < 60) { throw new Error('Phaser v3.60 or later is required'); } super(pluginManager); this.pane = null; this.style = null; } init (data) { } start () { this.pane = new Pane({ title: 'Inspector' }); this.add(); this.style = document.createElement('style'); this.style.innerText = '.tp-dfwv { top: 0; width: 320px; max-height: 100%; overflow: auto }'; document.head.appendChild(this.style); } stop () { document.head.removeChild(this.style); this.style = null; this.pane.dispose(); this.pane = null; } destroy () { this.stop(); super.destroy(); } add () { const { game, pane } = this; const { anims, cache, device, input, loop, registry, renderer, scale, scene, sound, textures } = game; const { keyboard, touch } = input; pane.addButton({ title: 'Refresh' }).on('click', () => { console.time('refresh'); pane.refresh(); console.timeEnd('refresh'); }); const folder = pane.addFolder({ title: 'Game', expanded: false }); folder.addMonitor(game, 'hasFocus'); folder.addMonitor(game, 'isPaused'); folder.addButton({ title: 'Pause' }).on('click', () => { console.info('Pause game'); game.pause(); }); folder.addButton({ title: 'Resume' }).on('click', () => { console.info('Resume game'); game.resume(); }); folder.addButton({ title: 'Step' }).on('click', () => { const t = performance.now(); const dt = loop._target; console.info('step', t, dt); game.step(t, dt); }); folder.addButton({ title: 'Destroy' }).on('click', () => { console.info('Destroy game'); game.destroy(true); }); const animsFolder = folder.addFolder({ title: 'Animations', expanded: false }); animsFolder.addButton({ title: 'Pause all' }).on('click', () => { console.info('Pause all animations'); anims.pauseAll(); }); animsFolder.addButton({ title: 'Resume all' }).on('click', () => { console.info('Resume all animations'); anims.resumeAll(); }); animsFolder.addButton({ title: 'Print animations' }).on('click', () => { console.info('Animations:'); console.table(anims.anims.getArray().map(animToPrint)); }); animsFolder.addButton({ title: 'Print JSON' }).on('click', () => { console.info(JSON.stringify(anims.toJSON())); }); const cacheFolder = folder.addFolder({ title: 'Cache', expanded: false }); cacheFolder.addButton({ title: 'Print cache contents' }).on('click', () => { printCaches(cache); }); const configFolder = folder.addFolder({ title: 'Config', expanded: false }); configFolder.addButton({ title: 'Print game config' }).on('click', () => { console.info('Game config (partial):'); console.table(copyToSafeObj(game.config)); }); const deviceFolder = folder.addFolder({ title: 'Device', expanded: false }); deviceFolder.addButton({ title: 'Print device info' }).on('click', () => { console.info('Device:'); printDevice(device); }); const inputFolder = folder.addFolder({ title: 'Input', expanded: false }); inputFolder.addMonitor(input, 'isOver'); inputFolder.addInput(input, 'enabled'); inputFolder.addInput(input, 'globalTopOnly'); for (const pointer of input.pointers) { AddPointer(pointer, inputFolder); } if (keyboard) { const keyboardFolder = folder.addFolder({ title: 'Keyboard', expanded: false }); keyboardFolder.addInput(keyboard, 'enabled'); keyboardFolder.addMonitor(keyboard, 'preventDefault'); } if (touch) { const touchFolder = folder.addFolder({ title: 'Touch', expanded: false }); touchFolder.addInput(touch, 'capture'); touchFolder.addInput(touch, 'enabled'); } const loopProxy = { get 'getDuration()' () { return loop.getDuration(); } }; const loopFolder = folder.addFolder({ title: 'Loop', expanded: false }); loopFolder.addMonitor(loop, 'actualFps', { view: 'graph', min: 0, max: 120 }); loopFolder.addMonitor(loop, 'delta', { view: 'graph', min: 0, max: 50 }); loopFolder.addMonitor(loop, 'frame', { format: Math.floor }); loopFolder.addMonitor(loopProxy, 'getDuration()'); loopFolder.addMonitor(loop, 'now'); loopFolder.addMonitor(loop, 'rawDelta', { view: 'graph', min: 0, max: 50 }); loopFolder.addMonitor(loop, 'running'); loopFolder.addMonitor(loop, 'startTime'); loopFolder.addMonitor(loop, 'time'); loopFolder.addButton({ title: 'Sleep' }).on('click', () => { console.info('Sleep game loop'); loop.sleep(); }); loopFolder.addButton({ title: 'Wake' }).on('click', () => { console.info('Wake game loop'); loop.wake(); }); loopFolder.addButton({ title: 'Step' }).on('click', () => { const t = performance.now(); console.info('step', t); loop.step(t); }); const registryFolder = folder.addFolder({ title: 'Registry', expanded: false }); registryFolder.addButton({ title: 'Print registry values' }).on('click', () => { console.info('Registry:'); console.table(registry.getAll()); }); const rendererFolder = folder.addFolder({ title: 'Renderer', expanded: false }); if (renderer.type === Phaser.CANVAS) { rendererFolder.addInput(renderer, 'antialias'); rendererFolder.addMonitor(renderer, 'drawCount', { view: 'graph', min: 0, max: 100 }); } rendererFolder.addButton({ title: 'Snapshot' }).on('click', () => { console.info('Snapshot'); renderer.snapshot(snapshot); }); rendererFolder.addButton({ title: 'Print config' }).on('click', () => { console.info('Renderer config:'); console.table(copyToSafeObj(renderer.config)); }); const scaleFolder = folder.addFolder({ title: 'Scale', expanded: false }); scaleFolder.addMonitor(scale, 'width'); scaleFolder.addMonitor(scale, 'height'); scaleFolder.addButton({ title: 'Start full screen' }).on('click', () => { console.info('Start fullscreen'); scale.startFullscreen(); }); scaleFolder.addButton({ title: 'Stop full screen' }).on('click', () => { console.info('Stop fullscreen'); scale.stopFullscreen(); }); const sceneFolder = folder.addFolder({ title: 'Scenes', expanded: false }); sceneFolder.addButton({ title: 'Print scenes' }).on('click', () => { console.info('Scenes:'); console.table(scene.scenes.map(sceneToPrint)); }); sceneFolder.addButton({ title: 'Add visible controls' }).on('click', () => { AddScenes(scene.getScenes(false), sceneFolder); }); const soundFolder = folder.addFolder({ title: 'Sound', expanded: false }); soundFolder.addInput(sound, 'mute'); soundFolder.addInput(sound, 'pauseOnBlur'); soundFolder.addMonitor(sound.sounds, 'length', { label: 'sounds.length', format: FormatLength }); soundFolder.addInput(sound, 'volume', { min: 0, max: 1, step: 0.1 }); soundFolder.addButton({ title: 'Pause all' }).on('click', () => { console.info('Pause all sounds'); sound.pauseAll(); }); soundFolder.addButton({ title: 'Remove all' }).on('click', () => { console.info('Remove all sounds'); sound.removeAll(); }); soundFolder.addButton({ title: 'Resume all' }).on('click', () => { console.info('Resume all sounds'); sound.resumeAll(); }); soundFolder.addButton({ title: 'Stop all' }).on('click', () => { console.info('Stop all sounds'); sound.stopAll(); }); soundFolder.addButton({ title: 'Print sounds' }).on('click', () => { console.info('Sounds:'); console.table(sound.sounds.map(soundToPrint)); }); const texturesFolder = folder.addFolder({ title: 'Textures', expanded: false }); texturesFolder.addButton({ title: 'Print textures' }).on('click', () => { console.info('Textures:'); console.table(Object.values(textures.list).map(textureToPrint)); }); } }