├── .gitignore ├── .eslintignore ├── media ├── bat.png ├── 04b03.png └── rotate.png ├── src ├── engine │ ├── gfx │ │ ├── COASprite.js │ │ ├── filters │ │ │ ├── color │ │ │ │ ├── colorStep.frag │ │ │ │ ├── colorMatrix.frag │ │ │ │ └── ColorStepFilter.js │ │ │ ├── invert │ │ │ │ ├── invert.frag │ │ │ │ └── InvertFilter.js │ │ │ ├── gray │ │ │ │ ├── gray.frag │ │ │ │ └── GrayFilter.js │ │ │ ├── sepia │ │ │ │ ├── sepia.frag │ │ │ │ └── SepiaFilter.js │ │ │ ├── pixelate │ │ │ │ ├── pixelate.frag │ │ │ │ └── PixelateFilter.js │ │ │ ├── displacement │ │ │ │ ├── displacement.frag │ │ │ │ ├── displacement.vert │ │ │ │ └── DisplacementFilter.js │ │ │ ├── noise │ │ │ │ ├── noise.frag │ │ │ │ └── NoiseFilter.js │ │ │ ├── rgb │ │ │ │ ├── rgbSplit.frag │ │ │ │ └── RGBSplitFilter.js │ │ │ ├── blur │ │ │ │ ├── blurDir.frag │ │ │ │ ├── SmartBlurFilter.js │ │ │ │ ├── blur.frag │ │ │ │ ├── blurDir.vert │ │ │ │ ├── smartBlur.frag │ │ │ │ ├── blurX.vert │ │ │ │ ├── blurY.vert │ │ │ │ ├── BlurYFilter.js │ │ │ │ ├── BlurXFilter.js │ │ │ │ └── BlurFilter.js │ │ │ ├── crosshatch │ │ │ │ ├── CrossHatchFilter.js │ │ │ │ └── crosshatch.frag │ │ │ ├── twist │ │ │ │ ├── twist.frag │ │ │ │ └── TwistFilter.js │ │ │ ├── dot │ │ │ │ ├── dotScreen.frag │ │ │ │ └── DotScreenFilter.js │ │ │ ├── shockwave │ │ │ │ ├── shockwave.frag │ │ │ │ └── ShockwaveFilter.js │ │ │ ├── dropshadow │ │ │ │ ├── blurYTint.frag │ │ │ │ ├── blurYTint.vert │ │ │ │ └── BlurYTintFilter.js │ │ │ ├── tiltshift │ │ │ │ ├── TiltShiftXFilter.js │ │ │ │ ├── TiltShiftYFilter.js │ │ │ │ ├── tiltShift.frag │ │ │ │ ├── TiltShiftFilter.js │ │ │ │ └── TiltShiftAxisFilter.js │ │ │ ├── ascii │ │ │ │ ├── ascii.frag │ │ │ │ └── AsciiFilter.js │ │ │ ├── convolution │ │ │ │ ├── convolution.frag │ │ │ │ └── ConvolutionFilter.js │ │ │ ├── index.js │ │ │ └── bloom │ │ │ │ └── BloomFilter.js │ │ ├── loaders │ │ │ ├── index.js │ │ │ ├── dirname.js │ │ │ └── textureParser.js │ │ ├── accessibility │ │ │ ├── index.js │ │ │ └── accessibleTarget.js │ │ ├── core │ │ │ ├── renderers │ │ │ │ ├── webgl │ │ │ │ │ ├── utils │ │ │ │ │ │ ├── StencilMaskStack.js │ │ │ │ │ │ └── ObjectRenderer.js │ │ │ │ │ ├── filters │ │ │ │ │ │ ├── spriteMaskFilter.vert │ │ │ │ │ │ ├── spriteMaskFilter.frag │ │ │ │ │ │ ├── FXAAFilter.js │ │ │ │ │ │ ├── FXAA.vert │ │ │ │ │ │ ├── SpriteMaskFilter.js │ │ │ │ │ │ └── AbstractFilter.js │ │ │ │ │ ├── managers │ │ │ │ │ │ ├── WebGLManager.js │ │ │ │ │ │ ├── BlendModeManager.js │ │ │ │ │ │ └── MaskManager.js │ │ │ │ │ └── shaders │ │ │ │ │ │ ├── PrimitiveShader.js │ │ │ │ │ │ ├── ComplexPrimitiveShader.js │ │ │ │ │ │ └── TextureShader.js │ │ │ │ └── canvas │ │ │ │ │ └── utils │ │ │ │ │ ├── CanvasMaskManager.js │ │ │ │ │ └── CanvasBuffer.js │ │ │ ├── utils │ │ │ │ └── pluginTarget.js │ │ │ ├── math │ │ │ │ ├── Circle.js │ │ │ │ ├── Rectangle.js │ │ │ │ ├── Ellipse.js │ │ │ │ ├── RoundedRectangle.js │ │ │ │ └── Polygon.js │ │ │ ├── particles │ │ │ │ └── webgl │ │ │ │ │ └── ParticleShader.js │ │ │ ├── textures │ │ │ │ └── TextureUvs.js │ │ │ └── graphics │ │ │ │ ├── GraphicsData.js │ │ │ │ └── webgl │ │ │ │ └── WebGLGraphicsData.js │ │ ├── interaction │ │ │ ├── interactiveTarget.js │ │ │ ├── index.js │ │ │ └── InteractionData.js │ │ ├── Node.js │ │ ├── Text.js │ │ ├── Sprite.js │ │ ├── BitmapText.js │ │ ├── Rope.js │ │ ├── TilingSprite.js │ │ ├── Plane.js │ │ ├── utils │ │ │ └── index.js │ │ ├── mesh │ │ │ ├── webgl │ │ │ │ └── MeshShader.js │ │ │ └── Plane.js │ │ ├── AnimatedSprite.js │ │ └── Graphics.js │ ├── polyfill │ │ ├── Math.sign.js │ │ ├── index.js │ │ ├── Number.isFinite.js │ │ ├── Object.assign.js │ │ └── requestAnimationFrame.js │ ├── loader │ │ ├── index.js │ │ ├── middlewares │ │ │ ├── caching │ │ │ │ └── memory.js │ │ │ └── parsing │ │ │ │ └── blob.js │ │ ├── parse-uri.js │ │ └── b64.js │ ├── physics │ │ └── const.js │ ├── utils │ │ ├── array.js │ │ ├── poolable.js │ │ ├── color.js │ │ ├── math.js │ │ └── object.js │ ├── storage │ │ ├── index.js │ │ └── PersistentData.js │ ├── anime │ │ └── utils.js │ ├── Component.js │ ├── Behavior.js │ ├── resize.js │ ├── System.js │ ├── analytics.js │ └── timer │ │ └── Clock.js ├── behaviors │ ├── FaceTheMouse.js │ ├── RotateAroundPoint.js │ ├── Health.js │ ├── FireBullet.js │ ├── FourWayMove.js │ ├── HorizontalMove.js │ ├── VerticalMove.js │ └── WrapAroundScreen.js └── game │ ├── Loading.js │ ├── main.js │ └── config.js ├── .editorconfig ├── CONTRIBUTING.md ├── .eslintrc.js └── index.html /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | node_modules 3 | dist 4 | -------------------------------------------------------------------------------- /.eslintignore: -------------------------------------------------------------------------------- 1 | howler.core.js 2 | async.js 3 | earcut.js 4 | -------------------------------------------------------------------------------- /media/bat.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pixelpicosean/lesser-panda/HEAD/media/bat.png -------------------------------------------------------------------------------- /media/04b03.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pixelpicosean/lesser-panda/HEAD/media/04b03.png -------------------------------------------------------------------------------- /media/rotate.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pixelpicosean/lesser-panda/HEAD/media/rotate.png -------------------------------------------------------------------------------- /src/engine/gfx/COASprite.js: -------------------------------------------------------------------------------- 1 | import COASprite from './core/sprites/COASprite'; 2 | 3 | export default (sconsKey, entityName) => new COASprite(sconsKey, entityName); 4 | -------------------------------------------------------------------------------- /src/engine/gfx/filters/color/colorStep.frag: -------------------------------------------------------------------------------- 1 | precision mediump float; 2 | 3 | varying vec2 vTextureCoord; 4 | 5 | uniform sampler2D uSampler; 6 | uniform float step; 7 | 8 | void main(void) 9 | { 10 | vec4 color = texture2D(uSampler, vTextureCoord); 11 | 12 | color = floor(color * step) / step; 13 | 14 | gl_FragColor = color; 15 | } 16 | -------------------------------------------------------------------------------- /src/engine/polyfill/Math.sign.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | // References: 4 | // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/sign 5 | 6 | if (!Math.sign) { 7 | Math.sign = function(x) { 8 | x = +x; 9 | if (x === 0 || isNaN(x)) { 10 | return x; 11 | } 12 | return x > 0 ? 1 : -1; 13 | }; 14 | } 15 | -------------------------------------------------------------------------------- /src/engine/loader/index.js: -------------------------------------------------------------------------------- 1 | import Loader from './Loader'; 2 | import Resource from './Resource'; 3 | import { encodeBinary } from './b64'; 4 | import config from 'game/config'; 5 | 6 | const base64 = { 7 | encodeBinary, 8 | }; 9 | 10 | export { 11 | Loader, 12 | Resource, 13 | base64, 14 | }; 15 | 16 | export default new Loader(config.baseUrl || 'media'); 17 | -------------------------------------------------------------------------------- /src/engine/gfx/filters/invert/invert.frag: -------------------------------------------------------------------------------- 1 | precision mediump float; 2 | 3 | varying vec2 vTextureCoord; 4 | 5 | uniform float invert; 6 | uniform sampler2D uSampler; 7 | 8 | void main(void) 9 | { 10 | gl_FragColor = texture2D(uSampler, vTextureCoord); 11 | 12 | gl_FragColor.rgb = mix( (vec3(1)-gl_FragColor.rgb) * gl_FragColor.a, gl_FragColor.rgb, 1.0 - invert); 13 | } 14 | -------------------------------------------------------------------------------- /src/engine/gfx/filters/gray/gray.frag: -------------------------------------------------------------------------------- 1 | precision mediump float; 2 | 3 | varying vec2 vTextureCoord; 4 | varying vec4 vColor; 5 | 6 | uniform sampler2D uSampler; 7 | uniform float gray; 8 | 9 | void main(void) 10 | { 11 | gl_FragColor = texture2D(uSampler, vTextureCoord); 12 | gl_FragColor.rgb = mix(gl_FragColor.rgb, vec3(0.2126*gl_FragColor.r + 0.7152*gl_FragColor.g + 0.0722*gl_FragColor.b), gray); 13 | } 14 | -------------------------------------------------------------------------------- /src/engine/gfx/loaders/index.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @author Mat Groves 3 | * @copyright 2013-2015 GoodBoyDigital 4 | * @license {@link https://github.com/pixijs/pixi.js/blob/master/LICENSE|MIT License} 5 | */ 6 | 7 | export default { 8 | // parsers 9 | bitmapFontParser: require('./bitmapFontParser'), 10 | spritesheetParser: require('./spritesheetParser'), 11 | textureParser: require('./textureParser'), 12 | }; 13 | -------------------------------------------------------------------------------- /src/engine/gfx/accessibility/index.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @file Main export of the accessibility library 3 | * @author Mat Groves 4 | * @copyright 2013-2015 GoodBoyDigital 5 | * @license {@link https://github.com/pixijs/pixi.js/blob/master/LICENSE|MIT License} 6 | */ 7 | 8 | export default { 9 | accessibleTarget: require('./accessibleTarget'), 10 | AccessibilityManager: require('./AccessibilityManager'), 11 | }; 12 | -------------------------------------------------------------------------------- /src/engine/gfx/filters/sepia/sepia.frag: -------------------------------------------------------------------------------- 1 | precision mediump float; 2 | 3 | varying vec2 vTextureCoord; 4 | 5 | uniform sampler2D uSampler; 6 | uniform float sepia; 7 | 8 | const mat3 sepiaMatrix = mat3(0.3588, 0.7044, 0.1368, 0.2990, 0.5870, 0.1140, 0.2392, 0.4696, 0.0912); 9 | 10 | void main(void) 11 | { 12 | gl_FragColor = texture2D(uSampler, vTextureCoord); 13 | gl_FragColor.rgb = mix( gl_FragColor.rgb, gl_FragColor.rgb * sepiaMatrix, sepia); 14 | } 15 | -------------------------------------------------------------------------------- /src/engine/physics/const.js: -------------------------------------------------------------------------------- 1 | // Direction enum 2 | export const RIGHT = Object.freeze({ angle: 0, angleInDegree: 0 }); 3 | export const BOTTOM = Object.freeze({ angle: Math.PI * 0.5, angleInDegree: 90 }); 4 | export const LEFT = Object.freeze({ angle: Math.PI, angleInDegree: 180 }); 5 | export const TOP = Object.freeze({ angle: Math.PI * 1.5, angleInDegree: 270 }); 6 | 7 | // Shapes 8 | export const BOX = 0; 9 | export const CIRC = 1; 10 | export const POLY = 2; 11 | -------------------------------------------------------------------------------- /src/engine/polyfill/index.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | import './Object.assign'; 4 | import './requestAnimationFrame'; 5 | import './Math.sign'; 6 | import './Number.isFinite'; 7 | 8 | if (!window.ArrayBuffer) { 9 | window.ArrayBuffer = Array; 10 | } 11 | if (!window.Float32Array) { 12 | window.Float32Array = Array; 13 | } 14 | if (!window.Uint32Array) { 15 | window.Uint32Array = Array; 16 | } 17 | if (!window.Uint16Array) { 18 | window.Uint16Array = Array; 19 | } 20 | -------------------------------------------------------------------------------- /src/engine/gfx/filters/pixelate/pixelate.frag: -------------------------------------------------------------------------------- 1 | precision mediump float; 2 | 3 | varying vec2 vTextureCoord; 4 | 5 | uniform vec4 dimensions; 6 | uniform vec2 pixelSize; 7 | uniform sampler2D uSampler; 8 | 9 | void main(void) 10 | { 11 | vec2 coord = vTextureCoord; 12 | 13 | vec2 size = dimensions.xy / pixelSize; 14 | 15 | vec2 color = floor( ( vTextureCoord * size ) ) / size + pixelSize/dimensions.xy * 0.5; 16 | 17 | gl_FragColor = texture2D(uSampler, color); 18 | } 19 | -------------------------------------------------------------------------------- /src/engine/gfx/filters/displacement/displacement.frag: -------------------------------------------------------------------------------- 1 | precision mediump float; 2 | 3 | varying vec2 vMapCoord; 4 | varying vec2 vTextureCoord; 5 | varying vec4 vColor; 6 | 7 | uniform vec2 scale; 8 | 9 | uniform sampler2D uSampler; 10 | uniform sampler2D mapSampler; 11 | 12 | void main(void) 13 | { 14 | vec4 map = texture2D(mapSampler, vMapCoord); 15 | 16 | map -= 0.5; 17 | map.xy *= scale; 18 | 19 | gl_FragColor = texture2D(uSampler, vec2(vTextureCoord.x + map.x, vTextureCoord.y + map.y)); 20 | } 21 | -------------------------------------------------------------------------------- /src/engine/gfx/filters/noise/noise.frag: -------------------------------------------------------------------------------- 1 | precision highp float; 2 | 3 | varying vec2 vTextureCoord; 4 | varying vec4 vColor; 5 | 6 | uniform float noise; 7 | uniform sampler2D uSampler; 8 | 9 | float rand(vec2 co) 10 | { 11 | return fract(sin(dot(co.xy, vec2(12.9898, 78.233))) * 43758.5453); 12 | } 13 | 14 | void main() 15 | { 16 | vec4 color = texture2D(uSampler, vTextureCoord); 17 | 18 | float diff = (rand(vTextureCoord) - 0.5) * noise; 19 | 20 | color.r += diff; 21 | color.g += diff; 22 | color.b += diff; 23 | 24 | gl_FragColor = color; 25 | } 26 | -------------------------------------------------------------------------------- /src/engine/gfx/core/renderers/webgl/utils/StencilMaskStack.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Generic Mask Stack data structure 3 | * @class 4 | */ 5 | export default function StencilMaskStack() { 6 | /** 7 | * The actual stack 8 | * 9 | * @member {any[]} 10 | */ 11 | this.stencilStack = []; 12 | 13 | /** 14 | * TODO @alvin 15 | * 16 | * @member {boolean} 17 | */ 18 | this.reverse = true; 19 | 20 | /** 21 | * Internal count 22 | * 23 | * @member {number} 24 | */ 25 | this.count = 0; 26 | } 27 | 28 | StencilMaskStack.prototype.constructor = StencilMaskStack; 29 | -------------------------------------------------------------------------------- /src/engine/gfx/filters/rgb/rgbSplit.frag: -------------------------------------------------------------------------------- 1 | precision mediump float; 2 | 3 | varying vec2 vTextureCoord; 4 | 5 | uniform sampler2D uSampler; 6 | uniform vec4 dimensions; 7 | uniform vec2 red; 8 | uniform vec2 green; 9 | uniform vec2 blue; 10 | 11 | void main(void) 12 | { 13 | gl_FragColor.r = texture2D(uSampler, vTextureCoord + red/dimensions.xy).r; 14 | gl_FragColor.g = texture2D(uSampler, vTextureCoord + green/dimensions.xy).g; 15 | gl_FragColor.b = texture2D(uSampler, vTextureCoord + blue/dimensions.xy).b; 16 | gl_FragColor.a = texture2D(uSampler, vTextureCoord).a; 17 | } 18 | -------------------------------------------------------------------------------- /src/engine/gfx/filters/displacement/displacement.vert: -------------------------------------------------------------------------------- 1 | attribute vec2 aVertexPosition; 2 | attribute vec2 aTextureCoord; 3 | attribute vec4 aColor; 4 | 5 | uniform mat3 projectionMatrix; 6 | uniform mat3 otherMatrix; 7 | 8 | varying vec2 vMapCoord; 9 | varying vec2 vTextureCoord; 10 | varying vec4 vColor; 11 | 12 | void main(void) 13 | { 14 | gl_Position = vec4((projectionMatrix * vec3(aVertexPosition, 1.0)).xy, 0.0, 1.0); 15 | vTextureCoord = aTextureCoord; 16 | vMapCoord = ( otherMatrix * vec3( aTextureCoord, 1.0) ).xy; 17 | vColor = vec4(aColor.rgb * aColor.a, aColor.a); 18 | } 19 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | # EditorConfig helps developers define and maintain consistent 2 | # coding styles between different editors and IDEs 3 | # editorconfig.org 4 | 5 | root = true 6 | 7 | 8 | [*] 9 | end_of_line = lf 10 | charset = utf-8 11 | trim_trailing_whitespace = true 12 | insert_final_newline = true 13 | indent_style = space 14 | indent_size = 2 15 | 16 | [*.js] 17 | indent_style = space 18 | indent_size = 2 19 | 20 | [*.css] 21 | indent_style = space 22 | indent_size = 2 23 | 24 | [*.html] 25 | indent_style = space 26 | indent_size = 2 27 | 28 | [*.{diff,md}] 29 | trim_trailing_whitespace = false 30 | -------------------------------------------------------------------------------- /src/engine/gfx/core/renderers/webgl/filters/spriteMaskFilter.vert: -------------------------------------------------------------------------------- 1 | attribute vec2 aVertexPosition; 2 | attribute vec2 aTextureCoord; 3 | attribute vec4 aColor; 4 | 5 | uniform mat3 projectionMatrix; 6 | uniform mat3 otherMatrix; 7 | 8 | varying vec2 vMaskCoord; 9 | varying vec2 vTextureCoord; 10 | varying vec4 vColor; 11 | 12 | void main(void) 13 | { 14 | gl_Position = vec4((projectionMatrix * vec3(aVertexPosition, 1.0)).xy, 0.0, 1.0); 15 | vTextureCoord = aTextureCoord; 16 | vMaskCoord = ( otherMatrix * vec3( aTextureCoord, 1.0) ).xy; 17 | vColor = vec4(aColor.rgb * aColor.a, aColor.a); 18 | } 19 | -------------------------------------------------------------------------------- /src/engine/gfx/filters/blur/blurDir.frag: -------------------------------------------------------------------------------- 1 | precision lowp float; 2 | 3 | varying vec2 vTextureCoord; 4 | varying vec2 vBlurTexCoords[3]; 5 | varying vec4 vColor; 6 | 7 | uniform sampler2D uSampler; 8 | 9 | void main(void) 10 | { 11 | gl_FragColor = vec4(0.0); 12 | 13 | gl_FragColor += texture2D(uSampler, vTextureCoord ) * 0.3989422804014327; 14 | gl_FragColor += texture2D(uSampler, vBlurTexCoords[ 0]) * 0.2419707245191454; 15 | gl_FragColor += texture2D(uSampler, vBlurTexCoords[ 1]) * 0.05399096651318985; 16 | gl_FragColor += texture2D(uSampler, vBlurTexCoords[ 2]) * 0.004431848411938341; 17 | } 18 | -------------------------------------------------------------------------------- /src/engine/gfx/filters/crosshatch/CrossHatchFilter.js: -------------------------------------------------------------------------------- 1 | const AbstractFilter = require('../../core/renderers/webgl/filters/AbstractFilter'); 2 | 3 | /** 4 | * A Cross Hatch effect filter. 5 | * 6 | * @class 7 | * @extends AbstractFilter 8 | */ 9 | function CrossHatchFilter() { 10 | AbstractFilter.call(this, 11 | // vertex shader 12 | null, 13 | // fragment shader 14 | require('./crosshatch.frag') 15 | ); 16 | } 17 | 18 | CrossHatchFilter.prototype = Object.create(AbstractFilter.prototype); 19 | CrossHatchFilter.prototype.constructor = CrossHatchFilter; 20 | module.exports = CrossHatchFilter; 21 | -------------------------------------------------------------------------------- /src/engine/loader/middlewares/caching/memory.js: -------------------------------------------------------------------------------- 1 | // a simple in-memory cache for resources 2 | const cache = {}; 3 | 4 | export function memoryMiddlewareFactory() { 5 | return function memoryMiddleware(resource, next) { 6 | // if cached, then set data and complete the resource 7 | if (cache[resource.url]) { 8 | resource.data = cache[resource.url]; 9 | resource.complete(); // marks resource load complete and stops processing before middlewares 10 | } 11 | // if not cached, wait for complete and store it in the cache. 12 | else { 13 | resource.onComplete.once(() => (cache[this.url] = this.data)); 14 | } 15 | 16 | next(); 17 | }; 18 | }; 19 | -------------------------------------------------------------------------------- /src/engine/gfx/filters/twist/twist.frag: -------------------------------------------------------------------------------- 1 | precision mediump float; 2 | 3 | varying vec2 vTextureCoord; 4 | 5 | uniform sampler2D uSampler; 6 | uniform float radius; 7 | uniform float angle; 8 | uniform vec2 offset; 9 | 10 | void main(void) 11 | { 12 | vec2 coord = vTextureCoord - offset; 13 | float dist = length(coord); 14 | 15 | if (dist < radius) 16 | { 17 | float ratio = (radius - dist) / radius; 18 | float angleMod = ratio * ratio * angle; 19 | float s = sin(angleMod); 20 | float c = cos(angleMod); 21 | coord = vec2(coord.x * c - coord.y * s, coord.x * s + coord.y * c); 22 | } 23 | 24 | gl_FragColor = texture2D(uSampler, coord+offset); 25 | } 26 | -------------------------------------------------------------------------------- /src/engine/gfx/core/renderers/webgl/filters/spriteMaskFilter.frag: -------------------------------------------------------------------------------- 1 | precision lowp float; 2 | 3 | varying vec2 vMaskCoord; 4 | varying vec2 vTextureCoord; 5 | varying vec4 vColor; 6 | 7 | uniform sampler2D uSampler; 8 | uniform float alpha; 9 | uniform sampler2D mask; 10 | 11 | void main(void) 12 | { 13 | // check clip! this will stop the mask bleeding out from the edges 14 | vec2 text = abs( vMaskCoord - 0.5 ); 15 | text = step(0.5, text); 16 | float clip = 1.0 - max(text.y, text.x); 17 | vec4 original = texture2D(uSampler, vTextureCoord); 18 | vec4 masky = texture2D(mask, vMaskCoord); 19 | original *= (masky.r * masky.a * alpha * clip); 20 | gl_FragColor = original; 21 | } 22 | -------------------------------------------------------------------------------- /src/engine/gfx/filters/blur/SmartBlurFilter.js: -------------------------------------------------------------------------------- 1 | const AbstractFilter = require('../../core/renderers/webgl/filters/AbstractFilter'); 2 | 3 | /** 4 | * A Smart Blur Filter. 5 | * 6 | * @class 7 | * @extends AbstractFilter 8 | */ 9 | function SmartBlurFilter() { 10 | AbstractFilter.call(this, 11 | // vertex shader 12 | null, 13 | // fragment shader 14 | require('./smartBlur.frag'), 15 | // uniforms 16 | { 17 | delta: { type: 'v2', value: { x: 0.1, y: 0.0 } }, 18 | } 19 | ); 20 | } 21 | 22 | SmartBlurFilter.prototype = Object.create(AbstractFilter.prototype); 23 | SmartBlurFilter.prototype.constructor = SmartBlurFilter; 24 | module.exports = SmartBlurFilter; 25 | -------------------------------------------------------------------------------- /src/engine/gfx/loaders/dirname.js: -------------------------------------------------------------------------------- 1 | // Split a filename into [root, dir, basename, ext], unix version 2 | // 'root' is just a slash, or nothing. 3 | var splitPathRe = 4 | /^(\/?|)([\s\S]*?)((?:\.{1,2}|[^\/]+?|)(\.[^.\/]*|))(?:[\/]*)$/; 5 | var splitPath = function(filename) { 6 | return splitPathRe.exec(filename).slice(1); 7 | }; 8 | 9 | export default (path) => { 10 | var result = splitPath(path), 11 | root = result[0], 12 | dir = result[1]; 13 | 14 | if (!root && !dir) { 15 | // No dirname whatsoever 16 | return '.'; 17 | } 18 | 19 | if (dir) { 20 | // It has a dirname, strip trailing slash 21 | dir = dir.substr(0, dir.length - 1); 22 | } 23 | 24 | return root + dir; 25 | }; 26 | -------------------------------------------------------------------------------- /src/engine/gfx/filters/dot/dotScreen.frag: -------------------------------------------------------------------------------- 1 | precision mediump float; 2 | 3 | varying vec2 vTextureCoord; 4 | varying vec4 vColor; 5 | 6 | uniform vec4 dimensions; 7 | uniform sampler2D uSampler; 8 | 9 | uniform float angle; 10 | uniform float scale; 11 | 12 | float pattern() 13 | { 14 | float s = sin(angle), c = cos(angle); 15 | vec2 tex = vTextureCoord * dimensions.xy; 16 | vec2 point = vec2( 17 | c * tex.x - s * tex.y, 18 | s * tex.x + c * tex.y 19 | ) * scale; 20 | return (sin(point.x) * sin(point.y)) * 4.0; 21 | } 22 | 23 | void main() 24 | { 25 | vec4 color = texture2D(uSampler, vTextureCoord); 26 | float average = (color.r + color.g + color.b) / 3.0; 27 | gl_FragColor = vec4(vec3(average * 10.0 - 5.0 + pattern()), color.a); 28 | } 29 | -------------------------------------------------------------------------------- /src/engine/gfx/accessibility/accessibleTarget.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Default property values of accessible objects 3 | * used by {@link AccessibilityManager}. 4 | * 5 | * @mixin 6 | * @example 7 | * function MyObject() {} 8 | * 9 | * Object.assign( 10 | * MyObject.prototype, 11 | * accessibility.accessibleTarget 12 | * ); 13 | */ 14 | export default { 15 | 16 | /** 17 | * @todo Needs docs. 18 | */ 19 | accessible: false, 20 | 21 | /** 22 | * @todo Needs docs. 23 | */ 24 | accessibleTitle: null, 25 | 26 | /** 27 | * @todo Needs docs. 28 | */ 29 | tabIndex: 0, 30 | 31 | /** 32 | * @todo Needs docs. 33 | */ 34 | _accessibleActive: false, 35 | 36 | /** 37 | * @todo Needs docs. 38 | */ 39 | _accessibleDiv: false, 40 | 41 | }; 42 | -------------------------------------------------------------------------------- /src/engine/utils/array.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Array utility functions. 3 | * 4 | * @module engine/utils/array 5 | */ 6 | 7 | /** 8 | * Remove items of an arry 9 | * 10 | * @param {array} arr The target array 11 | * @param {number} startIdx The index to begin removing from (inclusive) 12 | * @param {number} removeCount How many items to remove 13 | */ 14 | export function removeItems(arr, startIdx, removeCount) { 15 | let i, len, length = arr.length; 16 | 17 | if (startIdx >= length || removeCount === 0) { 18 | return; 19 | } 20 | 21 | removeCount = (startIdx + removeCount > length ? length - startIdx : removeCount); 22 | for (i = startIdx, len = length - removeCount; i < len; ++i) { 23 | arr[i] = arr[i + removeCount]; 24 | } 25 | 26 | arr.length = len; 27 | }; 28 | -------------------------------------------------------------------------------- /src/engine/gfx/filters/shockwave/shockwave.frag: -------------------------------------------------------------------------------- 1 | precision lowp float; 2 | 3 | varying vec2 vTextureCoord; 4 | 5 | uniform sampler2D uSampler; 6 | 7 | uniform vec2 center; 8 | uniform vec3 params; // 10.0, 0.8, 0.1 9 | uniform float time; 10 | 11 | void main() 12 | { 13 | vec2 uv = vTextureCoord; 14 | vec2 texCoord = uv; 15 | 16 | float dist = distance(uv, center); 17 | 18 | if ( (dist <= (time + params.z)) && (dist >= (time - params.z)) ) 19 | { 20 | float diff = (dist - time); 21 | float powDiff = 1.0 - pow(abs(diff*params.x), params.y); 22 | 23 | float diffTime = diff * powDiff; 24 | vec2 diffUV = normalize(uv - center); 25 | texCoord = uv + (diffUV * diffTime); 26 | } 27 | 28 | gl_FragColor = texture2D(uSampler, texCoord); 29 | } 30 | -------------------------------------------------------------------------------- /src/engine/gfx/filters/blur/blur.frag: -------------------------------------------------------------------------------- 1 | precision lowp float; 2 | 3 | varying vec2 vTextureCoord; 4 | varying vec2 vBlurTexCoords[6]; 5 | varying vec4 vColor; 6 | 7 | uniform sampler2D uSampler; 8 | 9 | void main(void) 10 | { 11 | gl_FragColor = vec4(0.0); 12 | 13 | gl_FragColor += texture2D(uSampler, vBlurTexCoords[ 0])*0.004431848411938341; 14 | gl_FragColor += texture2D(uSampler, vBlurTexCoords[ 1])*0.05399096651318985; 15 | gl_FragColor += texture2D(uSampler, vBlurTexCoords[ 2])*0.2419707245191454; 16 | gl_FragColor += texture2D(uSampler, vTextureCoord )*0.3989422804014327; 17 | gl_FragColor += texture2D(uSampler, vBlurTexCoords[ 3])*0.2419707245191454; 18 | gl_FragColor += texture2D(uSampler, vBlurTexCoords[ 4])*0.05399096651318985; 19 | gl_FragColor += texture2D(uSampler, vBlurTexCoords[ 5])*0.004431848411938341; 20 | } 21 | -------------------------------------------------------------------------------- /src/engine/gfx/filters/blur/blurDir.vert: -------------------------------------------------------------------------------- 1 | attribute vec2 aVertexPosition; 2 | attribute vec2 aTextureCoord; 3 | attribute vec4 aColor; 4 | 5 | uniform float strength; 6 | uniform float dirX; 7 | uniform float dirY; 8 | uniform mat3 projectionMatrix; 9 | 10 | varying vec2 vTextureCoord; 11 | varying vec4 vColor; 12 | varying vec2 vBlurTexCoords[3]; 13 | 14 | void main(void) 15 | { 16 | gl_Position = vec4((projectionMatrix * vec3((aVertexPosition), 1.0)).xy, 0.0, 1.0); 17 | vTextureCoord = aTextureCoord; 18 | 19 | vBlurTexCoords[0] = aTextureCoord + vec2( (0.004 * strength) * dirX, (0.004 * strength) * dirY ); 20 | vBlurTexCoords[1] = aTextureCoord + vec2( (0.008 * strength) * dirX, (0.008 * strength) * dirY ); 21 | vBlurTexCoords[2] = aTextureCoord + vec2( (0.012 * strength) * dirX, (0.012 * strength) * dirY ); 22 | 23 | vColor = vec4(aColor.rgb * aColor.a, aColor.a); 24 | } 25 | -------------------------------------------------------------------------------- /src/engine/gfx/interaction/interactiveTarget.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Default property values of interactive objects 3 | * used by {@link interaction.InteractionManager}. 4 | * 5 | * @mixin 6 | * @example 7 | * function MyObject() {} 8 | * 9 | * Object.assign( 10 | * MyObject.prototype, 11 | * interaction.interactiveTarget 12 | * ); 13 | */ 14 | export default { 15 | /** 16 | * @todo Needs docs. 17 | */ 18 | interactive: false, 19 | /** 20 | * @todo Needs docs. 21 | */ 22 | buttonMode: false, 23 | /** 24 | * @todo Needs docs. 25 | */ 26 | interactiveChildren: true, 27 | /** 28 | * @todo Needs docs. 29 | */ 30 | defaultCursor: 'pointer', 31 | 32 | // some internal checks.. 33 | 34 | /** 35 | * @todo Needs docs. 36 | * @private 37 | */ 38 | _over: false, 39 | /** 40 | * @todo Needs docs. 41 | * @private 42 | */ 43 | _touchDown: false, 44 | }; 45 | -------------------------------------------------------------------------------- /src/engine/storage/index.js: -------------------------------------------------------------------------------- 1 | import storage from './storage'; 2 | 3 | import Data from './Data'; 4 | import PersistentData from './PersistentData'; 5 | 6 | /** 7 | * Session storage. 8 | * @type {Data} 9 | */ 10 | export const session = new Data(); 11 | /** 12 | * Persistent storage. 13 | * @type {Data} 14 | */ 15 | export const persistent = new PersistentData(); 16 | 17 | /** 18 | * Storage module provides functionalities to manage saving data. There 19 | * is a `session` storage for session data store and change event dispatching, 20 | * and a `persistent` storage for persistent data store and change event dispatching. 21 | * 22 | * Config: 23 | * - `storage.id`: namespace to save persistent data to 24 | * 25 | * @module engine/storage 26 | * 27 | * @requires engine/storage/storage 28 | * @requires engine/storage/data 29 | * @requires engine/storage/persistent-data 30 | */ 31 | export default storage; 32 | -------------------------------------------------------------------------------- /src/engine/gfx/core/renderers/webgl/managers/WebGLManager.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @class 3 | * @param renderer {WebGLRenderer} The renderer this manager works for. 4 | */ 5 | export default function WebGLManager(renderer) { 6 | /** 7 | * The renderer this manager works for. 8 | * 9 | * @member {WebGLRenderer} 10 | */ 11 | this.renderer = renderer; 12 | 13 | this.renderer.on('context', this.onContextChange, this); 14 | } 15 | 16 | WebGLManager.prototype.constructor = WebGLManager; 17 | 18 | /** 19 | * Generic method called when there is a WebGL context change. 20 | */ 21 | WebGLManager.prototype.onContextChange = function() { 22 | // do some codes init! 23 | }; 24 | 25 | /** 26 | * Generic destroy methods to be overridden by the subclass 27 | */ 28 | WebGLManager.prototype.destroy = function() { 29 | this.renderer.off('context', this.onContextChange, this); 30 | 31 | this.renderer = null; 32 | }; 33 | -------------------------------------------------------------------------------- /src/engine/polyfill/Number.isFinite.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | /** 4 | * Number.isFinite 5 | * Copyright (c) 2014 marlun78 6 | * MIT License, https://gist.github.com/marlun78/bd0800cf5e8053ba9f83 7 | * 8 | * Spec: http://people.mozilla.org/~jorendorff/es6-draft.html#sec-number.isfinite 9 | */ 10 | if (typeof(Number.isFinite) !== 'function') { 11 | /** 12 | * `Number.isFinite` polyfill 13 | * @param {*} value Value to check 14 | * @return {Boolean} Whether the value is a finite number 15 | */ 16 | Number.isFinite = function(value) { 17 | // 1. If Type(number) is not Number, return false. 18 | if (typeof(value) !== 'number') { 19 | return false; 20 | } 21 | // 2. If number is NaN, +∞, or −∞, return false. 22 | if (value !== value || value === Infinity || value === -Infinity) { 23 | return false; 24 | } 25 | // 3. Otherwise, return true. 26 | return true; 27 | }; 28 | } 29 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing to LesserPanda 2 | 3 | LesserPanda welcomes contribution from everyone. Here's some guidelines 4 | if you are thinking of helping us. 5 | 6 | ## Contrinutions 7 | 8 | The development of LesserPanda is using Git-Flow, please always send PR 9 | to `develop` branch. 10 | 11 | ### Branches 12 | 13 | - master: Stable 14 | - develop: Branch for development(latest) 15 | - feature/*: Branch for a specific feature, will be merged into `develop` while finished 16 | - doc: API doc branch 17 | 18 | ### Internal Documentation 19 | 20 | TODO: 21 | 22 | - Some rules to generate better API documentations 23 | 24 | ### Issues 25 | 26 | ## Pull Request 27 | 28 | TODO: 29 | 30 | Make sure code style matches, check `.eslintrc` for more information. 31 | Run `eslint --fix --quiet src/engine/**/*.js` to test. 32 | 33 | - How to create PR 34 | 35 | ## Communication 36 | 37 | TODO: 38 | 39 | - Create a Slack channel 40 | -------------------------------------------------------------------------------- /src/engine/gfx/filters/blur/smartBlur.frag: -------------------------------------------------------------------------------- 1 | precision mediump float; 2 | 3 | varying vec2 vTextureCoord; 4 | 5 | uniform sampler2D uSampler; 6 | uniform vec2 delta; 7 | 8 | float random(vec3 scale, float seed) 9 | { 10 | return fract(sin(dot(gl_FragCoord.xyz + seed, scale)) * 43758.5453 + seed); 11 | } 12 | 13 | void main(void) 14 | { 15 | vec4 color = vec4(0.0); 16 | float total = 0.0; 17 | 18 | float offset = random(vec3(12.9898, 78.233, 151.7182), 0.0); 19 | 20 | for (float t = -30.0; t <= 30.0; t++) 21 | { 22 | float percent = (t + offset - 0.5) / 30.0; 23 | float weight = 1.0 - abs(percent); 24 | vec4 sample = texture2D(uSampler, vTextureCoord + delta * percent); 25 | sample.rgb *= sample.a; 26 | color += sample * weight; 27 | total += weight; 28 | } 29 | 30 | gl_FragColor = color / total; 31 | gl_FragColor.rgb /= gl_FragColor.a + 0.00001; 32 | } 33 | -------------------------------------------------------------------------------- /src/engine/gfx/filters/dropshadow/blurYTint.frag: -------------------------------------------------------------------------------- 1 | precision lowp float; 2 | 3 | varying vec2 vTextureCoord; 4 | varying vec2 vBlurTexCoords[6]; 5 | varying vec4 vColor; 6 | 7 | uniform vec3 color; 8 | uniform float alpha; 9 | 10 | uniform sampler2D uSampler; 11 | 12 | void main(void) 13 | { 14 | vec4 sum = vec4(0.0); 15 | 16 | sum += texture2D(uSampler, vBlurTexCoords[ 0])*0.004431848411938341; 17 | sum += texture2D(uSampler, vBlurTexCoords[ 1])*0.05399096651318985; 18 | sum += texture2D(uSampler, vBlurTexCoords[ 2])*0.2419707245191454; 19 | sum += texture2D(uSampler, vTextureCoord )*0.3989422804014327; 20 | sum += texture2D(uSampler, vBlurTexCoords[ 3])*0.2419707245191454; 21 | sum += texture2D(uSampler, vBlurTexCoords[ 4])*0.05399096651318985; 22 | sum += texture2D(uSampler, vBlurTexCoords[ 5])*0.004431848411938341; 23 | 24 | gl_FragColor = vec4( color.rgb * sum.a * alpha, sum.a * alpha ); 25 | } 26 | -------------------------------------------------------------------------------- /src/engine/gfx/filters/blur/blurX.vert: -------------------------------------------------------------------------------- 1 | attribute vec2 aVertexPosition; 2 | attribute vec2 aTextureCoord; 3 | attribute vec4 aColor; 4 | 5 | uniform float strength; 6 | uniform mat3 projectionMatrix; 7 | 8 | varying vec2 vTextureCoord; 9 | varying vec4 vColor; 10 | varying vec2 vBlurTexCoords[6]; 11 | 12 | void main(void) 13 | { 14 | gl_Position = vec4((projectionMatrix * vec3((aVertexPosition), 1.0)).xy, 0.0, 1.0); 15 | vTextureCoord = aTextureCoord; 16 | 17 | vBlurTexCoords[ 0] = aTextureCoord + vec2(-0.012 * strength, 0.0); 18 | vBlurTexCoords[ 1] = aTextureCoord + vec2(-0.008 * strength, 0.0); 19 | vBlurTexCoords[ 2] = aTextureCoord + vec2(-0.004 * strength, 0.0); 20 | vBlurTexCoords[ 3] = aTextureCoord + vec2( 0.004 * strength, 0.0); 21 | vBlurTexCoords[ 4] = aTextureCoord + vec2( 0.008 * strength, 0.0); 22 | vBlurTexCoords[ 5] = aTextureCoord + vec2( 0.012 * strength, 0.0); 23 | 24 | vColor = vec4(aColor.rgb * aColor.a, aColor.a); 25 | } 26 | -------------------------------------------------------------------------------- /src/engine/gfx/filters/blur/blurY.vert: -------------------------------------------------------------------------------- 1 | attribute vec2 aVertexPosition; 2 | attribute vec2 aTextureCoord; 3 | attribute vec4 aColor; 4 | 5 | uniform float strength; 6 | uniform mat3 projectionMatrix; 7 | 8 | varying vec2 vTextureCoord; 9 | varying vec4 vColor; 10 | varying vec2 vBlurTexCoords[6]; 11 | 12 | void main(void) 13 | { 14 | gl_Position = vec4((projectionMatrix * vec3((aVertexPosition), 1.0)).xy, 0.0, 1.0); 15 | vTextureCoord = aTextureCoord; 16 | 17 | vBlurTexCoords[ 0] = aTextureCoord + vec2(0.0, -0.012 * strength); 18 | vBlurTexCoords[ 1] = aTextureCoord + vec2(0.0, -0.008 * strength); 19 | vBlurTexCoords[ 2] = aTextureCoord + vec2(0.0, -0.004 * strength); 20 | vBlurTexCoords[ 3] = aTextureCoord + vec2(0.0, 0.004 * strength); 21 | vBlurTexCoords[ 4] = aTextureCoord + vec2(0.0, 0.008 * strength); 22 | vBlurTexCoords[ 5] = aTextureCoord + vec2(0.0, 0.012 * strength); 23 | 24 | vColor = vec4(aColor.rgb * aColor.a, aColor.a); 25 | } 26 | -------------------------------------------------------------------------------- /src/engine/gfx/interaction/index.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @author Mat Groves 3 | * @copyright 2013-2015 GoodBoyDigital 4 | * @license {@link https://github.com/pixijs/pixi.js/blob/master/LICENSE|MIT License} 5 | */ 6 | 7 | // Mix interactiveTarget into Node.prototype 8 | import Node from '../core/Node'; 9 | import interactiveTarget from './interactiveTarget'; 10 | Object.assign(Node.prototype, interactiveTarget); 11 | 12 | // Register as renderer plugin 13 | import InteractionManager from './InteractionManager'; 14 | import WebGLRenderer from '../core/renderers/webgl/WebGLRenderer'; 15 | import CanvasRenderer from '../core/renderers/canvas/CanvasRenderer'; 16 | WebGLRenderer.registerPlugin('interaction', InteractionManager); 17 | CanvasRenderer.registerPlugin('interaction', InteractionManager); 18 | 19 | import InteractionData from './InteractionData'; 20 | 21 | export { 22 | InteractionData, 23 | InteractionManager, 24 | interactiveTarget, 25 | }; 26 | -------------------------------------------------------------------------------- /src/engine/gfx/loaders/textureParser.js: -------------------------------------------------------------------------------- 1 | import Texture from '../core/textures/Texture'; 2 | import BaseTexture from '../core/textures/BaseTexture'; 3 | import { getResolutionOfUrl, BaseTextureCache, TextureCache } from '../core/utils'; 4 | import loader from 'engine/loader'; 5 | import { Resource } from 'engine/loader'; 6 | 7 | export default () => { 8 | return function(resource, next) { 9 | // create a new texture if the data is an Image object 10 | if (resource.data && (resource.type === Resource.TYPE.IMAGE)) { 11 | var baseTexture = new BaseTexture(resource.data, null, getResolutionOfUrl(resource.url)); 12 | baseTexture.imageUrl = resource.url; 13 | resource.texture = new Texture(baseTexture); 14 | // lets also add the frame to pixi's global cache for fromFrame and fromImage fucntions 15 | BaseTextureCache[resource.url] = baseTexture; 16 | TextureCache[resource.url] = resource.texture; 17 | } 18 | 19 | next(); 20 | }; 21 | }; 22 | -------------------------------------------------------------------------------- /src/behaviors/FaceTheMouse.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Let the actor always face to the mouse 3 | */ 4 | 5 | const Behavior = require('engine/Behavior'); 6 | require('engine/gfx/interaction'); 7 | 8 | class FaceTheMouse extends Behavior { 9 | constructor() { 10 | super(); 11 | 12 | this.type = 'FaceTheMouse'; 13 | 14 | this.mousePos = null; 15 | this.posCache = null; 16 | 17 | this.rotation = 0; 18 | } 19 | init(ent) { 20 | super.init(ent); 21 | 22 | this.entity.canEverTick = true; 23 | } 24 | update(_, dt) { 25 | if (!this.mousePos) { 26 | this.mousePos = this.entity.game.sysGfx.mouse; 27 | this.posCache = this.mousePos.clone(); 28 | } 29 | 30 | this.rotation = this.posCache.copy(this.mousePos) 31 | .subtract(this.entity.position) 32 | .angle(); 33 | 34 | if (this.entity.gfx) { 35 | this.entity.gfx.rotation = this.rotation; 36 | } 37 | } 38 | } 39 | 40 | Behavior.register('FaceTheMouse', FaceTheMouse); 41 | 42 | module.exports = FaceTheMouse; 43 | -------------------------------------------------------------------------------- /src/engine/gfx/filters/dropshadow/blurYTint.vert: -------------------------------------------------------------------------------- 1 | attribute vec2 aVertexPosition; 2 | attribute vec2 aTextureCoord; 3 | attribute vec4 aColor; 4 | 5 | uniform float strength; 6 | uniform vec2 offset; 7 | 8 | uniform mat3 projectionMatrix; 9 | 10 | varying vec2 vTextureCoord; 11 | varying vec4 vColor; 12 | varying vec2 vBlurTexCoords[6]; 13 | 14 | void main(void) 15 | { 16 | gl_Position = vec4((projectionMatrix * vec3((aVertexPosition+offset), 1.0)).xy, 0.0, 1.0); 17 | vTextureCoord = aTextureCoord; 18 | 19 | vBlurTexCoords[ 0] = aTextureCoord + vec2(0.0, -0.012 * strength); 20 | vBlurTexCoords[ 1] = aTextureCoord + vec2(0.0, -0.008 * strength); 21 | vBlurTexCoords[ 2] = aTextureCoord + vec2(0.0, -0.004 * strength); 22 | vBlurTexCoords[ 3] = aTextureCoord + vec2(0.0, 0.004 * strength); 23 | vBlurTexCoords[ 4] = aTextureCoord + vec2(0.0, 0.008 * strength); 24 | vBlurTexCoords[ 5] = aTextureCoord + vec2(0.0, 0.012 * strength); 25 | 26 | vColor = vec4(aColor.rgb * aColor.a, aColor.a); 27 | } 28 | -------------------------------------------------------------------------------- /src/behaviors/RotateAroundPoint.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Let the entity always move around a point 3 | */ 4 | 5 | const Behavior = require('engine/Behavior'); 6 | const Vector = require('engine/Vector'); 7 | 8 | const DefaultSettings = { 9 | Center: Vector.create(), 10 | CCW: false, 11 | Speed: Math.PI, 12 | }; 13 | 14 | class RotateAroundPoint extends Behavior { 15 | constructor() { 16 | super(); 17 | 18 | this.type = 'RotateAroundPoint'; 19 | 20 | this.Center = Vector.create(); 21 | this.rotation = 0; 22 | } 23 | init(ent, settings) { 24 | super.init(ent); 25 | 26 | Object.assign(this, DefaultSettings, settings); 27 | 28 | this.entity.canFixedUpdate = true; 29 | 30 | this.radius = this.entity.position.distance(this.Center); 31 | } 32 | fixedUpdate(_, dt) { 33 | this.rotation += (dt * this.Speed); 34 | 35 | this.entity.position.set(this.Cadius, 0) 36 | .rotate(this.rotation) 37 | .add(this.Center); 38 | } 39 | } 40 | 41 | Behavior.register('RotateAroundPoint', RotateAroundPoint); 42 | 43 | module.exports = RotateAroundPoint; 44 | -------------------------------------------------------------------------------- /src/engine/gfx/filters/tiltshift/TiltShiftXFilter.js: -------------------------------------------------------------------------------- 1 | const TiltShiftAxisFilter = require('./TiltShiftAxisFilter'); 2 | 3 | /** 4 | * @author Vico @vicocotea 5 | * original filter https://github.com/evanw/glfx.js/blob/master/src/filters/blur/tiltshift.js by Evan Wallace : http://madebyevan.com/ 6 | */ 7 | 8 | /** 9 | * A TiltShiftXFilter. 10 | * 11 | * @class 12 | * @extends TiltShiftAxisFilter 13 | */ 14 | function TiltShiftXFilter() { 15 | TiltShiftAxisFilter.call(this); 16 | } 17 | 18 | TiltShiftXFilter.prototype = Object.create(TiltShiftAxisFilter.prototype); 19 | TiltShiftXFilter.prototype.constructor = TiltShiftXFilter; 20 | module.exports = TiltShiftXFilter; 21 | 22 | /** 23 | * Updates the filter delta values. 24 | * 25 | */ 26 | TiltShiftXFilter.prototype.updateDelta = function() { 27 | var dx = this.uniforms.end.value.x - this.uniforms.start.value.x; 28 | var dy = this.uniforms.end.value.y - this.uniforms.start.value.y; 29 | var d = Math.sqrt(dx * dx + dy * dy); 30 | 31 | this.uniforms.delta.value.x = dx / d; 32 | this.uniforms.delta.value.y = dy / d; 33 | }; 34 | -------------------------------------------------------------------------------- /src/engine/gfx/filters/tiltshift/TiltShiftYFilter.js: -------------------------------------------------------------------------------- 1 | const TiltShiftAxisFilter = require('./TiltShiftAxisFilter'); 2 | 3 | /** 4 | * @author Vico @vicocotea 5 | * original filter https://github.com/evanw/glfx.js/blob/master/src/filters/blur/tiltshift.js by Evan Wallace : http://madebyevan.com/ 6 | */ 7 | 8 | /** 9 | * A TiltShiftYFilter. 10 | * 11 | * @class 12 | * @extends TiltShiftAxisFilter 13 | */ 14 | function TiltShiftYFilter() { 15 | TiltShiftAxisFilter.call(this); 16 | } 17 | 18 | TiltShiftYFilter.prototype = Object.create(TiltShiftAxisFilter.prototype); 19 | TiltShiftYFilter.prototype.constructor = TiltShiftYFilter; 20 | module.exports = TiltShiftYFilter; 21 | 22 | /** 23 | * Updates the filter delta values. 24 | * 25 | */ 26 | TiltShiftYFilter.prototype.updateDelta = function() { 27 | var dx = this.uniforms.end.value.x - this.uniforms.start.value.x; 28 | var dy = this.uniforms.end.value.y - this.uniforms.start.value.y; 29 | var d = Math.sqrt(dx * dx + dy * dy); 30 | 31 | this.uniforms.delta.value.x = -dy / d; 32 | this.uniforms.delta.value.y = dx / d; 33 | }; 34 | -------------------------------------------------------------------------------- /src/engine/gfx/filters/crosshatch/crosshatch.frag: -------------------------------------------------------------------------------- 1 | precision mediump float; 2 | 3 | varying vec2 vTextureCoord; 4 | 5 | uniform sampler2D uSampler; 6 | 7 | void main(void) 8 | { 9 | float lum = length(texture2D(uSampler, vTextureCoord.xy).rgb); 10 | 11 | gl_FragColor = vec4(1.0, 1.0, 1.0, 1.0); 12 | 13 | if (lum < 1.00) 14 | { 15 | if (mod(gl_FragCoord.x + gl_FragCoord.y, 10.0) == 0.0) 16 | { 17 | gl_FragColor = vec4(0.0, 0.0, 0.0, 1.0); 18 | } 19 | } 20 | 21 | if (lum < 0.75) 22 | { 23 | if (mod(gl_FragCoord.x - gl_FragCoord.y, 10.0) == 0.0) 24 | { 25 | gl_FragColor = vec4(0.0, 0.0, 0.0, 1.0); 26 | } 27 | } 28 | 29 | if (lum < 0.50) 30 | { 31 | if (mod(gl_FragCoord.x + gl_FragCoord.y - 5.0, 10.0) == 0.0) 32 | { 33 | gl_FragColor = vec4(0.0, 0.0, 0.0, 1.0); 34 | } 35 | } 36 | 37 | if (lum < 0.3) 38 | { 39 | if (mod(gl_FragCoord.x - gl_FragCoord.y - 5.0, 10.0) == 0.0) 40 | { 41 | gl_FragColor = vec4(0.0, 0.0, 0.0, 1.0); 42 | } 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /src/engine/loader/parse-uri.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | export default function(str, opts) { 4 | opts = opts || {}; 5 | 6 | var o = { 7 | key: ['source', 'protocol', 'authority', 'userInfo', 'user', 'password', 'host', 'port', 'relative', 'path', 'directory', 'file', 'query', 'anchor'], 8 | q: { 9 | name: 'queryKey', 10 | parser: /(?:^|&)([^&=]*)=?([^&]*)/g, 11 | }, 12 | parser: { 13 | strict: /^(?:([^:\/?#]+):)?(?:\/\/((?:(([^:@]*)(?::([^:@]*))?)?@)?([^:\/?#]*)(?::(\d*))?))?((((?:[^?#\/]*\/)*)([^?#]*))(?:\?([^#]*))?(?:#(.*))?)/, 14 | loose: /^(?:(?![^:@]+:[^:@\/]*@)([^:\/?#.]+):)?(?:\/\/)?((?:(([^:@]*)(?::([^:@]*))?)?@)?([^:\/?#]*)(?::(\d*))?)(((\/(?:[^?#](?![^?#\/]*\.[^?#\/.]+(?:[?#]|$)))*\/?)?([^?#\/]*))(?:\?([^#]*))?(?:#(.*))?)/, 15 | }, 16 | }; 17 | 18 | var m = o.parser[opts.strictMode ? 'strict' : 'loose'].exec(str); 19 | var uri = {}; 20 | var i = 14; 21 | 22 | while (i--) {uri[o.key[i]] = m[i] || '';} 23 | 24 | uri[o.q.name] = {}; 25 | uri[o.key[12]].replace(o.q.parser, function($0, $1, $2) { 26 | if ($1) {uri[o.q.name][$1] = $2;} 27 | }); 28 | 29 | return uri; 30 | }; 31 | -------------------------------------------------------------------------------- /src/engine/gfx/filters/color/colorMatrix.frag: -------------------------------------------------------------------------------- 1 | precision mediump float; 2 | 3 | varying vec2 vTextureCoord; 4 | uniform sampler2D uSampler; 5 | uniform float m[25]; 6 | 7 | void main(void) 8 | { 9 | 10 | vec4 c = texture2D(uSampler, vTextureCoord); 11 | 12 | gl_FragColor.r = (m[0] * c.r); 13 | gl_FragColor.r += (m[1] * c.g); 14 | gl_FragColor.r += (m[2] * c.b); 15 | gl_FragColor.r += (m[3] * c.a); 16 | gl_FragColor.r += m[4] * c.a; 17 | 18 | gl_FragColor.g = (m[5] * c.r); 19 | gl_FragColor.g += (m[6] * c.g); 20 | gl_FragColor.g += (m[7] * c.b); 21 | gl_FragColor.g += (m[8] * c.a); 22 | gl_FragColor.g += m[9] * c.a; 23 | 24 | gl_FragColor.b = (m[10] * c.r); 25 | gl_FragColor.b += (m[11] * c.g); 26 | gl_FragColor.b += (m[12] * c.b); 27 | gl_FragColor.b += (m[13] * c.a); 28 | gl_FragColor.b += m[14] * c.a; 29 | 30 | gl_FragColor.a = (m[15] * c.r); 31 | gl_FragColor.a += (m[16] * c.g); 32 | gl_FragColor.a += (m[17] * c.b); 33 | gl_FragColor.a += (m[18] * c.a); 34 | gl_FragColor.a += m[19] * c.a; 35 | 36 | } 37 | -------------------------------------------------------------------------------- /src/engine/gfx/core/renderers/webgl/managers/BlendModeManager.js: -------------------------------------------------------------------------------- 1 | import WebGLManager from './WebGLManager'; 2 | 3 | /** 4 | * @class 5 | * @extends WebGlManager 6 | * @param renderer {WebGLRenderer} The renderer this manager works for. 7 | */ 8 | export default function BlendModeManager(renderer) { 9 | WebGLManager.call(this, renderer); 10 | 11 | /** 12 | * @member {number} 13 | */ 14 | this.currentBlendMode = 99999; 15 | } 16 | 17 | BlendModeManager.prototype = Object.create(WebGLManager.prototype); 18 | BlendModeManager.prototype.constructor = BlendModeManager; 19 | 20 | /** 21 | * Sets-up the given blendMode from WebGL's point of view. 22 | * 23 | * @param blendMode {number} the blendMode, should be a Pixi const, such as `PIXI.BLEND_MODES.ADD`. See 24 | * {@link BLEND_MODES} for possible values. 25 | */ 26 | BlendModeManager.prototype.setBlendMode = function(blendMode) { 27 | if (this.currentBlendMode === blendMode) { 28 | return false; 29 | } 30 | 31 | this.currentBlendMode = blendMode; 32 | 33 | var mode = this.renderer.blendModes[this.currentBlendMode]; 34 | this.renderer.gl.blendFunc(mode[0], mode[1]); 35 | 36 | return true; 37 | }; 38 | -------------------------------------------------------------------------------- /src/engine/gfx/filters/gray/GrayFilter.js: -------------------------------------------------------------------------------- 1 | const AbstractFilter = require('../../core/renderers/webgl/filters/AbstractFilter'); 2 | 3 | /** 4 | * This greyscales the palette of your Display Objects. 5 | * 6 | * @class 7 | * @extends AbstractFilter 8 | */ 9 | function GrayFilter() { 10 | AbstractFilter.call(this, 11 | // vertex shader 12 | null, 13 | // fragment shader 14 | require('./gray.frag'), 15 | // set the uniforms 16 | { 17 | gray: { type: '1f', value: 1 }, 18 | } 19 | ); 20 | } 21 | 22 | GrayFilter.prototype = Object.create(AbstractFilter.prototype); 23 | GrayFilter.prototype.constructor = GrayFilter; 24 | module.exports = GrayFilter; 25 | 26 | Object.defineProperties(GrayFilter.prototype, { 27 | /** 28 | * The strength of the gray. 1 will make the object black and white, 0 will make the object its normal color. 29 | * 30 | * @member {number} 31 | * @memberof filters.GrayFilter# 32 | */ 33 | gray: { 34 | get: function() { 35 | return this.uniforms.gray.value; 36 | }, 37 | set: function(value) { 38 | this.uniforms.gray.value = value; 39 | }, 40 | }, 41 | }); 42 | -------------------------------------------------------------------------------- /src/engine/gfx/filters/ascii/ascii.frag: -------------------------------------------------------------------------------- 1 | precision mediump float; 2 | 3 | uniform vec4 dimensions; 4 | uniform float pixelSize; 5 | uniform sampler2D uSampler; 6 | 7 | float character(float n, vec2 p) 8 | { 9 | p = floor(p*vec2(4.0, -4.0) + 2.5); 10 | if (clamp(p.x, 0.0, 4.0) == p.x && clamp(p.y, 0.0, 4.0) == p.y) 11 | { 12 | if (int(mod(n/exp2(p.x + 5.0*p.y), 2.0)) == 1) return 1.0; 13 | } 14 | return 0.0; 15 | } 16 | 17 | void main() 18 | { 19 | vec2 uv = gl_FragCoord.xy; 20 | 21 | vec3 col = texture2D(uSampler, floor( uv / pixelSize ) * pixelSize / dimensions.xy).rgb; 22 | 23 | float gray = (col.r + col.g + col.b) / 3.0; 24 | 25 | float n = 65536.0; // . 26 | if (gray > 0.2) n = 65600.0; // : 27 | if (gray > 0.3) n = 332772.0; // * 28 | if (gray > 0.4) n = 15255086.0; // o 29 | if (gray > 0.5) n = 23385164.0; // & 30 | if (gray > 0.6) n = 15252014.0; // 8 31 | if (gray > 0.7) n = 13199452.0; // @ 32 | if (gray > 0.8) n = 11512810.0; // # 33 | 34 | vec2 p = mod( uv / ( pixelSize * 0.5 ), 2.0) - vec2(1.0); 35 | col = col * character(n, p); 36 | 37 | gl_FragColor = vec4(col, 1.0); 38 | } 39 | -------------------------------------------------------------------------------- /src/engine/utils/poolable.js: -------------------------------------------------------------------------------- 1 | import core from 'engine/core'; 2 | 3 | const EmptySettings = {}; 4 | 5 | /** 6 | * Add ability of object pooling to a class(function). 7 | * @param {function} ctor Target class(constructor). 8 | * @param {number} preAllocSize How many instances to alloc at the beginning. 9 | * @return {function} Class itself for chaining. 10 | */ 11 | export default function(ctor, preAllocSize = 20) { 12 | // Mark as poolabled 13 | ctor.canBePooled = true; 14 | 15 | // Pre-allocate instances when resources are loaded 16 | core.once('ready', function() { 17 | ctor.pool = Array(preAllocSize); 18 | for (let i = 0; i < preAllocSize; i++) { 19 | ctor.pool[i] = new ctor(0, 0, EmptySettings); 20 | } 21 | }); 22 | 23 | // Get an initialized instance 24 | ctor.create = function(x, y, s) { 25 | let a = this.pool.pop(); 26 | if (!a) { 27 | a = new this(x, y, s); 28 | } 29 | a.CTOR = ctor; 30 | a.isRemoved = false; 31 | a.init(x, y, s); 32 | return a; 33 | }; 34 | 35 | // Recycle for later use 36 | ctor.recycle = function(s) { 37 | this.pool.push(s); 38 | }; 39 | 40 | return ctor; 41 | }; 42 | -------------------------------------------------------------------------------- /src/engine/gfx/filters/invert/InvertFilter.js: -------------------------------------------------------------------------------- 1 | const AbstractFilter = require('../../core/renderers/webgl/filters/AbstractFilter'); 2 | 3 | /** 4 | * This inverts your Display Objects colors. 5 | * 6 | * @class 7 | * @extends AbstractFilter 8 | */ 9 | function InvertFilter() { 10 | AbstractFilter.call(this, 11 | // vertex shader 12 | null, 13 | // fragment shader 14 | require('./invert.frag'), 15 | // custom uniforms 16 | { 17 | invert: { type: '1f', value: 1 }, 18 | } 19 | ); 20 | } 21 | 22 | InvertFilter.prototype = Object.create(AbstractFilter.prototype); 23 | InvertFilter.prototype.constructor = InvertFilter; 24 | module.exports = InvertFilter; 25 | 26 | Object.defineProperties(InvertFilter.prototype, { 27 | /** 28 | * The strength of the invert. `1` will fully invert the colors, and 29 | * `0` will make the object its normal color. 30 | * 31 | * @member {number} 32 | * @memberof filters.InvertFilter# 33 | */ 34 | invert: { 35 | get: function() { 36 | return this.uniforms.invert.value; 37 | }, 38 | set: function(value) { 39 | this.uniforms.invert.value = value; 40 | }, 41 | }, 42 | }); 43 | -------------------------------------------------------------------------------- /src/engine/gfx/filters/sepia/SepiaFilter.js: -------------------------------------------------------------------------------- 1 | const AbstractFilter = require('../../core/renderers/webgl/filters/AbstractFilter'); 2 | 3 | /** 4 | * This applies a sepia effect to your Display Objects. 5 | * 6 | * @class 7 | * @extends AbstractFilter 8 | */ 9 | function SepiaFilter() { 10 | AbstractFilter.call(this, 11 | // vertex shader 12 | null, 13 | // fragment shader 14 | require('./sepia.frag'), 15 | // custom uniforms 16 | { 17 | sepia: { type: '1f', value: 1 }, 18 | } 19 | ); 20 | } 21 | 22 | SepiaFilter.prototype = Object.create(AbstractFilter.prototype); 23 | SepiaFilter.prototype.constructor = SepiaFilter; 24 | module.exports = SepiaFilter; 25 | 26 | Object.defineProperties(SepiaFilter.prototype, { 27 | /** 28 | * The strength of the sepia. `1` will apply the full sepia effect, and 29 | * `0` will make the object its normal color. 30 | * 31 | * @member {number} 32 | * @memberof filters.SepiaFilter# 33 | */ 34 | sepia: { 35 | get: function() { 36 | return this.uniforms.sepia.value; 37 | }, 38 | set: function(value) { 39 | this.uniforms.sepia.value = value; 40 | }, 41 | }, 42 | }); 43 | -------------------------------------------------------------------------------- /src/engine/gfx/filters/color/ColorStepFilter.js: -------------------------------------------------------------------------------- 1 | const AbstractFilter = require('../../core/renderers/webgl/filters/AbstractFilter'); 2 | 3 | /** 4 | * This lowers the color depth of your image by the given amount, producing an image with a smaller palette. 5 | * 6 | * @class 7 | * @extends AbstractFilter 8 | */ 9 | function ColorStepFilter() { 10 | AbstractFilter.call(this, 11 | // vertex shader 12 | null, 13 | // fragment shader 14 | require('./colorStep.frag'), 15 | // custom uniforms 16 | { 17 | step: { type: '1f', value: 5 }, 18 | } 19 | ); 20 | } 21 | 22 | ColorStepFilter.prototype = Object.create(AbstractFilter.prototype); 23 | ColorStepFilter.prototype.constructor = ColorStepFilter; 24 | module.exports = ColorStepFilter; 25 | 26 | Object.defineProperties(ColorStepFilter.prototype, { 27 | /** 28 | * The number of steps to reduce the palette by. 29 | * 30 | * @member {number} 31 | * @memberof filters.ColorStepFilter# 32 | */ 33 | step: { 34 | get: function() { 35 | return this.uniforms.step.value; 36 | }, 37 | set: function(value) { 38 | this.uniforms.step.value = value; 39 | }, 40 | }, 41 | }); 42 | -------------------------------------------------------------------------------- /src/engine/utils/color.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Color utility functions. 3 | * 4 | * @module engine/utils/color 5 | */ 6 | 7 | /** 8 | * Converts a hex color number to an [R, G, B] array 9 | * 10 | * @param {number} hex Color hex number 11 | * @param {number[]} [out=[]] Output 12 | * @return {number[]} An array representing the [R, G, B] of the color. 13 | */ 14 | export function hex2rgb(hex, out) { 15 | out = out || []; 16 | 17 | out[0] = (hex >> 16 & 0xFF) / 255; 18 | out[1] = (hex >> 8 & 0xFF) / 255; 19 | out[2] = (hex & 0xFF) / 255; 20 | 21 | return out; 22 | }; 23 | 24 | /** 25 | * Converts a hex color number to a string. 26 | * 27 | * @param {number} hex Color hex number 28 | * @return {string} The color string. 29 | */ 30 | export function hex2string(hex) { 31 | hex = hex.toString(16); 32 | hex = '000000'.substr(0, 6 - hex.length) + hex; 33 | 34 | return '#' + hex; 35 | }; 36 | 37 | /** 38 | * Converts a color as an [R, G, B] array to a hex number 39 | * 40 | * @param {number[]} rgb List of numbers representing a color. 41 | * @return {number} The color number in hex. 42 | */ 43 | export function rgb2hex(rgb) { 44 | return ((rgb[0] * 255 << 16) + (rgb[1] * 255 << 8) + rgb[2] * 255); 45 | }; 46 | -------------------------------------------------------------------------------- /src/engine/gfx/Node.js: -------------------------------------------------------------------------------- 1 | import Node from './core/Node'; 2 | import { BLEND_MODES } from './const'; 3 | 4 | /** 5 | * Factory function for `Node`. 6 | * 7 | * @param {object} data Data to create the instance from 8 | * @return {Node} Node instance 9 | */ 10 | export default function(data) { 11 | const inst = new Node(); 12 | 13 | for (let k in data) { 14 | switch (k) { 15 | // Directly set 16 | // - Node 17 | case 'alpha': 18 | case 'width': 19 | case 'height': 20 | case 'rotation': 21 | case 'visible': 22 | case 'x': 23 | case 'y': 24 | case 'interactive': 25 | inst[k] = data[k]; 26 | break; 27 | 28 | // Set vector 29 | // - Node 30 | case 'pivot': 31 | case 'position': 32 | case 'skew': 33 | inst[k].x = data[k].x || 0; 34 | inst[k].y = data[k].y || 0; 35 | break; 36 | 37 | // - Node 38 | case 'scale': 39 | inst[k].x = data[k].x || 1; 40 | inst[k].y = data[k].y || 1; 41 | break; 42 | 43 | // Set blend mode 44 | case 'blendMode': 45 | inst.blendMode = BLEND_MODES[data[k]]; 46 | break; 47 | } 48 | } 49 | 50 | return inst; 51 | }; 52 | -------------------------------------------------------------------------------- /src/engine/gfx/core/renderers/webgl/utils/ObjectRenderer.js: -------------------------------------------------------------------------------- 1 | import WebGLManager from '../managers/WebGLManager'; 2 | 3 | /** 4 | * Base for a common object renderer that can be used as a system renderer plugin. 5 | * 6 | * @class 7 | * @extends WebGLManager 8 | * @param renderer {WebGLRenderer} The renderer this object renderer works for. 9 | */ 10 | export default function ObjectRenderer(renderer) { 11 | WebGLManager.call(this, renderer); 12 | } 13 | 14 | 15 | ObjectRenderer.prototype = Object.create(WebGLManager.prototype); 16 | ObjectRenderer.prototype.constructor = ObjectRenderer; 17 | 18 | /** 19 | * Starts the renderer and sets the shader 20 | * 21 | */ 22 | ObjectRenderer.prototype.start = function() { 23 | // set the shader.. 24 | }; 25 | 26 | /** 27 | * Stops the renderer 28 | * 29 | */ 30 | ObjectRenderer.prototype.stop = function() { 31 | this.flush(); 32 | }; 33 | 34 | /** 35 | * flushes 36 | * 37 | */ 38 | ObjectRenderer.prototype.flush = function() { 39 | // flush! 40 | }; 41 | 42 | /** 43 | * Renders an object 44 | * 45 | * @param object {DisplayObject} The object to render. 46 | */ 47 | ObjectRenderer.prototype.render = function(object) { /* eslint no-unused-vars:0*/ 48 | // render the object 49 | }; 50 | -------------------------------------------------------------------------------- /src/engine/anime/utils.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Utilty functions for animation module. 3 | * @module engine/anime/utils 4 | */ 5 | 6 | /** 7 | * Get the real target and its property key from 8 | * a root context and full path of the target property. 9 | * @param {object} context Root context that contains the target 10 | * @param {string} fullPath Full path of the target property 11 | * @return {array} [target, key] or undefined if no property matches 12 | */ 13 | export function getTargetAndKey(context, fullPath) { 14 | let path = fullPath.split('.'); 15 | // Path is just the property key 16 | if (path.length === 1) { 17 | return [context, fullPath]; 18 | } 19 | else { 20 | let target = context; 21 | for (let i = 0; i < path.length - 1; i++) { 22 | if (target.hasOwnProperty(path[i])) { 23 | target = target[path[i]]; 24 | } 25 | else { 26 | console.log(`[Warning]: anim target "${path[i]}" not found`); 27 | return undefined; 28 | } 29 | } 30 | 31 | if (!target.hasOwnProperty(path[path.length - 1])) { 32 | console.log(`[Warning]: anim target "${path[path.length - 1]}" not found`); 33 | return undefined; 34 | } 35 | return [target, path[path.length - 1]]; 36 | } 37 | }; 38 | -------------------------------------------------------------------------------- /src/engine/gfx/filters/noise/NoiseFilter.js: -------------------------------------------------------------------------------- 1 | const AbstractFilter = require('../../core/renderers/webgl/filters/AbstractFilter'); 2 | 3 | /** 4 | * @author Vico @vicocotea 5 | * original filter: https://github.com/evanw/glfx.js/blob/master/src/filters/adjust/noise.js 6 | */ 7 | 8 | /** 9 | * A Noise effect filter. 10 | * 11 | * @class 12 | * @extends AbstractFilter 13 | */ 14 | function NoiseFilter() { 15 | AbstractFilter.call(this, 16 | // vertex shader 17 | null, 18 | // fragment shader 19 | require('./noise.frag'), 20 | // custom uniforms 21 | { 22 | noise: { type: '1f', value: 0.5 }, 23 | } 24 | ); 25 | } 26 | 27 | NoiseFilter.prototype = Object.create(AbstractFilter.prototype); 28 | NoiseFilter.prototype.constructor = NoiseFilter; 29 | module.exports = NoiseFilter; 30 | 31 | Object.defineProperties(NoiseFilter.prototype, { 32 | /** 33 | * The amount of noise to apply. 34 | * 35 | * @member {number} 36 | * @memberof filters.NoiseFilter# 37 | * @default 0.5 38 | */ 39 | noise: { 40 | get: function() { 41 | return this.uniforms.noise.value; 42 | }, 43 | set: function(value) { 44 | this.uniforms.noise.value = value; 45 | }, 46 | }, 47 | }); 48 | -------------------------------------------------------------------------------- /src/engine/gfx/filters/tiltshift/tiltShift.frag: -------------------------------------------------------------------------------- 1 | precision mediump float; 2 | 3 | varying vec2 vTextureCoord; 4 | 5 | uniform sampler2D uSampler; 6 | uniform float blur; 7 | uniform float gradientBlur; 8 | uniform vec2 start; 9 | uniform vec2 end; 10 | uniform vec2 delta; 11 | uniform vec2 texSize; 12 | 13 | float random(vec3 scale, float seed) 14 | { 15 | return fract(sin(dot(gl_FragCoord.xyz + seed, scale)) * 43758.5453 + seed); 16 | } 17 | 18 | void main(void) 19 | { 20 | vec4 color = vec4(0.0); 21 | float total = 0.0; 22 | 23 | float offset = random(vec3(12.9898, 78.233, 151.7182), 0.0); 24 | vec2 normal = normalize(vec2(start.y - end.y, end.x - start.x)); 25 | float radius = smoothstep(0.0, 1.0, abs(dot(vTextureCoord * texSize - start, normal)) / gradientBlur) * blur; 26 | 27 | for (float t = -30.0; t <= 30.0; t++) 28 | { 29 | float percent = (t + offset - 0.5) / 30.0; 30 | float weight = 1.0 - abs(percent); 31 | vec4 sample = texture2D(uSampler, vTextureCoord + delta / texSize * percent * radius); 32 | sample.rgb *= sample.a; 33 | color += sample * weight; 34 | total += weight; 35 | } 36 | 37 | gl_FragColor = color / total; 38 | gl_FragColor.rgb /= gl_FragColor.a + 0.00001; 39 | } 40 | -------------------------------------------------------------------------------- /src/engine/Component.js: -------------------------------------------------------------------------------- 1 | import Vector from 'engine/Vector'; 2 | 3 | /** 4 | * Component interface. 5 | * Not necessary to inherit this class, just a simple interface. 6 | */ 7 | export default class Component { 8 | get rotation() { 9 | return (this.entity) ? this.entity.rotation : this._rotation; 10 | } 11 | set rotation(v) { 12 | this._rotation = v; 13 | if (this.entity) { 14 | this.entity.rotation = v; 15 | } 16 | } 17 | 18 | /** 19 | * Key name this component will be saved as in owner entity 20 | * @type {String} 21 | * @readonly 22 | */ 23 | get key() { 24 | return ''; 25 | } 26 | 27 | constructor() { 28 | this.entity = null; 29 | 30 | this.position = Vector.create(); 31 | this._rotation = 0; 32 | } 33 | 34 | attach(entity) { 35 | // Recycle vectors if this is not attached to Entity 36 | if (!this.entity) { 37 | Vector.recycle(this.position); 38 | } 39 | 40 | // Replace the vectors with the entity 41 | this.position = entity.position; 42 | 43 | this.entity = entity; 44 | } 45 | detach() { 46 | if (this.entity) { 47 | // De-reference to the entity's vectors 48 | this.position = this.position.clone(); 49 | 50 | this.entity = null; 51 | } 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /src/engine/gfx/filters/ascii/AsciiFilter.js: -------------------------------------------------------------------------------- 1 | import AbstractFilter from '../../core/renderers/webgl/filters/AbstractFilter'; 2 | 3 | // TODO (cengler) - The Y is flipped in this shader for some reason. 4 | 5 | /** 6 | * @author Vico @vicocotea 7 | * original shader : https://www.shadertoy.com/view/lssGDj by @movAX13h 8 | */ 9 | 10 | /** 11 | * An ASCII filter. 12 | * 13 | * @class 14 | * @extends AbstractFilter 15 | */ 16 | export default function AsciiFilter() { 17 | AbstractFilter.call(this, 18 | // vertex shader 19 | null, 20 | // fragment shader 21 | require('./ascii.frag'), 22 | // custom uniforms 23 | { 24 | dimensions: { type: '4fv', value: new Float32Array([0, 0, 0, 0]) }, 25 | pixelSize: { type: '1f', value: 8 }, 26 | } 27 | ); 28 | } 29 | 30 | AsciiFilter.prototype = Object.create(AbstractFilter.prototype); 31 | AsciiFilter.prototype.constructor = AsciiFilter; 32 | 33 | Object.defineProperties(AsciiFilter.prototype, { 34 | /** 35 | * The pixel size used by the filter. 36 | * 37 | * @member {number} 38 | * @memberof filters.AsciiFilter# 39 | */ 40 | size: { 41 | get: function() { 42 | return this.uniforms.pixelSize.value; 43 | }, 44 | set: function(value) { 45 | this.uniforms.pixelSize.value = value; 46 | }, 47 | }, 48 | }); 49 | -------------------------------------------------------------------------------- /src/engine/polyfill/Object.assign.js: -------------------------------------------------------------------------------- 1 | /* eslint-disable no-unused-vars */ 2 | 'use strict'; 3 | var hasOwnProperty = Object.prototype.hasOwnProperty; 4 | var propIsEnumerable = Object.prototype.propertyIsEnumerable; 5 | 6 | /** 7 | * Convert a value to object. 8 | * @private 9 | * @param {*} val Value to convert 10 | * @return {Object} Object from the value 11 | */ 12 | function toObject(val) { 13 | if (val === null || val === undefined) { 14 | throw new TypeError('Object.assign cannot be called with null or undefined'); 15 | } 16 | 17 | return Object(val); 18 | } 19 | 20 | if (typeof(Object.assign) !== 'function') { 21 | Object.assign = function(target, source) { 22 | var from; 23 | var to = toObject(target); 24 | var symbols; 25 | 26 | for (var s = 1; s < arguments.length; s++) { 27 | from = Object(arguments[s]); 28 | 29 | for (var key in from) { 30 | if (hasOwnProperty.call(from, key)) { 31 | to[key] = from[key]; 32 | } 33 | } 34 | 35 | if (Object.getOwnPropertySymbols) { 36 | symbols = Object.getOwnPropertySymbols(from); 37 | for (var i = 0; i < symbols.length; i++) { 38 | if (propIsEnumerable.call(from, symbols[i])) { 39 | to[symbols[i]] = from[symbols[i]]; 40 | } 41 | } 42 | } 43 | } 44 | 45 | return to; 46 | }; 47 | } 48 | -------------------------------------------------------------------------------- /src/engine/gfx/filters/pixelate/PixelateFilter.js: -------------------------------------------------------------------------------- 1 | const AbstractFilter = require('../../core/renderers/webgl/filters/AbstractFilter'); 2 | 3 | /** 4 | * This filter applies a pixelate effect making display objects appear 'blocky'. 5 | * 6 | * @class 7 | * @extends AbstractFilter 8 | * @memberof filters 9 | */ 10 | function PixelateFilter() { 11 | AbstractFilter.call(this, 12 | // vertex shader 13 | null, 14 | // fragment shader 15 | require('./pixelate.frag'), 16 | // custom uniforms 17 | { 18 | dimensions: { type: '4fv', value: new Float32Array([0, 0, 0, 0]) }, 19 | pixelSize: { type: 'v2', value: { x: 10, y: 10 } }, 20 | } 21 | ); 22 | } 23 | 24 | PixelateFilter.prototype = Object.create(AbstractFilter.prototype); 25 | PixelateFilter.prototype.constructor = PixelateFilter; 26 | module.exports = PixelateFilter; 27 | 28 | Object.defineProperties(PixelateFilter.prototype, { 29 | /** 30 | * This a point that describes the size of the blocks. 31 | * x is the width of the block and y is the height. 32 | * 33 | * @member {Vector} 34 | * @memberof filters.PixelateFilter# 35 | */ 36 | size: { 37 | get: function() { 38 | return this.uniforms.pixelSize.value; 39 | }, 40 | set: function(value) { 41 | this.uniforms.pixelSize.value = value; 42 | }, 43 | }, 44 | }); 45 | -------------------------------------------------------------------------------- /src/engine/gfx/core/renderers/webgl/filters/FXAAFilter.js: -------------------------------------------------------------------------------- 1 | import AbstractFilter from './AbstractFilter'; 2 | 3 | 4 | /** 5 | * 6 | * Basic FXAA implementation based on the code on geeks3d.com with the 7 | * modification that the texture2DLod stuff was removed since it's 8 | * unsupported by WebGL. 9 | * 10 | * -- 11 | * From: 12 | * https://github.com/mitsuhiko/webgl-meincraft 13 | * 14 | * @class 15 | * @extends AbstractFilter 16 | * 17 | */ 18 | export default function FXAAFilter() { 19 | AbstractFilter.call(this, 20 | // vertex shader 21 | require('./FXAA.vert'), 22 | // fragme.t shader 23 | require('./FXAA.frag'), 24 | // uniforms 25 | { 26 | resolution: { type: 'v2', value: { x: 1, y: 1 } }, 27 | } 28 | ); 29 | 30 | } 31 | 32 | FXAAFilter.prototype = Object.create(AbstractFilter.prototype); 33 | FXAAFilter.prototype.constructor = FXAAFilter; 34 | 35 | /** 36 | * Applies the filter 37 | * 38 | * @param renderer {PIXI.WebGLRenderer} The renderer to retrieve the filter from 39 | * @param input {PIXI.RenderTarget} 40 | * @param output {PIXI.RenderTarget} 41 | */ 42 | FXAAFilter.prototype.applyFilter = function(renderer, input, output) { 43 | var filterManager = renderer.filterManager; 44 | 45 | var shader = this.getShader(renderer); 46 | // draw the filter... 47 | filterManager.applyFilter(shader, input, output); 48 | }; 49 | -------------------------------------------------------------------------------- /src/engine/gfx/filters/convolution/convolution.frag: -------------------------------------------------------------------------------- 1 | precision mediump float; 2 | 3 | varying mediump vec2 vTextureCoord; 4 | 5 | uniform sampler2D uSampler; 6 | uniform vec2 texelSize; 7 | uniform float matrix[9]; 8 | 9 | void main(void) 10 | { 11 | vec4 c11 = texture2D(uSampler, vTextureCoord - texelSize); // top left 12 | vec4 c12 = texture2D(uSampler, vec2(vTextureCoord.x, vTextureCoord.y - texelSize.y)); // top center 13 | vec4 c13 = texture2D(uSampler, vec2(vTextureCoord.x + texelSize.x, vTextureCoord.y - texelSize.y)); // top right 14 | 15 | vec4 c21 = texture2D(uSampler, vec2(vTextureCoord.x - texelSize.x, vTextureCoord.y)); // mid left 16 | vec4 c22 = texture2D(uSampler, vTextureCoord); // mid center 17 | vec4 c23 = texture2D(uSampler, vec2(vTextureCoord.x + texelSize.x, vTextureCoord.y)); // mid right 18 | 19 | vec4 c31 = texture2D(uSampler, vec2(vTextureCoord.x - texelSize.x, vTextureCoord.y + texelSize.y)); // bottom left 20 | vec4 c32 = texture2D(uSampler, vec2(vTextureCoord.x, vTextureCoord.y + texelSize.y)); // bottom center 21 | vec4 c33 = texture2D(uSampler, vTextureCoord + texelSize); // bottom right 22 | 23 | gl_FragColor = 24 | c11 * matrix[0] + c12 * matrix[1] + c13 * matrix[2] + 25 | c21 * matrix[3] + c22 * matrix[4] + c23 * matrix[5] + 26 | c31 * matrix[6] + c32 * matrix[7] + c33 * matrix[8]; 27 | 28 | gl_FragColor.a = c22.a; 29 | } 30 | -------------------------------------------------------------------------------- /src/engine/gfx/Text.js: -------------------------------------------------------------------------------- 1 | import Text from './core/text/Text'; 2 | import { BLEND_MODES } from './const'; 3 | import './core/sprites/webgl/SpriteRenderer'; 4 | 5 | /** 6 | * Factory function for `Text`. 7 | * 8 | * @param {object} data Data to create the instance from 9 | * @return {Text} Text instance 10 | */ 11 | export default function(data) { 12 | const inst = new Text(data.text, data, data.resolution || 1); 13 | 14 | for (let k in data) { 15 | switch (k) { 16 | // Directly set 17 | // - Node 18 | case 'alpha': 19 | case 'width': 20 | case 'height': 21 | case 'rotation': 22 | case 'visible': 23 | case 'x': 24 | case 'y': 25 | case 'interactive': 26 | // - Sprite 27 | case 'tint': 28 | inst[k] = data[k]; 29 | break; 30 | 31 | // Set vector 32 | // - Node 33 | case 'pivot': 34 | case 'position': 35 | case 'skew': 36 | 37 | // - Sprite 38 | case 'anchor': 39 | inst[k].x = data[k].x || 0; 40 | inst[k].y = data[k].y || 0; 41 | break; 42 | 43 | // - Node 44 | case 'scale': 45 | inst[k].x = data[k].x || 1; 46 | inst[k].y = data[k].y || 1; 47 | break; 48 | 49 | // Set blend mode 50 | case 'blendMode': 51 | inst.blendMode = BLEND_MODES[data[k]]; 52 | break; 53 | } 54 | } 55 | 56 | return inst; 57 | }; 58 | -------------------------------------------------------------------------------- /src/engine/gfx/core/utils/pluginTarget.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Mixins functionality to make an object have "plugins". 3 | * 4 | * @mixin 5 | * @param {object} obj The object to mix into. 6 | * @example 7 | * function MyObject() {} 8 | * 9 | * pluginTarget.mixin(MyObject); 10 | */ 11 | function pluginTarget(obj) { 12 | obj.__plugins = {}; 13 | 14 | /** 15 | * Adds a plugin to an object 16 | * 17 | * @param {string} pluginName The events that should be listed. 18 | * @param {Function} ctor The constructor function for the plugin. 19 | */ 20 | obj.registerPlugin = function(pluginName, ctor) { 21 | obj.__plugins[pluginName] = ctor; 22 | }; 23 | 24 | /** 25 | * Instantiates all the plugins of this object 26 | */ 27 | obj.prototype.initPlugins = function() { 28 | this.plugins = this.plugins || {}; 29 | 30 | for (var o in obj.__plugins) { 31 | this.plugins[o] = new (obj.__plugins[o])(this); 32 | } 33 | }; 34 | 35 | /** 36 | * Removes all the plugins of this object 37 | */ 38 | obj.prototype.destroyPlugins = function() { 39 | for (var o in this.plugins) { 40 | this.plugins[o].destroy(); 41 | this.plugins[o] = null; 42 | } 43 | 44 | this.plugins = null; 45 | }; 46 | } 47 | 48 | 49 | /** 50 | * Mixes in the properties of the pluginTarget into another object 51 | * 52 | * @param {object} obj The obj to mix into 53 | */ 54 | export function mixin(obj) { 55 | pluginTarget(obj); 56 | }; 57 | -------------------------------------------------------------------------------- /.eslintrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | "env": { 3 | "browser": true, 4 | "es6": true, 5 | "commonjs": true, 6 | }, 7 | "parserOptions": { 8 | "sourceType": "module", 9 | }, 10 | "globals": { 11 | "ga": true, 12 | }, 13 | "extends": "eslint:recommended", 14 | "rules": { 15 | "indent": [ 16 | "error", 17 | 2, 18 | { 19 | "MemberExpression": 1, 20 | "outerIIFEBody": 0, 21 | "SwitchCase": 1, 22 | }, 23 | ], 24 | "linebreak-style": ["error", "unix"], 25 | 26 | "quotes": ["error", "single"], 27 | 28 | "semi": ["error", "always"], 29 | "semi-spacing": ["error"], 30 | "space-before-function-paren": ["error", "never"], 31 | "space-infix-ops": ["error", { "int32Hint": false }], 32 | "keyword-spacing": ["error", { "after": true }], 33 | "spaced-comment": ["error", "always"], 34 | "no-irregular-whitespace": ["error"], 35 | "no-multi-spaces": ["error", { exceptions: { "Property": false } }], 36 | "no-trailing-spaces": ["error"], 37 | "no-whitespace-before-property": ["error"], 38 | "space-before-blocks": ["error", "always"], 39 | "space-in-parens": ["error", "never"], 40 | 41 | "curly": ["error", "all"], 42 | "object-curly-spacing": ["error", "always"], 43 | "brace-style": ["error", "stroustrup", { "allowSingleLine": true }], 44 | 45 | "comma-dangle": ["error", "always-multiline"], 46 | "no-console": "off", 47 | "no-fallthrough": "off", 48 | }, 49 | }; 50 | -------------------------------------------------------------------------------- /src/engine/gfx/Sprite.js: -------------------------------------------------------------------------------- 1 | import Sprite from './core/sprites/Sprite'; 2 | import { textureFromData } from './utils'; 3 | import { BLEND_MODES } from './const'; 4 | import './core/sprites/webgl/SpriteRenderer'; 5 | 6 | /** 7 | * Factory function for `Sprite`. 8 | * 9 | * @param {object} data Data to create the instance from 10 | * @return {Sprite} Sprite instance 11 | */ 12 | export default function(data) { 13 | const tex = textureFromData(data.texture); 14 | const inst = new Sprite(tex); 15 | 16 | for (let k in data) { 17 | switch (k) { 18 | // Directly set 19 | // - Node 20 | case 'alpha': 21 | case 'width': 22 | case 'height': 23 | case 'rotation': 24 | case 'visible': 25 | case 'x': 26 | case 'y': 27 | case 'interactive': 28 | // - Sprite 29 | case 'tint': 30 | inst[k] = data[k]; 31 | break; 32 | 33 | // Set vector 34 | // - Node 35 | case 'pivot': 36 | case 'position': 37 | case 'skew': 38 | 39 | // - Sprite 40 | case 'anchor': 41 | inst[k].x = data[k].x || 0; 42 | inst[k].y = data[k].y || 0; 43 | break; 44 | 45 | // - Node 46 | case 'scale': 47 | inst[k].x = data[k].x || 1; 48 | inst[k].y = data[k].y || 1; 49 | break; 50 | 51 | // Set blend mode 52 | case 'blendMode': 53 | inst.blendMode = BLEND_MODES[data[k]]; 54 | break; 55 | } 56 | } 57 | 58 | return inst; 59 | }; 60 | -------------------------------------------------------------------------------- /src/engine/gfx/core/renderers/webgl/filters/FXAA.vert: -------------------------------------------------------------------------------- 1 | 2 | precision mediump float; 3 | 4 | attribute vec2 aVertexPosition; 5 | attribute vec2 aTextureCoord; 6 | attribute vec4 aColor; 7 | 8 | uniform mat3 projectionMatrix; 9 | uniform vec2 resolution; 10 | 11 | varying vec2 vTextureCoord; 12 | varying vec4 vColor; 13 | 14 | varying vec2 vResolution; 15 | 16 | //texcoords computed in vertex step 17 | //to avoid dependent texture reads 18 | varying vec2 v_rgbNW; 19 | varying vec2 v_rgbNE; 20 | varying vec2 v_rgbSW; 21 | varying vec2 v_rgbSE; 22 | varying vec2 v_rgbM; 23 | 24 | 25 | void texcoords(vec2 fragCoord, vec2 resolution, 26 | out vec2 v_rgbNW, out vec2 v_rgbNE, 27 | out vec2 v_rgbSW, out vec2 v_rgbSE, 28 | out vec2 v_rgbM) { 29 | vec2 inverseVP = 1.0 / resolution.xy; 30 | v_rgbNW = (fragCoord + vec2(-1.0, -1.0)) * inverseVP; 31 | v_rgbNE = (fragCoord + vec2(1.0, -1.0)) * inverseVP; 32 | v_rgbSW = (fragCoord + vec2(-1.0, 1.0)) * inverseVP; 33 | v_rgbSE = (fragCoord + vec2(1.0, 1.0)) * inverseVP; 34 | v_rgbM = vec2(fragCoord * inverseVP); 35 | } 36 | 37 | void main(void){ 38 | gl_Position = vec4((projectionMatrix * vec3(aVertexPosition, 1.0)).xy, 0.0, 1.0); 39 | vTextureCoord = aTextureCoord; 40 | vColor = vec4(aColor.rgb * aColor.a, aColor.a); 41 | vResolution = resolution; 42 | 43 | //compute the texture coords and send them to varyings 44 | texcoords(aTextureCoord * resolution, resolution, v_rgbNW, v_rgbNE, v_rgbSW, v_rgbSE, v_rgbM); 45 | } 46 | -------------------------------------------------------------------------------- /src/engine/utils/math.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Math utility functions and constant variables. 3 | * 4 | * @module engine/utils/math 5 | */ 6 | 7 | /** 8 | * PI * 2 9 | * @type {number} 10 | */ 11 | export const PI_2 = Math.PI * 2; 12 | 13 | /** 14 | * Half PI 15 | * @type {number} 16 | */ 17 | export const HALF_PI = Math.PI / 2; 18 | 19 | /** 20 | * Force a value within the boundaries by clamping `x` to the range `[a, b]`. 21 | * 22 | * @param {number} x Target value to clamp 23 | * @param {number} a Min value 24 | * @param {number} b Max value 25 | * @return {number} Clamped value 26 | */ 27 | export function clamp(x, a, b) { 28 | return (x < a) ? a : ((x > b) ? b : x); 29 | }; 30 | 31 | /** 32 | * Bring the value between min and max. 33 | * 34 | * Values larger than `max` are wrapped back to `min` 35 | * and vice-versa. 36 | * 37 | * @param {number} value value to process 38 | * @param {number} min lowest valid value 39 | * @param {number} max largest valid value 40 | * @return {number} result 41 | */ 42 | export function wrap(value, min, max) { 43 | if (value < min) {return max + (value % max);} 44 | if (value >= max) {return value % max;} 45 | return value; 46 | }; 47 | 48 | /** 49 | * Bring the value between 0 and 2*PI. 50 | * 51 | * Valid values for the length of a circle in radians is 52 | * 2*PI. 53 | * 54 | * @param {number} val value to process 55 | * @return {number} a value in 0..2*PI interval 56 | */ 57 | export function circWrap(val) { 58 | return math.wrap(val, 0, math.PI_2); 59 | }; 60 | -------------------------------------------------------------------------------- /src/engine/storage/PersistentData.js: -------------------------------------------------------------------------------- 1 | import Data from './Data'; 2 | import storage from './storage'; 3 | 4 | /** 5 | * Persistent data storage, save/load data from `localStorage`. 6 | * 7 | * @class PersistentData 8 | * @constructor 9 | * @extends {Data} 10 | */ 11 | export default class PersistentData extends Data { 12 | /** 13 | * Save data to localStorage. 14 | * @memberof PersistentData 15 | * @method save 16 | */ 17 | save() { 18 | storage.set('savedata', this.data); 19 | } 20 | /** 21 | * Load data from localStorage. 22 | * @memberof PersistentData 23 | * @method load 24 | */ 25 | load() { 26 | var i, valid, key, value; 27 | 28 | var data = storage.get('savedata', this.defaultVal); 29 | 30 | for (i = 0; i < this.keys.length; i++) { 31 | key = this.keys[i]; 32 | value = data[key]; 33 | 34 | // Check whether the valus is valid 35 | valid = false; 36 | if ( 37 | (this.defaultVal[key] instanceof Array) && 38 | (value instanceof Array) && (value.length >= this.defaultVal[key].length) 39 | ) { 40 | valid = true; 41 | } 42 | else if ( 43 | ((typeof(this.defaultVal[key]) === 'boolean') && (typeof(value) === 'boolean')) || 44 | ((typeof(this.defaultVal[key]) === 'number') && (typeof(value) === 'number')) || 45 | ((typeof(this.defaultVal[key]) === 'string') && (typeof(value) === 'string')) 46 | ) { 47 | valid = true; 48 | } 49 | 50 | this.set(key, valid ? data[key] : this.defaultVal[key]); 51 | } 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /src/engine/gfx/BitmapText.js: -------------------------------------------------------------------------------- 1 | import BitmapText from './core/text/BitmapText'; 2 | import { BLEND_MODES } from './const'; 3 | import './core/sprites/webgl/SpriteRenderer'; 4 | 5 | /** 6 | * Factory function for `BitmapText`. 7 | * 8 | * @param {object} data Data to create the instance from 9 | * @return {BitmapText} BitmapText instance 10 | */ 11 | export default function(data) { 12 | const inst = new BitmapText(data.text, data); 13 | 14 | for (let k in data) { 15 | switch (k) { 16 | // Directly set 17 | // - Node 18 | case 'alpha': 19 | case 'width': 20 | case 'height': 21 | case 'rotation': 22 | case 'visible': 23 | case 'x': 24 | case 'y': 25 | case 'interactive': 26 | // - Sprite 27 | case 'tint': 28 | // - BitmapText 29 | case 'maxWidth': 30 | case 'maxLineHeight': 31 | inst[k] = data[k]; 32 | break; 33 | 34 | // Set vector 35 | // - Node 36 | case 'pivot': 37 | case 'position': 38 | case 'skew': 39 | inst[k].x = data[k].x || 0; 40 | inst[k].y = data[k].y || 0; 41 | break; 42 | 43 | // - Node 44 | case 'scale': 45 | inst[k].x = data[k].x || 1; 46 | inst[k].y = data[k].y || 1; 47 | break; 48 | 49 | // - BitmapText 50 | case 'font': 51 | inst.font = data.font; 52 | break; 53 | 54 | // Set blend mode 55 | case 'blendMode': 56 | inst.blendMode = BLEND_MODES[data[k]]; 57 | break; 58 | } 59 | } 60 | 61 | return inst; 62 | }; 63 | -------------------------------------------------------------------------------- /src/engine/gfx/Rope.js: -------------------------------------------------------------------------------- 1 | import Mesh from './mesh/Mesh'; 2 | import Rope from './mesh/Rope'; 3 | import { BLEND_MODES } from './const'; 4 | import { textureFromData } from './utils'; 5 | import './mesh/webgl/MeshRenderer'; 6 | import './mesh/webgl/MeshShader'; 7 | 8 | /** 9 | * Factory function for `Rope`. 10 | * 11 | * @param {object} data Data to create the instance from 12 | * @return {Rope} Rope instance 13 | */ 14 | export default function(data) { 15 | const tex = textureFromData(data.texture); 16 | const inst = new Rope(tex, data.points); 17 | 18 | for (let k in data) { 19 | switch (k) { 20 | // Directly set 21 | // - Node 22 | case 'alpha': 23 | case 'width': 24 | case 'height': 25 | case 'rotation': 26 | case 'visible': 27 | case 'x': 28 | case 'y': 29 | case 'interactive': 30 | // - Mesh 31 | case 'canvasPadding': 32 | inst[k] = data[k]; 33 | break; 34 | 35 | // Set vector 36 | // - Node 37 | case 'pivot': 38 | case 'position': 39 | case 'skew': 40 | inst[k].x = data[k].x || 0; 41 | inst[k].y = data[k].y || 0; 42 | break; 43 | 44 | // - Node 45 | case 'scale': 46 | inst[k].x = data[k].x || 1; 47 | inst[k].y = data[k].y || 1; 48 | break; 49 | 50 | // Set blend mode 51 | case 'blendMode': 52 | inst.blendMode = BLEND_MODES[data[k]]; 53 | break; 54 | 55 | // Set draw mode 56 | case 'drawMode': 57 | inst.drawMode = Mesh.DRAW_MODES[data[k]]; 58 | break; 59 | } 60 | } 61 | 62 | return inst; 63 | }; 64 | -------------------------------------------------------------------------------- /src/engine/gfx/TilingSprite.js: -------------------------------------------------------------------------------- 1 | import TilingSprite from './core/sprites/TilingSprite'; 2 | import { textureFromData } from './utils'; 3 | import { BLEND_MODES } from './const'; 4 | 5 | /** 6 | * Factory function for `TilingSprite`. 7 | * 8 | * @param {object} data Data to create the instance from 9 | * @return {TilingSprite} TilingSprite instance 10 | */ 11 | export default function(data) { 12 | const tex = textureFromData(data.texture); 13 | const inst = new TilingSprite(tex, data.width || tex.width, data.height || tex.height); 14 | 15 | for (let k in data) { 16 | switch (k) { 17 | // Directly set 18 | // - Node 19 | case 'alpha': 20 | case 'width': 21 | case 'height': 22 | case 'rotation': 23 | case 'visible': 24 | case 'x': 25 | case 'y': 26 | case 'interactive': 27 | // - Sprite 28 | case 'tint': 29 | inst[k] = data[k]; 30 | break; 31 | 32 | // Set vector 33 | // - Node 34 | case 'pivot': 35 | case 'position': 36 | case 'skew': 37 | 38 | // - Sprite 39 | case 'anchor': 40 | 41 | // - TilingSprite 42 | case 'tilePosition': 43 | case 'tileScale': 44 | inst[k].x = data[k].x || 0; 45 | inst[k].y = data[k].y || 0; 46 | break; 47 | 48 | // - Node 49 | case 'scale': 50 | inst[k].x = data[k].x || 1; 51 | inst[k].y = data[k].y || 1; 52 | break; 53 | 54 | // Set blend mode 55 | case 'blendMode': 56 | inst.blendMode = BLEND_MODES[data[k]]; 57 | break; 58 | } 59 | } 60 | 61 | return inst; 62 | }; 63 | -------------------------------------------------------------------------------- /src/engine/Behavior.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Behavior base class 3 | * @interface 4 | */ 5 | export default class Behavior { 6 | /** 7 | * @constructor 8 | */ 9 | constructor() { 10 | /** 11 | * Type of this behavior. 12 | * Every behavior should have a unique type! 13 | * @type {String} 14 | */ 15 | this.type = 'Behavior'; 16 | 17 | /** 18 | * Reference to the entity 19 | * @type {Entity} 20 | */ 21 | this.entity = null; 22 | } 23 | /** 24 | * Initialize 25 | * @param {Entity} entity Entity of this behavior 26 | * @param {Object} [settings] Settings to merge 27 | */ 28 | init(entity, settings) { 29 | this.entity = entity; 30 | } 31 | /** 32 | * Update 33 | * @memberof Behavior# 34 | * @param {Number} dt Delta time 35 | * @param {Number} sec Delta time in seconds 36 | */ 37 | update(dt, sec) {} /* eslint no-unused-vars:0 */ 38 | /** 39 | * Fixed update 40 | * @memberof Behavior# 41 | * @param {Number} dt Delta time 42 | * @param {Number} sec Delta time in seconds 43 | */ 44 | fixedUpdate(dt, sec) {} /* eslint no-unused-vars:0 */ 45 | } 46 | 47 | /** 48 | * Behavior class map. 49 | * @type {Object} 50 | */ 51 | Behavior.types = {}; 52 | 53 | /** 54 | * Register a Behavior 55 | * @param {String} type Type of this Behavior 56 | * @param {Function} ctor Class of this behavior 57 | */ 58 | Behavior.register = function(type, ctor) { 59 | if (!Behavior.types[type]) { 60 | Behavior.types[type] = ctor; 61 | } 62 | else { 63 | console.log('[WARNING]: "' + type + '" behavior is already registered!'); 64 | } 65 | }; 66 | -------------------------------------------------------------------------------- /src/engine/gfx/interaction/InteractionData.js: -------------------------------------------------------------------------------- 1 | import Vector from 'engine/Vector'; 2 | 3 | /** 4 | * Holds all information related to an Interaction event 5 | * 6 | * @class 7 | * @memberof interaction 8 | */ 9 | export default function InteractionData() { 10 | /** 11 | * This point stores the global coords of where the touch/mouse event happened 12 | * 13 | * @member {Point} 14 | */ 15 | this.global = new Vector(); 16 | 17 | /** 18 | * The target Sprite that was interacted with 19 | * 20 | * @member {Sprite} 21 | */ 22 | this.target = null; 23 | 24 | /** 25 | * When passed to an event handler, this will be the original DOM Event that was captured 26 | * 27 | * @member {Event} 28 | */ 29 | this.originalEvent = null; 30 | } 31 | 32 | InteractionData.prototype.constructor = InteractionData; 33 | 34 | /** 35 | * This will return the local coordinates of the specified displayObject for this InteractionData 36 | * 37 | * @param displayObject {DisplayObject} The DisplayObject that you would like the local coords off 38 | * @param [point] {Point} A Point object in which to store the value, optional (otherwise will create a new point) 39 | * @param [globalPos] {Point} A Point object containing your custom global coords, optional (otherwise will use the current global coords) 40 | * @return {Point} A point containing the coordinates of the InteractionData position relative to the DisplayObject 41 | */ 42 | InteractionData.prototype.getLocalPosition = function(displayObject, point, globalPos) { 43 | return displayObject.worldTransform.applyInverse(globalPos || this.global, point); 44 | }; 45 | -------------------------------------------------------------------------------- /src/engine/gfx/Plane.js: -------------------------------------------------------------------------------- 1 | import Mesh from './mesh/Mesh'; 2 | import Plane from './mesh/Plane'; 3 | import { BLEND_MODES } from './const'; 4 | import { textureFromData } from './utils'; 5 | import './mesh/webgl/MeshRenderer'; 6 | import './mesh/webgl/MeshShader'; 7 | 8 | /** 9 | * Factory function for `Plane`. 10 | * 11 | * @param {object} data Data to create the instance from 12 | * @return {Plane} Plane instance 13 | */ 14 | export default function(data) { 15 | const tex = textureFromData(data.texture); 16 | const inst = new Plane(tex, data.segmentsX, data.segmentsY); 17 | 18 | for (let k in data) { 19 | switch (k) { 20 | // Directly set 21 | // - Node 22 | case 'alpha': 23 | case 'width': 24 | case 'height': 25 | case 'rotation': 26 | case 'visible': 27 | case 'x': 28 | case 'y': 29 | case 'interactive': 30 | // - Mesh 31 | case 'canvasPadding': 32 | inst[k] = data[k]; 33 | break; 34 | 35 | // Set vector 36 | // - Node 37 | case 'pivot': 38 | case 'position': 39 | case 'skew': 40 | inst[k].x = data[k].x || 0; 41 | inst[k].y = data[k].y || 0; 42 | break; 43 | 44 | // - Node 45 | case 'scale': 46 | inst[k].x = data[k].x || 1; 47 | inst[k].y = data[k].y || 1; 48 | break; 49 | 50 | // Set blend mode 51 | case 'blendMode': 52 | inst.blendMode = BLEND_MODES[data[k]]; 53 | break; 54 | 55 | // Set draw mode 56 | case 'drawMode': 57 | inst.drawMode = Mesh.DRAW_MODES[data[k]]; 58 | break; 59 | } 60 | } 61 | 62 | return inst; 63 | }; 64 | -------------------------------------------------------------------------------- /src/engine/gfx/core/renderers/canvas/utils/CanvasMaskManager.js: -------------------------------------------------------------------------------- 1 | import CanvasGraphics from './CanvasGraphics'; 2 | 3 | /** 4 | * A set of functions used to handle masking. 5 | * 6 | * @class 7 | */ 8 | export default class CanvasMaskManager { 9 | constructor() {} 10 | 11 | /** 12 | * This method adds it to the current stack of masks. 13 | * 14 | * @param maskData {object} the maskData that will be pushed 15 | * @param renderer {PIXI.WebGLRenderer|PIXI.CanvasRenderer} The renderer context to use. 16 | */ 17 | pushMask(maskData, renderer) { 18 | 19 | renderer.context.save(); 20 | 21 | let cacheAlpha = maskData.alpha; 22 | let transform = maskData.worldTransform; 23 | let resolution = renderer.resolution; 24 | 25 | renderer.context.setTransform( 26 | transform.a * resolution, 27 | transform.b * resolution, 28 | transform.c * resolution, 29 | transform.d * resolution, 30 | transform.tx * resolution, 31 | transform.ty * resolution 32 | ); 33 | 34 | // TODO suport sprite alpha masks?? 35 | // lots of effort required. If demand is great enough.. 36 | if (!maskData.texture) { 37 | CanvasGraphics.renderGraphicsMask(maskData, renderer.context); 38 | renderer.context.clip(); 39 | } 40 | 41 | maskData.worldAlpha = cacheAlpha; 42 | } 43 | 44 | /** 45 | * Restores the current drawing context to the state it was before the mask was applied. 46 | * 47 | * @param renderer {PIXI.WebGLRenderer|PIXI.CanvasRenderer} The renderer context to use. 48 | */ 49 | popMask(renderer) { 50 | renderer.context.restore(); 51 | } 52 | 53 | destroy() {} 54 | } 55 | -------------------------------------------------------------------------------- /src/engine/gfx/filters/index.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @file Main export of the PIXI filters library 3 | * @author Mat Groves 4 | * @copyright 2013-2015 GoodBoyDigital 5 | * @license {@link https://github.com/pixijs/pixi.js/blob/master/LICENSE|MIT License} 6 | */ 7 | 8 | export default { 9 | AsciiFilter: require('./ascii/AsciiFilter'), 10 | BloomFilter: require('./bloom/BloomFilter'), 11 | BlurFilter: require('./blur/BlurFilter'), 12 | BlurXFilter: require('./blur/BlurXFilter'), 13 | BlurYFilter: require('./blur/BlurYFilter'), 14 | BlurDirFilter: require('./blur/BlurDirFilter'), 15 | ColorMatrixFilter: require('./color/ColorMatrixFilter'), 16 | ColorStepFilter: require('./color/ColorStepFilter'), 17 | ConvolutionFilter: require('./convolution/ConvolutionFilter'), 18 | CrossHatchFilter: require('./crosshatch/CrossHatchFilter'), 19 | DisplacementFilter: require('./displacement/DisplacementFilter'), 20 | DotScreenFilter: require('./dot/DotScreenFilter'), 21 | GrayFilter: require('./gray/GrayFilter'), 22 | DropShadowFilter: require('./dropshadow/DropShadowFilter'), 23 | InvertFilter: require('./invert/InvertFilter'), 24 | NoiseFilter: require('./noise/NoiseFilter'), 25 | PixelateFilter: require('./pixelate/PixelateFilter'), 26 | RGBSplitFilter: require('./rgb/RGBSplitFilter'), 27 | ShockwaveFilter: require('./shockwave/ShockwaveFilter'), 28 | SepiaFilter: require('./sepia/SepiaFilter'), 29 | SmartBlurFilter: require('./blur/SmartBlurFilter'), 30 | TiltShiftFilter: require('./tiltshift/TiltShiftFilter'), 31 | TiltShiftXFilter: require('./tiltshift/TiltShiftXFilter'), 32 | TiltShiftYFilter: require('./tiltshift/TiltShiftYFilter'), 33 | TwistFilter: require('./twist/TwistFilter'), 34 | }; 35 | -------------------------------------------------------------------------------- /src/engine/resize.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @module engine/resize 3 | */ 4 | let pRatio = 1.0, cRatio = 1.0; 5 | 6 | /** 7 | * Calculate how to scale a content to fill its container in `outer-box` mode. 8 | * @param {Vector} containerSize Size of container. 9 | * @param {Vector} contentSize Size of the content. 10 | * @return {object} { left , top , scale } 11 | */ 12 | export function outerBoxResize(containerSize, contentSize) { 13 | pRatio = containerSize.x / containerSize.y; 14 | cRatio = contentSize.x / contentSize.y; 15 | 16 | let result = { left: 0, top: 0, scale: 1 }; 17 | if (pRatio > cRatio) { 18 | result.scale = containerSize.y / contentSize.y; 19 | result.left = (containerSize.x - contentSize.x * result.scale) * 0.5; 20 | } 21 | else { 22 | result.scale = containerSize.x / contentSize.x; 23 | result.top = (containerSize.y - contentSize.y * result.scale) * 0.5; 24 | } 25 | 26 | return result; 27 | }; 28 | 29 | /** 30 | * Calculate how to scale a content to fill its container in `inner-box` mode. 31 | * @param {Vector} containerSize Size of container. 32 | * @param {Vector} contentSize Size of the content. 33 | * @return {object} { left , top , scale } 34 | */ 35 | export function innerBoxResize(containerSize, contentSize) { 36 | pRatio = containerSize.x / containerSize.y; 37 | cRatio = contentSize.x / contentSize.y; 38 | 39 | let result = { left: 0, top: 0, scale: 1 }; 40 | if (pRatio < cRatio) { 41 | result.scale = containerSize.y / contentSize.y; 42 | result.left = (containerSize.x - contentSize.x * result.scale) * 0.5; 43 | } 44 | else { 45 | result.scale = containerSize.x / contentSize.x; 46 | result.top = (containerSize.y - contentSize.y * result.scale) * 0.5; 47 | } 48 | 49 | return result; 50 | }; 51 | -------------------------------------------------------------------------------- /src/engine/gfx/core/renderers/webgl/shaders/PrimitiveShader.js: -------------------------------------------------------------------------------- 1 | import Shader from './Shader'; 2 | 3 | /** 4 | * This shader is used to draw simple primitive shapes for {@link Graphics}. 5 | * 6 | * @class 7 | * @extends Shader 8 | * @param shaderManager {ShaderManager} The webgl shader manager this shader works for. 9 | */ 10 | export default function PrimitiveShader(shaderManager) { 11 | Shader.call(this, 12 | shaderManager, 13 | // vertex shader 14 | [ 15 | 'attribute vec2 aVertexPosition;', 16 | 'attribute vec4 aColor;', 17 | 18 | 'uniform mat3 translationMatrix;', 19 | 'uniform mat3 projectionMatrix;', 20 | 21 | 'uniform float alpha;', 22 | 'uniform float flipY;', 23 | 'uniform vec3 tint;', 24 | 25 | 'varying vec4 vColor;', 26 | 27 | 'void main(void){', 28 | ' gl_Position = vec4((projectionMatrix * translationMatrix * vec3(aVertexPosition, 1.0)).xy, 0.0, 1.0);', 29 | ' vColor = aColor * vec4(tint * alpha, alpha);', 30 | '}', 31 | ].join('\n'), 32 | // fragment shader 33 | [ 34 | 'precision mediump float;', 35 | 36 | 'varying vec4 vColor;', 37 | 38 | 'void main(void){', 39 | ' gl_FragColor = vColor;', 40 | '}', 41 | ].join('\n'), 42 | // custom uniforms 43 | { 44 | tint: { type: '3f', value: [0, 0, 0] }, 45 | alpha: { type: '1f', value: 0 }, 46 | translationMatrix: { type: 'mat3', value: new Float32Array(9) }, 47 | projectionMatrix: { type: 'mat3', value: new Float32Array(9) }, 48 | }, 49 | // custom attributes 50 | { 51 | aVertexPosition:0, 52 | aColor:0, 53 | } 54 | ); 55 | } 56 | 57 | PrimitiveShader.prototype = Object.create(Shader.prototype); 58 | PrimitiveShader.prototype.constructor = PrimitiveShader; 59 | -------------------------------------------------------------------------------- /src/engine/gfx/utils/index.js: -------------------------------------------------------------------------------- 1 | import Texture from '../core/textures/Texture'; 2 | import Rectangle from '../core/math/Rectangle'; 3 | import loader from 'engine/loader'; 4 | 5 | /** 6 | * Get texture instance from data. 7 | * @memberof module:engine/gfx/utils 8 | * @param {String|Array|Texture} data Key of the texture. 9 | * @return {Texture|undefined} Texture instance of `undefined` 10 | */ 11 | export function textureFromData(data) { 12 | if (!data) { 13 | return undefined; 14 | } 15 | else if (typeof(data) === 'string') { 16 | return loader.resources[data].texture; 17 | } 18 | else if (Array.isArray(data)) { 19 | return loader.resources[data[0]].textures[data[1]]; 20 | } 21 | else if (data.hasOwnProperty('baseTexture')) { 22 | return data; 23 | } 24 | }; 25 | 26 | /** 27 | * Create textures for tiles in a tileset. Can also be used to extract 28 | * grid based sprite-sheets. 29 | * @memberof module:engine/gfx/utils 30 | * @param {Texture} tilesetp Tileset texture. 31 | * @param {number} tileWidth Width of a single tile. 32 | * @param {number} tileHeight Height of a single tile. 33 | * @return {array} List of textures. 34 | */ 35 | export function filmstrip(tilesetp, tileWidth, tileHeight) { 36 | var tileset = textureFromData(tilesetp); 37 | var strip = []; 38 | 39 | var w = tileset.width; 40 | var h = tileset.height; 41 | var crop = tileset.crop; 42 | 43 | var sheet = tileset.baseTexture; 44 | 45 | var cols = Math.floor(w / tileWidth); 46 | var rows = Math.floor(h / tileHeight); 47 | 48 | var q = 0, r = 0; 49 | for (r = 0; r < rows; r++) { 50 | for (q = 0; q < cols; q++) { 51 | strip.push(new Texture(sheet, new Rectangle(q * tileWidth + crop.x, r * tileHeight + crop.y, tileWidth, tileHeight))); 52 | } 53 | } 54 | 55 | return strip; 56 | }; 57 | -------------------------------------------------------------------------------- /src/engine/gfx/filters/dot/DotScreenFilter.js: -------------------------------------------------------------------------------- 1 | const AbstractFilter = require('../../core/renderers/webgl/filters/AbstractFilter'); 2 | 3 | /** 4 | * @author Mat Groves http://matgroves.com/ @Doormat23 5 | * original filter: https://github.com/evanw/glfx.js/blob/master/src/filters/fun/dotscreen.js 6 | */ 7 | 8 | /** 9 | * This filter applies a dotscreen effect making display objects appear to be made out of 10 | * black and white halftone dots like an old printer. 11 | * 12 | * @class 13 | * @extends AbstractFilter 14 | */ 15 | function DotScreenFilter() { 16 | AbstractFilter.call(this, 17 | // vertex shader 18 | null, 19 | // fragment shader 20 | require('./dotScreen.frag'), 21 | // custom uniforms 22 | { 23 | scale: { type: '1f', value: 1 }, 24 | angle: { type: '1f', value: 5 }, 25 | dimensions: { type: '4fv', value: [0, 0, 0, 0] }, 26 | } 27 | ); 28 | } 29 | 30 | DotScreenFilter.prototype = Object.create(AbstractFilter.prototype); 31 | DotScreenFilter.prototype.constructor = DotScreenFilter; 32 | module.exports = DotScreenFilter; 33 | 34 | Object.defineProperties(DotScreenFilter.prototype, { 35 | /** 36 | * The scale of the effect. 37 | * @member {number} 38 | * @memberof filters.DotScreenFilter# 39 | */ 40 | scale: { 41 | get: function() { 42 | return this.uniforms.scale.value; 43 | }, 44 | set: function(value) { 45 | this.uniforms.scale.value = value; 46 | }, 47 | }, 48 | 49 | /** 50 | * The radius of the effect. 51 | * @member {number} 52 | * @memberof filters.DotScreenFilter# 53 | */ 54 | angle: { 55 | get: function() { 56 | return this.uniforms.angle.value; 57 | }, 58 | set: function(value) { 59 | this.uniforms.angle.value = value; 60 | }, 61 | }, 62 | }); 63 | -------------------------------------------------------------------------------- /src/engine/gfx/mesh/webgl/MeshShader.js: -------------------------------------------------------------------------------- 1 | import Shader from '../../core/renderers/webgl/shaders/Shader'; 2 | import ShaderManager from '../../core/renderers/webgl/managers/ShaderManager'; 3 | 4 | /** 5 | * @class 6 | * @extends Shader 7 | * @memberof mesh 8 | * @param shaderManager {ShaderManager} The WebGL shader manager this shader works for. 9 | */ 10 | export default class MeshShader extends Shader { 11 | constructor(shaderManager) { 12 | super(shaderManager, 13 | // vertex shader 14 | [ 15 | 'precision lowp float;', 16 | 'attribute vec2 aVertexPosition;', 17 | 'attribute vec2 aTextureCoord;', 18 | 19 | 'uniform mat3 translationMatrix;', 20 | 'uniform mat3 projectionMatrix;', 21 | 22 | 'varying vec2 vTextureCoord;', 23 | 24 | 'void main(void){', 25 | ' gl_Position = vec4((projectionMatrix * translationMatrix * vec3(aVertexPosition, 1.0)).xy, 0.0, 1.0);', 26 | ' vTextureCoord = aTextureCoord;', 27 | '}', 28 | ].join('\n'), 29 | [ 30 | 'precision lowp float;', 31 | 32 | 'varying vec2 vTextureCoord;', 33 | 'uniform float alpha;', 34 | 35 | 'uniform sampler2D uSampler;', 36 | 37 | 'void main(void){', 38 | ' gl_FragColor = texture2D(uSampler, vTextureCoord) * alpha ;', 39 | '}', 40 | ].join('\n'), 41 | // custom uniforms 42 | { 43 | alpha: { type: '1f', value: 0 }, 44 | translationMatrix: { type: 'mat3', value: new Float32Array(9) }, 45 | projectionMatrix: { type: 'mat3', value: new Float32Array(9) }, 46 | }, 47 | // custom attributes 48 | { 49 | aVertexPosition:0, 50 | aTextureCoord:0, 51 | } 52 | ); 53 | } 54 | } 55 | 56 | ShaderManager.registerPlugin('meshShader', MeshShader); 57 | -------------------------------------------------------------------------------- /src/engine/gfx/core/renderers/webgl/shaders/ComplexPrimitiveShader.js: -------------------------------------------------------------------------------- 1 | import Shader from './Shader'; 2 | 3 | /** 4 | * This shader is used to draw complex primitive shapes for {@link Graphics}. 5 | * 6 | * @class 7 | * @extends Shader 8 | * @param shaderManager {ShaderManager} The webgl shader manager this shader works for. 9 | */ 10 | export default function ComplexPrimitiveShader(shaderManager) { 11 | Shader.call(this, 12 | shaderManager, 13 | // vertex shader 14 | [ 15 | 'attribute vec2 aVertexPosition;', 16 | 17 | 'uniform mat3 translationMatrix;', 18 | 'uniform mat3 projectionMatrix;', 19 | 20 | 'uniform vec3 tint;', 21 | 'uniform float alpha;', 22 | 'uniform vec3 color;', 23 | 24 | 'varying vec4 vColor;', 25 | 26 | 'void main(void){', 27 | ' gl_Position = vec4((projectionMatrix * translationMatrix * vec3(aVertexPosition, 1.0)).xy, 0.0, 1.0);', 28 | ' vColor = vec4(color * alpha * tint, alpha);',// " * vec4(tint * alpha, alpha);', 29 | '}', 30 | ].join('\n'), 31 | // fragment shader 32 | [ 33 | 'precision mediump float;', 34 | 35 | 'varying vec4 vColor;', 36 | 37 | 'void main(void){', 38 | ' gl_FragColor = vColor;', 39 | '}', 40 | ].join('\n'), 41 | // custom uniforms 42 | { 43 | tint: { type: '3f', value: [0, 0, 0] }, 44 | alpha: { type: '1f', value: 0 }, 45 | color: { type: '3f', value: [0,0,0] }, 46 | translationMatrix: { type: 'mat3', value: new Float32Array(9) }, 47 | projectionMatrix: { type: 'mat3', value: new Float32Array(9) }, 48 | }, 49 | // attributes 50 | { 51 | aVertexPosition:0, 52 | } 53 | ); 54 | } 55 | 56 | ComplexPrimitiveShader.prototype = Object.create(Shader.prototype); 57 | ComplexPrimitiveShader.prototype.constructor = ComplexPrimitiveShader; 58 | -------------------------------------------------------------------------------- /src/behaviors/Health.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Health management 3 | * 4 | * @event heal 5 | * @event receiveDamage 6 | * @event kill 7 | * @event health Health is changed 8 | */ 9 | 10 | const Behavior = require('engine/Behavior'); 11 | const { clamp } = require('engine/utils/math'); 12 | 13 | const DefaultSettings = { 14 | /* Max health value */ 15 | MaxHealth: 3, 16 | DamageInvincibleTime: 0, 17 | HealInvincibleTime: 0, 18 | }; 19 | 20 | class Health extends Behavior { 21 | constructor() { 22 | super(); 23 | 24 | this.type = 'Health'; 25 | 26 | // Constants 27 | this.MaxHealth = 0; 28 | this.DamageInvincibleTime = 0; 29 | this.HealInvincibleTime = 0; 30 | 31 | // Properties 32 | this.health = 0; 33 | this.invincibleTimer = 0; 34 | } 35 | 36 | init(ent, settings) { 37 | super.init(ent); 38 | 39 | this.entity.canFixedTick = true; 40 | 41 | Object.assign(this, DefaultSettings, settings); 42 | 43 | // Init variables 44 | this.health = this.MaxHealth; 45 | this.invincibleTimer = 0; 46 | } 47 | fixedUpdate(dt) { 48 | if (this.invincibleTimer > 0) { 49 | this.invincibleTimer -= dt; 50 | } 51 | } 52 | 53 | // Actions 54 | // Recover some health 55 | heal(v) { 56 | this.health = clamp(this.health + v, 1, this.MaxHealth); 57 | 58 | this.entity.events.emit('heal', v); 59 | } 60 | // Received damages 61 | receiveDamage(dmg) { 62 | if (this.invincibleTimer > 0) return; 63 | 64 | this.health = clamp(this.health - dmg, 0, this.MaxHealth); 65 | 66 | if (this.health === 0) { 67 | this.entity.events.emit('kill'); 68 | return; 69 | } 70 | 71 | this.invincibleTimer = this.DamageInvincibleTime; 72 | 73 | this.entity.events.emit('receiveDamage', dmg); 74 | } 75 | } 76 | 77 | Behavior.register('Health', Health); 78 | 79 | module.exports = Health; 80 | -------------------------------------------------------------------------------- /src/engine/gfx/filters/rgb/RGBSplitFilter.js: -------------------------------------------------------------------------------- 1 | const AbstractFilter = require('../../core/renderers/webgl/filters/AbstractFilter'); 2 | 3 | /** 4 | * An RGB Split Filter. 5 | * 6 | * @class 7 | * @extends AbstractFilter 8 | */ 9 | function RGBSplitFilter() { 10 | AbstractFilter.call(this, 11 | // vertex shader 12 | null, 13 | // fragment shader 14 | require('./rgbSplit.frag'), 15 | // custom uniforms 16 | { 17 | red: { type: 'v2', value: { x: 20, y: 20 } }, 18 | green: { type: 'v2', value: { x: -20, y: 20 } }, 19 | blue: { type: 'v2', value: { x: 20, y: -20 } }, 20 | dimensions: { type: '4fv', value: [0, 0, 0, 0] }, 21 | } 22 | ); 23 | } 24 | 25 | RGBSplitFilter.prototype = Object.create(AbstractFilter.prototype); 26 | RGBSplitFilter.prototype.constructor = RGBSplitFilter; 27 | module.exports = RGBSplitFilter; 28 | 29 | Object.defineProperties(RGBSplitFilter.prototype, { 30 | /** 31 | * Red channel offset. 32 | * 33 | * @member {Vector} 34 | * @memberof filters.RGBSplitFilter# 35 | */ 36 | red: { 37 | get: function() { 38 | return this.uniforms.red.value; 39 | }, 40 | set: function(value) { 41 | this.uniforms.red.value = value; 42 | }, 43 | }, 44 | 45 | /** 46 | * Green channel offset. 47 | * 48 | * @member {Vector} 49 | * @memberof filters.RGBSplitFilter# 50 | */ 51 | green: { 52 | get: function() { 53 | return this.uniforms.green.value; 54 | }, 55 | set: function(value) { 56 | this.uniforms.green.value = value; 57 | }, 58 | }, 59 | 60 | /** 61 | * Blue offset. 62 | * 63 | * @member {Vector} 64 | * @memberof filters.RGBSplitFilter# 65 | */ 66 | blue: { 67 | get: function() { 68 | return this.uniforms.blue.value; 69 | }, 70 | set: function(value) { 71 | this.uniforms.blue.value = value; 72 | }, 73 | }, 74 | }); 75 | -------------------------------------------------------------------------------- /src/engine/gfx/filters/twist/TwistFilter.js: -------------------------------------------------------------------------------- 1 | const AbstractFilter = require('../../core/renderers/webgl/filters/AbstractFilter'); 2 | 3 | /** 4 | * This filter applies a twist effect making display objects appear twisted in the given direction. 5 | * 6 | * @class 7 | * @extends AbstractFilter 8 | */ 9 | function TwistFilter() { 10 | AbstractFilter.call(this, 11 | // vertex shader 12 | null, 13 | // fragment shader 14 | require('./twist.frag'), 15 | // custom uniforms 16 | { 17 | radius: { type: '1f', value: 0.5 }, 18 | angle: { type: '1f', value: 5 }, 19 | offset: { type: 'v2', value: { x: 0.5, y: 0.5 } }, 20 | } 21 | ); 22 | } 23 | 24 | TwistFilter.prototype = Object.create(AbstractFilter.prototype); 25 | TwistFilter.prototype.constructor = TwistFilter; 26 | module.exports = TwistFilter; 27 | 28 | Object.defineProperties(TwistFilter.prototype, { 29 | /** 30 | * This point describes the the offset of the twist. 31 | * 32 | * @member {Point} 33 | * @memberof filters.TwistFilter# 34 | */ 35 | offset: { 36 | get: function() { 37 | return this.uniforms.offset.value; 38 | }, 39 | set: function(value) { 40 | this.uniforms.offset.value = value; 41 | }, 42 | }, 43 | 44 | /** 45 | * This radius of the twist. 46 | * 47 | * @member {number} 48 | * @memberof filters.TwistFilter# 49 | */ 50 | radius: { 51 | get: function() { 52 | return this.uniforms.radius.value; 53 | }, 54 | set: function(value) { 55 | this.uniforms.radius.value = value; 56 | }, 57 | }, 58 | 59 | /** 60 | * This angle of the twist. 61 | * 62 | * @member {number} 63 | * @memberof filters.TwistFilter# 64 | */ 65 | angle: { 66 | get: function() { 67 | return this.uniforms.angle.value; 68 | }, 69 | set: function(value) { 70 | this.uniforms.angle.value = value; 71 | }, 72 | }, 73 | }); 74 | -------------------------------------------------------------------------------- /src/game/Loading.js: -------------------------------------------------------------------------------- 1 | import core from 'engine/core'; 2 | import Game from 'engine/Game'; 3 | import loader from 'engine/loader'; 4 | import Gfx from 'engine/gfx'; 5 | import Graphics from 'engine/gfx/Graphics'; 6 | import Text from 'engine/gfx/Text'; 7 | 8 | const BAR_WIDTH = Math.floor(core.width * 0.75); 9 | const BAR_HEIGHT = Math.floor(BAR_WIDTH * 0.075); 10 | 11 | export default class Loading extends Game { 12 | constructor() { 13 | super(); 14 | 15 | this.addSystem(new Gfx()); 16 | 17 | this.barBg = Graphics({}).addTo(this.sysGfx.root); 18 | this.barBg.clear(); 19 | this.barBg.beginFill(0x5f574f); 20 | this.barBg.drawRect(0, -BAR_HEIGHT * 0.5, BAR_WIDTH, BAR_HEIGHT); 21 | this.barBg.endFill(); 22 | 23 | this.bar = Graphics({}).addTo(this.sysGfx.root); 24 | this.bar.clear(); 25 | this.bar.beginFill(0xffffff); 26 | this.bar.drawRect(0, -BAR_HEIGHT * 0.5, 1, BAR_HEIGHT); 27 | this.bar.endFill(); 28 | 29 | this.pct = Text({ 30 | text: '100%', 31 | font: `${BAR_HEIGHT - 2}px "Helvetica Neue", "Calibri Light", Roboto, sans-serif`, 32 | fill: '#ffffff', 33 | anchor: { x: 0.5, y: 0.5 }, 34 | position: { x: core.width / 2, y: core.height / 2 - BAR_HEIGHT * 1.5 }, 35 | }).addTo(this.sysGfx.root); 36 | 37 | this.barBg.position = this.bar.position.set(core.width / 2 - BAR_WIDTH / 2, core.height / 2); 38 | } 39 | 40 | awake({ gameClass }) { 41 | let redraw = () => { 42 | this.pct.text = `${loader.progress | 0}%`; 43 | 44 | this.bar.clear(); 45 | this.bar.beginFill(0xffffff); 46 | this.bar.drawRect(0, -BAR_HEIGHT * 0.5, Math.floor(BAR_WIDTH * loader.progress * 0.01), BAR_HEIGHT); 47 | this.bar.endFill(); 48 | }; 49 | 50 | let h = loader.onProgress.add(redraw); 51 | const ready = () => { 52 | h.detach(); 53 | core.setGame(gameClass, true); 54 | 55 | core.emit('ready'); 56 | }; 57 | 58 | loader.onComplete.once(ready); 59 | loader.load(); 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /src/engine/polyfill/requestAnimationFrame.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | // References: 4 | // http://paulirish.com/2011/requestanimationframe-for-smart-animating/ 5 | // https://gist.github.com/1579671 6 | // http://updates.html5rocks.com/2012/05/requestAnimationFrame-API-now-with-sub-millisecond-precision 7 | // https://gist.github.com/timhall/4078614 8 | // https://github.com/Financial-Times/polyfill-service/tree/master/polyfills/requestAnimationFrame 9 | 10 | // Date.now 11 | if (!(Date.now && Date.prototype.getTime)) { 12 | Date.now = function() { 13 | return new Date().getTime(); 14 | }; 15 | } 16 | 17 | // performance.now 18 | if (!(window.performance && window.performance.now)) { 19 | var startTime = Date.now(); 20 | if (!window.performance) { 21 | window.performance = {}; 22 | } 23 | window.performance.now = function() { 24 | return Date.now() - startTime; 25 | }; 26 | } 27 | 28 | // requestAnimationFrame 29 | var lastTime = Date.now(); 30 | var vendors = ['ms', 'moz', 'webkit', 'o']; 31 | 32 | for (var x = 0; x < vendors.length && !window.requestAnimationFrame; ++x) { 33 | window.requestAnimationFrame = window[vendors[x] + 'RequestAnimationFrame']; 34 | window.cancelAnimationFrame = window[vendors[x] + 'CancelAnimationFrame'] || 35 | window[vendors[x] + 'CancelRequestAnimationFrame']; 36 | } 37 | 38 | if (!window.requestAnimationFrame) { 39 | window.requestAnimationFrame = function(callback) { 40 | if (typeof callback !== 'function') { 41 | throw new TypeError(callback + 'is not a function'); 42 | } 43 | 44 | var currentTime = Date.now(), 45 | delay = 16 + lastTime - currentTime; 46 | 47 | if (delay < 0) { 48 | delay = 0; 49 | } 50 | 51 | lastTime = currentTime; 52 | 53 | return setTimeout(function() { 54 | lastTime = Date.now(); 55 | callback(performance.now()); 56 | }, delay); 57 | }; 58 | } 59 | 60 | if (!window.cancelAnimationFrame) { 61 | window.cancelAnimationFrame = function(id) { 62 | clearTimeout(id); 63 | }; 64 | } 65 | -------------------------------------------------------------------------------- /src/engine/gfx/filters/blur/BlurYFilter.js: -------------------------------------------------------------------------------- 1 | const AbstractFilter = require('../../core/renderers/webgl/filters/AbstractFilter'); 2 | /** 3 | * The BlurYFilter applies a horizontal Gaussian blur to an object. 4 | * 5 | * @class 6 | * @extends AbstractFilter 7 | */ 8 | function BlurYFilter() { 9 | AbstractFilter.call(this, 10 | // vertex shader 11 | require('./blurY.vert'), 12 | // fragment shader 13 | require('./blur.frag'), 14 | // set the uniforms 15 | { 16 | strength: { type: '1f', value: 1 }, 17 | } 18 | ); 19 | 20 | this.passes = 1; 21 | this.strength = 4; 22 | } 23 | 24 | BlurYFilter.prototype = Object.create(AbstractFilter.prototype); 25 | BlurYFilter.prototype.constructor = BlurYFilter; 26 | module.exports = BlurYFilter; 27 | 28 | BlurYFilter.prototype.applyFilter = function(renderer, input, output, clear) { 29 | var shader = this.getShader(renderer); 30 | 31 | this.uniforms.strength.value = Math.abs(this.strength) / 4 / this.passes * (input.frame.height / input.size.height); 32 | 33 | if (this.passes === 1) { 34 | renderer.filterManager.applyFilter(shader, input, output, clear); 35 | } 36 | else { 37 | var renderTarget = renderer.filterManager.getRenderTarget(true); 38 | var flip = input; 39 | var flop = renderTarget; 40 | 41 | for (var i = 0; i < this.passes - 1; i++) { 42 | renderer.filterManager.applyFilter(shader, flip, flop, true); 43 | 44 | var temp = flop; 45 | flop = flip; 46 | flip = temp; 47 | } 48 | 49 | renderer.filterManager.applyFilter(shader, flip, output, clear); 50 | 51 | renderer.filterManager.returnRenderTarget(renderTarget); 52 | } 53 | }; 54 | 55 | 56 | Object.defineProperties(BlurYFilter.prototype, { 57 | /** 58 | * Sets the strength of both the blur. 59 | * 60 | * @member {number} 61 | * @memberof filters.BlurYFilter# 62 | * @default 2 63 | */ 64 | blur: { 65 | get: function() { 66 | return this.strength; 67 | }, 68 | set: function(value) { 69 | this.padding = Math.abs(value) * 0.5; 70 | this.strength = value; 71 | }, 72 | }, 73 | }); 74 | -------------------------------------------------------------------------------- /src/engine/gfx/core/math/Circle.js: -------------------------------------------------------------------------------- 1 | import Rectangle from './Rectangle'; 2 | import { SHAPES } from '../../const'; 3 | 4 | /** 5 | * The Circle object can be used to specify a hit area for displayObjects 6 | * 7 | * @class 8 | */ 9 | export default class Circle { 10 | /** 11 | * @constructor 12 | * @param {number} x The X coordinate of the center of this circle 13 | * @param {number} y The Y coordinate of the center of this circle 14 | * @param {number} radius The radius of the circle 15 | */ 16 | constructor(x, y, radius) { 17 | /** 18 | * @member {number} 19 | * @default 0 20 | */ 21 | this.x = x || 0; 22 | 23 | /** 24 | * @member {number} 25 | * @default 0 26 | */ 27 | this.y = y || 0; 28 | 29 | /** 30 | * @member {number} 31 | * @default 0 32 | */ 33 | this.radius = radius || 0; 34 | 35 | /** 36 | * The type of the object, mainly used to avoid `instanceof` checks 37 | * 38 | * @member {number} 39 | */ 40 | this.type = SHAPES.CIRC; 41 | } 42 | 43 | /** 44 | * Creates a clone of this Circle instance 45 | * 46 | * @return {Circle} a copy of the Circle 47 | */ 48 | clone() { 49 | return new Circle(this.x, this.y, this.radius); 50 | } 51 | 52 | /** 53 | * Checks whether the x and y coordinates given are contained within this circle 54 | * 55 | * @param {number} x The X coordinate of the point to test 56 | * @param {number} y The Y coordinate of the point to test 57 | * @return {boolean} Whether the x/y coordinates are within this Circle 58 | */ 59 | contains(x, y) { 60 | if (this.radius <= 0) { 61 | return false; 62 | } 63 | 64 | var dx = (this.x - x), 65 | dy = (this.y - y), 66 | r2 = this.radius * this.radius; 67 | 68 | dx *= dx; 69 | dy *= dy; 70 | 71 | return (dx + dy <= r2); 72 | } 73 | 74 | /** 75 | * Returns the framing rectangle of the circle as a Rectangle object 76 | * 77 | * @return {Rectangle} the framing rectangle 78 | */ 79 | getBounds() { 80 | return new Rectangle(this.x - this.radius, this.y - this.radius, this.radius * 2, this.radius * 2); 81 | } 82 | } 83 | -------------------------------------------------------------------------------- /src/engine/gfx/core/particles/webgl/ParticleShader.js: -------------------------------------------------------------------------------- 1 | const TextureShader = require('../../renderers/webgl/shaders/TextureShader'); 2 | 3 | /** 4 | * @class 5 | * @extends TextureShader 6 | * @param shaderManager {ShaderManager} The webgl shader manager this shader works for. 7 | */ 8 | class ParticleShader extends TextureShader { 9 | constructor(shaderManager) { 10 | super(shaderManager, 11 | // vertex shader 12 | [ 13 | 'attribute vec2 aVertexPosition;', 14 | 'attribute vec2 aTextureCoord;', 15 | 'attribute float aColor;', 16 | 17 | 'attribute vec2 aPositionCoord;', 18 | 'attribute vec2 aScale;', 19 | 'attribute float aRotation;', 20 | 21 | 'uniform mat3 projectionMatrix;', 22 | 23 | 'varying vec2 vTextureCoord;', 24 | 'varying float vColor;', 25 | 26 | 'void main(void){', 27 | ' vec2 v = aVertexPosition;', 28 | 29 | ' v.x = (aVertexPosition.x) * cos(aRotation) - (aVertexPosition.y) * sin(aRotation);', 30 | ' v.y = (aVertexPosition.x) * sin(aRotation) + (aVertexPosition.y) * cos(aRotation);', 31 | ' v = v + aPositionCoord;', 32 | 33 | ' gl_Position = vec4((projectionMatrix * vec3(v, 1.0)).xy, 0.0, 1.0);', 34 | 35 | ' vTextureCoord = aTextureCoord;', 36 | ' vColor = aColor;', 37 | '}', 38 | ].join('\n'), 39 | // hello 40 | [ 41 | 'precision lowp float;', 42 | 43 | 'varying vec2 vTextureCoord;', 44 | 'varying float vColor;', 45 | 46 | 'uniform sampler2D uSampler;', 47 | 'uniform float uAlpha;', 48 | 49 | 'void main(void){', 50 | ' vec4 color = texture2D(uSampler, vTextureCoord) * vColor * uAlpha;', 51 | ' if (color.a == 0.0) discard;', 52 | ' gl_FragColor = color;', 53 | '}', 54 | ].join('\n'), 55 | // custom uniforms 56 | { 57 | uAlpha: { type: '1f', value: 1 }, 58 | }, 59 | // custom attributes 60 | { 61 | aPositionCoord: 0, 62 | // aScale: 0, 63 | aRotation: 0, 64 | } 65 | ); 66 | 67 | // TEMP HACK 68 | 69 | } 70 | } 71 | 72 | module.exports = ParticleShader; 73 | -------------------------------------------------------------------------------- /src/game/main.js: -------------------------------------------------------------------------------- 1 | import core from 'engine/core'; 2 | import loader from 'engine/loader'; 3 | import Game from 'engine/Game'; 4 | 5 | // Requite any systems 6 | import Gfx from 'engine/gfx'; 7 | import Anime from 'engine/anime'; 8 | 9 | // Requite anything else you want to use 10 | import BitmapText from 'engine/gfx/BitmapText'; 11 | import AnimatedSprite from 'engine/gfx/AnimatedSprite'; 12 | 13 | // Loading screen 14 | import Loading from 'game/Loading'; 15 | 16 | // Load some resources 17 | loader.add('04b03.fnt'); 18 | loader.add('bat', 'bat.png'); 19 | 20 | // A game acts like a scene/screen or whatever you call 21 | class MyGame extends Game { 22 | constructor() { 23 | super(); 24 | 25 | // FPS for fixed update 26 | this.desiredFPS = 60; 27 | 28 | // Add systems you want to have 29 | this 30 | .addSystem(new Anime()) 31 | .addSystem(new Gfx()); 32 | 33 | // Create some layers 34 | this.sysGfx 35 | .createLayer('background') 36 | .createLayer('entity') 37 | .createLayer('actor', 'entity') 38 | .createLayer('fx', 'entity') 39 | .createLayer('hud', 'entity') 40 | .createLayer('ui'); 41 | 42 | // Add some gfx elements 43 | const label = BitmapText({ 44 | text: 'It Works!', 45 | font: '16px 04b03', 46 | }).addTo(this.sysGfx.layers.background); 47 | label.position.set(core.width / 2 - label.width / 2, core.height / 2 - label.height / 2); 48 | 49 | const monster = AnimatedSprite({ 50 | textures: ['bat', 51, 57], 51 | anims: [ 52 | ['fly', [0,1,2,3,4], { speed: 10 }], 53 | ['atk', [5,6,7], { speed: 8, loop: false }], 54 | ['hurt', [8,9,8,9,8,9], { speed: 8, loop: false }], 55 | ['kill', [10,11,12,13], { speed: 8, loop: false }], 56 | ], 57 | }).addTo(this.sysGfx.layers.background); 58 | monster.position.set(50); 59 | monster.anchor.set(0.5); 60 | monster.play('fly'); 61 | 62 | // Animate something 63 | this.sysAnime.tween(monster) 64 | .to({ 'position.x': 250 }, 2000) 65 | .to({ 'scale.x': -1 }, 10) 66 | .to({ 'position.x': 50 }, 2000) 67 | .to({ 'scale.x': +1 }, 10) 68 | .repeat(100); 69 | } 70 | } 71 | 72 | core.main(MyGame, Loading); 73 | -------------------------------------------------------------------------------- /src/behaviors/FireBullet.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Fire bullets to a specific direction 3 | */ 4 | 5 | const keyboard = require('engine/keyboard'); 6 | const Behavior = require('engine/Behavior'); 7 | const Vector = require('engine/Vector'); 8 | 9 | const DefaultSettings = { 10 | /* Max ammo to */ 11 | MaxAmmo: 5, 12 | 13 | /* Time between fires */ 14 | Cooldown: 200, 15 | 16 | /* Bullet class that accept `emitter` and `direction` */ 17 | BulletActor: null, 18 | /* Which layer to add this bullet to */ 19 | BulletLayer: null, 20 | }; 21 | 22 | class FireBullet extends Behavior { 23 | constructor() { 24 | super(); 25 | 26 | this.type = 'FireBullet'; 27 | 28 | // Constants 29 | this.MaxAmmo = 5; 30 | 31 | this.Cooldown = 200; 32 | 33 | this.BulletActor = null; 34 | this.BulletLayer = null; 35 | 36 | // Properties 37 | this.cdTimer = 0; 38 | this.ammo = 0; 39 | 40 | this.bulletConfig = { 41 | emitter: null, 42 | direction: 0, 43 | }; 44 | } 45 | init(ent, settings) { 46 | super.init(ent); 47 | 48 | Object.assign(this, DefaultSettings, settings); 49 | 50 | this.entity.canFixedTick = true; 51 | 52 | this.cdTimer = 0; 53 | this.ammo = this.MaxAmmo; 54 | } 55 | fixedUpdate(dt) { 56 | if (this.cdTimer > 0) { 57 | this.cdTimer -= dt; 58 | } 59 | } 60 | 61 | // Actions 62 | fire(position, direction) { 63 | if (this.cdTimer > 0) return; 64 | 65 | this.bulletConfig.emitter = this.entity; 66 | this.bulletConfig.direction = direction; 67 | 68 | if (this.bulletActor && this.entity && this.entity.game) { 69 | if (this.ammo > 0) { 70 | this.ammo -= 1; 71 | this.entity.events.emit('ammoChange', this.ammo); 72 | this.cdTimer = this.Cooldown; 73 | this.entity.game.spawnEntity(this.BulletActor, position.x, position.y, this.BulletLayer, this.bulletConfig); 74 | } 75 | } 76 | } 77 | reload(amount) { 78 | if (!amount) { 79 | this.ammo = this.MaxAmmo; 80 | } 81 | else { 82 | this.ammo = amount; 83 | } 84 | this.entity.events.emit('ammoChange', this.ammo); 85 | } 86 | } 87 | 88 | Behavior.register('FireBullet', FireBullet); 89 | 90 | module.exports = FireBullet; 91 | -------------------------------------------------------------------------------- /src/engine/gfx/core/textures/TextureUvs.js: -------------------------------------------------------------------------------- 1 | import GroupD8 from '../math/GroupD8'; 2 | 3 | /** 4 | * A standard object to store the Uvs of a texture 5 | * 6 | * @class 7 | * @private 8 | */ 9 | export default class TextureUvs { 10 | constructor() { 11 | this.x0 = 0; 12 | this.y0 = 0; 13 | 14 | this.x1 = 1; 15 | this.y1 = 0; 16 | 17 | this.x2 = 1; 18 | this.y2 = 1; 19 | 20 | this.x3 = 0; 21 | this.y3 = 1; 22 | } 23 | 24 | /** 25 | * Sets the texture Uvs based on the given frame information 26 | * @param frame {Rectangle} 27 | * @param baseFrame {Rectangle} 28 | * @param rotate {number} Rotation of frame, see {@link GroupD8} 29 | * @private 30 | */ 31 | set(frame, baseFrame, rotate) { 32 | var tw = baseFrame.width; 33 | var th = baseFrame.height; 34 | 35 | if (rotate) { 36 | // width and height div 2 div baseFrame size 37 | var swapWidthHeight = GroupD8.isSwapWidthHeight(rotate); 38 | var w2 = (swapWidthHeight ? frame.height : frame.width) / 2 / tw; 39 | var h2 = (swapWidthHeight ? frame.width : frame.height) / 2 / th; 40 | // coordinates of center 41 | var cX = frame.x / tw + w2; 42 | var cY = frame.y / th + h2; 43 | rotate = GroupD8.add(rotate, GroupD8.NW); // NW is top-left corner 44 | this.x0 = cX + w2 * GroupD8.uX(rotate); 45 | this.y0 = cY + h2 * GroupD8.uY(rotate); 46 | rotate = GroupD8.add(rotate, 2); // rotate 90 degrees clockwise 47 | this.x1 = cX + w2 * GroupD8.uX(rotate); 48 | this.y1 = cY + h2 * GroupD8.uY(rotate); 49 | rotate = GroupD8.add(rotate, 2); 50 | this.x2 = cX + w2 * GroupD8.uX(rotate); 51 | this.y2 = cY + h2 * GroupD8.uY(rotate); 52 | rotate = GroupD8.add(rotate, 2); 53 | this.x3 = cX + w2 * GroupD8.uX(rotate); 54 | this.y3 = cY + h2 * GroupD8.uY(rotate); 55 | } 56 | else { 57 | 58 | this.x0 = frame.x / tw; 59 | this.y0 = frame.y / th; 60 | 61 | this.x1 = (frame.x + frame.width) / tw; 62 | this.y1 = frame.y / th; 63 | 64 | this.x2 = (frame.x + frame.width) / tw; 65 | this.y2 = (frame.y + frame.height) / th; 66 | 67 | this.x3 = frame.x / tw; 68 | this.y3 = (frame.y + frame.height) / th; 69 | } 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /src/engine/loader/middlewares/parsing/blob.js: -------------------------------------------------------------------------------- 1 | import Resource from '../../Resource'; 2 | import { encodeBinary } from '../../b64'; 3 | 4 | const Url = window.URL || window.webkitURL; 5 | 6 | // a middleware for transforming XHR loaded Blobs into more useful objects 7 | export function blobMiddlewareFactory() { 8 | return function blobMiddleware(resource, next) { 9 | if (!resource.data) { 10 | next(); 11 | 12 | return; 13 | } 14 | 15 | // if this was an XHR load of a blob 16 | if (resource.xhr && resource.xhrType === Resource.XHR_RESPONSE_TYPE.BLOB) { 17 | // if there is no blob support we probably got a binary string back 18 | if (!window.Blob || typeof resource.data === 'string') { 19 | const type = resource.xhr.getResponseHeader('content-type'); 20 | 21 | // this is an image, convert the binary string into a data url 22 | if (type && type.indexOf('image') === 0) { 23 | resource.data = new Image(); 24 | resource.data.src = `data:${type};base64,${encodeBinary(resource.xhr.responseText)}`; 25 | 26 | resource.type = Resource.TYPE.IMAGE; 27 | 28 | // wait until the image loads and then callback 29 | resource.data.onload = () => { 30 | resource.data.onload = null; 31 | 32 | next(); 33 | }; 34 | 35 | // next will be called on load 36 | return; 37 | } 38 | } 39 | // if content type says this is an image, then we should transform the blob into an Image object 40 | else if (resource.data.type.indexOf('image') === 0) { 41 | const src = Url.createObjectURL(resource.data); 42 | 43 | resource.blob = resource.data; 44 | resource.data = new Image(); 45 | resource.data.src = src; 46 | 47 | resource.type = Resource.TYPE.IMAGE; 48 | 49 | // cleanup the no longer used blob after the image loads 50 | // TODO: Is this correct? Will the image be invalid after revoking? 51 | resource.data.onload = () => { 52 | Url.revokeObjectURL(src); 53 | resource.data.onload = null; 54 | 55 | next(); 56 | }; 57 | 58 | // next will be called on load. 59 | return; 60 | } 61 | } 62 | 63 | next(); 64 | }; 65 | }; 66 | -------------------------------------------------------------------------------- /src/engine/utils/object.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Object utility functions. 3 | * 4 | * @module engine/utils/object 5 | */ 6 | 7 | /** 8 | * Merge any number of associative arrays into first. 9 | * 10 | * All arguments are expected to be associative arrays. 11 | * If same key appears multiple times the final value 12 | * will come from last argument that contains it. 13 | * 14 | * @param {Object} a Objects to merge 15 | * @return {object} first argument 16 | * 17 | * @example 18 | * mergeMultiple({ a: { var_1: 1 } }, { a: { var_1: 2 } }); 19 | * // return { a: { var_1: 2 } } 20 | */ 21 | export function mergeMultiple(a) { 22 | var i = 0, b, key, value; 23 | for (i = 1; i < arguments.length; i++) { 24 | b = arguments[i]; 25 | 26 | for (key in b) { 27 | value = b[key]; 28 | 29 | if (typeof a[key] !== 'undefined') { 30 | if (typeof a[key] === 'object') { 31 | mergeMultiple(a[key], value); 32 | } 33 | else { 34 | a[key] = value; 35 | } 36 | } 37 | else { 38 | a[key] = value; 39 | } 40 | } 41 | } 42 | return a; 43 | }; 44 | 45 | /** 46 | * Deeply merge an object into the another. 47 | * 48 | * @param {Object} original Target object to merge to 49 | * @param {Object} extended Object to merge to the first one 50 | * @return {Object} First object 51 | * 52 | * @example 53 | * merge({ a: { var_1: 1 } }, { a: { var_1: 2 } }); 54 | * // return { a: { var_1: 2 } } 55 | */ 56 | export function merge(original, extended) { 57 | for (var key in extended) { 58 | var ext = extended[key]; 59 | var extType = typeof ext; 60 | if (extType !== 'undefined') { 61 | if (extType !== 'object') { 62 | // ugly, perhaps there is a better way? 63 | if (extType === 'string') { 64 | if (ext === 'true') { 65 | ext = true; 66 | } 67 | else if (ext === 'false') { 68 | ext = false; 69 | } 70 | } 71 | 72 | original[key] = ext; 73 | } 74 | else { 75 | if (!original[key] || typeof(original[key]) !== 'object') { 76 | original[key] = Array.isArray(ext) ? [] : {}; 77 | } 78 | merge(original[key], ext); 79 | } 80 | } 81 | } 82 | 83 | return original; 84 | }; 85 | -------------------------------------------------------------------------------- /src/behaviors/FourWayMove.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Make the Actor able to move horizontally or vertically. 3 | */ 4 | 5 | const keyboard = require('engine/keyboard'); 6 | const Behavior = require('engine/Behavior'); 7 | const Vector = require('engine/Vector'); 8 | 9 | const DefaultSettings = { 10 | /* Move speed */ 11 | Speed: 200, 12 | 13 | /* Whether use keyboard to control */ 14 | UseKeyboard: true, 15 | /* Hold to move left, when `useKeyboard` is true */ 16 | LeftKey: 'LEFT', 17 | /* Hold to move right, when `useKeyboard` is true */ 18 | RightKey: 'RIGHT', 19 | /* Hold to move up, when `useKeyboard` is true */ 20 | UpKey: 'UP', 21 | /* Hold to move down, when `useKeyboard` is true */ 22 | DownKey: 'DOWN', 23 | }; 24 | 25 | class FourWayMove extends Behavior { 26 | constructor() { 27 | super(); 28 | 29 | this.type = 'FourWayMove'; 30 | 31 | this.dir = Vector.create(); 32 | this.left = false; 33 | this.right = false; 34 | this.up = false; 35 | this.down = false; 36 | } 37 | init(ent, settings) { 38 | super.init(ent); 39 | 40 | Object.assign(this, DefaultSettings, settings); 41 | 42 | this.entity.canFixedTick = true; 43 | } 44 | fixedUpdate(_, dt) { 45 | if (this.UseKeyboard) { 46 | this.dir.set(0); 47 | if (keyboard.down(this.LeftKey)) this.dir.x -= 1; 48 | if (keyboard.down(this.RightKey)) this.dir.x += 1; 49 | if (keyboard.down(this.UpKey)) this.dir.y -= 1; 50 | if (keyboard.down(this.DownKey)) this.dir.y += 1; 51 | } 52 | 53 | this.dir.normalize(); 54 | 55 | this.entity.position.x += this.dir.x * this.Speed * dt; 56 | this.entity.position.y += this.dir.y * this.Speed * dt; 57 | } 58 | 59 | // Actions 60 | // Move left 61 | moveLeft() { 62 | this.dir.x = -1; 63 | } 64 | // Move right 65 | moveRight() { 66 | this.dir.x = 1; 67 | } 68 | // Move up 69 | moveUp() { 70 | this.dir.y = -1; 71 | } 72 | // Move down 73 | moveDown() { 74 | this.dir.y = 1; 75 | } 76 | // Stop horizontal movement 77 | stopX() { 78 | this.dir.x = 0; 79 | } 80 | // Stop vertical movement 81 | stopY() { 82 | this.dir.y = 0; 83 | } 84 | // Stop 85 | stop() { 86 | this.dir.set(0); 87 | } 88 | } 89 | 90 | Behavior.register('FourWayMove', FourWayMove); 91 | 92 | module.exports = FourWayMove; 93 | -------------------------------------------------------------------------------- /src/engine/gfx/filters/blur/BlurXFilter.js: -------------------------------------------------------------------------------- 1 | const AbstractFilter = require('../../core/renderers/webgl/filters/AbstractFilter'); 2 | 3 | /** 4 | * The BlurXFilter applies a horizontal Gaussian blur to an object. 5 | * 6 | * @class 7 | * @extends AbstractFilter 8 | */ 9 | function BlurXFilter() { 10 | AbstractFilter.call(this, 11 | // vertex shader 12 | require('./blurX.vert'), 13 | // fragment shader 14 | require('./blur.frag'), 15 | // set the uniforms 16 | { 17 | strength: { type: '1f', value: 1 }, 18 | } 19 | ); 20 | 21 | /** 22 | * Sets the number of passes for blur. More passes means higher quaility bluring. 23 | * 24 | * @member {number} 25 | * @default 1 26 | */ 27 | this.passes = 1; 28 | 29 | this.strength = 4; 30 | } 31 | 32 | BlurXFilter.prototype = Object.create(AbstractFilter.prototype); 33 | BlurXFilter.prototype.constructor = BlurXFilter; 34 | module.exports = BlurXFilter; 35 | 36 | BlurXFilter.prototype.applyFilter = function(renderer, input, output, clear) { 37 | var shader = this.getShader(renderer); 38 | 39 | this.uniforms.strength.value = this.strength / 4 / this.passes * (input.frame.width / input.size.width); 40 | 41 | if (this.passes === 1) { 42 | renderer.filterManager.applyFilter(shader, input, output, clear); 43 | } 44 | else { 45 | var renderTarget = renderer.filterManager.getRenderTarget(true); 46 | var flip = input; 47 | var flop = renderTarget; 48 | 49 | for (var i = 0; i < this.passes - 1; i++) { 50 | renderer.filterManager.applyFilter(shader, flip, flop, true); 51 | 52 | var temp = flop; 53 | flop = flip; 54 | flip = temp; 55 | } 56 | 57 | renderer.filterManager.applyFilter(shader, flip, output, clear); 58 | 59 | renderer.filterManager.returnRenderTarget(renderTarget); 60 | } 61 | }; 62 | 63 | 64 | Object.defineProperties(BlurXFilter.prototype, { 65 | /** 66 | * Sets the strength of both the blur. 67 | * 68 | * @member {number} 69 | * @memberof filters.BlurXFilter# 70 | * @default 2 71 | */ 72 | blur: { 73 | get: function() { 74 | return this.strength; 75 | }, 76 | set: function(value) { 77 | this.padding = Math.abs(value) * 0.5; 78 | this.strength = value; 79 | }, 80 | }, 81 | }); 82 | -------------------------------------------------------------------------------- /src/engine/gfx/core/math/Rectangle.js: -------------------------------------------------------------------------------- 1 | import { SHAPES } from '../../const'; 2 | 3 | /** 4 | * the Rectangle object is an area defined by its position, as indicated by its top-left corner point (x, y) and by its width and its height. 5 | * 6 | * @class 7 | */ 8 | export default class Rectangle { 9 | /** 10 | * @constructor 11 | * @param {number} x The X coordinate of the upper-left corner of the rectangle 12 | * @param {number} y The Y coordinate of the upper-left corner of the rectangle 13 | * @param {number} width The overall width of this rectangle 14 | * @param {number} height The overall height of this rectangle 15 | */ 16 | constructor(x, y, width, height) { 17 | /** 18 | * @member {number} 19 | * @default 0 20 | */ 21 | this.x = x || 0; 22 | 23 | /** 24 | * @member {number} 25 | * @default 0 26 | */ 27 | this.y = y || 0; 28 | 29 | /** 30 | * @member {number} 31 | * @default 0 32 | */ 33 | this.width = width || 0; 34 | 35 | /** 36 | * @member {number} 37 | * @default 0 38 | */ 39 | this.height = height || 0; 40 | 41 | /** 42 | * The type of the object, mainly used to avoid `instanceof` checks 43 | * 44 | * @member {number} 45 | */ 46 | this.type = SHAPES.RECT; 47 | } 48 | 49 | /** 50 | * Creates a clone of this Rectangle 51 | * 52 | * @return {Rectangle} a copy of the rectangle 53 | */ 54 | clone() { 55 | return new Rectangle(this.x, this.y, this.width, this.height); 56 | } 57 | 58 | /** 59 | * Checks whether the x and y coordinates given are contained within this Rectangle 60 | * 61 | * @param {number} x The X coordinate of the point to test 62 | * @param {number} y The Y coordinate of the point to test 63 | * @return {boolean} Whether the x/y coordinates are within this Rectangle 64 | */ 65 | contains(x, y) { 66 | if (this.width <= 0 || this.height <= 0) { 67 | return false; 68 | } 69 | 70 | if (x >= this.x && x < this.x + this.width) { 71 | if (y >= this.y && y < this.y + this.height) { 72 | return true; 73 | } 74 | } 75 | 76 | return false; 77 | } 78 | } 79 | 80 | /** 81 | * A constant empty rectangle. 82 | * 83 | * @static 84 | * @constant 85 | */ 86 | Rectangle.EMPTY = new Rectangle(0, 0, 0, 0); 87 | -------------------------------------------------------------------------------- /src/engine/gfx/core/renderers/canvas/utils/CanvasBuffer.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Creates a Canvas element of the given size. 3 | * 4 | * @class 5 | * @param width {number} the width for the newly created canvas 6 | * @param height {number} the height for the newly created canvas 7 | */ 8 | export default class CanvasBuffer { 9 | constructor(width, height) { 10 | /** 11 | * The Canvas object that belongs to this CanvasBuffer. 12 | * 13 | * @member {HTMLCanvasElement} 14 | */ 15 | this.canvas = document.createElement('canvas'); 16 | 17 | /** 18 | * A CanvasRenderingContext2D object representing a two-dimensional rendering context. 19 | * 20 | * @member {CanvasRenderingContext2D} 21 | */ 22 | this.context = this.canvas.getContext('2d'); 23 | 24 | this.canvas.width = width; 25 | this.canvas.height = height; 26 | } 27 | 28 | /** 29 | * Clears the canvas that was created by the CanvasBuffer class. 30 | * 31 | * @private 32 | */ 33 | clear() { 34 | this.context.setTransform(1, 0, 0, 1, 0, 0); 35 | this.context.clearRect(0,0, this.canvas.width, this.canvas.height); 36 | } 37 | 38 | /** 39 | * Resizes the canvas to the specified width and height. 40 | * 41 | * @param width {number} the new width of the canvas 42 | * @param height {number} the new height of the canvas 43 | */ 44 | resize(width, height) { 45 | this.canvas.width = width; 46 | this.canvas.height = height; 47 | } 48 | 49 | /** 50 | * Destroys this canvas. 51 | * 52 | */ 53 | destroy() { 54 | this.context = null; 55 | this.canvas = null; 56 | } 57 | } 58 | 59 | Object.defineProperties(CanvasBuffer.prototype, { 60 | /** 61 | * The width of the canvas buffer in pixels. 62 | * 63 | * @member {number} 64 | * @memberof PIXI.CanvasBuffer# 65 | */ 66 | width: { 67 | get: function() { 68 | return this.canvas.width; 69 | }, 70 | set: function(val) { 71 | this.canvas.width = val; 72 | }, 73 | }, 74 | /** 75 | * The height of the canvas buffer in pixels. 76 | * 77 | * @member {number} 78 | * @memberof PIXI.CanvasBuffer# 79 | */ 80 | height: { 81 | get: function() { 82 | return this.canvas.height; 83 | }, 84 | set: function(val) { 85 | this.canvas.height = val; 86 | }, 87 | }, 88 | }); 89 | -------------------------------------------------------------------------------- /src/engine/gfx/filters/dropshadow/BlurYTintFilter.js: -------------------------------------------------------------------------------- 1 | const AbstractFilter = require('../../core/renderers/webgl/filters/AbstractFilter'); 2 | 3 | /** 4 | * The BlurYTintFilter applies a vertical Gaussian blur to an object. 5 | * 6 | * @class 7 | * @extends AbstractFilter 8 | */ 9 | function BlurYTintFilter() { 10 | AbstractFilter.call(this, 11 | // vertex shader 12 | require('./blurYTint.vert'), 13 | // fragment shader 14 | require('./blurYTint.frag'), 15 | // set the uniforms 16 | { 17 | blur: { type: '1f', value: 1 / 512 }, 18 | color: { type: 'c', value: [0,0,0] }, 19 | alpha: { type: '1f', value: 0.7 }, 20 | offset: { type: '2f', value:[5, 5] }, 21 | strength: { type: '1f', value:1 }, 22 | } 23 | ); 24 | 25 | this.passes = 1; 26 | this.strength = 4; 27 | } 28 | 29 | BlurYTintFilter.prototype = Object.create(AbstractFilter.prototype); 30 | BlurYTintFilter.prototype.constructor = BlurYTintFilter; 31 | module.exports = BlurYTintFilter; 32 | 33 | BlurYTintFilter.prototype.applyFilter = function(renderer, input, output, clear) { 34 | var shader = this.getShader(renderer); 35 | 36 | this.uniforms.strength.value = this.strength / 4 / this.passes * (input.frame.height / input.size.height); 37 | 38 | if (this.passes === 1) { 39 | renderer.filterManager.applyFilter(shader, input, output, clear); 40 | } 41 | else { 42 | var renderTarget = renderer.filterManager.getRenderTarget(true); 43 | var flip = input; 44 | var flop = renderTarget; 45 | 46 | for (var i = 0; i < this.passes - 1; i++) { 47 | renderer.filterManager.applyFilter(shader, flip, flop, clear); 48 | 49 | var temp = flop; 50 | flop = flip; 51 | flip = temp; 52 | } 53 | 54 | renderer.filterManager.applyFilter(shader, flip, output, clear); 55 | 56 | renderer.filterManager.returnRenderTarget(renderTarget); 57 | } 58 | }; 59 | 60 | 61 | Object.defineProperties(BlurYTintFilter.prototype, { 62 | /** 63 | * Sets the strength of both the blur. 64 | * 65 | * @member {number} 66 | * @memberof filters.BlurYTintFilter# 67 | * @default 2 68 | */ 69 | blur: { 70 | get: function() { 71 | return this.strength; 72 | }, 73 | set: function(value) { 74 | this.padding = value * 0.5; 75 | this.strength = value; 76 | }, 77 | }, 78 | }); 79 | -------------------------------------------------------------------------------- /src/engine/loader/b64.js: -------------------------------------------------------------------------------- 1 | const _keyStr = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/='; 2 | 3 | export function encodeBinary(input) { 4 | let output = ''; 5 | let inx = 0; 6 | 7 | while (inx < input.length) { 8 | // Fill byte buffer array 9 | const bytebuffer = [0, 0, 0]; 10 | const encodedCharIndexes = [0, 0, 0, 0]; 11 | 12 | for (let jnx = 0; jnx < bytebuffer.length; ++jnx) { 13 | if (inx < input.length) { 14 | // throw away high-order byte, as documented at: 15 | // https://developer.mozilla.org/En/Using_XMLHttpRequest#Handling_binary_data 16 | bytebuffer[jnx] = input.charCodeAt(inx++) & 0xff; 17 | } 18 | else { 19 | bytebuffer[jnx] = 0; 20 | } 21 | } 22 | 23 | // Get each encoded character, 6 bits at a time 24 | // index 1: first 6 bits 25 | encodedCharIndexes[0] = bytebuffer[0] >> 2; 26 | 27 | // index 2: second 6 bits (2 least significant bits from input byte 1 + 4 most significant bits from byte 2) 28 | encodedCharIndexes[1] = ((bytebuffer[0] & 0x3) << 4) | (bytebuffer[1] >> 4); 29 | 30 | // index 3: third 6 bits (4 least significant bits from input byte 2 + 2 most significant bits from byte 3) 31 | encodedCharIndexes[2] = ((bytebuffer[1] & 0x0f) << 2) | (bytebuffer[2] >> 6); 32 | 33 | // index 3: forth 6 bits (6 least significant bits from input byte 3) 34 | encodedCharIndexes[3] = bytebuffer[2] & 0x3f; 35 | 36 | // Determine whether padding happened, and adjust accordingly 37 | const paddingBytes = inx - (input.length - 1); 38 | 39 | switch (paddingBytes) { 40 | case 2: 41 | // Set last 2 characters to padding char 42 | encodedCharIndexes[3] = 64; 43 | encodedCharIndexes[2] = 64; 44 | break; 45 | 46 | case 1: 47 | // Set last character to padding char 48 | encodedCharIndexes[3] = 64; 49 | break; 50 | 51 | default: 52 | break; // No padding - proceed 53 | } 54 | 55 | // Now we will grab each appropriate character out of our keystring 56 | // based on our index array and append it to the output string 57 | for (let jnx = 0; jnx < encodedCharIndexes.length; ++jnx) { 58 | output += _keyStr.charAt(encodedCharIndexes[jnx]); 59 | } 60 | } 61 | 62 | return output; 63 | }; 64 | -------------------------------------------------------------------------------- /index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Lesser Panda 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 70 | 71 | 72 |
73 | 74 |
75 | 76 | 77 | 78 | -------------------------------------------------------------------------------- /src/engine/gfx/filters/shockwave/ShockwaveFilter.js: -------------------------------------------------------------------------------- 1 | const AbstractFilter = require('../../core/renderers/webgl/filters/AbstractFilter'); 2 | 3 | /** 4 | * The ColorMatrixFilter class lets you apply a 4x4 matrix transformation on the RGBA 5 | * color and alpha values of every pixel on your displayObject to produce a result 6 | * with a new set of RGBA color and alpha values. It's pretty powerful! 7 | * 8 | * @class 9 | * @extends AbstractFilter 10 | */ 11 | function ShockwaveFilter() { 12 | AbstractFilter.call(this, 13 | // vertex shader 14 | null, 15 | // fragment shader 16 | require('./shockwave.frag'), 17 | // custom uniforms 18 | { 19 | center: { type: 'v2', value: { x: 0.5, y: 0.5 } }, 20 | params: { type: 'v3', value: { x: 10, y: 0.8, z: 0.1 } }, 21 | time: { type: '1f', value: 0 }, 22 | } 23 | ); 24 | } 25 | 26 | ShockwaveFilter.prototype = Object.create(AbstractFilter.prototype); 27 | ShockwaveFilter.prototype.constructor = ShockwaveFilter; 28 | module.exports = ShockwaveFilter; 29 | 30 | Object.defineProperties(ShockwaveFilter.prototype, { 31 | /** 32 | * Sets the center of the shockwave in normalized screen coords. That is 33 | * (0,0) is the top-left and (1,1) is the bottom right. 34 | * 35 | * @member {object} 36 | * @memberof filters.ShockwaveFilter# 37 | */ 38 | center: { 39 | get: function() { 40 | return this.uniforms.center.value; 41 | }, 42 | set: function(value) { 43 | this.uniforms.center.value = value; 44 | }, 45 | }, 46 | /** 47 | * Sets the params of the shockwave. These modify the look and behavior of 48 | * the shockwave as it ripples out. 49 | * 50 | * @member {object} 51 | * @memberof filters.ShockwaveFilter# 52 | */ 53 | params: { 54 | get: function() { 55 | return this.uniforms.params.value; 56 | }, 57 | set: function(value) { 58 | this.uniforms.params.value = value; 59 | }, 60 | }, 61 | /** 62 | * Sets the elapsed time of the shockwave. This controls the speed at which 63 | * the shockwave ripples out. 64 | * 65 | * @member {number} 66 | * @memberof filters.ShockwaveFilter# 67 | */ 68 | time: { 69 | get: function() { 70 | return this.uniforms.time.value; 71 | }, 72 | set: function(value) { 73 | this.uniforms.time.value = value; 74 | }, 75 | }, 76 | }); 77 | -------------------------------------------------------------------------------- /src/engine/gfx/core/math/Ellipse.js: -------------------------------------------------------------------------------- 1 | import Rectangle from './Rectangle'; 2 | import { SHAPES } from '../../const'; 3 | 4 | /** 5 | * The Ellipse object can be used to specify a hit area for displayObjects 6 | * 7 | * @class 8 | */ 9 | export default class Ellipse { 10 | /** 11 | * @constructor 12 | * @param {number} x The X coordinate of the center of the ellipse 13 | * @param {number} y The Y coordinate of the center of the ellipse 14 | * @param {number} width The half width of this ellipse 15 | * @param {number} height The half height of this ellipse 16 | */ 17 | constructor(x = 0, y = 0, width = 0, height = 0) { 18 | /** 19 | * @member {number} 20 | * @default 0 21 | */ 22 | this.x = x; 23 | 24 | /** 25 | * @member {number} 26 | * @default 0 27 | */ 28 | this.y = y; 29 | 30 | /** 31 | * @member {number} 32 | * @default 0 33 | */ 34 | this.width = width; 35 | 36 | /** 37 | * @member {number} 38 | * @default 0 39 | */ 40 | this.height = height; 41 | 42 | /** 43 | * The type of the object, mainly used to avoid `instanceof` checks 44 | * 45 | * @member {number} 46 | */ 47 | this.type = SHAPES.ELIP; 48 | } 49 | 50 | /** 51 | * Creates a clone of this Ellipse instance 52 | * 53 | * @return {Ellipse} a copy of the ellipse 54 | */ 55 | clone() { 56 | return new Ellipse(this.x, this.y, this.width, this.height); 57 | } 58 | 59 | /** 60 | * Checks whether the x and y coordinates given are contained within this ellipse 61 | * 62 | * @param {number} x The X coordinate of the point to test 63 | * @param {number} y The Y coordinate of the point to test 64 | * @return {boolean} Whether the x/y coords are within this ellipse 65 | */ 66 | contains(x, y) { 67 | if (this.width <= 0 || this.height <= 0) { 68 | return false; 69 | } 70 | 71 | // normalize the coords to an ellipse with center 0,0 72 | var normx = ((x - this.x) / this.width), 73 | normy = ((y - this.y) / this.height); 74 | 75 | normx *= normx; 76 | normy *= normy; 77 | 78 | return (normx + normy <= 1); 79 | } 80 | 81 | /** 82 | * Returns the framing rectangle of the ellipse as a Rectangle object 83 | * 84 | * @return {Rectangle} the framing rectangle 85 | */ 86 | getBounds() { 87 | return new Rectangle(this.x - this.width, this.y - this.height, this.width, this.height); 88 | } 89 | } 90 | -------------------------------------------------------------------------------- /src/behaviors/HorizontalMove.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Make the target object able to move horizontally. 3 | */ 4 | 5 | const keyboard = require('engine/keyboard'); 6 | const Behavior = require('engine/Behavior'); 7 | 8 | const DefaultSettings = { 9 | /* Move speed */ 10 | Speed: 200, 11 | 12 | /* Range of the movement, limit to a range or keep it undefined to avoid */ 13 | Range: undefined, 14 | /** 15 | * Percentage of start x location in the range if exist 16 | * when range is defined 17 | */ 18 | StartPct: 0, 19 | 20 | /* Whether use keyboard to control */ 21 | UseKeyboard: true, 22 | /* Hold to move left, when `useKeyboard` is true */ 23 | LeftKey: 'LEFT', 24 | /* Hold to move right, when `useKeyboard` is true */ 25 | RightKey: 'RIGHT', 26 | }; 27 | 28 | class HorizontalMove extends Behavior { 29 | constructor() { 30 | super(); 31 | 32 | this.type = 'HorizontalMove'; 33 | 34 | this.dir = 0; 35 | this.left = 0; 36 | this.right = 0; 37 | this.hasRange = false; 38 | } 39 | init(ent, settings) { 40 | super.init(ent); 41 | 42 | Object.assign(this, DefaultSettings, settings); 43 | 44 | this.entity.canFixedTick = true; 45 | 46 | this.hasRange = Number.isFinite(this.Range); 47 | if (this.hasRange) { 48 | this.left = this.entity.position.x - this.Range * this.StartPct; 49 | this.right = this.left + this.Range; 50 | } 51 | } 52 | fixedUpdate(_, dt) { 53 | if (this.UseKeyboard) { 54 | this.dir = 0; 55 | if (keyboard.down(this.LeftKey)) this.dir -= 1; 56 | if (keyboard.down(this.RightKey)) this.dir += 1; 57 | } 58 | 59 | this.entity.position.x += this.dir * this.Speed * dt; 60 | 61 | if (this.dir !== 0 && this.hasRange) { 62 | if (this.entity.position.x > this.right) { 63 | this.entity.position.x = this.right; 64 | this.dir = 0; 65 | this.entity.events.emit('reachEnd'); 66 | } 67 | else if (this.entity.position.x < this.left) { 68 | this.entity.position.x = this.left; 69 | this.dir = 0; 70 | this.entity.events.emit('reachStart'); 71 | } 72 | } 73 | } 74 | 75 | // Actions 76 | // Start to move left 77 | moveLeft() { 78 | this.dir = -1; 79 | } 80 | // Start to move right 81 | moveRight() { 82 | this.dir = 1; 83 | } 84 | // Stop 85 | stop() { 86 | this.dir = 0; 87 | } 88 | } 89 | 90 | Behavior.register('HorizontalMove', HorizontalMove); 91 | 92 | module.exports = HorizontalMove; 93 | -------------------------------------------------------------------------------- /src/behaviors/VerticalMove.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Make the target object able to move vertically. 3 | * 4 | * @protocol { 5 | * position: Vector 6 | * } 7 | */ 8 | 9 | const keyboard = require('engine/keyboard'); 10 | const Behavior = require('engine/Behavior'); 11 | 12 | const DefaultSettings = { 13 | /* Move speed */ 14 | Speed: 200, 15 | 16 | /* Range of the movement, limit to a range or keep it undefined to avoid */ 17 | Range: undefined, 18 | /** 19 | * Percentage of start y location in the range if exist 20 | * when range is defined 21 | */ 22 | StartPct: 0, 23 | 24 | /* Whether use keyboard to control */ 25 | UseKeyboard: true, 26 | /* Hold to move up, when `useKeyboard` is true */ 27 | UpKey: 'UP', 28 | /* Hold to move down, when `useKeyboard` is true */ 29 | DownKey: 'DOWN', 30 | }; 31 | 32 | class VerticalMove extends Behavior { 33 | constructor() { 34 | super(); 35 | 36 | this.type = 'VerticalMove'; 37 | 38 | this.dir = 0; 39 | this.top = 0; 40 | this.bottom = 0; 41 | this.hasRange = false; 42 | } 43 | init(ent, settings) { 44 | super.init(ent); 45 | 46 | Object.assign(this, DefaultSettings, settings); 47 | 48 | this.entity.canFixedTick = true; 49 | 50 | this.hasRange = Number.isFinite(this.Range); 51 | if (this.hasRange) { 52 | this.top = this.entity.position.y - this.Range * this.StartPct; 53 | this.bottom = this.top + this.Range; 54 | } 55 | } 56 | fixedUpdate(_, dt) { 57 | if (this.UseKeyboard) { 58 | this.dir = 0; 59 | if (keyboard.down(this.UpKey)) this.dir -= 1; 60 | if (keyboard.down(this.DownKey)) this.dir += 1; 61 | } 62 | 63 | this.entity.position.y += this.dir * this.Speed * dt; 64 | 65 | if (this.dir !== 0 && this.hasRange) { 66 | if (this.entity.position.y > this.bottom) { 67 | this.entity.position.y = this.bottom; 68 | this.dir = 0; 69 | this.entity.events.emit('reachEnd'); 70 | } 71 | else if (this.entity.position.y < this.top) { 72 | this.entity.position.y = this.top; 73 | this.dir = 0; 74 | this.entity.events.emit('reachStart'); 75 | } 76 | } 77 | } 78 | 79 | // Actions 80 | // Start to move up 81 | moveUp() { 82 | this.dir = -1; 83 | } 84 | // Start to move down 85 | moveDown() { 86 | this.dir = 1; 87 | } 88 | // Stop 89 | stop() { 90 | this.dir = 0; 91 | } 92 | } 93 | 94 | Behavior.register('VerticalMove', VerticalMove); 95 | 96 | module.exports = VerticalMove; 97 | -------------------------------------------------------------------------------- /src/engine/gfx/AnimatedSprite.js: -------------------------------------------------------------------------------- 1 | import AnimatedSprite from './core/sprites/AnimatedSprite'; 2 | import { BLEND_MODES } from './const'; 3 | import './core/sprites/webgl/SpriteRenderer'; 4 | 5 | /** 6 | * Factory function for `AnimatedSprite`. 7 | * 8 | * @example 9 | * // Create an instance 10 | * let anim = AnimatedSprite({ 11 | * textures: [...], 12 | * anims: [ 13 | * // Each animation as an array 14 | * ['idle', [0,1], { loop: true, speed: 5 }], 15 | * ['walk', [2,3,4,5], { loop: true, speed: 8 }], 16 | * ['jump', [6,7], { loop: false, speed: 4 }], 17 | * 18 | * // Or as a object with named settings 19 | * { name: 'fall', frames: [8], settings: { loop: false, speed: 1 } }, 20 | * ], 21 | * }) 22 | * 23 | * @example 24 | * // Play pre-defined animations, and switch to fall when it's finished 25 | * anim.play('jump').once('finish', () => { 26 | * anim.play('fall'); 27 | * }); 28 | * 29 | * @param {object} data Data to create AnimatedSprite from 30 | * @return {AnimatedSprite} AnimatedSprite instance 31 | */ 32 | export default function(data) { 33 | const inst = new AnimatedSprite(data.textures); 34 | const anims = data.anims; 35 | 36 | let def, i; 37 | if (Array.isArray(anims)) { 38 | for (i = 0; i < anims.length; i++) { 39 | def = anims[i]; 40 | if (Array.isArray(def)) { 41 | inst.addAnim(def[0], def[1], def[2]); 42 | } 43 | else { 44 | inst.addAnim(def.name, def.frames, def.settings); 45 | } 46 | } 47 | } 48 | 49 | for (let k in data) { 50 | switch (k) { 51 | // Directly set 52 | // - Node 53 | case 'alpha': 54 | case 'width': 55 | case 'height': 56 | case 'rotation': 57 | case 'visible': 58 | case 'x': 59 | case 'y': 60 | case 'interactive': 61 | // - Sprite 62 | case 'tint': 63 | inst[k] = data[k]; 64 | break; 65 | 66 | // Set vector 67 | // - Node 68 | case 'pivot': 69 | case 'position': 70 | case 'skew': 71 | 72 | // - Sprite 73 | case 'anchor': 74 | inst[k].x = data[k].x || 0; 75 | inst[k].y = data[k].y || 0; 76 | break; 77 | 78 | // - Node 79 | case 'scale': 80 | inst[k].x = data[k].x || 1; 81 | inst[k].y = data[k].y || 1; 82 | break; 83 | 84 | // Set blend mode 85 | case 'blendMode': 86 | inst.blendMode = BLEND_MODES[data[k]]; 87 | break; 88 | } 89 | } 90 | 91 | return inst; 92 | }; 93 | -------------------------------------------------------------------------------- /src/engine/gfx/core/math/RoundedRectangle.js: -------------------------------------------------------------------------------- 1 | import { SHAPES } from '../../const'; 2 | 3 | /** 4 | * The Rounded Rectangle object is an area that has nice rounded corners, as indicated by its top-left corner point (x, y) and by its width and its height and its radius. 5 | * 6 | * @class 7 | */ 8 | export default class RoundedRectangle { 9 | /** 10 | * @constructor 11 | * @param {number} x The X coordinate of the upper-left corner of the rounded rectangle 12 | * @param {number} y The Y coordinate of the upper-left corner of the rounded rectangle 13 | * @param {number} width The overall width of this rounded rectangle 14 | * @param {number} height The overall height of this rounded rectangle 15 | * @param {number} radius Controls the radius of the rounded corners 16 | */ 17 | constructor(x, y, width, height, radius) { 18 | /** 19 | * @member {number} 20 | * @default 0 21 | */ 22 | this.x = x || 0; 23 | 24 | /** 25 | * @member {number} 26 | * @default 0 27 | */ 28 | this.y = y || 0; 29 | 30 | /** 31 | * @member {number} 32 | * @default 0 33 | */ 34 | this.width = width || 0; 35 | 36 | /** 37 | * @member {number} 38 | * @default 0 39 | */ 40 | this.height = height || 0; 41 | 42 | /** 43 | * @member {number} 44 | * @default 20 45 | */ 46 | this.radius = radius || 20; 47 | 48 | /** 49 | * The type of the object, mainly used to avoid `instanceof` checks 50 | * 51 | * @member {number} 52 | */ 53 | this.type = SHAPES.RREC; 54 | } 55 | 56 | /** 57 | * Creates a clone of this Rounded Rectangle 58 | * 59 | * @return {RoundedRectangle} a copy of the rounded rectangle 60 | */ 61 | clone() { 62 | return new RoundedRectangle(this.x, this.y, this.width, this.height, this.radius); 63 | } 64 | 65 | /** 66 | * Checks whether the x and y coordinates given are contained within this Rounded Rectangle 67 | * 68 | * @param {number} x The X coordinate of the point to test 69 | * @param {number} y The Y coordinate of the point to test 70 | * @return {boolean} Whether the x/y coordinates are within this Rounded Rectangle 71 | */ 72 | contains(x, y) { 73 | if (this.width <= 0 || this.height <= 0) { 74 | return false; 75 | } 76 | 77 | if (x >= this.x && x <= this.x + this.width) { 78 | if (y >= this.y && y <= this.y + this.height) { 79 | return true; 80 | } 81 | } 82 | 83 | return false; 84 | } 85 | } 86 | -------------------------------------------------------------------------------- /src/engine/gfx/core/renderers/webgl/filters/SpriteMaskFilter.js: -------------------------------------------------------------------------------- 1 | import AbstractFilter from './AbstractFilter'; 2 | import Matrix from '../../../math/Matrix'; 3 | 4 | /** 5 | * The SpriteMaskFilter class 6 | * 7 | * @class 8 | * @extends AbstractFilter 9 | * @param sprite {Sprite} the target sprite 10 | */ 11 | export default function SpriteMaskFilter(sprite) { 12 | var maskMatrix = new Matrix(); 13 | 14 | AbstractFilter.call(this, 15 | require('./spriteMaskFilter.vert'), 16 | require('./spriteMaskFilter.frag'), 17 | { 18 | mask: { type: 'sampler2D', value: sprite._texture }, 19 | alpha: { type: 'f', value: 1 }, 20 | otherMatrix: { type: 'mat3', value: maskMatrix.toArray(true) }, 21 | } 22 | ); 23 | 24 | this.maskSprite = sprite; 25 | this.maskMatrix = maskMatrix; 26 | } 27 | 28 | SpriteMaskFilter.prototype = Object.create(AbstractFilter.prototype); 29 | SpriteMaskFilter.prototype.constructor = SpriteMaskFilter; 30 | 31 | /** 32 | * Applies the filter 33 | * 34 | * @param renderer {WebGLRenderer} The renderer to retrieve the filter from 35 | * @param input {RenderTarget} 36 | * @param output {RenderTarget} 37 | */ 38 | SpriteMaskFilter.prototype.applyFilter = function(renderer, input, output) { 39 | var filterManager = renderer.filterManager; 40 | 41 | this.uniforms.mask.value = this.maskSprite._texture; 42 | 43 | filterManager.calculateMappedMatrix(input.frame, this.maskSprite, this.maskMatrix); 44 | 45 | this.uniforms.otherMatrix.value = this.maskMatrix.toArray(true); 46 | this.uniforms.alpha.value = this.maskSprite.worldAlpha; 47 | 48 | var shader = this.getShader(renderer); 49 | // draw the filter... 50 | filterManager.applyFilter(shader, input, output); 51 | }; 52 | 53 | 54 | Object.defineProperties(SpriteMaskFilter.prototype, { 55 | /** 56 | * The texture used for the displacement map. Must be power of 2 sized texture. 57 | * 58 | * @member {Texture} 59 | * @memberof SpriteMaskFilter# 60 | */ 61 | map: { 62 | get: function() { 63 | return this.uniforms.mask.value; 64 | }, 65 | set: function(value) { 66 | this.uniforms.mask.value = value; 67 | }, 68 | }, 69 | 70 | /** 71 | * The offset used to move the displacement map. 72 | * 73 | * @member {Point} 74 | * @memberof SpriteMaskFilter# 75 | */ 76 | offset: { 77 | get: function() { 78 | return this.uniforms.offset.value; 79 | }, 80 | set: function(value) { 81 | this.uniforms.offset.value = value; 82 | }, 83 | }, 84 | }); 85 | -------------------------------------------------------------------------------- /src/engine/gfx/core/graphics/GraphicsData.js: -------------------------------------------------------------------------------- 1 | /** 2 | * A GraphicsData object. 3 | * 4 | * @class 5 | */ 6 | export default class GraphicsData { 7 | /** 8 | * @constructor 9 | * @param {number} lineWidth the width of the line to draw 10 | * @param {number} lineColor the color of the line to draw 11 | * @param {number} lineAlpha the alpha of the line to draw 12 | * @param {number} fillColor the color of the fill 13 | * @param {number} fillAlpha the alpha of the fill 14 | * @param {boolean} fill whether or not the shape is filled with a colour 15 | * @param {Circle|Rectangle|Ellipse|Line|Polygon} shape The shape object to draw. 16 | */ 17 | constructor(lineWidth, lineColor, lineAlpha, fillColor, fillAlpha, fill, shape) { 18 | /* 19 | * @member {number} the width of the line to draw 20 | */ 21 | this.lineWidth = lineWidth; 22 | 23 | /* 24 | * @member {number} the color of the line to draw 25 | */ 26 | this.lineColor = lineColor; 27 | /* 28 | * @member {number} the alpha of the line to draw 29 | */ 30 | this.lineAlpha = lineAlpha; 31 | /* 32 | * @member {number} cached tint of the line to draw 33 | */ 34 | this._lineTint = lineColor; 35 | 36 | /* 37 | * @member {number} the color of the fill 38 | */ 39 | this.fillColor = fillColor; 40 | 41 | /* 42 | * @member {number} the alpha of the fill 43 | */ 44 | this.fillAlpha = fillAlpha; 45 | 46 | /* 47 | * @member {number} cached tint of the fill 48 | */ 49 | this._fillTint = fillColor; 50 | 51 | /* 52 | * @member {boolean} whether or not the shape is filled with a colour 53 | */ 54 | this.fill = fill; 55 | 56 | /* 57 | * @member {PIXI.Circle|PIXI.Rectangle|PIXI.Ellipse|PIXI.Line|PIXI.Polygon} The shape object to draw. 58 | */ 59 | this.shape = shape; 60 | 61 | /* 62 | * @member {number} The type of the shape, see the Const.Shapes file for all the existing types, 63 | */ 64 | this.type = shape.type; 65 | } 66 | 67 | /** 68 | * Creates a new GraphicsData object with the same values as this one. 69 | * 70 | * @return {GraphicsData} Cloned GraphicsData instance 71 | */ 72 | clone() { 73 | return new GraphicsData( 74 | this.lineWidth, 75 | this.lineColor, 76 | this.lineAlpha, 77 | this.fillColor, 78 | this.fillAlpha, 79 | this.fill, 80 | this.shape 81 | ); 82 | } 83 | 84 | /** 85 | * Destroys the Graphics data. 86 | */ 87 | destroy() { 88 | this.shape = null; 89 | } 90 | } 91 | -------------------------------------------------------------------------------- /src/behaviors/WrapAroundScreen.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Wrap around the screen, works with or without camera. 3 | */ 4 | 5 | const core = require('engine/core'); 6 | const Behavior = require('engine/Behavior'); 7 | 8 | const DefaultSettings = { 9 | Vertical: true, 10 | Horizontal: true, 11 | }; 12 | 13 | export default class WrapAroundScreen extends Behavior { 14 | constructor() { 15 | super(); 16 | 17 | this.type = 'WrapAroundScreen'; 18 | 19 | // Constants 20 | this.Vertical = true; 21 | this.Horizontal = true; 22 | 23 | // Properties 24 | this.left = 0; 25 | this.right = 0; 26 | this.top = 0; 27 | this.bottom = 0; 28 | this.width = 0; 29 | this.height = 0; 30 | 31 | this.targetLeft = 0; 32 | this.targetRight = 0; 33 | this.targetTop = 0; 34 | this.targetBottom = 0; 35 | 36 | this.targetBounds; 37 | } 38 | init(ent, settings) { 39 | super.init(ent); 40 | 41 | Object.assign(this, DefaultSettings, settings); 42 | 43 | this.entity.canFixedTick = true; 44 | } 45 | fixedUpdate(_, dt) { 46 | // Update bounds 47 | if (this.entity.game.camera) { 48 | this.left = this.entity.game.camera.left; 49 | this.right = this.entity.game.camera.right; 50 | this.up = this.entity.game.camera.up; 51 | this.bottom = this.entity.game.camera.bottom; 52 | } 53 | else { 54 | this.left = 0; 55 | this.right = core.width; 56 | this.up = 0; 57 | this.bottom = core.height; 58 | } 59 | this.width = this.right - this.left; 60 | this.height = this.bottom - this.up; 61 | 62 | this.targetBounds = this.entity.gfx.getLocalBounds(); 63 | this.targetLeft = this.targetBounds.x + this.entity.position.x; 64 | this.targetRight = this.targetBounds.x + this.targetBounds.width + this.entity.position.x; 65 | this.targetTop = this.targetBounds.y + this.entity.position.y; 66 | this.targetBottom = this.targetBounds.y + this.targetBounds.height + this.entity.position.y; 67 | 68 | if (this.horizontal) { 69 | if (this.targetRight < this.left) { 70 | this.entity.position.x += this.width; 71 | } 72 | else if (this.targetLeft > this.right) { 73 | this.entity.position.x -= this.width; 74 | } 75 | } 76 | if (this.vertical) { 77 | if (this.targetBottom < this.up) { 78 | this.entity.position.y += this.height; 79 | } 80 | else if (this.targetTop > this.bottom) { 81 | this.entity.position.y -= this.height; 82 | } 83 | } 84 | } 85 | } 86 | 87 | Behavior.register('WrapAroundScreen', WrapAroundScreen); 88 | 89 | module.exports = WrapAroundScreen; 90 | -------------------------------------------------------------------------------- /src/engine/System.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Interface of sub-systems of game. 3 | * @interface 4 | */ 5 | export default class System { 6 | /** 7 | * @constructor 8 | */ 9 | constructor() { 10 | /** 11 | * Name of this system 12 | * @type {String} 13 | * @readonly 14 | */ 15 | this.name = ''; 16 | 17 | /** 18 | * Reference to the owner game instance 19 | * @type {Game} 20 | * @readonly 21 | */ 22 | this.game = null; 23 | } 24 | 25 | /** 26 | * Callback that will be invoked when owner game is awake. 27 | * @method awake 28 | * @memberof System# 29 | * @param {Object} settings Settings passed to the game 30 | */ 31 | awake(settings) {} /* eslint no-unused-vars:0 */ 32 | /** 33 | * Callback that will be invoked each idle frame(animation frame). 34 | * @method update 35 | * @memberof System# 36 | * @param {Number} dt Delta time in millisecond 37 | * @param {Number} dtSec Delta time in second 38 | */ 39 | update(dt, dtSec) {} /* eslint no-unused-vars:0 */ 40 | /** 41 | * Callback that will be invoked each fixed frame(based on Game#desiredFPS). 42 | * @method fixedUpdate 43 | * @memberof System# 44 | * @param {Number} dt Delta time in millisecond 45 | * @param {Number} dtSec Delta time in second 46 | */ 47 | fixedUpdate(dt, dtSec) {} /* eslint no-unused-vars:0 */ 48 | /** 49 | * Callback that will be invoked when owner game is freeze. 50 | * @method freeze 51 | * @memberof System# 52 | */ 53 | freeze() {} 54 | 55 | /** 56 | * Callback that will be invoked on each entity spawn. 57 | * @method onEntitySpawn 58 | * @memberof System# 59 | * @param {Entity} ent Entity instance 60 | */ 61 | onEntitySpawn(ent) {} /* eslint no-unused-vars:0 */ 62 | /** 63 | * Callback that will be invoked on each entity remove. 64 | * @method onEntityRemove 65 | * @memberof System# 66 | * @param {Entity} ent Entity instance 67 | */ 68 | onEntityRemove(ent) {} /* eslint no-unused-vars:0 */ 69 | /** 70 | * Callback that will be invoked when an entity changes its tag. 71 | * @method onEntityTagChange 72 | * @memberof System# 73 | * @param {Entity} ent Entity instance 74 | * @param {String} tag New tag 75 | */ 76 | onEntityTagChange(ent, tag) {} /* eslint no-unused-vars:0 */ 77 | 78 | /** 79 | * Callback that will be invoked when owner game will pause. 80 | * @method onPause 81 | * @memberof System# 82 | */ 83 | onPause() {} 84 | /** 85 | * Callback that will be invoked when owner game will resume from pause. 86 | * @method onResume 87 | * @memberof System# 88 | */ 89 | onResume() {} 90 | } 91 | -------------------------------------------------------------------------------- /src/engine/gfx/filters/bloom/BloomFilter.js: -------------------------------------------------------------------------------- 1 | const AbstractFilter = require('../../core/renderers/webgl/filters/AbstractFilter'); 2 | const BlurXFilter = require('../blur/BlurXFilter'); 3 | const BlurYFilter = require('../blur/BlurYFilter'); 4 | const CONST = require('../../const'); 5 | 6 | /** 7 | * The BloomFilter applies a Gaussian blur to an object. 8 | * The strength of the blur can be set for x- and y-axis separately. 9 | * 10 | * @class 11 | * @extends AbstractFilter 12 | */ 13 | function BloomFilter() { 14 | AbstractFilter.call(this); 15 | 16 | this.blurXFilter = new BlurXFilter(); 17 | this.blurYFilter = new BlurYFilter(); 18 | 19 | this.defaultFilter = new AbstractFilter(); 20 | } 21 | 22 | BloomFilter.prototype = Object.create(AbstractFilter.prototype); 23 | BloomFilter.prototype.constructor = BloomFilter; 24 | module.exports = BloomFilter; 25 | 26 | BloomFilter.prototype.applyFilter = function(renderer, input, output) { 27 | var renderTarget = renderer.filterManager.getRenderTarget(true); 28 | 29 | // TODO - copyTexSubImage2D could be used here? 30 | this.defaultFilter.applyFilter(renderer, input, output); 31 | 32 | this.blurXFilter.applyFilter(renderer, input, renderTarget); 33 | 34 | renderer.blendModeManager.setBlendMode(CONST.BLEND_MODES.SCREEN); 35 | 36 | this.blurYFilter.applyFilter(renderer, renderTarget, output); 37 | 38 | renderer.blendModeManager.setBlendMode(CONST.BLEND_MODES.NORMAL); 39 | 40 | renderer.filterManager.returnRenderTarget(renderTarget); 41 | }; 42 | 43 | Object.defineProperties(BloomFilter.prototype, { 44 | /** 45 | * Sets the strength of both the blurX and blurY properties simultaneously 46 | * 47 | * @member {number} 48 | * @memberOf filters.BloomFilter# 49 | * @default 2 50 | */ 51 | blur: { 52 | get: function() { 53 | return this.blurXFilter.blur; 54 | }, 55 | set: function(value) { 56 | this.blurXFilter.blur = this.blurYFilter.blur = value; 57 | }, 58 | }, 59 | 60 | /** 61 | * Sets the strength of the blurX property 62 | * 63 | * @member {number} 64 | * @memberOf filters.BloomFilter# 65 | * @default 2 66 | */ 67 | blurX: { 68 | get: function() { 69 | return this.blurXFilter.blur; 70 | }, 71 | set: function(value) { 72 | this.blurXFilter.blur = value; 73 | }, 74 | }, 75 | 76 | /** 77 | * Sets the strength of the blurY property 78 | * 79 | * @member {number} 80 | * @memberOf filters.BloomFilter# 81 | * @default 2 82 | */ 83 | blurY: { 84 | get: function() { 85 | return this.blurYFilter.blur; 86 | }, 87 | set: function(value) { 88 | this.blurYFilter.blur = value; 89 | }, 90 | }, 91 | }); 92 | -------------------------------------------------------------------------------- /src/engine/gfx/filters/convolution/ConvolutionFilter.js: -------------------------------------------------------------------------------- 1 | const AbstractFilter = require('../../core/renderers/webgl/filters/AbstractFilter'); 2 | 3 | /** 4 | * The ConvolutionFilter class applies a matrix convolution filter effect. 5 | * A convolution combines pixels in the input image with neighboring pixels to produce a new image. 6 | * A wide variety of image effects can be achieved through convolutions, including blurring, edge 7 | * detection, sharpening, embossing, and beveling. The matrix should be specified as a 9 point Array. 8 | * See http://docs.gimp.org/en/plug-in-convmatrix.html for more info. 9 | * 10 | * @class 11 | * @extends AbstractFilter 12 | * @param matrix {number[]} An array of values used for matrix transformation. Specified as a 9 point Array. 13 | * @param width {number} Width of the object you are transforming 14 | * @param height {number} Height of the object you are transforming 15 | */ 16 | function ConvolutionFilter(matrix, width, height) { 17 | AbstractFilter.call(this, 18 | // vertex shader 19 | null, 20 | // fragment shader 21 | require('./convolution.frag'), 22 | // custom uniforms 23 | { 24 | matrix: { type: '1fv', value: new Float32Array(matrix) }, 25 | texelSize: { type: 'v2', value: { x: 1 / width, y: 1 / height } }, 26 | } 27 | ); 28 | } 29 | 30 | ConvolutionFilter.prototype = Object.create(AbstractFilter.prototype); 31 | ConvolutionFilter.prototype.constructor = ConvolutionFilter; 32 | module.exports = ConvolutionFilter; 33 | 34 | Object.defineProperties(ConvolutionFilter.prototype, { 35 | /** 36 | * An array of values used for matrix transformation. Specified as a 9 point Array. 37 | * 38 | * @member {number[]} 39 | * @memberof filters.ConvolutionFilter# 40 | */ 41 | matrix: { 42 | get: function() { 43 | return this.uniforms.matrix.value; 44 | }, 45 | set: function(value) { 46 | this.uniforms.matrix.value = new Float32Array(value); 47 | }, 48 | }, 49 | 50 | /** 51 | * Width of the object you are transforming 52 | * 53 | * @member {number} 54 | * @memberof filters.ConvolutionFilter# 55 | */ 56 | width: { 57 | get: function() { 58 | return 1 / this.uniforms.texelSize.value.x; 59 | }, 60 | set: function(value) { 61 | this.uniforms.texelSize.value.x = 1 / value; 62 | }, 63 | }, 64 | 65 | /** 66 | * Height of the object you are transforming 67 | * 68 | * @member {number} 69 | * @memberof filters.ConvolutionFilter# 70 | */ 71 | height: { 72 | get: function() { 73 | return 1 / this.uniforms.texelSize.value.y; 74 | }, 75 | set: function(value) { 76 | this.uniforms.texelSize.value.y = 1 / value; 77 | }, 78 | }, 79 | }); 80 | -------------------------------------------------------------------------------- /src/engine/gfx/core/renderers/webgl/managers/MaskManager.js: -------------------------------------------------------------------------------- 1 | import WebGLManager from './WebGLManager'; 2 | import AlphaMaskFilter from '../filters/SpriteMaskFilter'; 3 | 4 | /** 5 | * @class 6 | * @param renderer {WebGLRenderer} The renderer this manager works for. 7 | */ 8 | export default function MaskManager(renderer) { 9 | WebGLManager.call(this, renderer); 10 | 11 | this.stencilStack = []; 12 | this.reverse = true; 13 | this.count = 0; 14 | 15 | this.alphaMaskPool = []; 16 | } 17 | 18 | MaskManager.prototype = Object.create(WebGLManager.prototype); 19 | MaskManager.prototype.constructor = MaskManager; 20 | 21 | /** 22 | * Applies the Mask and adds it to the current filter stack. 23 | * 24 | * @param graphics {Graphics} 25 | * @param webGLData {any[]} 26 | */ 27 | MaskManager.prototype.pushMask = function(target, maskData) { 28 | if (maskData.texture) { 29 | this.pushSpriteMask(target, maskData); 30 | } 31 | else { 32 | this.pushStencilMask(target, maskData); 33 | } 34 | 35 | }; 36 | 37 | /** 38 | * Removes the last mask from the mask stack and doesn't return it. 39 | * 40 | * @param target {RenderTarget} 41 | * @param maskData {any[]} 42 | */ 43 | MaskManager.prototype.popMask = function(target, maskData) { 44 | if (maskData.texture) { 45 | this.popSpriteMask(target, maskData); 46 | } 47 | else { 48 | this.popStencilMask(target, maskData); 49 | } 50 | }; 51 | 52 | /** 53 | * Applies the Mask and adds it to the current filter stack. 54 | * 55 | * @param target {RenderTarget} 56 | * @param maskData {any[]} 57 | */ 58 | MaskManager.prototype.pushSpriteMask = function(target, maskData) { 59 | var alphaMaskFilter = this.alphaMaskPool.pop(); 60 | 61 | if (!alphaMaskFilter) { 62 | alphaMaskFilter = [new AlphaMaskFilter(maskData)]; 63 | } 64 | 65 | alphaMaskFilter[0].maskSprite = maskData; 66 | this.renderer.filterManager.pushFilter(target, alphaMaskFilter); 67 | }; 68 | 69 | /** 70 | * Removes the last filter from the filter stack and doesn't return it. 71 | * 72 | */ 73 | MaskManager.prototype.popSpriteMask = function() { 74 | var filters = this.renderer.filterManager.popFilter(); 75 | 76 | this.alphaMaskPool.push(filters); 77 | }; 78 | 79 | 80 | /** 81 | * Applies the Mask and adds it to the current filter stack. 82 | * 83 | * @param target {RenderTarget} 84 | * @param maskData {any[]} 85 | */ 86 | MaskManager.prototype.pushStencilMask = function(target, maskData) { 87 | this.renderer.stencilManager.pushMask(maskData); 88 | }; 89 | 90 | /** 91 | * Removes the last filter from the filter stack and doesn't return it. 92 | * 93 | * @param target {RenderTarget} 94 | * @param maskData {any[]} 95 | */ 96 | MaskManager.prototype.popStencilMask = function(target, maskData) { 97 | this.renderer.stencilManager.popMask(maskData); 98 | }; 99 | -------------------------------------------------------------------------------- /src/engine/gfx/filters/displacement/DisplacementFilter.js: -------------------------------------------------------------------------------- 1 | const AbstractFilter = require('../../core/renderers/webgl/filters/AbstractFilter'); 2 | const Vector = require('engine/Vector'); 3 | const { Matrix } = require('../../core/math'); 4 | 5 | /** 6 | * The DisplacementFilter class uses the pixel values from the specified texture (called the displacement map) to perform a displacement of an object. 7 | * You can use this filter to apply all manor of crazy warping effects 8 | * Currently the r property of the texture is used to offset the x and the g property of the texture is used to offset the y. 9 | * 10 | * @class 11 | * @extends AbstractFilter 12 | * @param sprite {Sprite} the sprite used for the displacement map. (make sure its added to the scene!) 13 | */ 14 | function DisplacementFilter(sprite, scale) { 15 | var maskMatrix = new Matrix(); 16 | sprite.renderable = false; 17 | 18 | AbstractFilter.call(this, 19 | // vertex shader 20 | require('./displacement.vert'), 21 | // fragment shader 22 | require('./displacement.frag'), 23 | // uniforms 24 | { 25 | mapSampler: { type: 'sampler2D', value: sprite.texture }, 26 | otherMatrix: { type: 'mat3', value: maskMatrix.toArray(true) }, 27 | scale: { type: 'v2', value: { x: 1, y: 1 } }, 28 | } 29 | ); 30 | 31 | this.maskSprite = sprite; 32 | this.maskMatrix = maskMatrix; 33 | 34 | if (scale === null || scale === undefined) { 35 | scale = 20; 36 | } 37 | 38 | this.scale = Vector.create(scale, scale); 39 | } 40 | 41 | DisplacementFilter.prototype = Object.create(AbstractFilter.prototype); 42 | DisplacementFilter.prototype.constructor = DisplacementFilter; 43 | module.exports = DisplacementFilter; 44 | 45 | DisplacementFilter.prototype.applyFilter = function(renderer, input, output) { 46 | var filterManager = renderer.filterManager; 47 | 48 | filterManager.calculateMappedMatrix(input.frame, this.maskSprite, this.maskMatrix); 49 | 50 | this.uniforms.otherMatrix.value = this.maskMatrix.toArray(true); 51 | this.uniforms.scale.value.x = this.scale.x * (1 / input.frame.width); 52 | this.uniforms.scale.value.y = this.scale.y * (1 / input.frame.height); 53 | 54 | var shader = this.getShader(renderer); 55 | // draw the filter... 56 | filterManager.applyFilter(shader, input, output); 57 | }; 58 | 59 | 60 | Object.defineProperties(DisplacementFilter.prototype, { 61 | /** 62 | * The texture used for the displacement map. Must be power of 2 sized texture. 63 | * 64 | * @member {Texture} 65 | * @memberof filters.DisplacementFilter# 66 | */ 67 | map: { 68 | get: function() { 69 | return this.uniforms.mapSampler.value; 70 | }, 71 | set: function(value) { 72 | this.uniforms.mapSampler.value = value; 73 | 74 | }, 75 | }, 76 | }); 77 | -------------------------------------------------------------------------------- /src/engine/gfx/filters/blur/BlurFilter.js: -------------------------------------------------------------------------------- 1 | const AbstractFilter = require('../../core/renderers/webgl/filters/AbstractFilter'); 2 | const BlurXFilter = require('./BlurXFilter'); 3 | const BlurYFilter = require('./BlurYFilter'); 4 | 5 | /** 6 | * The BlurFilter applies a Gaussian blur to an object. 7 | * The strength of the blur can be set for x- and y-axis separately. 8 | * 9 | * @class 10 | * @extends AbstractFilter 11 | */ 12 | function BlurFilter() { 13 | AbstractFilter.call(this); 14 | 15 | this.blurXFilter = new BlurXFilter(); 16 | this.blurYFilter = new BlurYFilter(); 17 | } 18 | 19 | BlurFilter.prototype = Object.create(AbstractFilter.prototype); 20 | BlurFilter.prototype.constructor = BlurFilter; 21 | module.exports = BlurFilter; 22 | 23 | BlurFilter.prototype.applyFilter = function(renderer, input, output) { 24 | var renderTarget = renderer.filterManager.getRenderTarget(true); 25 | 26 | this.blurXFilter.applyFilter(renderer, input, renderTarget); 27 | this.blurYFilter.applyFilter(renderer, renderTarget, output); 28 | 29 | renderer.filterManager.returnRenderTarget(renderTarget); 30 | }; 31 | 32 | Object.defineProperties(BlurFilter.prototype, { 33 | /** 34 | * Sets the strength of both the blurX and blurY properties simultaneously 35 | * 36 | * @member {number} 37 | * @memberOf filters.BlurFilter# 38 | * @default 2 39 | */ 40 | blur: { 41 | get: function() { 42 | return this.blurXFilter.blur; 43 | }, 44 | set: function(value) { 45 | this.padding = Math.abs(value) * 0.5; 46 | this.blurXFilter.blur = this.blurYFilter.blur = value; 47 | }, 48 | }, 49 | 50 | /** 51 | * Sets the number of passes for blur. More passes means higher quaility bluring. 52 | * 53 | * @member {number} 54 | * @memberof filters.BlurYFilter# 55 | * @default 1 56 | */ 57 | passes: { 58 | get: function() { 59 | return this.blurXFilter.passes; 60 | }, 61 | set: function(value) { 62 | this.blurXFilter.passes = this.blurYFilter.passes = value; 63 | }, 64 | }, 65 | 66 | /** 67 | * Sets the strength of the blurX property 68 | * 69 | * @member {number} 70 | * @memberOf filters.BlurFilter# 71 | * @default 2 72 | */ 73 | blurX: { 74 | get: function() { 75 | return this.blurXFilter.blur; 76 | }, 77 | set: function(value) { 78 | this.blurXFilter.blur = value; 79 | }, 80 | }, 81 | 82 | /** 83 | * Sets the strength of the blurY property 84 | * 85 | * @member {number} 86 | * @memberOf filters.BlurFilter# 87 | * @default 2 88 | */ 89 | blurY: { 90 | get: function() { 91 | return this.blurYFilter.blur; 92 | }, 93 | set: function(value) { 94 | this.blurYFilter.blur = value; 95 | }, 96 | }, 97 | }); 98 | -------------------------------------------------------------------------------- /src/engine/gfx/filters/tiltshift/TiltShiftFilter.js: -------------------------------------------------------------------------------- 1 | const AbstractFilter = require('../../core/renderers/webgl/filters/AbstractFilter'); 2 | const TiltShiftXFilter = require('./TiltShiftXFilter'); 3 | const TiltShiftYFilter = require('./TiltShiftYFilter'); 4 | 5 | /** 6 | * @author Vico @vicocotea 7 | * original filter https://github.com/evanw/glfx.js/blob/master/src/filters/blur/tiltshift.js by Evan Wallace : http://madebyevan.com/ 8 | */ 9 | 10 | /** 11 | * A TiltShift Filter. Manages the pass of both a TiltShiftXFilter and TiltShiftYFilter. 12 | * 13 | * @class 14 | * @extends AbstractFilter 15 | */ 16 | function TiltShiftFilter() { 17 | AbstractFilter.call(this); 18 | 19 | this.tiltShiftXFilter = new TiltShiftXFilter(); 20 | this.tiltShiftYFilter = new TiltShiftYFilter(); 21 | } 22 | 23 | TiltShiftFilter.prototype = Object.create(AbstractFilter.prototype); 24 | TiltShiftFilter.prototype.constructor = TiltShiftFilter; 25 | module.exports = TiltShiftFilter; 26 | 27 | TiltShiftFilter.prototype.applyFilter = function(renderer, input, output) { 28 | var renderTarget = renderer.filterManager.getRenderTarget(true); 29 | 30 | this.tiltShiftXFilter.applyFilter(renderer, input, renderTarget); 31 | 32 | this.tiltShiftYFilter.applyFilter(renderer, renderTarget, output); 33 | 34 | renderer.filterManager.returnRenderTarget(renderTarget); 35 | }; 36 | 37 | Object.defineProperties(TiltShiftFilter.prototype, { 38 | /** 39 | * The strength of the blur. 40 | * 41 | * @member {number} 42 | * @memberof filters.TiltShiftFilter# 43 | */ 44 | blur: { 45 | get: function() { 46 | return this.tiltShiftXFilter.blur; 47 | }, 48 | set: function(value) { 49 | this.tiltShiftXFilter.blur = this.tiltShiftYFilter.blur = value; 50 | }, 51 | }, 52 | 53 | /** 54 | * The strength of the gradient blur. 55 | * 56 | * @member {number} 57 | * @memberof filters.TiltShiftFilter# 58 | */ 59 | gradientBlur: { 60 | get: function() { 61 | return this.tiltShiftXFilter.gradientBlur; 62 | }, 63 | set: function(value) { 64 | this.tiltShiftXFilter.gradientBlur = this.tiltShiftYFilter.gradientBlur = value; 65 | }, 66 | }, 67 | 68 | /** 69 | * The Y value to start the effect at. 70 | * 71 | * @member {number} 72 | * @memberof filters.TiltShiftFilter# 73 | */ 74 | start: { 75 | get: function() { 76 | return this.tiltShiftXFilter.start; 77 | }, 78 | set: function(value) { 79 | this.tiltShiftXFilter.start = this.tiltShiftYFilter.start = value; 80 | }, 81 | }, 82 | 83 | /** 84 | * The Y value to end the effect at. 85 | * 86 | * @member {number} 87 | * @memberof filters.TiltShiftFilter# 88 | */ 89 | end: { 90 | get: function() { 91 | return this.tiltShiftXFilter.end; 92 | }, 93 | set: function(value) { 94 | this.tiltShiftXFilter.end = this.tiltShiftYFilter.end = value; 95 | }, 96 | }, 97 | }); 98 | -------------------------------------------------------------------------------- /src/engine/gfx/core/renderers/webgl/shaders/TextureShader.js: -------------------------------------------------------------------------------- 1 | import Shader from './Shader'; 2 | 3 | /** 4 | * @class 5 | * @extends Shader 6 | * @param shaderManager {ShaderManager} The webgl shader manager this shader works for. 7 | * @param [vertexSrc] {string} The source of the vertex shader. 8 | * @param [fragmentSrc] {string} The source of the fragment shader. 9 | * @param [customUniforms] {object} Custom uniforms to use to augment the built-in ones. 10 | * @param [fragmentSrc] {string} The source of the fragment shader. 11 | */ 12 | export default function TextureShader(shaderManager, vertexSrc, fragmentSrc, customUniforms, customAttributes) { 13 | var uniforms = { 14 | uSampler: { type: 'sampler2D', value: 0 }, 15 | projectionMatrix: { type: 'mat3', value: new Float32Array([ 16 | 1, 0, 0, 17 | 0, 1, 0, 18 | 0, 0, 1, 19 | ]) }, 20 | }; 21 | 22 | if (customUniforms) { 23 | for (var u in customUniforms) { 24 | uniforms[u] = customUniforms[u]; 25 | } 26 | } 27 | 28 | 29 | var attributes = { 30 | aVertexPosition: 0, 31 | aTextureCoord: 0, 32 | aColor: 0, 33 | }; 34 | 35 | if (customAttributes) { 36 | for (var a in customAttributes) { 37 | attributes[a] = customAttributes[a]; 38 | } 39 | } 40 | 41 | /** 42 | * The vertex shader. 43 | * 44 | * @member {string} 45 | */ 46 | vertexSrc = vertexSrc || TextureShader.defaultVertexSrc; 47 | 48 | /** 49 | * The fragment shader. 50 | * 51 | * @member {string} 52 | */ 53 | fragmentSrc = fragmentSrc || TextureShader.defaultFragmentSrc; 54 | 55 | Shader.call(this, shaderManager, vertexSrc, fragmentSrc, uniforms, attributes); 56 | } 57 | 58 | // constructor 59 | TextureShader.prototype = Object.create(Shader.prototype); 60 | TextureShader.prototype.constructor = TextureShader; 61 | 62 | /** 63 | * The default vertex shader source 64 | * 65 | * @static 66 | * @constant 67 | */ 68 | TextureShader.defaultVertexSrc = [ 69 | 'precision lowp float;', 70 | 'attribute vec2 aVertexPosition;', 71 | 'attribute vec2 aTextureCoord;', 72 | 'attribute vec4 aColor;', 73 | 74 | 'uniform mat3 projectionMatrix;', 75 | 76 | 'varying vec2 vTextureCoord;', 77 | 'varying vec4 vColor;', 78 | 79 | 'void main(void){', 80 | ' gl_Position = vec4((projectionMatrix * vec3(aVertexPosition, 1.0)).xy, 0.0, 1.0);', 81 | ' vTextureCoord = aTextureCoord;', 82 | ' vColor = vec4(aColor.rgb * aColor.a, aColor.a);', 83 | '}', 84 | ].join('\n'); 85 | 86 | /** 87 | * The default fragment shader source 88 | * 89 | * @static 90 | * @constant 91 | */ 92 | TextureShader.defaultFragmentSrc = [ 93 | 'precision lowp float;', 94 | 95 | 'varying vec2 vTextureCoord;', 96 | 'varying vec4 vColor;', 97 | 98 | 'uniform sampler2D uSampler;', 99 | 100 | 'void main(void){', 101 | ' gl_FragColor = texture2D(uSampler, vTextureCoord) * vColor ;', 102 | '}', 103 | ].join('\n'); 104 | -------------------------------------------------------------------------------- /src/engine/gfx/core/graphics/webgl/WebGLGraphicsData.js: -------------------------------------------------------------------------------- 1 | /** 2 | * An object containing WebGL specific properties to be used by the WebGL renderer 3 | * 4 | * @class 5 | * @private 6 | */ 7 | export default class WebGLGraphicsData { 8 | /** 9 | * @constructor 10 | * @param {WebGLRenderingContext} gl the current WebGL drawing context 11 | */ 12 | constructor(gl) { 13 | 14 | /** 15 | * The current WebGL drawing context 16 | * 17 | * @member {WebGLRenderingContext} 18 | */ 19 | this.gl = gl; 20 | 21 | // TODO does this need to be split before uploding?? 22 | /** 23 | * An array of color components (r,g,b) 24 | * @member {number[]} 25 | */ 26 | this.color = [0,0,0]; // color split! 27 | 28 | /** 29 | * An array of points to draw 30 | * @member {PIXI.Point[]} 31 | */ 32 | this.points = []; 33 | 34 | /** 35 | * The indices of the vertices 36 | * @member {number[]} 37 | */ 38 | this.indices = []; 39 | /** 40 | * The main buffer 41 | * @member {WebGLBuffer} 42 | */ 43 | this.buffer = gl.createBuffer(); 44 | 45 | /** 46 | * The index buffer 47 | * @member {WebGLBuffer} 48 | */ 49 | this.indexBuffer = gl.createBuffer(); 50 | 51 | /** 52 | * todo @alvin 53 | * @member {number} 54 | */ 55 | this.mode = 1; 56 | 57 | /** 58 | * The alpha of the graphics 59 | * @member {number} 60 | */ 61 | this.alpha = 1; 62 | 63 | /** 64 | * Whether this graphics is dirty or not 65 | * @member {boolean} 66 | */ 67 | this.dirty = true; 68 | 69 | this.glPoints = null; 70 | this.glIndices = null; 71 | } 72 | 73 | /** 74 | * Resets the vertices and the indices 75 | */ 76 | reset() { 77 | this.points.length = 0; 78 | this.indices.length = 0; 79 | } 80 | 81 | /** 82 | * Binds the buffers and uploads the data 83 | */ 84 | upload() { 85 | const gl = this.gl; 86 | 87 | // this.lastIndex = graphics.graphicsData.length; 88 | this.glPoints = new Float32Array(this.points); 89 | 90 | gl.bindBuffer(gl.ARRAY_BUFFER, this.buffer); 91 | gl.bufferData(gl.ARRAY_BUFFER, this.glPoints, gl.STATIC_DRAW); 92 | 93 | this.glIndices = new Uint16Array(this.indices); 94 | 95 | gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, this.indexBuffer); 96 | gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, this.glIndices, gl.STATIC_DRAW); 97 | 98 | this.dirty = false; 99 | } 100 | 101 | /** 102 | * Destroy this data 103 | */ 104 | destroy() { 105 | this.color = null; 106 | this.points = null; 107 | this.indices = null; 108 | 109 | this.gl.deleteBuffer(this.buffer); 110 | this.gl.deleteBuffer(this.indexBuffer); 111 | 112 | this.gl = null; 113 | 114 | this.buffer = null; 115 | this.indexBuffer = null; 116 | 117 | this.glPoints = null; 118 | this.glIndices = null; 119 | } 120 | } 121 | -------------------------------------------------------------------------------- /src/game/config.js: -------------------------------------------------------------------------------- 1 | export default { 2 | /** 3 | * Logic size of the game 4 | */ 5 | width: 320, 6 | height: 200, 7 | 8 | gfx: { 9 | /** 10 | * Using WebGL when available or not? 11 | * @type {Boolean} 12 | */ 13 | webgl: true, 14 | /** 15 | * The resolution of the renderer, used for hi-resolution 16 | * textures and better text rendering. 17 | * 18 | * You only need higher resolutions while using hi-res 19 | * textures(i.e. image@2x.png), or better Text renderering. 20 | * Higher resolution means larger Canvas, which may cause 21 | * performance issues, especially on mobile devices. 22 | * 23 | * The value can be numbers, which will be directly used 24 | * by the renderer 25 | * Or an object with some fields: 26 | * - retina {Boolean} Whether take retina into account 27 | * - values {Array} Available resolutions 28 | * @type {Number|Object} 29 | */ 30 | resolution: { 31 | retina: true, 32 | values: [1, 2], 33 | }, 34 | /** 35 | * Default scale mode (linear or nearest) 36 | * @type {String} 37 | */ 38 | scaleMode: 'nearest', 39 | /** 40 | * If the render view is transparent. 41 | * @type {Boolean} 42 | */ 43 | transparent: false, 44 | /** 45 | * Sets antialias (only applicable in chrome at the moment). 46 | * @type {Boolean} 47 | */ 48 | antialias: false, 49 | /** 50 | * Enables drawing buffer preservation, enable this if you 51 | * need to call toDataUrl on the webgl context. 52 | * @type {Boolean} 53 | */ 54 | preserveDrawingBuffer: false, 55 | }, 56 | 57 | /** 58 | * Default canvas view ID. 59 | * @type {String} 60 | */ 61 | canvas: 'game', 62 | 63 | /** 64 | * Base URL of assets. 65 | * @type {String} 66 | */ 67 | baseUrl: 'media', 68 | 69 | /** 70 | * Audio specific settings. 71 | * @type {Object} 72 | */ 73 | audio: { 74 | /** 75 | * Supported audio file format. 76 | * Only files with these extensions will be load. 77 | * @type {Array} 78 | */ 79 | use: ['webm', 'mp3'], 80 | }, 81 | 82 | /** 83 | * How does the game resize? 84 | * available values: 85 | * + letter-box Scale with CSS and align to the center 86 | * + crop Resize the canvas to size of window 87 | * + scale-inner Resize the canvas and scale `container` of scene to show more 88 | * + scale-outer Resize the canvas and scale `container` of scene to show less 89 | */ 90 | resizeMode: 'letter-box', 91 | 92 | /** 93 | * Whether pause the game when page loses focus. 94 | */ 95 | pauseOnHide: false, 96 | 97 | /** 98 | * Whether show a image or text to tell players to rotate device 99 | * Only available on "mobile device". 100 | * @type {Boolean} 101 | */ 102 | showRotatePrompt: true, 103 | rotatePromptBGColor: 'black', 104 | rotatePromptFontColor: 'white', 105 | rotatePromptImg: 'media/rotate.png', 106 | rotatePromptMsg: 'Please Rotate Your Device!', 107 | 108 | storage: { 109 | id: 'lpanda', 110 | }, 111 | }; 112 | -------------------------------------------------------------------------------- /src/engine/gfx/core/renderers/webgl/filters/AbstractFilter.js: -------------------------------------------------------------------------------- 1 | import DefaultShader from '../shaders/TextureShader'; 2 | 3 | /** 4 | * This is the base class for creating a PIXI filter. Currently only WebGL supports filters. 5 | * If you want to make a custom filter this should be your base class. 6 | * 7 | * @class 8 | * @param vertexSrc {string|string[]} The vertex shader source as an array of strings. 9 | * @param fragmentSrc {string|string[]} The fragment shader source as an array of strings. 10 | * @param uniforms {object} An object containing the uniforms for this filter. 11 | */ 12 | export default function AbstractFilter(vertexSrc, fragmentSrc, uniforms) { 13 | 14 | /** 15 | * An array of shaders 16 | * @member {PIXI.Shader[]} 17 | * @private 18 | */ 19 | this.shaders = []; 20 | 21 | /** 22 | * The extra padding that the filter might need 23 | * @member {number} 24 | */ 25 | this.padding = 0; 26 | 27 | /** 28 | * The uniforms as an object 29 | * @member {object} 30 | */ 31 | this.uniforms = uniforms || {}; 32 | 33 | 34 | /** 35 | * The code of the vertex shader 36 | * @member {string[]} 37 | * @private 38 | */ 39 | this.vertexSrc = vertexSrc || DefaultShader.defaultVertexSrc; 40 | 41 | /** 42 | * The code of the frament shader 43 | * @member {string[]} 44 | * @private 45 | */ 46 | this.fragmentSrc = fragmentSrc || DefaultShader.defaultFragmentSrc; 47 | 48 | // TODO a reminder - would be cool to have lower res filters as this would give better performance. 49 | 50 | // typeof fragmentSrc === 'string' ? fragmentSrc.split('') : (fragmentSrc || []); 51 | 52 | } 53 | 54 | AbstractFilter.prototype.constructor = AbstractFilter; 55 | 56 | /** 57 | * Grabs a shader from the current renderer 58 | * 59 | * @param renderer {PIXI.WebGLRenderer} The renderer to retrieve the shader from 60 | */ 61 | AbstractFilter.prototype.getShader = function(renderer) { 62 | var gl = renderer.gl; 63 | 64 | var shader = this.shaders[gl.id]; 65 | 66 | if (!shader) { 67 | shader = new DefaultShader(renderer.shaderManager, 68 | this.vertexSrc, 69 | this.fragmentSrc, 70 | this.uniforms, 71 | this.attributes 72 | ); 73 | 74 | this.shaders[gl.id] = shader; 75 | } 76 | 77 | return shader; 78 | }; 79 | 80 | /** 81 | * Applies the filter 82 | * 83 | * @param renderer {PIXI.WebGLRenderer} The renderer to retrieve the filter from 84 | * @param input {PIXI.RenderTarget} 85 | * @param output {PIXI.RenderTarget} 86 | * @param clear {boolean} Whether or not we want to clear the outputTarget 87 | */ 88 | AbstractFilter.prototype.applyFilter = function(renderer, input, output, clear) { 89 | var shader = this.getShader(renderer); 90 | 91 | renderer.filterManager.applyFilter(shader, input, output, clear); 92 | }; 93 | 94 | /** 95 | * Syncs a uniform between the class object and the shaders. 96 | * 97 | */ 98 | AbstractFilter.prototype.syncUniform = function(uniform) { 99 | for (var i = 0, j = this.shaders.length; i < j; ++i) { 100 | this.shaders[i].syncUniform(uniform); 101 | } 102 | }; 103 | -------------------------------------------------------------------------------- /src/engine/gfx/core/math/Polygon.js: -------------------------------------------------------------------------------- 1 | import Vector from 'engine/Vector'; 2 | import { SHAPES } from '../../const'; 3 | 4 | const FieldX = 'x'; 5 | const FieldY = 'y'; 6 | 7 | /** 8 | * @class 9 | */ 10 | export default class Polygon { 11 | /** 12 | * @constructor 13 | * @param {Vector[]|number[]|Vector|number} points_ This can be an array of Points that form the polygon, 14 | * a flat array of numbers that will be interpreted as [x,y, x,y, ...], or the arguments passed can be 15 | * all the points of the polygon e.g. `new Polygon(new Vector(), new Vector(), ...)`, or the 16 | * arguments passed can be flat x,y values e.g. `new Polygon(x,y, x,y, x,y, ...)` where `x` and `y` are 17 | * Numbers. 18 | */ 19 | constructor(points_) { 20 | // prevents an argument assignment deopt 21 | // see section 3.1: https://github.com/petkaantonov/bluebird/wiki/Optimization-killers#3-managing-arguments 22 | var points = points_; 23 | 24 | // if points isn't an array, use arguments as the array 25 | if (!Array.isArray(points)) { 26 | // prevents an argument leak deopt 27 | // see section 3.2: https://github.com/petkaantonov/bluebird/wiki/Optimization-killers#3-managing-arguments 28 | points = new Array(arguments.length); 29 | 30 | for (var a = 0; a < points.length; ++a) { 31 | points[a] = arguments[a]; 32 | } 33 | } 34 | 35 | // if this is an array of points, convert it to a flat array of numbers 36 | if ((FieldX in points[0]) && (FieldY in points[0])) { 37 | var p = []; 38 | for (var i = 0, il = points.length; i < il; i++) { 39 | p.push(points[i].x, points[i].y); 40 | } 41 | 42 | points = p; 43 | } 44 | 45 | this.closed = true; 46 | 47 | /** 48 | * An array of the points of this polygon 49 | * 50 | * @member {number[]} 51 | */ 52 | this.points = points; 53 | 54 | /** 55 | * The type of the object, mainly used to avoid `instanceof` checks 56 | * 57 | * @member {number} 58 | */ 59 | this.type = SHAPES.POLY; 60 | } 61 | 62 | /** 63 | * Creates a clone of this polygon 64 | * 65 | * @return {Polygon} a copy of the polygon 66 | */ 67 | clone() { 68 | return new Polygon(this.points.slice()); 69 | } 70 | 71 | /** 72 | * Checks whether the x and y coordinates passed to this function are contained within this polygon 73 | * 74 | * @param {number} x The X coordinate of the point to test 75 | * @param {number} y The Y coordinate of the point to test 76 | * @return {boolean} Whether the x/y coordinates are within this polygon 77 | */ 78 | contains(x, y) { 79 | var inside = false; 80 | 81 | // use some raycasting to test hits 82 | // https://github.com/substack/point-in-polygon/blob/master/index.js 83 | var length = this.points.length / 2; 84 | 85 | for (var i = 0, j = length - 1; i < length; j = i++) { 86 | var xi = this.points[i * 2], yi = this.points[i * 2 + 1], 87 | xj = this.points[j * 2], yj = this.points[j * 2 + 1], 88 | intersect = ((yi > y) !== (yj > y)) && (x < (xj - xi) * (y - yi) / (yj - yi) + xi); 89 | 90 | if (intersect) { 91 | inside = !inside; 92 | } 93 | } 94 | 95 | return inside; 96 | } 97 | } 98 | -------------------------------------------------------------------------------- /src/engine/gfx/filters/tiltshift/TiltShiftAxisFilter.js: -------------------------------------------------------------------------------- 1 | const AbstractFilter = require('../../core/renderers/webgl/filters/AbstractFilter'); 2 | 3 | /** 4 | * @author Vico @vicocotea 5 | * original filter https://github.com/evanw/glfx.js/blob/master/src/filters/blur/tiltshift.js by Evan Wallace : http://madebyevan.com/ 6 | */ 7 | 8 | /** 9 | * A TiltShiftAxisFilter. 10 | * 11 | * @class 12 | * @extends AbstractFilter 13 | */ 14 | function TiltShiftAxisFilter() { 15 | AbstractFilter.call(this, 16 | // vertex shader 17 | null, 18 | // fragment shader 19 | require('./tiltShift.frag'), 20 | // custom uniforms 21 | { 22 | blur: { type: '1f', value: 100 }, 23 | gradientBlur: { type: '1f', value: 600 }, 24 | start: { type: 'v2', value: { x: 0, y: window.innerHeight / 2 } }, 25 | end: { type: 'v2', value: { x: 600, y: window.innerHeight / 2 } }, 26 | delta: { type: 'v2', value: { x: 30, y: 30 } }, 27 | texSize: { type: 'v2', value: { x: window.innerWidth, y: window.innerHeight } }, 28 | } 29 | ); 30 | 31 | this.updateDelta(); 32 | } 33 | 34 | TiltShiftAxisFilter.prototype = Object.create(AbstractFilter.prototype); 35 | TiltShiftAxisFilter.prototype.constructor = TiltShiftAxisFilter; 36 | module.exports = TiltShiftAxisFilter; 37 | 38 | /** 39 | * Updates the filter delta values. 40 | * This is overridden in the X and Y filters, does nothing for this class. 41 | * 42 | */ 43 | TiltShiftAxisFilter.prototype.updateDelta = function() { 44 | this.uniforms.delta.value.x = 0; 45 | this.uniforms.delta.value.y = 0; 46 | }; 47 | 48 | Object.defineProperties(TiltShiftAxisFilter.prototype, { 49 | /** 50 | * The strength of the blur. 51 | * 52 | * @member {number} 53 | * @memberof filters.TiltShiftAxisFilter# 54 | */ 55 | blur: { 56 | get: function() { 57 | return this.uniforms.blur.value; 58 | }, 59 | set: function(value) { 60 | this.uniforms.blur.value = value; 61 | }, 62 | }, 63 | 64 | /** 65 | * The strength of the gradient blur. 66 | * 67 | * @member {number} 68 | * @memberof filters.TiltShiftAxisFilter# 69 | */ 70 | gradientBlur: { 71 | get: function() { 72 | return this.uniforms.gradientBlur.value; 73 | }, 74 | set: function(value) { 75 | this.uniforms.gradientBlur.value = value; 76 | }, 77 | }, 78 | 79 | /** 80 | * The X value to start the effect at. 81 | * 82 | * @member {Vector} 83 | * @memberof filters.TiltShiftAxisFilter# 84 | */ 85 | start: { 86 | get: function() { 87 | return this.uniforms.start.value; 88 | }, 89 | set: function(value) { 90 | this.uniforms.start.value = value; 91 | this.updateDelta(); 92 | }, 93 | }, 94 | 95 | /** 96 | * The X value to end the effect at. 97 | * 98 | * @member {Vector} 99 | * @memberof filters.TiltShiftAxisFilter# 100 | */ 101 | end: { 102 | get: function() { 103 | return this.uniforms.end.value; 104 | }, 105 | set: function(value) { 106 | this.uniforms.end.value = value; 107 | this.updateDelta(); 108 | }, 109 | }, 110 | }); 111 | -------------------------------------------------------------------------------- /src/engine/gfx/mesh/Plane.js: -------------------------------------------------------------------------------- 1 | import Mesh from './Mesh'; 2 | 3 | /** 4 | * The Plane allows you to draw a plane with customizable segments across x/y axis. 5 | * 6 | *```js 7 | * var Plane = new Plane(Texture.fromImage("snake.png")); 8 | * ``` 9 | * 10 | * @class 11 | * @extends mesh.Mesh 12 | * @memberof mesh 13 | * @param {Texture} texture - The texture to use on the Plane. 14 | * @param {int} segmentsX - The number ox x segments 15 | * @param {int} segmentsY - The number of y segments 16 | */ 17 | export default class Plane extends Mesh { 18 | constructor(texture, segmentsX = 10, segmentsY = 10) { 19 | super(texture); 20 | 21 | /** 22 | * Tracker for if the Plane is ready to be drawn. Needed because Mesh ctor can 23 | * call _onTextureUpdated which could call refresh too early. 24 | * 25 | * @member {boolean} 26 | * @private 27 | */ 28 | this._ready = true; 29 | 30 | this.segmentsX = segmentsX; 31 | this.segmentsY = segmentsY; 32 | 33 | this.drawMode = Mesh.DRAW_MODES.TRIANGLES; 34 | this.refresh(); 35 | } 36 | 37 | /** 38 | * Refreshes 39 | * 40 | */ 41 | refresh() { 42 | var total = this.segmentsX * this.segmentsY; 43 | var verts = []; 44 | var colors = []; 45 | var uvs = []; 46 | var indices = []; 47 | var texture = this.texture; 48 | 49 | var segmentsXSub = this.segmentsX - 1; 50 | var segmentsYSub = this.segmentsY - 1; 51 | var i = 0; 52 | 53 | var sizeX = texture.width / segmentsXSub; 54 | var sizeY = texture.height / segmentsYSub; 55 | 56 | for (i = 0; i < total; i++) { 57 | 58 | var x = (i % this.segmentsX); 59 | var y = ((i / this.segmentsX) | 0); 60 | 61 | 62 | verts.push((x * sizeX), 63 | (y * sizeY)); 64 | 65 | // this works for rectangular textures. 66 | uvs.push(texture._uvs.x0 + (texture._uvs.x1 - texture._uvs.x0) * (x / (this.segmentsX - 1)), texture._uvs.y0 + (texture._uvs.y3 - texture._uvs.y0) * (y / (this.segmentsY - 1))); 67 | } 68 | 69 | // cons 70 | 71 | var totalSub = segmentsXSub * segmentsYSub; 72 | 73 | for (i = 0; i < totalSub; i++) { 74 | 75 | var xpos = i % segmentsXSub; 76 | var ypos = (i / segmentsXSub) | 0; 77 | 78 | 79 | var value = (ypos * this.segmentsX) + xpos; 80 | var value2 = (ypos * this.segmentsX) + xpos + 1; 81 | var value3 = ((ypos + 1) * this.segmentsX) + xpos; 82 | var value4 = ((ypos + 1) * this.segmentsX) + xpos + 1; 83 | 84 | indices.push(value, value2, value3); 85 | indices.push(value2, value4, value3); 86 | } 87 | 88 | 89 | // console.log(indices) 90 | this.vertices = new Float32Array(verts); 91 | this.uvs = new Float32Array(uvs); 92 | this.colors = new Float32Array(colors); 93 | this.indices = new Uint16Array(indices); 94 | } 95 | 96 | /** 97 | * Clear texture UVs when new texture is set 98 | * 99 | * @private 100 | */ 101 | _onTextureUpdate() { 102 | super._onTextureUpdate(); 103 | 104 | // wait for the Plane ctor to finish before calling refresh 105 | if (this._ready) { 106 | this.refresh(); 107 | } 108 | } 109 | } 110 | -------------------------------------------------------------------------------- /src/engine/analytics.js: -------------------------------------------------------------------------------- 1 | import device from 'engine/device'; 2 | import config from 'game/config'; 3 | 4 | /** 5 | * Google Analytics tracking. 6 | * Note: This class is protected, use the default instance instead. 7 | * 8 | * @example 9 | * // Add your GA code into `game/config` 10 | * analytics: { 11 | * id: 'my-ga-code' 12 | * } 13 | * 14 | * // Import the {@link module:engine/analytics} module 15 | * import analytics from 'engine/analytics'; 16 | * 17 | * // Send to the server 18 | * analytics.send('category', 'action', 'label', 'value'); 19 | * 20 | * @class Analytics 21 | */ 22 | export class Analytics { 23 | /** 24 | * @constructor 25 | * @param {Object} settings Settings object 26 | */ 27 | constructor(settings) { 28 | /** 29 | * @private 30 | */ 31 | this.trackId = settings.id; 32 | 33 | if (!navigator.onLine) {return;} 34 | 35 | if (device.cocoonJS) { 36 | this.clientId = Date.now(); 37 | var request = new XMLHttpRequest(); 38 | var params = 'v=1&tid=' + this.trackId + '&cid=' + this.clientId + '&t=pageview&dp=%2F'; 39 | request.open('POST', 'http://www.google-analytics.com/collect', true); 40 | request.send(params); 41 | } 42 | else { 43 | (function(i, s, o, g, r, a, m) { 44 | i['GoogleAnalyticsObject'] = r; 45 | i[r] = i[r] || function() { 46 | (i[r].q = i[r].q || []).push(arguments); 47 | }; 48 | 49 | i[r].l = 1 * new Date(); 50 | a = s.createElement(o); 51 | m = s.getElementsByTagName(o)[0]; 52 | a.async = 1; 53 | a.src = g; 54 | m.parentNode.insertBefore(a, m); 55 | })(window, document, 'script', '//www.google-analytics.com/analytics.js', 'ga'); 56 | 57 | ga('create', this.trackId, 'auto'); 58 | ga('send', 'pageview'); 59 | } 60 | } 61 | 62 | /** 63 | * Send event to analytics. 64 | * @method send 65 | * @memberof Analytics# 66 | * @param {String} category Category of this event 67 | * @param {String} action Action of this event 68 | * @param {String} [label] Label of this event 69 | * @param {String} [value] Value of this event 70 | */ 71 | send(category, action, label, value) { 72 | if (!navigator.onLine) {return;} 73 | 74 | if (device.cocoonJS) { 75 | let request = new XMLHttpRequest(); 76 | let params = 'v=1&tid=' + this.trackId + '&cid=' + this.clientId + '&t=event&ec=' + category + '&ea=' + action; 77 | if (typeof label !== 'undefined') {params += '&el=' + label;} 78 | if (typeof value !== 'undefined') {params += '&ev=' + value;} 79 | request.open('POST', 'http://www.google-analytics.com/collect', true); 80 | request.send(params); 81 | } 82 | else { 83 | ga('send', 'event', category, action, label, value); 84 | } 85 | } 86 | } 87 | 88 | /** 89 | * Analytics module makes it easier to work with Google Analytics. 90 | * 91 | * An instance of {@link Analytics} is already created as the module 92 | * exports. 93 | * 94 | * See {@link Analytics} for more information. 95 | * 96 | * @exports engine/analytics 97 | * 98 | * @requires module:engine/device 99 | */ 100 | export default (new Analytics(Object.assign({ 101 | id: '', 102 | }, config.analytics))); 103 | -------------------------------------------------------------------------------- /src/engine/timer/Clock.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @class Clock 3 | */ 4 | export default class Clock { 5 | /** 6 | * @property {number} elapsed Time elapsed since start. 7 | * @readonly 8 | */ 9 | get elapsed() { return this.duration - this._count; } 10 | 11 | /** 12 | * @property {number} left Time left till the end. 13 | * @readonly 14 | */ 15 | get left() { return this._count; } 16 | 17 | /** 18 | * Clock constructor should not be used directly, use the static methods instead: 19 | * 20 | * @constructor 21 | * @param {number} [ms] Time 22 | */ 23 | constructor(ms) { 24 | /** 25 | * @type {number} 26 | * @private 27 | */ 28 | this._count = 0; 29 | 30 | /** 31 | * Duration of this timer. 32 | * @type {number} 33 | * @default 0 34 | */ 35 | this.duration = 0; 36 | 37 | /** 38 | * Whether this timer should repeat. 39 | * @type {boolean} 40 | * @default false 41 | */ 42 | this.repeat = false; 43 | 44 | /** 45 | * Whether this timer is already removed. 46 | * @type {boolean} 47 | * @protected 48 | * @default false 49 | */ 50 | this.removed = false; 51 | 52 | /** 53 | * Callback 54 | * @type {function} 55 | * @private 56 | */ 57 | this.callback = null; 58 | /** 59 | * Callback context 60 | * @type {object} 61 | * @private 62 | */ 63 | this.callbackCtx = null; 64 | 65 | this.set(ms); 66 | } 67 | 68 | /** 69 | * Set duration for timer. 70 | * @method set 71 | * @memberof Clock# 72 | * @param {number} ms Time to set to 73 | * @return {Clock} Self for chaining 74 | */ 75 | set(ms) { 76 | if (Number.isFinite(ms)) { 77 | this.duration = ms; 78 | } 79 | else { 80 | this.duration = 0; 81 | } 82 | return this.reset(); 83 | } 84 | 85 | /** 86 | * Reset timer to current duration. 87 | * @method reset 88 | * @memberof Clock# 89 | * @return {Clock} Self for chaining 90 | */ 91 | reset() { 92 | this.removed = false; 93 | this._count = this.duration; 94 | return this; 95 | } 96 | 97 | /** 98 | * Pause timer. 99 | * @method pause 100 | * @memberof Clock# 101 | * @return {Clock} Self for chaining 102 | */ 103 | pause() { 104 | this.paused = true; 105 | return this; 106 | } 107 | 108 | /** 109 | * Resume paused timer. 110 | * @method resume 111 | * @memberof Clock# 112 | * @return {Clock} Self for chaining 113 | */ 114 | resume() { 115 | this.paused = false; 116 | return this; 117 | } 118 | 119 | /** 120 | * Update method that is called by timer system. 121 | * @method update 122 | * @memberof Clock# 123 | * @protected 124 | * @param {number} delta Delta time 125 | */ 126 | update(delta) { 127 | if (this.removed || this.paused) {return;} 128 | 129 | this._count -= delta; 130 | if (this._count < 0) { 131 | this._count = 0; 132 | 133 | if (typeof this.callback === 'function') { 134 | this.callback.call(this.callbackCtx); 135 | } 136 | 137 | if (this.repeat && !this.removed) { 138 | this.reset(); 139 | } 140 | else { 141 | this.removed = true; 142 | } 143 | } 144 | } 145 | } 146 | -------------------------------------------------------------------------------- /src/engine/gfx/Graphics.js: -------------------------------------------------------------------------------- 1 | import Graphics from './core/graphics/Graphics'; 2 | import Vector from 'engine/Vector'; 3 | import { BLEND_MODES } from './const'; 4 | import './core/graphics/webgl/GraphicsRenderer'; 5 | 6 | const DEFAULT_POLYGON_VERTICES = [ 7 | Vector.create(-4, -4), 8 | Vector.create(+4, -4), 9 | Vector.create(+4, +4), 10 | Vector.create(-4, +4), 11 | ]; 12 | 13 | /** 14 | * Factory function for `Graphics`. 15 | * 16 | * @param {object} data Data to create the instance from 17 | * @return {Graphics} Graphics instance 18 | */ 19 | export default function(data) { 20 | let inst = new Graphics(); 21 | 22 | // TODO: add fill/stroke support 23 | inst.beginFill(data.color || 0x000000); 24 | let shape = data.shape || 'Box'; 25 | if (shape.toLowerCase() === 'circle') { 26 | inst.drawCircle(0, 0, data.radius || 8); 27 | } 28 | else if (shape.toLowerCase() === 'box') { 29 | let w = data.width || 8; 30 | let h = data.height || 8; 31 | 32 | let anchor = 'center'; 33 | if (typeof(data.anchor) === 'string') { 34 | anchor = data.anchor.toLowerCase(); 35 | } 36 | 37 | switch (anchor.toLowerCase()) { 38 | case 'center': 39 | inst.drawRect(-w / 2, -h / 2, w, h); 40 | break; 41 | 42 | case 'left': 43 | inst.drawRect(0, -h / 2, w, h); 44 | break; 45 | case 'right': 46 | inst.drawRect(-w, -h / 2, w, h); 47 | break; 48 | case 'top': 49 | inst.drawRect(-w / 2, 0, w, h); 50 | break; 51 | case 'bottom': 52 | inst.drawRect(-w / 2, -h, w, h); 53 | break; 54 | 55 | case 'topleft': 56 | inst.drawRect(0, 0, w, h); 57 | break; 58 | case 'topright': 59 | inst.drawRect(-w, 0, w, h); 60 | break; 61 | case 'bottomleft': 62 | inst.drawRect(0, -h, w, h); 63 | break; 64 | case 'bottomright': 65 | inst.drawRect(-w, -h, w, h); 66 | break; 67 | } 68 | } 69 | else if (shape.toLowerCase() === 'polygon') { 70 | let points = data.points || DEFAULT_POLYGON_VERTICES; 71 | inst.moveTo(points[0].x, points[0].y); 72 | for (let i = 1; i < points.length; i++) { 73 | inst.lineTo(points[i].x, points[i].y); 74 | } 75 | } 76 | inst.endFill(); 77 | 78 | for (let k in data) { 79 | switch (k) { 80 | // Directly set 81 | // - Node 82 | case 'alpha': 83 | case 'width': 84 | case 'height': 85 | case 'rotation': 86 | case 'visible': 87 | case 'x': 88 | case 'y': 89 | case 'interactive': 90 | // - Sprite 91 | case 'tint': 92 | // - Graphics 93 | case 'boundsPadding': 94 | inst[k] = data[k]; 95 | break; 96 | 97 | // Set vector 98 | // - Node 99 | case 'pivot': 100 | case 'position': 101 | case 'skew': 102 | inst[k].x = data[k].x || 0; 103 | inst[k].y = data[k].y || 0; 104 | break; 105 | 106 | // - Node 107 | case 'scale': 108 | inst[k].x = data[k].x || 1; 109 | inst[k].y = data[k].y || 1; 110 | break; 111 | 112 | // Set blend mode 113 | case 'blendMode': 114 | inst.blendMode = BLEND_MODES[data[k]]; 115 | break; 116 | } 117 | } 118 | 119 | return inst; 120 | }; 121 | --------------------------------------------------------------------------------