├── .gitignore ├── .prettierignore ├── .prettierrc ├── .prettierrc.json ├── LICENSE ├── README.md ├── docs ├── _config.yml ├── generated │ ├── .nojekyll │ ├── assets │ │ ├── highlight.css │ │ ├── icons.js │ │ ├── icons.svg │ │ ├── main.js │ │ ├── navigation.js │ │ ├── search.js │ │ └── style.css │ ├── classes │ │ ├── ContainerSizeCalculator.html │ │ ├── DefaultScreenSizeCalculator.html │ │ ├── FixedSizeCalculator.html │ │ ├── NoResizeScreenSizeCalculator.html │ │ ├── Scene.html │ │ └── SceneManager.html │ ├── hierarchy.html │ ├── index.html │ ├── interfaces │ │ ├── IController.html │ │ ├── IResizable.html │ │ ├── IScreenSizeCalculator.html │ │ ├── ISize.html │ │ └── IUpdatable.html │ ├── modules.html │ └── variables │ │ └── VERSION.html └── img │ ├── Hierarchy.png │ └── zindex.png ├── package-lock.json ├── package.json ├── rollup.config.ts ├── src ├── _version.ts ├── core │ ├── IController.ts │ ├── IResizable.ts │ ├── IScreenSizeCalculator.ts │ ├── IUpdatable.ts │ ├── Scene.ts │ ├── SceneManager.ts │ └── screen-size │ │ ├── ContainerSizeCalculator.ts │ │ ├── DefaultScreenSizeCalculator.ts │ │ ├── FixedSizeCalculator.ts │ │ └── NoResizeScreenSizeCalculator.ts └── index.ts ├── tsconfig.json ├── tslint.json └── version.js /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules/ 2 | /dist 3 | .vscode 4 | .user 5 | .cache 6 | .rpt2_cache 7 | -------------------------------------------------------------------------------- /.prettierignore: -------------------------------------------------------------------------------- 1 | .vscode 2 | docs 3 | dist 4 | .prettierrc 5 | .prettierrc.json 6 | package-lock.json 7 | package.json 8 | README.md 9 | rollup.config.js 10 | tsconfig.json 11 | tslint.json 12 | version.js 13 | -------------------------------------------------------------------------------- /.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "printWidth": 140, 3 | "tabWidth": 4, 4 | "trailingComma": "all", 5 | "singleQuote": true 6 | } 7 | -------------------------------------------------------------------------------- /.prettierrc.json: -------------------------------------------------------------------------------- 1 | {} 2 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019-2024 Enriko Riba 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Scene Graph Engine for PIXI 2 | 3 | Link to [API documentation](https://enriko-riba.github.io/pixi-scenegraph/generated/index.html) 4 | 5 | ## What is pixi-scenegraph? 6 | **pixi-scenegraph** is a package providing scene management features for **PIXI v8** 7 | It allows defining scenes and switching between them e.g. MainMenuScene, GameScene, OptionsScene etc. 8 | 9 | The following image represents the object hierarchy: 10 | 11 | ![Hierarchy](https://enriko-riba.github.io/pixi-scenegraph/img/Hierarchy.png "Object hierarchy") 12 | 13 | **pixi-scenegraph** is written in typescript and aimed for typescript users but not limited to typescript only projects. 14 | >*.ts -> import {SceneManager} from "pixi-scenegraph"; 15 | 16 | >*.js -> var sg = require("pixi-scenegraph"); let scm = new sg.SceneManager(); 17 | 18 | ### What is a Scene 19 | A Scene is like a PIXI stage, a container holding all objects we want to display. Think of scenes as game state containers e.g: loading scene, menu scene, options scene, in-game scene etc. 20 | 21 | A scene **must have a unique name** and the SceneManager can reference scenes by that name: 22 | 23 | sceneManager.ActivateScene("sceneName"); 24 | 25 | Only one scene at a time is active and only the active scene is rendered. A scene can have a HudOverlay which is a container object rendered over the scene. In addition a MasterHudOverlay can be attached to the SceneManager. The MasterHudOverlay is rendered over all other content. 26 | 27 | Z-Index 28 | 29 | ![z ordering](https://enriko-riba.github.io/pixi-scenegraph/img/zindex.png "Z Ordering") 30 | 31 | ### Show me a 'Hello World' example 32 | const scm = new SceneManager(renderOptions); 33 | const myScene = new MyScene(); 34 | scm.AddScene(myScene); 35 | scm.ActivateScene(myScene); // or by name scm.ActivateScene('scene_name') 36 | 37 | ### How do I switch scenes? 38 | const myScene1 = new MyScene1(); // name id 'scene_1' 39 | const myScene2 = new MyScene2(); // name id 'scene_2' 40 | const menuScene = new MenuScene(); // name id 'menu' 41 | scm.AddScene(myScene1); 42 | scm.AddScene(myScene2); 43 | scm.AddScene(menuScene); 44 | scm.ActivateScene(menuScene); 45 | 46 | inside the MenuScene class: 47 | 48 | btnStart.onClick = () => this.sceneManager.ActivateScene("scene_1"); 49 | 50 | ### Angular 7 based example 51 | [git repository](https://github.com/enriko-riba/scenegraph-ng) 52 | 53 | ## Documentation 54 | [API documentation](https://enriko-riba.github.io/pixi-scenegraph/generated/index.html) 55 | -------------------------------------------------------------------------------- /docs/_config.yml: -------------------------------------------------------------------------------- 1 | theme: jekyll-theme-architect 2 | -------------------------------------------------------------------------------- /docs/generated/.nojekyll: -------------------------------------------------------------------------------- 1 | TypeDoc added this file to prevent GitHub Pages from using Jekyll. You can turn off this behavior by setting the `githubPages` option to false. -------------------------------------------------------------------------------- /docs/generated/assets/highlight.css: -------------------------------------------------------------------------------- 1 | :root { 2 | --light-hl-0: #0000FF; 3 | --dark-hl-0: #569CD6; 4 | --light-hl-1: #000000; 5 | --dark-hl-1: #D4D4D4; 6 | --light-hl-2: #0070C1; 7 | --dark-hl-2: #4FC1FF; 8 | --light-hl-3: #001080; 9 | --dark-hl-3: #9CDCFE; 10 | --light-hl-4: #795E26; 11 | --dark-hl-4: #DCDCAA; 12 | --light-code-background: #FFFFFF; 13 | --dark-code-background: #1E1E1E; 14 | } 15 | 16 | @media (prefers-color-scheme: light) { :root { 17 | --hl-0: var(--light-hl-0); 18 | --hl-1: var(--light-hl-1); 19 | --hl-2: var(--light-hl-2); 20 | --hl-3: var(--light-hl-3); 21 | --hl-4: var(--light-hl-4); 22 | --code-background: var(--light-code-background); 23 | } } 24 | 25 | @media (prefers-color-scheme: dark) { :root { 26 | --hl-0: var(--dark-hl-0); 27 | --hl-1: var(--dark-hl-1); 28 | --hl-2: var(--dark-hl-2); 29 | --hl-3: var(--dark-hl-3); 30 | --hl-4: var(--dark-hl-4); 31 | --code-background: var(--dark-code-background); 32 | } } 33 | 34 | :root[data-theme='light'] { 35 | --hl-0: var(--light-hl-0); 36 | --hl-1: var(--light-hl-1); 37 | --hl-2: var(--light-hl-2); 38 | --hl-3: var(--light-hl-3); 39 | --hl-4: var(--light-hl-4); 40 | --code-background: var(--light-code-background); 41 | } 42 | 43 | :root[data-theme='dark'] { 44 | --hl-0: var(--dark-hl-0); 45 | --hl-1: var(--dark-hl-1); 46 | --hl-2: var(--dark-hl-2); 47 | --hl-3: var(--dark-hl-3); 48 | --hl-4: var(--dark-hl-4); 49 | --code-background: var(--dark-code-background); 50 | } 51 | 52 | .hl-0 { color: var(--hl-0); } 53 | .hl-1 { color: var(--hl-1); } 54 | .hl-2 { color: var(--hl-2); } 55 | .hl-3 { color: var(--hl-3); } 56 | .hl-4 { color: var(--hl-4); } 57 | pre, code { background: var(--code-background); } 58 | -------------------------------------------------------------------------------- /docs/generated/assets/icons.js: -------------------------------------------------------------------------------- 1 | (function() { 2 | addIcons(); 3 | function addIcons() { 4 | if (document.readyState === "loading") return document.addEventListener("DOMContentLoaded", addIcons); 5 | const svg = document.body.appendChild(document.createElementNS("http://www.w3.org/2000/svg", "svg")); 6 | svg.innerHTML = `""`; 7 | svg.style.display = "none"; 8 | if (location.protocol === "file:") updateUseElements(); 9 | } 10 | 11 | function updateUseElements() { 12 | document.querySelectorAll("use").forEach(el => { 13 | if (el.getAttribute("href").includes("#icon-")) { 14 | el.setAttribute("href", el.getAttribute("href").replace(/.*#/, "#")); 15 | } 16 | }); 17 | } 18 | })() -------------------------------------------------------------------------------- /docs/generated/assets/icons.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /docs/generated/assets/navigation.js: -------------------------------------------------------------------------------- 1 | window.navigationData = "data:application/octet-stream;base64,H4sIAAAAAAAACo2RQWvCQBCF/8ucg8FIRXK1LXioBYNexMN0M+ridiO7o0jE/y4RiWubTLzue++bN7PLMzCdGFIYF5ZRW3KZLmmMRh0McuEggj3yFlJQBr0nH7cYe1v+NRDBTtsc0n4yukQ1+53WeDCcKUdku/iCWZrxqU+Ud7EbTBJzWszI65JeKy65pSmZIkv/cbfnztwXWtxQQ5tQlSiT6j9dYUwI0ZbJrVGRjwP9GZO8DUPMbXf8MdRMqWURIh865HUf+S9aly3VKkWMzvc5cvtqtSxBFh+zbPI9fRCO6HQV8vFdek4PksvqCmMJBxidAwAA" -------------------------------------------------------------------------------- /docs/generated/assets/search.js: -------------------------------------------------------------------------------- 1 | window.searchData = "data:application/octet-stream;base64,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"; -------------------------------------------------------------------------------- /docs/generated/classes/ContainerSizeCalculator.html: -------------------------------------------------------------------------------- 1 | ContainerSizeCalculator | pixi-scenegraph

Class ContainerSizeCalculator

Preserves the container width and height, no aspect ratio is calculated.

2 |

Implements

Constructors

Methods

Constructors

Methods

8 | -------------------------------------------------------------------------------- /docs/generated/classes/DefaultScreenSizeCalculator.html: -------------------------------------------------------------------------------- 1 | DefaultScreenSizeCalculator | pixi-scenegraph

Class DefaultScreenSizeCalculator

Calculates viewport that horizontally fits in the device and still preserves the designed aspect ratio.

2 |

Implements

Constructors

Properties

Methods

Constructors

Properties

designedHeight: number
designedWidth: number

Methods

11 | -------------------------------------------------------------------------------- /docs/generated/classes/FixedSizeCalculator.html: -------------------------------------------------------------------------------- 1 | FixedSizeCalculator | pixi-scenegraph

Class FixedSizeCalculator

Returns the designed width and height with aspect ratio 1.

2 |

Implements

Constructors

Methods

Constructors

Methods

8 | -------------------------------------------------------------------------------- /docs/generated/classes/NoResizeScreenSizeCalculator.html: -------------------------------------------------------------------------------- 1 | NoResizeScreenSizeCalculator | pixi-scenegraph

Class NoResizeScreenSizeCalculator

Simply returns the available screen size without any scaling.

2 |

Implements

Constructors

Methods

Constructors

Methods

8 | -------------------------------------------------------------------------------- /docs/generated/hierarchy.html: -------------------------------------------------------------------------------- 1 | pixi-scenegraph

pixi-scenegraph

Class Hierarchy

2 | -------------------------------------------------------------------------------- /docs/generated/index.html: -------------------------------------------------------------------------------- 1 | pixi-scenegraph

pixi-scenegraph

Scene Graph Engine for PIXI

Link to API documentation

2 |

pixi-scenegraph is a package providing scene management features for PIXI v8 3 | It allows defining scenes and switching between them e.g. MainMenuScene, GameScene, OptionsScene etc.

4 |

The following image represents the object hierarchy:

5 |

Hierarchy

6 |

pixi-scenegraph is written in typescript and aimed for typescript users but not limited to typescript only projects.

7 |
8 |

*.ts -> import {SceneManager} from "pixi-scenegraph";

9 |
10 |
11 |

*.js -> var sg = require("pixi-scenegraph"); let scm = new sg.SceneManager();

12 |
13 |

A Scene is like a PIXI stage, a container holding all objects we want to display. Think of scenes as game state containers e.g: loading scene, menu scene, options scene, in-game scene etc.

14 |

A scene must have a unique name and the SceneManager can reference scenes by that name:

15 |
sceneManager.ActivateScene("sceneName");
16 | 
17 |

Only one scene at a time is active and only the active scene is rendered. A scene can have a HudOverlay which is a container object rendered over the scene. In addition a MasterHudOverlay can be attached to the SceneManager. The MasterHudOverlay is rendered over all other content.

18 |

Z-Index

19 |

z ordering

20 |
const scm = new SceneManager(renderOptions);
21 | const myScene = new MyScene();
22 | scm.AddScene(myScene);
23 | scm.ActivateScene(myScene); // or by name scm.ActivateScene('scene_name')
24 | 
25 |
const myScene1 = new MyScene1();     //  name id 'scene_1'
26 | const myScene2 = new MyScene2();     //  name id 'scene_2'
27 | const menuScene = new MenuScene();   //  name id 'menu'
28 | scm.AddScene(myScene1);
29 | scm.AddScene(myScene2);
30 | scm.AddScene(menuScene);
31 | scm.ActivateScene(menuScene);
32 | 
33 |

inside the MenuScene class:

34 |
btnStart.onClick = () => this.sceneManager.ActivateScene("scene_1");
35 | 
36 |

git repository

37 |

API documentation

38 |
39 | -------------------------------------------------------------------------------- /docs/generated/interfaces/IController.html: -------------------------------------------------------------------------------- 1 | IController | pixi-scenegraph

Interface IController

Defines a controller. Controllers are non-renderable objects who's update() method is invoked once per frame. 2 | A controller can run either for all scopes or in the scope of a scene. 3 | If a scope is defined, the Controller.update() is invoked only if the ActiveScene.Name matches the scope.

4 |
interface IController {
    id: string;
    scope: string;
    update(dt: number, activeScene: null | Scene): void;
}

Properties

id 5 | scope 6 |

Methods

update 7 |

Properties

id: string

The unique controller id.

8 |
scope: string

A scene name, if present activates the controller only while the scene is active. 9 | If no scope is defined the controller is active regardless of the active scene.

10 |

Methods

  • Invoked once per frame, depending on scope.

    11 |

    Parameters

    • dt: number

      the elapsed time in milliseconds

      12 |
    • activeScene: null | Scene

      the current scene

      13 |

    Returns void

14 | -------------------------------------------------------------------------------- /docs/generated/interfaces/IResizable.html: -------------------------------------------------------------------------------- 1 | IResizable | pixi-scenegraph

Interface IResizable

Defines a component that implements an onResize method.

2 |
interface IResizable {
    onResize(): void;
}

Implemented by

Methods

Methods

4 | -------------------------------------------------------------------------------- /docs/generated/interfaces/IScreenSizeCalculator.html: -------------------------------------------------------------------------------- 1 | IScreenSizeCalculator | pixi-scenegraph

Interface IScreenSizeCalculator

Contract for screen size and aspect ratio calculation. 2 | The SceneManager delegates all screen size measurements to an IScreenSizeCalculator instance.

3 |

The calculated screen size is game specific and can return any value fit for a particular game design. 4 | Two implementations are provided out of the box: the default DefaultScreenSizeCalculator and NoResizeScreenSizeCalculator. 5 | The DefaultScreenSizeCalculator calculates a viewport that horizontally fits on screen and preserves the given aspect rastio 6 | while the NoResizeScreenSizeCalculator always returns the full screen size disregarding any aspect ratio constraints.

7 |

The IScreenSizeCalculator is used in resize events in the following manner:

8 |
const avlSize = this.screenSizeCalculator.GetAvailableSize();
const aspect = this.screenSizeCalculator.GetAspectRatio();
const size = this.screenSizeCalculator.CalculateSize(avlSize, aspect);
this.renderer.resize(size.x, size.y); 9 |
10 | 11 |
interface IScreenSizeCalculator {
    CalculateScale(size: ISize): ISize;
    CalculateSize(): ISize;
}

Implemented by

Methods

Methods

16 | -------------------------------------------------------------------------------- /docs/generated/interfaces/ISize.html: -------------------------------------------------------------------------------- 1 | ISize | pixi-scenegraph

Interface ISize

interface ISize {
    x: number;
    y: number;
}

Properties

x 2 | y 3 |

Properties

x: number
y: number
4 | -------------------------------------------------------------------------------- /docs/generated/interfaces/IUpdatable.html: -------------------------------------------------------------------------------- 1 | IUpdatable | pixi-scenegraph

Interface IUpdatable

Supports onUpdate, fired on each animation frame.

2 |

elapsed time delta

3 |

total time, useful for TWEEN.update and other libs depending on total time

4 |
interface IUpdatable {
    onUpdate(dt: number, timestamp: number): void;
}

Implemented by

Methods

Methods

  • Parameters

    • dt: number
    • timestamp: number

    Returns void

6 | -------------------------------------------------------------------------------- /docs/generated/modules.html: -------------------------------------------------------------------------------- 1 | pixi-scenegraph

pixi-scenegraph

Index

Classes

Interfaces

Variables

VERSION 13 |
14 | -------------------------------------------------------------------------------- /docs/generated/variables/VERSION.html: -------------------------------------------------------------------------------- 1 | VERSION | pixi-scenegraph

Variable VERSIONConst

VERSION: "2.8.0" = '2.8.0'
2 | -------------------------------------------------------------------------------- /docs/img/Hierarchy.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/enriko-riba/pixi-scenegraph/993d5509ab6c7d9e03f6f36d014a6c3b232a8518/docs/img/Hierarchy.png -------------------------------------------------------------------------------- /docs/img/zindex.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/enriko-riba/pixi-scenegraph/993d5509ab6c7d9e03f6f36d014a6c3b232a8518/docs/img/zindex.png -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "pixi-scenegraph", 3 | "version": "2.9.0", 4 | "description": "typescript scene engine for pixi.js 8.7", 5 | "main": "dist/index.js", 6 | "module": "dist/index.es.js", 7 | "types": "dist/index.d.ts", 8 | "scripts": { 9 | "_clear": "rimraf ./dist", 10 | "_version": "node version.js", 11 | "doc": "typedoc src/index.ts --exclude **/node_modules/** --out docs/generated", 12 | "format": "npx prettier --write .", 13 | "prePublish": "npm run _version && npm run format && npm run build && npm run doc", 14 | "build": "npm run _clear && npx rollup -c rollup.config.ts --configPlugin typescript", 15 | "test": "echo \"Error: no test specified\" && exit 1", 16 | "update": "npx npm-check-updates -u && npm install" 17 | }, 18 | "keywords": [ 19 | "pixi", 20 | "scenegraph", 21 | "game-engine", 22 | "game-dev" 23 | ], 24 | "repository": { 25 | "type": "git", 26 | "url": "git+https://github.com/enriko-riba/pixi-scenegraph.git" 27 | }, 28 | "author": "Enriko Riba", 29 | "license": "MIT", 30 | "devDependencies": { 31 | "@rollup/plugin-typescript": "12.1.2", 32 | "@types/offscreencanvas": "^2019.7.3", 33 | "prettier": "3.4.2", 34 | "rimraf": "6.0.1", 35 | "rollup": "4.32.0", 36 | "tslib": "^2.8.1", 37 | "typedoc": "0.27.6", 38 | "typescript": "5.7.3" 39 | }, 40 | "peerDependencies": { 41 | "pixi.js": "^8.7.2" 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /rollup.config.ts: -------------------------------------------------------------------------------- 1 | import typescript from '@rollup/plugin-typescript'; 2 | import pkg from './package.json' assert { type: 'json' }; 3 | 4 | export default { 5 | input: 'src/index.ts', 6 | output: [ 7 | { file: pkg.main, format: 'cjs' }, 8 | { file: pkg.module, format: 'es' }, 9 | ], 10 | external: ['pixi.js'], 11 | plugins: [typescript()], 12 | }; 13 | -------------------------------------------------------------------------------- /src/_version.ts: -------------------------------------------------------------------------------- 1 | export const VERSION = '2.9.0'; 2 | -------------------------------------------------------------------------------- /src/core/IController.ts: -------------------------------------------------------------------------------- 1 | import { Scene } from './Scene'; 2 | 3 | /** 4 | * Defines a controller. Controllers are non-renderable objects who's update() method is invoked once per frame. 5 | * A controller can run either for all scopes or in the scope of a scene. 6 | * If a scope is defined, the Controller.update() is invoked only if the ActiveScene.Name matches the scope. 7 | */ 8 | export interface IController { 9 | /** 10 | * The unique controller id. 11 | */ 12 | readonly id: string; 13 | 14 | /** 15 | * A scene name, if present activates the controller only while the scene is active. 16 | * If no scope is defined the controller is active regardless of the active scene. 17 | */ 18 | readonly scope: string; 19 | 20 | /** 21 | * Invoked once per frame, depending on scope. 22 | * @param dt - the elapsed time in milliseconds 23 | * @param activeScene - the current scene 24 | */ 25 | update(dt: number, activeScene: Scene | null): void; 26 | } 27 | -------------------------------------------------------------------------------- /src/core/IResizable.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Defines a component that implements an onResize method. 3 | */ 4 | export interface IResizable { 5 | onResize(): void; 6 | } 7 | -------------------------------------------------------------------------------- /src/core/IScreenSizeCalculator.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Contract for screen size and aspect ratio calculation. 3 | * The `SceneManager` delegates all screen size measurements to an `IScreenSizeCalculator` instance. 4 | * @remarks The calculated screen size is game specific and can return any value fit for a particular game design. 5 | * Two implementations are provided out of the box: the default {@link DefaultScreenSizeCalculator} and {@link NoResizeScreenSizeCalculator}. 6 | * The {@link DefaultScreenSizeCalculator} calculates a viewport that horizontally fits on screen and preserves the given aspect rastio 7 | * while the {@link NoResizeScreenSizeCalculator} always returns the full screen size disregarding any aspect ratio constraints. 8 | * 9 | * The IScreenSizeCalculator is used in resize events in the following manner: 10 | * ``` 11 | * const avlSize = this.screenSizeCalculator.GetAvailableSize(); 12 | * const aspect = this.screenSizeCalculator.GetAspectRatio(); 13 | * const size = this.screenSizeCalculator.CalculateSize(avlSize, aspect); 14 | * this.renderer.resize(size.x, size.y); 15 | * ``` 16 | */ 17 | export interface IScreenSizeCalculator { 18 | /** 19 | * Returns the screen size the renderer will actually use. 20 | */ 21 | CalculateSize(): ISize; 22 | 23 | /** 24 | * Returns the scale to be applied to all scenes. 25 | * @param size 26 | */ 27 | CalculateScale(size: ISize): ISize; 28 | } 29 | 30 | export interface ISize { 31 | x: number; 32 | y: number; 33 | } 34 | -------------------------------------------------------------------------------- /src/core/IUpdatable.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Supports onUpdate, fired on each animation frame. 3 | * @param dt - elapsed time delta 4 | * @param timestamp - total time, useful for TWEEN.update and other libs depending on total time 5 | */ 6 | export interface IUpdatable { 7 | onUpdate(dt: number, timestamp: number): void; 8 | } 9 | -------------------------------------------------------------------------------- /src/core/Scene.ts: -------------------------------------------------------------------------------- 1 | import { Color, ColorSource, Container, ContainerChild, IRenderLayer } from 'pixi.js'; 2 | import { IResizable } from './IResizable'; 3 | import { IUpdatable } from './IUpdatable'; 4 | 5 | /** 6 | * Represents a scene instance. 7 | * Only one scene at a time is rendered. 8 | */ 9 | export abstract class Scene extends Container implements IResizable, IUpdatable { 10 | public Name: string; 11 | 12 | private paused: boolean = false; 13 | private hud: Container | null = null; 14 | private backgroundColor: Color; 15 | private clearValue: boolean = true; 16 | 17 | /** 18 | * Creates a new scene instance. 19 | * @param name - the scene name. 20 | */ 21 | constructor(name: string) { 22 | super(); 23 | this.backgroundColor = new Color(0x0); 24 | this.Name = name; 25 | } 26 | 27 | /** 28 | * Fired every time the scene is to be activated. 29 | * @remarks If a new scene is activated, `currentScene.onDeactivate()` is fired first followed by `newScene.onActivate()` 30 | */ 31 | public onActivate(): void { 32 | // tslint ignore 33 | } 34 | 35 | /** 36 | * Fired every time the scene is deactivated. 37 | * Note: this function has no implementation, override in derived class to add functionality. 38 | */ 39 | public onDeactivate(): void { 40 | // tslint ignore 41 | } 42 | 43 | /** 44 | * Fired every time the window resizes, the scene is about to be activated (before `onActivate()`) and after a MasterHudOverlay is set. 45 | * Note: this function has no implementation, override in derived class to add functionality. 46 | * @remarks Note that this function is fired only for the current (active) scene! 47 | */ 48 | public onResize(): void { 49 | // tslint ignore 50 | } 51 | 52 | /** 53 | * Fired on each animation frame. 54 | * Note: this function has no implementation, override in derived class to add functionality. 55 | * @param dt - elapsed time delta 56 | * @param timestamp - total time, useful for TWEEN.update and other libs depending on total time 57 | */ 58 | public onUpdate(dt: number, timestamp: number): void { 59 | // tslint ignore 60 | } 61 | 62 | /** 63 | * Fired when the scene is destroyed. 64 | * Note: this function has no implementation, override in derived class to add functionality. 65 | */ 66 | public onDestroy(): void { 67 | // tslint ignore 68 | } 69 | 70 | /** 71 | * Gets the scene background color. 72 | */ 73 | public get BackGroundColor(): Color { 74 | return this.backgroundColor; 75 | } 76 | 77 | /** 78 | * Sets the scene background color. 79 | */ 80 | public set BackGroundColor(color: ColorSource) { 81 | this.backgroundColor = new Color(color); 82 | } 83 | 84 | /** 85 | * Gets the scene hud overlay container. 86 | */ 87 | public get HudOverlay(): Container | (Container & IUpdatable) | null { 88 | return this.hud; 89 | } 90 | 91 | /** 92 | * Sets the scene hud overlay container. 93 | */ 94 | public set HudOverlay(hud: Container | (Container & IUpdatable) | null) { 95 | this.hud = hud; 96 | } 97 | 98 | /** 99 | * Invokes the action on every child display object, no matter how deep they are nested. 100 | */ 101 | public enumerateChildren(action: (d: Container) => void): void { 102 | const container = this; 103 | Scene.enumerateChildren(container, action); 104 | } 105 | 106 | /** 107 | * Invokes the action on every child display object, no matter how deep they are nested. 108 | */ 109 | public static enumerateChildren(container: Container, action: (d: Container) => void): void { 110 | for (let i = 0; i < container.children.length; i++) { 111 | let c = container.children[i] as Container; 112 | action(c); 113 | if (c.children && c.children.length > 0) { 114 | Scene.enumerateChildren(c, action); 115 | } 116 | } 117 | } 118 | 119 | /** 120 | * Adds one or more children to the container. 121 | * Multiple items can be added like: `myScene.addChild(childOne, childTwo, childThree)` 122 | * @param child PIXI.Container 123 | */ 124 | public addChild(...child: T): T[0] { 125 | const displayObject = super.addChild(...child); 126 | return displayObject; 127 | } 128 | 129 | /** 130 | * Adds a child object to the scene. 131 | * @param child PIXI.Container 132 | * @param index position in the display object list where the child is inserted 133 | */ 134 | public addChildAt(child: T, index: number): T { 135 | const displayObject = super.addChildAt(child, index); 136 | return displayObject as T; 137 | } 138 | 139 | /** 140 | * Pauses the scene. A paused scene is not rendered and its `onUpdate()` events are not fired. 141 | */ 142 | public pause(): void { 143 | this.paused = true; 144 | } 145 | 146 | /** 147 | * Resumes the scene. 148 | */ 149 | public resume(): void { 150 | this.paused = false; 151 | } 152 | 153 | /** 154 | * Returns true if the scene is paused. 155 | */ 156 | public isPaused(): boolean { 157 | return this.paused; 158 | } 159 | 160 | /** 161 | * Gets the clear flag used by the PIXI renderer. 162 | */ 163 | public get clear() { 164 | return this.clearValue; 165 | } 166 | 167 | /** 168 | * Sets the clear flag used by the PIXI renderer. 169 | */ 170 | public set clear(clearFlag: boolean) { 171 | this.clearValue = clearFlag; 172 | } 173 | } 174 | -------------------------------------------------------------------------------- /src/core/SceneManager.ts: -------------------------------------------------------------------------------- 1 | import { Container, Application, Renderer } from 'pixi.js'; 2 | import { VERSION } from '../_version'; 3 | import { IScreenSizeCalculator } from './IScreenSizeCalculator'; 4 | import { DefaultScreenSizeCalculator } from './screen-size/DefaultScreenSizeCalculator'; 5 | import { Scene } from './Scene'; 6 | import { IController } from './IController'; 7 | import { IResizable } from './IResizable'; 8 | import { IUpdatable } from './IUpdatable'; 9 | 10 | /** 11 | * Handles multiple scenes, scene activation, rendering and updates. 12 | */ 13 | export class SceneManager { 14 | private static logVersion() { 15 | if (navigator.userAgent.toLowerCase().indexOf('chrome') > -1) { 16 | const fmtPurp = 'color:#fa1;background:#ff66a5;padding:5px 0;'; 17 | const fmtTxt = 'color:#fa1;background:#000;padding:5px 0;'; 18 | const fmtHearts = 'color:#f55;background:#ffc3dc;padding:5px 0;'; 19 | const args = [ 20 | ` %c %c pixi-scenegraph: ${VERSION} ✰ %c %c https://github.com/enriko-riba/pixi-scenegraph#readme ❤❤❤\t`, 21 | fmtPurp, 22 | fmtTxt, 23 | fmtPurp, 24 | fmtHearts, 25 | ]; 26 | console.info.apply(console, args); 27 | } else if (window.console) { 28 | console.info(`pixi-scenegraph: ${VERSION} ✰ https://github.com/enriko-riba/pixi-scenegraph#readme ❤❤❤`); 29 | } 30 | } 31 | 32 | /** 33 | * This object is only to support rendering masterHudOverlay together with the current scene! 34 | */ 35 | private masterContainer: Container; 36 | 37 | private masterHudOverlay: Container | (Container & IResizable & IUpdatable); 38 | private modalDialog: Container | null = null; 39 | private currentScene: Scene | null = null; 40 | private lastScene: Scene; 41 | private scenes: Scene[] = []; 42 | private controllers: IController[] = []; 43 | private app: Application; 44 | 45 | private designWidth: number; 46 | private designHeight: number; 47 | private screenSizeCalculator: IScreenSizeCalculator; 48 | 49 | private startTime: number; 50 | private timeStamp: number; 51 | private animationFrameHandle: number = -1; 52 | 53 | /** 54 | * Creates a new SceneManager instance. 55 | * 56 | * @param pixiApplication - the initialized PIXI Application 57 | * @param designWidth - the design width, if undefined the window.innerWidth is used 58 | * @param designHeight - the design height, if undefined the window.innerHeight is used 59 | * @param screenSizeCalculator - custom screen size calculator implementation, if undefined the default is used 60 | * @remarks The DefaultScreenSizeCalculator returns screen dimensions that horizontally fit in available screen 61 | * space but preserve the aspect ratio of the given width and height values. 62 | */ 63 | constructor(pixiApplication: Application, designWidth?: number, designHeight?: number, screenSizeCalculator?: IScreenSizeCalculator) { 64 | SceneManager.logVersion(); 65 | this.masterContainer = new Container(); 66 | this.app = pixiApplication; 67 | this.app.ticker.add(this.onRender, this); 68 | this.app.stage = this.masterContainer; 69 | this.designWidth = designWidth || window.innerWidth; 70 | this.designHeight = designHeight || window.innerHeight; 71 | this.screenSizeCalculator = screenSizeCalculator || new DefaultScreenSizeCalculator(this.designWidth, this.designHeight); 72 | window.removeEventListener('resize', this.resizeHandler); 73 | window.addEventListener('resize', this.resizeHandler, true); 74 | } 75 | 76 | /** 77 | * Returns the PIXI.Renderer instance. 78 | */ 79 | public get Renderer(): Renderer { 80 | return this.app.renderer as Renderer; 81 | } 82 | 83 | /** 84 | * Returns the PIXI.Application instance. 85 | */ 86 | public get Application(): Application { 87 | return this.app; 88 | } 89 | 90 | /** 91 | * Adds a controller. 92 | * @param controller - the controller instance 93 | */ 94 | public AddController(controller: IController) { 95 | this.controllers.push(controller); 96 | } 97 | 98 | /** 99 | * Removes a controller. 100 | * @param controllerOrId - the controller name or instance to be removed. 101 | */ 102 | public RemoveController(controllerOrId: IController | string) { 103 | const id = typeof controllerOrId !== 'string' ? controllerOrId.id : controllerOrId; 104 | this.controllers = this.controllers.filter((ctrl) => ctrl.id !== id); 105 | } 106 | 107 | /** 108 | * Returns the current scene instance. 109 | */ 110 | public get CurrentScene(): Scene { 111 | return this.currentScene as Scene; 112 | } 113 | 114 | /** 115 | * Adds a scene to the graph. 116 | */ 117 | public AddScene(scene: Scene): void { 118 | const found = this.scenes.filter((item: Scene) => item.Name === scene.Name); 119 | if (found && found.length > 0) { 120 | throw Error("Scene: '" + scene.Name + "' already exists!"); 121 | } 122 | this.scenes.push(scene); 123 | } 124 | 125 | /** 126 | * Removes all scenes from the graph. 127 | */ 128 | public RemoveAllScenes(): void { 129 | this.scenes.forEach((scene: Scene) => { 130 | scene.onDestroy(); 131 | scene.destroy({ children: true, texture: true, textureSource: true }); 132 | }); 133 | this.scenes = []; 134 | this.currentScene = null; 135 | } 136 | 137 | /** 138 | * Removes a scene from the graph. 139 | */ 140 | public RemoveScene(scene: Scene): void { 141 | this.scenes = this.scenes.filter((item: Scene) => { 142 | return item !== scene; 143 | }); 144 | scene.onDestroy(); 145 | scene.destroy({ children: true, texture: true, textureSource: true }); 146 | } 147 | 148 | /** 149 | * Returns true if the named scene exists. 150 | * @param name - the scene name 151 | */ 152 | public HasScene(name: string): boolean { 153 | const found = this.scenes.filter((item: Scene) => item.Name === name); 154 | return found && found.length > 0; 155 | } 156 | 157 | /** 158 | * Returns the scene by its name. 159 | * @param name - the scene name 160 | */ 161 | public GetScene(name: string): Scene { 162 | const found = this.scenes.filter((item: Scene) => item.Name === name); 163 | if (!found || found.length === 0) { 164 | throw Error("Scene: '" + name + "' not found"); 165 | } 166 | if (found.length > 1) { 167 | throw Error("Multiple scenes: '" + name + "' found"); 168 | } 169 | return found[0]; 170 | } 171 | 172 | /** 173 | * Activates a scene. 174 | * @param sceneOrName - the scene instance or scene name 175 | */ 176 | public ActivateScene(sceneOrName: Scene | string): void { 177 | let scene: Scene; 178 | if (typeof sceneOrName === 'string') { 179 | const found = this.scenes.filter((item: Scene) => item.Name === sceneOrName); 180 | if (!found || found.length === 0) { 181 | throw Error("Scene: '" + sceneOrName + "' not found"); 182 | } 183 | if (found.length > 1) { 184 | throw Error("Multiple scenes: '" + sceneOrName + "' found"); 185 | } 186 | scene = found[0]; 187 | } else { 188 | scene = sceneOrName as Scene; 189 | } 190 | 191 | if (this.currentScene && this.currentScene !== scene) { 192 | console.log('DeactivateScene ' + this.currentScene.Name); 193 | this.currentScene.onDeactivate(); 194 | } 195 | 196 | console.log('ActivateScene ' + scene.Name); 197 | this.startTime = 0; 198 | this.lastScene = (this.currentScene !== scene ? this.currentScene : this.lastScene) as Scene; 199 | this.currentScene = scene; 200 | this.app.renderer.background.color = scene.BackGroundColor; 201 | this.resizeHandler(); 202 | 203 | scene.onActivate(); 204 | 205 | this.masterContainer.removeChildren(); 206 | this.masterContainer.addChild(this.currentScene); 207 | 208 | if (this.currentScene.HudOverlay) { 209 | this.masterContainer.addChild(this.currentScene.HudOverlay); 210 | } 211 | 212 | if (this.masterHudOverlay) { 213 | this.masterContainer.addChild(this.masterHudOverlay); 214 | } 215 | } 216 | 217 | /** 218 | * Activates the previous scene. 219 | */ 220 | public ActivatePreviousScene() { 221 | this.ActivateScene(this.lastScene); 222 | } 223 | 224 | /** 225 | * Gets the master HUD overlay container. 226 | */ 227 | public get MasterHudOverlay() { 228 | return this.masterHudOverlay; 229 | } 230 | 231 | /** 232 | * Sets the master HUD overlay container. 233 | * Note: in order to set a master hud overlay, a scene must be active. 234 | */ 235 | public set MasterHudOverlay(hud: Container | (Container & IResizable & IUpdatable)) { 236 | this.masterHudOverlay = hud; 237 | this.masterContainer.removeChildren(); 238 | this.masterContainer.addChild(this.currentScene as Scene); 239 | if (this.currentScene && this.currentScene.HudOverlay) { 240 | this.masterContainer.addChild(this.currentScene.HudOverlay); 241 | } 242 | 243 | if (this.masterHudOverlay) { 244 | this.masterContainer.addChild(this.masterHudOverlay); 245 | } 246 | this.resizeHandler(); 247 | } 248 | 249 | /** 250 | * Returns true if a modal dialog is open. 251 | */ 252 | public get IsDialogOpen(): boolean { 253 | return this.modalDialog !== null; 254 | } 255 | 256 | /** 257 | * Adds a modal dialog over the scene. 258 | */ 259 | public ShowDialog(dialog: Container) { 260 | if (this.modalDialog) { 261 | this.masterContainer.removeChild(this.modalDialog); 262 | } 263 | if (dialog) { 264 | this.modalDialog = dialog; 265 | this.masterContainer.addChild(this.modalDialog); 266 | this.resizeHandler(); 267 | } 268 | } 269 | 270 | /** 271 | * Closes the modal dialog. 272 | */ 273 | public CloseDialog() { 274 | if (this.modalDialog) { 275 | this.masterContainer.removeChild(this.modalDialog); 276 | this.modalDialog = null; 277 | } 278 | } 279 | 280 | /** 281 | * Renders the current scene in a rendertexture. 282 | */ 283 | // public CaptureScene(): RenderTexture { 284 | // console.log(`Capturing scene, width: ${this.app.renderer.width}, height: ${this.app.renderer.height}`); 285 | // const options: RendererOptions = { 286 | // renderTexture: RenderTexture.create({ width: this.app.renderer.width, height: this.app.renderer.height }), 287 | // }; 288 | // this.app.renderer.render(this.currentScene as Scene, options); 289 | // return options.renderTexture as RenderTexture; 290 | // } 291 | 292 | /** 293 | * Cancels the animationFrame loop, removes all scenes and finally destroys the renderer. 294 | */ 295 | public Destroy = () => { 296 | cancelAnimationFrame(this.animationFrameHandle); 297 | if (this.currentScene) { 298 | this.currentScene.pause(); 299 | } 300 | this.scenes.forEach((scene: Scene) => { 301 | this.RemoveScene(scene); 302 | }); 303 | this.app.destroy(true, { 304 | children: true, 305 | texture: true, 306 | textureSource: true, 307 | }); 308 | }; 309 | 310 | /** 311 | * Calculates and sets the master container scale. 312 | */ 313 | private adjustSceneSize(screenSizeCalculator: IScreenSizeCalculator) { 314 | const size = screenSizeCalculator.CalculateSize(); 315 | this.app.renderer.resize(size.x, size.y); 316 | const scale = screenSizeCalculator.CalculateScale(size); 317 | this.masterContainer.scale.set(scale.x, scale.y); 318 | } 319 | 320 | /** 321 | * DOM event handler, invokes onResize to allow overriding resize logic. 322 | */ 323 | private resizeHandler = () => { 324 | this.adjustSceneSize(this.screenSizeCalculator); 325 | 326 | // resize current scene 327 | if (this.currentScene) { 328 | this.currentScene.onResize(); 329 | 330 | // resize current hud 331 | if (this.currentScene.HudOverlay && (this.currentScene.HudOverlay as any as IResizable).onResize) { 332 | (this.currentScene.HudOverlay as any as IResizable).onResize(); 333 | } 334 | } 335 | 336 | // resize current master hud 337 | var resizable = this.masterHudOverlay as any as IResizable; 338 | if (resizable && resizable.onResize) { 339 | resizable.onResize(); 340 | } 341 | }; 342 | 343 | private onRender = () => { 344 | if (!this.startTime) { 345 | this.startTime = Date.now(); 346 | } 347 | 348 | this.timeStamp = Date.now(); 349 | let dt = this.timeStamp - this.startTime; 350 | if (dt > 50) { 351 | dt = 50; 352 | } 353 | 354 | this.controllers.forEach((ctrl) => { 355 | if (!ctrl.scope || (this.currentScene && this.currentScene.Name === ctrl.scope)) { 356 | ctrl.update(dt, this.currentScene); 357 | } 358 | }); 359 | 360 | // exit if no scene or paused 361 | if (!this.currentScene || this.currentScene.isPaused()) { 362 | return; 363 | } 364 | 365 | if (this.masterHudOverlay && (this.masterHudOverlay as IUpdatable).onUpdate) { 366 | (this.masterHudOverlay as IUpdatable).onUpdate(dt, this.timeStamp); 367 | } 368 | 369 | if (this.currentScene.HudOverlay && (this.currentScene.HudOverlay as IUpdatable).onUpdate) { 370 | (this.currentScene.HudOverlay as IUpdatable).onUpdate(dt, this.timeStamp); 371 | } 372 | 373 | this.currentScene.onUpdate(dt, this.timeStamp); 374 | this.startTime = this.timeStamp; 375 | }; 376 | } 377 | -------------------------------------------------------------------------------- /src/core/screen-size/ContainerSizeCalculator.ts: -------------------------------------------------------------------------------- 1 | import { IScreenSizeCalculator, ISize } from '../IScreenSizeCalculator'; 2 | 3 | const Aspect = { 4 | x: 1, 5 | y: 1, 6 | }; 7 | 8 | /** 9 | * Preserves the container width and height, no aspect ratio is calculated. 10 | */ 11 | export class ContainerSizeCalculator implements IScreenSizeCalculator { 12 | constructor(private canvas: HTMLCanvasElement) {} 13 | 14 | /** 15 | * Returns the designed dimensions. 16 | */ 17 | public CalculateSize(): ISize { 18 | return { x: this.canvas.clientWidth, y: this.canvas.clientHeight }; 19 | } 20 | 21 | /** 22 | * Returns a fixed scale with value (1, 1). 23 | */ 24 | public CalculateScale(): ISize { 25 | return Aspect; 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /src/core/screen-size/DefaultScreenSizeCalculator.ts: -------------------------------------------------------------------------------- 1 | import { IScreenSizeCalculator, ISize } from '../IScreenSizeCalculator'; 2 | 3 | /** 4 | * Calculates viewport that horizontally fits in the device and still preserves the designed aspect ratio. 5 | */ 6 | export class DefaultScreenSizeCalculator implements IScreenSizeCalculator { 7 | private aspect: number; 8 | constructor( 9 | protected designedWidth: number, 10 | protected designedHeight: number, 11 | ) { 12 | this.aspect = this.designedWidth / this.designedHeight; 13 | } 14 | 15 | /** 16 | * Returns the largest size that fits in the physical screen dimensions while preserving the aspect ratio. 17 | */ 18 | public CalculateSize(): ISize { 19 | const availableSize = { x: window.innerWidth, y: window.innerHeight }; 20 | const maxWidth = Math.floor(this.aspect * availableSize.y); 21 | const maxHeight = Math.floor(window.innerHeight); 22 | return { x: Math.min(maxWidth, availableSize.x), y: Math.min(maxHeight, availableSize.y) }; 23 | } 24 | 25 | /** 26 | * Returns a scale applied to scenes so that they fit inside the calculated size. 27 | * @param calculatedSize - the maximum available screen size, usually returned by `CalculateSize()` 28 | */ 29 | public CalculateScale(calculatedSize: ISize): ISize { 30 | return { 31 | x: calculatedSize.x / this.designedWidth, 32 | y: calculatedSize.x / this.designedWidth, 33 | }; 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /src/core/screen-size/FixedSizeCalculator.ts: -------------------------------------------------------------------------------- 1 | import { IScreenSizeCalculator, ISize } from '../IScreenSizeCalculator'; 2 | 3 | const Aspect = { 4 | x: 1, 5 | y: 1, 6 | }; 7 | 8 | /** 9 | * Returns the designed width and height with aspect ratio 1. 10 | */ 11 | export class FixedSizeCalculator implements IScreenSizeCalculator { 12 | constructor( 13 | private designedWidth: number, 14 | private designedHeight: number, 15 | ) {} 16 | 17 | /** 18 | * Returns the designed dimensions. 19 | */ 20 | public CalculateSize(): ISize { 21 | return { x: this.designedWidth, y: this.designedHeight }; 22 | } 23 | 24 | /** 25 | * Returns a fixed scale with value (1, 1). 26 | */ 27 | public CalculateScale(): ISize { 28 | return Aspect; 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /src/core/screen-size/NoResizeScreenSizeCalculator.ts: -------------------------------------------------------------------------------- 1 | import { IScreenSizeCalculator, ISize } from '../IScreenSizeCalculator'; 2 | 3 | const Aspect = { 4 | x: 1, 5 | y: 1, 6 | }; 7 | 8 | /** 9 | * Simply returns the available screen size without any scaling. 10 | */ 11 | export class NoResizeScreenSizeCalculator implements IScreenSizeCalculator { 12 | constructor() {} 13 | 14 | /** 15 | * Returns the largest physical screen dimensions. 16 | */ 17 | public CalculateSize(): ISize { 18 | return { x: window.innerWidth, y: window.innerHeight }; 19 | } 20 | 21 | /** 22 | * Returns a fixed scale with value (1, 1). 23 | */ 24 | public CalculateScale(): ISize { 25 | return Aspect; 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /src/index.ts: -------------------------------------------------------------------------------- 1 | // resizing 2 | export { ISize, IScreenSizeCalculator } from './core/IScreenSizeCalculator'; 3 | export { DefaultScreenSizeCalculator } from './core/screen-size/DefaultScreenSizeCalculator'; 4 | export { NoResizeScreenSizeCalculator } from './core/screen-size/NoResizeScreenSizeCalculator'; 5 | export { FixedSizeCalculator } from './core/screen-size/FixedSizeCalculator'; 6 | export { ContainerSizeCalculator } from './core/screen-size/ContainerSizeCalculator'; 7 | 8 | // scenegraph 9 | export { SceneManager } from './core/SceneManager'; 10 | export { Scene } from './core/Scene'; 11 | export { IController } from './core/IController'; 12 | export { IResizable } from './core/IResizable'; 13 | export { IUpdatable } from './core/IUpdatable'; 14 | export { VERSION } from './_version'; 15 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "moduleResolution": "node", 4 | "target": "ESNext", 5 | "module": "ESNext", 6 | "lib": ["ESNext", "DOM"], 7 | "outDir": "dist", 8 | "strict": false, 9 | "declaration": true, 10 | "esModuleInterop": true, 11 | "resolveJsonModule": true, 12 | "noImplicitAny": true, 13 | "strictNullChecks": true, 14 | "strictFunctionTypes": true, 15 | "noImplicitThis": true, 16 | "alwaysStrict": true, 17 | "noUnusedLocals": false, 18 | "noImplicitReturns": true, 19 | "noFallthroughCasesInSwitch": true, 20 | "allowSyntheticDefaultImports": true, 21 | "experimentalDecorators": true, 22 | "noUnusedParameters": false, 23 | "removeComments": true, 24 | 25 | //"types": ["offscreencanvas"], 26 | "paths": { 27 | "mini-signals": [ 28 | "node_modules/resource-loader/typings/mini-signals.d.ts" 29 | ] 30 | }, 31 | "baseUrl": "./", 32 | }, 33 | "exclude": [ 34 | "node_modules", 35 | "dist" 36 | ], 37 | "include": [ 38 | "src" 39 | ] 40 | } -------------------------------------------------------------------------------- /tslint.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": ["tslint:recommended", "tslint-config-prettier"], 3 | "rules": { 4 | "no-console": false, 5 | "ordered-imports": false, 6 | "object-literal-sort-keys": false 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /version.js: -------------------------------------------------------------------------------- 1 | const pkg = require('./package.json'); 2 | const fs = require('fs'); 3 | fs.writeFile('./src/_version.ts', 'export const VERSION = "' + pkg.version + '";', function (err) { 4 | if (err) { 5 | return console.log(err); 6 | } 7 | 8 | console.log('VERSION.ts -> ' + pkg.version); 9 | }); 10 | --------------------------------------------------------------------------------