From: Rubén Beltrán del Río Date: Sat, 21 Apr 2018 08:48:01 +0000 (-0500) Subject: Render Sumo (#3) X-Git-Url: https://git.r.bdr.sh/rbdr/sumo/commitdiff_plain/0616b3f00653c66b5e34814653e33413b9ec034e?ds=inline Render Sumo (#3) * Use corrected components * Add renderable node * Add render system * Add sumo factory * Add systems and entities to app * Remove reference to weight / accel component * Update dependencies, add pixi and babel-polyfill * Use babel polyfill * Add component to store pixi containers * Add pixi container to the renderable support * Update factories to produce renderables * Update the renderable to use pixi It also uses the new event emitters in serpentity 2.1.0 * Initialize pixi and pass it to renderable system --- diff --git a/config/webpack.js b/config/webpack.js index db1837a..34cccbd 100644 --- a/config/webpack.js +++ b/config/webpack.js @@ -3,7 +3,7 @@ const Path = require('path'); module.exports = { - entry: './lib/sumo', + entry: ['babel-polyfill', './lib/sumo'], output: { path: Path.resolve(__dirname, '../assets'), diff --git a/lib/components/pixi_container.js b/lib/components/pixi_container.js new file mode 100644 index 0000000..5e5cb1f --- /dev/null +++ b/lib/components/pixi_container.js @@ -0,0 +1,25 @@ +import { Component } from '@serpentity/serpentity'; + +/** + * Component that stores a pixi container + * + * @extends {external:Serpentity.Component} + * @class PixiContainerComponent + * @param {object} config a configuration object to extend. + */ +export default class PixiContainerComponent extends Component { + constructor(config) { + + super(config); + + /** + * The properthy that holds the pixi container + * + * @property {external:PixiJs.Container} container + * @name container + * @instance + * @memberof PixiContainerComponent + */ + this.container = this.container || null; + } +}; diff --git a/lib/factories/pixi.js b/lib/factories/pixi.js new file mode 100644 index 0000000..e3c1b5a --- /dev/null +++ b/lib/factories/pixi.js @@ -0,0 +1,66 @@ +import { Graphics } from 'pixi.js'; + +/** + * Factory object that contains many methods to create prefab pixi + * objects + * + * @type object + * @name PixiFactory + */ +export default { + + /** + * Creates a sumo container + * + * @function createSumo + * @memberof PixiFactory + * @return {external:CreateJs.Container} the created container + */ + createSumo() { + + const radius = 25; + + // The body + const body = new Graphics(); + body.beginFill(0x87c5ea) + .drawCircle(0, 0, radius) + .endFill(); + + // The mouth + const mouth = new Graphics(); + mouth.lineStyle(10, 0xff0080, 1) + .arc( + 0, 0, // center + radius * 0.6, + Math.PI / 6, + 5 * Math.PI / 6 + ); + + const leftEye = new Graphics(); + leftEye.beginFill(0xffffff) + .drawCircle(-radius / 3 - radius / 8, -radius / 4, radius / 5) + .endFill(); + + const rightEye = new Graphics(); + rightEye.beginFill(0xffffff) + .drawCircle(radius / 3 + radius / 8, -radius / 4, radius / 5); + + const leftPupil = new Graphics(); + leftPupil.beginFill(0x11) + .drawCircle(-radius / 3 - radius / 8, -radius / 4, radius / 10); + + const rightPupil = new Graphics(); + leftPupil.beginFill(0x11) + .drawCircle(radius / 3 + radius / 8, -radius / 4, radius / 10); + + // The group + body.addChild(mouth); + body.addChild(leftEye); + body.addChild(rightEye); + body.addChild(leftPupil); + body.addChild(rightPupil); + + return body; + } +}; + diff --git a/lib/factories/sumo.js b/lib/factories/sumo.js new file mode 100644 index 0000000..b0e5416 --- /dev/null +++ b/lib/factories/sumo.js @@ -0,0 +1,51 @@ +import { Entity } from '@serpentity/serpentity'; + +import PositionComponent from '@serpentity/components.position'; +import PixiContainerComponent from '../components/pixi_container'; +import PixiFactory from '../factories/pixi'; + +/** + * Factory object that contains many methods to create prefab entities. + * + * @type object + * @name SumoFactory + */ +export default { + + /** + * Creates a sumo entity and adds it to the engine. Can override + * position in the config object + * + * @function createSumo + * @memberof SumoFactory + * @param {external:Serpentity} [engine] the serpentity engine to attach + * to. If not sent, it will not be attached. + * @param {object} [config] the config to override the entity, accepts + * the key `position` as an object with an x and y property. + * @return {external:Serpentity.Entity} the created entity + */ + createSumo(engine, config = {}) { + + const entity = new Entity(); + + entity.addComponent(new PositionComponent(config.position)); + + const container = config.container || { + container: PixiFactory.createSumo() + }; + + // Match the symbol position with the entity position on init + // Otherwise it's up to systems + const position = entity.getComponent(PositionComponent); + container.container.position.x = position.x; + container.container.position.y = position.y; + + entity.addComponent(new PixiContainerComponent(container)); + + if (engine) { + engine.addEntity(entity); + } + + return entity; + } +}; diff --git a/lib/nodes/renderable.js b/lib/nodes/renderable.js new file mode 100644 index 0000000..0a3a111 --- /dev/null +++ b/lib/nodes/renderable.js @@ -0,0 +1,27 @@ +import { Node } from '@serpentity/serpentity'; + +import PixiContainerComponent from '../components/pixi_container'; +import PositionComponent from '@serpentity/components.position'; + +/** + * Node identifying a renderable entity, should have position and a + * symbol to render + * + * @extends {external:Serpentity.Node} + * @class RenderableNode + */ +export default class RenderableNode extends Node { + +}; + +/** + * Holds the types that are used to identify a wave entity + * + * @property {object} types + * @name types + * @memberof WaveNode + */ +RenderableNode.types = { + position: PositionComponent, + container: PixiContainerComponent +}; diff --git a/lib/sumo.js b/lib/sumo.js index da72991..81f948f 100644 --- a/lib/sumo.js +++ b/lib/sumo.js @@ -1,8 +1,12 @@ +import RenderSystem from './systems/render'; +import SumoFactory from './factories/sumo'; import Serpentity from '@serpentity/serpentity'; +import { Application } from 'pixi.js'; /* global window document */ const internals = { + kBackgroundColor: 0xd8c590, kNoElementError: 'No element found. Cannot render.', // Handler for the window load event. Initializes and runs the app. @@ -56,6 +60,7 @@ internals.Sumo = class Sumo { // Initialization functions this._initializeCanvas(); + this._initializePixi(); this._initializeSystems(); this._initializeEntities(); } @@ -119,6 +124,18 @@ internals.Sumo = class Sumo { window.addEventListener('resize', this._resizeCanvas.bind(this)); } + // Initialize Pixi + + _initializePixi() { + + this._pixi = new Application({ + backgroundColor: internals.kBackgroundColor, + view: this._canvas, + width: this._canvas.width, + height: this._canvas.height + }); + } + // Resizes the canvas to a square the size of the smallest magnitude // of the window. @@ -143,12 +160,28 @@ internals.Sumo = class Sumo { _initializeSystems() { + this._engine.addSystem(new RenderSystem({ + application: this._pixi + })); } // Initializes the serpentity entities _initializeEntities() { + SumoFactory.createSumo(this._engine, { + position: { + x: 50, + y: 50 + } + }); + + SumoFactory.createSumo(this._engine, { + position: { + x: 309, + y: 112 + } + }); } }; diff --git a/lib/systems/render.js b/lib/systems/render.js new file mode 100644 index 0000000..8d3d446 --- /dev/null +++ b/lib/systems/render.js @@ -0,0 +1,99 @@ +import { System } from '@serpentity/serpentity'; + +import RenderableNode from '../nodes/renderable'; + +const internals = { + kNoPixiError: 'No pixi application passed to render system. Make sure you set the `application` key in the config object when initializing.' +}; + +/** + * Renders renderable objects to console + * + * @extends {external:Serpentity.System} + * @class RenderSystem + * @param {object} config a configuration object to extend. + */ +export default class RenderSystem extends System { + + constructor(config = {}) { + + super(); + + /** + * The node collection of renderable entities + * + * @property {external:Serpentity.NodeCollection} renderables + * @instance + * @memberof RenderSystem + */ + this.renderables = null; + + /** + * The pixi engine we will use to render + * + * @property {external:PixiJs.Application} renderables + * @instance + * @memberof RenderSystem + */ + this._application = config.application; + + if (!this._application) { + throw new Error(internals.kNoPixiError); + } + } + + /** + * Initializes system when added. Requests renderable nodes and + * attaches to event listeners to add / remove them to pixi stage + * + * @function added + * @memberof RenderSystem + * @instance + * @param {external:Serpentity.Engine} engine the serpentity engine to + * which we are getting added + */ + added(engine) { + + this.renderables = engine.getNodes(RenderableNode); + this.renderables.on('nodeAdded', (event) => { + + this._application.stage.addChild(event.node.container.container); + }); + this.renderables.on('nodeRemoved', (event) => { + + this._application.stage.removeChild(event.node.container.container); + }); + } + + /** + * Clears system resources when removed. + * + * @function removed + * @instance + * @memberof RenderSystem + */ + removed() { + + this.renderables.removeAllListeners('nodeAdded'); + this.renderables.removeAllListeners('nodeRemoved'); + this.renderables = null; + } + + /** + * Runs on every update of the loop. Prints the location of every + * renderable + * + * @function update + * @instance + * @param {Number} currentFrameDuration the duration of the current + * frame + * @memberof RenderSystem + */ + update(currentFrameDuration) { + + for (const renderable of this.renderables) { + renderable.container.container.position.x = renderable.position.x; + renderable.container.container.position.y = renderable.position.y; + } + } +}; diff --git a/package-lock.json b/package-lock.json index 8dc6d98..43c41d3 100644 --- a/package-lock.json +++ b/package-lock.json @@ -5,14 +5,22 @@ "requires": true, "dependencies": { "@serpentity/components.debug": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/@serpentity/components.debug/-/components.debug-1.0.0.tgz", - "integrity": "sha1-1mPRCQcWxVf3ShZYRDZjijWIfj4=" + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@serpentity/components.debug/-/components.debug-2.0.0.tgz", + "integrity": "sha512-IYFPQYuwhSSL6+jfQ5H9IVgkGwX2336Mowuk+8jK1t8Bqc373uZv6CPR3MSh/tc+Bi6bHEn+pZp1FMUxS8o12g==" }, - "@serpentity/serpentity": { + "@serpentity/components.position": { "version": "2.0.0", - "resolved": "https://registry.npmjs.org/@serpentity/serpentity/-/serpentity-2.0.0.tgz", - "integrity": "sha1-MEbOJKQ80vXLUbaVnkhXqZ6Bbl4=" + "resolved": "https://registry.npmjs.org/@serpentity/components.position/-/components.position-2.0.0.tgz", + "integrity": "sha512-umCLx12S3dV8d23bBSnB5CAvmqSjh9iCl2/JyOZmtOex2BkUUjIf2aqA3ZWVwpgoDmbPJMnejvghwuWHlkV4ww==" + }, + "@serpentity/serpentity": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/@serpentity/serpentity/-/serpentity-2.1.0.tgz", + "integrity": "sha512-tt+WMauSRdi/bnem9pxHfoOtpi17RZjgB/tQuNCK8X6EMcoly5FgjJotpsa4FsjpWARJvq9P2zJFilNwYOZRYg==", + "requires": { + "events": "2.0.0" + } }, "@sindresorhus/is": { "version": "0.7.0", @@ -926,6 +934,25 @@ "babel-types": "6.26.0" } }, + "babel-polyfill": { + "version": "6.26.0", + "resolved": "https://registry.npmjs.org/babel-polyfill/-/babel-polyfill-6.26.0.tgz", + "integrity": "sha1-N5k3q8Z9eJWXCtxiHyhM2WbPIVM=", + "dev": true, + "requires": { + "babel-runtime": "6.26.0", + "core-js": "2.5.5", + "regenerator-runtime": "0.10.5" + }, + "dependencies": { + "regenerator-runtime": { + "version": "0.10.5", + "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.10.5.tgz", + "integrity": "sha1-M2w+/BIgrc7dosn6tntaeVWjNlg=", + "dev": true + } + } + }, "babel-preset-env": { "version": "1.6.1", "resolved": "https://registry.npmjs.org/babel-preset-env/-/babel-preset-env-1.6.1.tgz", @@ -1196,6 +1223,11 @@ "integrity": "sha512-XBaoWE9RW8pPdPQNibZsW2zh8TW6gcarXp1FZPwT8Uop8ScSNldJEWf2k9l3HeTqdrEwsOsFcq74RiJECW34yA==", "dev": true }, + "bit-twiddle": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/bit-twiddle/-/bit-twiddle-1.0.2.tgz", + "integrity": "sha1-DGwfq+KyPRcXPZpht7cJPrnhdp4=" + }, "bluebird": { "version": "3.5.1", "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.5.1.tgz", @@ -2229,6 +2261,11 @@ "stream-shift": "1.0.0" } }, + "earcut": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/earcut/-/earcut-2.1.3.tgz", + "integrity": "sha512-AxdCdWUk1zzK/NuZ7e1ljj6IGC+VAdC3Qb7QQDsXpfNrc5IM8tL9nNXUmEGE6jRHTfZ10zhzRhtDmWVsR5pd3A==" + }, "editions": { "version": "1.3.4", "resolved": "https://registry.npmjs.org/editions/-/editions-1.3.4.tgz", @@ -2522,11 +2559,15 @@ "integrity": "sha1-Cr9PHKpbyx96nYrMbepPqqBLrJs=", "dev": true }, + "eventemitter3": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-2.0.3.tgz", + "integrity": "sha1-teEHm1n7XhuidxwKmTvgYKWMmbo=" + }, "events": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/events/-/events-1.1.1.tgz", - "integrity": "sha1-nr23Y1rQmccNzEwqH1AEKI6L2SQ=", - "dev": true + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/events/-/events-2.0.0.tgz", + "integrity": "sha512-r/M5YkNg9zwI8QbSf7tsDWWJvO3PGwZXyG7GpFAxtMASnHL2eblFd7iHiGPtyGKKFPZ59S63NeX10Ws6WqGDcg==" }, "evp_bytestokey": { "version": "1.0.3", @@ -4708,6 +4749,11 @@ "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=", "dev": true }, + "ismobilejs": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/ismobilejs/-/ismobilejs-0.4.1.tgz", + "integrity": "sha1-Gl8SbHD+05yT2jgPpiy65XI+fcI=" + }, "isobject": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", @@ -5567,6 +5613,11 @@ "integrity": "sha1-3z02Uqc/3ta5sLJBRub9BSNTRY4=", "dev": true }, + "mini-signals": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/mini-signals/-/mini-signals-1.2.0.tgz", + "integrity": "sha1-RbCAE8X65RokqhqTXNMXye1yHXQ=" + }, "minimalistic-assert": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz", @@ -5822,6 +5873,12 @@ "vm-browserify": "0.0.4" }, "dependencies": { + "events": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/events/-/events-1.1.1.tgz", + "integrity": "sha1-nr23Y1rQmccNzEwqH1AEKI6L2SQ=", + "dev": true + }, "punycode": { "version": "1.4.1", "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.4.1.tgz", @@ -5939,8 +5996,7 @@ "object-assign": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", - "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=", - "dev": true + "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=" }, "object-copy": { "version": "0.1.0", @@ -6275,6 +6331,11 @@ "integrity": "sha1-bVuTSkVpk7I9N/QKOC1vFmao5cY=", "dev": true }, + "parse-uri": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/parse-uri/-/parse-uri-1.0.0.tgz", + "integrity": "sha1-KHLcwi8aeXrN4Vg9igrClVLdrCA=" + }, "pascalcase": { "version": "0.1.1", "resolved": "https://registry.npmjs.org/pascalcase/-/pascalcase-0.1.1.tgz", @@ -6366,6 +6427,26 @@ "pinkie": "2.0.4" } }, + "pixi-gl-core": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/pixi-gl-core/-/pixi-gl-core-1.1.4.tgz", + "integrity": "sha1-i0tcQzsx5Bm8N53FZc4bg1qRs3I=" + }, + "pixi.js": { + "version": "4.7.3", + "resolved": "https://registry.npmjs.org/pixi.js/-/pixi.js-4.7.3.tgz", + "integrity": "sha512-uYCTtjYiqSpwN2kZLmjwAL+1yrdCL/pVI+waYlkVqAHBnuQCpOuGA/0UunbxZ4T13xVZ4wN+ftPlEjxWl0th3w==", + "requires": { + "bit-twiddle": "1.0.2", + "earcut": "2.1.3", + "eventemitter3": "2.0.3", + "ismobilejs": "0.4.1", + "object-assign": "4.1.1", + "pixi-gl-core": "1.1.4", + "remove-array-items": "1.0.0", + "resource-loader": "2.1.1" + } + }, "pkg-dir": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-2.0.0.tgz", @@ -6803,6 +6884,11 @@ } } }, + "remove-array-items": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/remove-array-items/-/remove-array-items-1.0.0.tgz", + "integrity": "sha1-B79CyzMvTPboXq2DteToltIyayE=" + }, "remove-trailing-separator": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/remove-trailing-separator/-/remove-trailing-separator-1.1.0.tgz", @@ -6923,6 +7009,15 @@ "integrity": "sha1-LGN/53yJOv0qZj/iGqkIAGjiBSo=", "dev": true }, + "resource-loader": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/resource-loader/-/resource-loader-2.1.1.tgz", + "integrity": "sha512-jRMGYUfa4AGk9ib45Wxc93lobhQVoiCUAUkWqsbb/fhGPge97YT1S8aC0xBEQpolMsrdmB3o7SH8VmIEvIDOLA==", + "requires": { + "mini-signals": "1.2.0", + "parse-uri": "1.0.0" + } + }, "responselike": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/responselike/-/responselike-1.0.2.tgz", diff --git a/package.json b/package.json index c7d0054..50f2a47 100644 --- a/package.json +++ b/package.json @@ -21,12 +21,15 @@ }, "homepage": "https://github.com/rbdr/sumo#readme", "dependencies": { - "@serpentity/components.debug": "^1.0.0", - "@serpentity/serpentity": "^2.0.0" + "@serpentity/components.debug": "^2.0.0", + "@serpentity/components.position": "^2.0.0", + "@serpentity/serpentity": "^2.1.0", + "pixi.js": "^4.7.3" }, "browserslist": "last two versions", "devDependencies": { "babel-core": "^6.26.0", + "babel-polyfill": "^6.26.0", "babel-preset-env": "^1.6.1", "babel-loader": "^7.1.4", "code": "^5.2.0",