From 669570c701ee606ffefb72282bf5bf42bbbc1dae Mon Sep 17 00:00:00 2001 From: photonstorm Date: Tue, 10 Dec 2013 12:23:42 +0000 Subject: [PATCH] Tidying up the examples and more Tilemap work. --- README.md | 14 +- bower.json | 37 + build/phaser.d.ts | 226 ++- examples/assets/tiles/tiles_spritesheet.png | Bin 0 -> 128835 bytes examples/display/bitmapdata wobble.js | 4 +- examples/wip/bmd.js | 2 +- examples/wip/tilemap.js | 16 +- src/core/Game.js | 4 +- src/core/Group.js | 4 +- src/gameobjects/DOMSprite.js | 88 + src/gameobjects/Text.js | 2 +- src/loader/Cache.js | 59 +- src/tilemap/Tilemap.js | 40 +- src/tilemap/TilemapLayer.js | 59 +- wip/examples/a_template.php | 39 - wip/examples/anchor1.php | 57 - wip/examples/bitmapFont1.php | 39 - wip/examples/body1.php | 62 - wip/examples/body2.php | 63 - wip/examples/body3.php | 69 - wip/examples/bring to top.php | 54 - wip/examples/bring to top2.php | 55 - wip/examples/bringToTop.php | 341 ---- wip/examples/bringToTop2.php | 41 - wip/examples/bringToTop3.php | 94 - wip/examples/button1.php | 67 - wip/examples/camera1.php | 72 - wip/examples/camera2.php | 175 -- wip/examples/camera3.php | 74 - wip/examples/camera4.php | 56 - wip/examples/camera5.php | 70 - wip/examples/camera_cull1.php | 68 - wip/examples/camerafollow.php | 79 - wip/examples/circle.html | 45 - wip/examples/collision_group_vs_group.php | 117 -- wip/examples/collision_sprite_vs_group.php | 100 - wip/examples/collision_sprite_vs_sprite.php | 65 - ...lision_sprite_vs_sprite_custom_process.php | 96 - wip/examples/crop.php | 46 - wip/examples/crop2.php | 46 - wip/examples/crop3.php | 48 - wip/examples/drag.php | 43 - wip/examples/fullscreen.php | 63 - wip/examples/get_bounds.php | 47 - wip/examples/graphics.php | 80 - wip/examples/group1.php | 84 - wip/examples/inherit.php | 167 -- wip/examples/input1.php | 48 - wip/examples/input2.php | 60 - wip/examples/invaders.php | 63 - wip/examples/js-physics.php | 26 - wip/examples/js.php | 109 -- wip/examples/js_full.php | 120 -- wip/examples/linkedlist1.php | 74 - wip/examples/loader 2.html | 59 - wip/examples/loader atlas json.html | 57 - wip/examples/loader atlas xml.html | 57 - wip/examples/loader audio.html | 57 - wip/examples/loader spritesheet.html | 57 - wip/examples/loader.html | 48 - wip/examples/mapcollide.php | 85 - wip/examples/mario.php | 60 - wip/examples/mariocombo.php | 48 - wip/examples/mariotogether.php | 45 - wip/examples/math sincos.html | 48 - wip/examples/motion1.php | 98 - wip/examples/pixi 1.html | 139 -- wip/examples/point.html | 44 - wip/examples/quadtree.php | 110 -- wip/examples/quadtree2.php | 90 - wip/examples/raf.html | 45 - wip/examples/rect1.php | 22 - wip/examples/rendertexture.php | 42 - wip/examples/rnd.html | 25 - wip/examples/signals.html | 35 - wip/examples/snap.php | 54 - wip/examples/sound1.php | 71 - wip/examples/sprite1.php | 43 - wip/examples/sprite2.php | 55 - wip/examples/sprite3.php | 65 - wip/examples/sprite4.php | 70 - wip/examples/sprite_extend.php | 56 - wip/examples/stage 1.php | 55 - wip/examples/stage 2.php | 66 - wip/examples/stage 3.php | 68 - wip/examples/stagecolor.php | 31 - wip/examples/statetest.php | 85 - wip/examples/test.php | 79 - wip/examples/text1.php | 31 - wip/examples/text2.php | 41 - wip/examples/text3.php | 82 - wip/examples/tilemap.php | 62 - wip/examples/tilesprite1.php | 63 - wip/examples/tilesprite2.php | 54 - wip/examples/touch1.php | 62 - wip/examples/tween.html | 47 - wip/examples/tween2.php | 42 - wip/examples/wip1.html | 141 -- wip/examples/world.php | 106 - wip/phaser clean up/ArcadePhysics.ts | 1121 ----------- wip/phaser clean up/Collision.ts | 1702 ----------------- wip/phaser clean up/CollisionMask.ts | 501 ----- wip/phaser clean up/Debug.ts | 90 - wip/phaser clean up/DebugPhysics.ts | 78 - wip/phaser clean up/Emitter.ts | 454 ----- wip/phaser clean up/Input.ts | 29 - wip/phaser clean up/Motion.ts | 409 ---- wip/phaser clean up/OldSprite.ts | 355 ---- wip/phaser clean up/PixelUtils.ts | 49 - wip/phaser clean up/Properties.ts | 36 - wip/phaser clean up/Tilemap.ts | 434 ----- wip/phaser clean up/aabb 1.ts | 64 - wip/phaser clean up/aabb vs aabb 1.ts | 81 - wip/phaser clean up/body1.ts | 206 -- wip/phaser clean up/obb vs obb.ts | 54 - 115 files changed, 372 insertions(+), 11768 deletions(-) create mode 100644 bower.json create mode 100644 examples/assets/tiles/tiles_spritesheet.png create mode 100644 src/gameobjects/DOMSprite.js delete mode 100644 wip/examples/a_template.php delete mode 100644 wip/examples/anchor1.php delete mode 100644 wip/examples/bitmapFont1.php delete mode 100644 wip/examples/body1.php delete mode 100644 wip/examples/body2.php delete mode 100644 wip/examples/body3.php delete mode 100644 wip/examples/bring to top.php delete mode 100644 wip/examples/bring to top2.php delete mode 100644 wip/examples/bringToTop.php delete mode 100644 wip/examples/bringToTop2.php delete mode 100644 wip/examples/bringToTop3.php delete mode 100644 wip/examples/button1.php delete mode 100644 wip/examples/camera1.php delete mode 100644 wip/examples/camera2.php delete mode 100644 wip/examples/camera3.php delete mode 100644 wip/examples/camera4.php delete mode 100644 wip/examples/camera5.php delete mode 100644 wip/examples/camera_cull1.php delete mode 100644 wip/examples/camerafollow.php delete mode 100644 wip/examples/circle.html delete mode 100644 wip/examples/collision_group_vs_group.php delete mode 100644 wip/examples/collision_sprite_vs_group.php delete mode 100644 wip/examples/collision_sprite_vs_sprite.php delete mode 100644 wip/examples/collision_sprite_vs_sprite_custom_process.php delete mode 100644 wip/examples/crop.php delete mode 100644 wip/examples/crop2.php delete mode 100644 wip/examples/crop3.php delete mode 100644 wip/examples/drag.php delete mode 100644 wip/examples/fullscreen.php delete mode 100644 wip/examples/get_bounds.php delete mode 100644 wip/examples/graphics.php delete mode 100644 wip/examples/group1.php delete mode 100644 wip/examples/inherit.php delete mode 100644 wip/examples/input1.php delete mode 100644 wip/examples/input2.php delete mode 100644 wip/examples/invaders.php delete mode 100644 wip/examples/js-physics.php delete mode 100644 wip/examples/js.php delete mode 100644 wip/examples/js_full.php delete mode 100644 wip/examples/linkedlist1.php delete mode 100644 wip/examples/loader 2.html delete mode 100644 wip/examples/loader atlas json.html delete mode 100644 wip/examples/loader atlas xml.html delete mode 100644 wip/examples/loader audio.html delete mode 100644 wip/examples/loader spritesheet.html delete mode 100644 wip/examples/loader.html delete mode 100644 wip/examples/mapcollide.php delete mode 100644 wip/examples/mario.php delete mode 100644 wip/examples/mariocombo.php delete mode 100644 wip/examples/mariotogether.php delete mode 100644 wip/examples/math sincos.html delete mode 100644 wip/examples/motion1.php delete mode 100644 wip/examples/pixi 1.html delete mode 100644 wip/examples/point.html delete mode 100644 wip/examples/quadtree.php delete mode 100644 wip/examples/quadtree2.php delete mode 100644 wip/examples/raf.html delete mode 100644 wip/examples/rect1.php delete mode 100644 wip/examples/rendertexture.php delete mode 100644 wip/examples/rnd.html delete mode 100644 wip/examples/signals.html delete mode 100644 wip/examples/snap.php delete mode 100644 wip/examples/sound1.php delete mode 100644 wip/examples/sprite1.php delete mode 100644 wip/examples/sprite2.php delete mode 100644 wip/examples/sprite3.php delete mode 100644 wip/examples/sprite4.php delete mode 100644 wip/examples/sprite_extend.php delete mode 100644 wip/examples/stage 1.php delete mode 100644 wip/examples/stage 2.php delete mode 100644 wip/examples/stage 3.php delete mode 100644 wip/examples/stagecolor.php delete mode 100644 wip/examples/statetest.php delete mode 100644 wip/examples/test.php delete mode 100644 wip/examples/text1.php delete mode 100644 wip/examples/text2.php delete mode 100644 wip/examples/text3.php delete mode 100644 wip/examples/tilemap.php delete mode 100644 wip/examples/tilesprite1.php delete mode 100644 wip/examples/tilesprite2.php delete mode 100644 wip/examples/touch1.php delete mode 100644 wip/examples/tween.html delete mode 100644 wip/examples/tween2.php delete mode 100644 wip/examples/wip1.html delete mode 100644 wip/examples/world.php delete mode 100644 wip/phaser clean up/ArcadePhysics.ts delete mode 100644 wip/phaser clean up/Collision.ts delete mode 100644 wip/phaser clean up/CollisionMask.ts delete mode 100644 wip/phaser clean up/Debug.ts delete mode 100644 wip/phaser clean up/DebugPhysics.ts delete mode 100644 wip/phaser clean up/Emitter.ts delete mode 100644 wip/phaser clean up/Input.ts delete mode 100644 wip/phaser clean up/Motion.ts delete mode 100644 wip/phaser clean up/OldSprite.ts delete mode 100644 wip/phaser clean up/PixelUtils.ts delete mode 100644 wip/phaser clean up/Properties.ts delete mode 100644 wip/phaser clean up/Tilemap.ts delete mode 100644 wip/phaser clean up/aabb 1.ts delete mode 100644 wip/phaser clean up/aabb vs aabb 1.ts delete mode 100644 wip/phaser clean up/body1.ts delete mode 100644 wip/phaser clean up/obb vs obb.ts diff --git a/README.md b/README.md index f00c2651..88db2db8 100644 --- a/README.md +++ b/README.md @@ -55,6 +55,8 @@ Updates: * Updated Pixi.js so that removing filters now works correctly without breaking the display list. * Phaser.Canvas.create updated to it can be given an ID as the 3rd parameter. * Updated display/fullscreen example to reflect new full screen change. +* Loads of updates to the TypeScript definitions files - games fully compile now and lots of missing classes added :) +* Removed 'null parent' check from Group constructor. Will parent to game.world only if parent value is undefined. Bug Fixes: @@ -66,7 +68,7 @@ Bug Fixes: * Group.length now returns the number of children in the Group regardless of their exists/alive state, or 0 if the Group is has no children. * Switch Camera.setBoundsToWorld to match world.bounds instead of world (thanks cocoademon) * Fixed an issue where passing null as the Group parent wouldn't set it to game.world as it should have (thanks tito100) -* Fixed Pixi bug (#425) incorrect width property for multi-line Bitmap (thanks jcd-as) +* Fixed Pixi bug (#425) incorrect width property for multi-line BitmapText (thanks jcd-as) You can view the Change Log for all previous versions at https://github.com/photonstorm/phaser/changelog.md @@ -81,6 +83,16 @@ We also provide a Grunt script that will build Phaser from source along with all Run `grunt` in the phaser folder for a list of command-line options. + +Bower +----- + +If you use bowser you can install phaser with: + +`bower install phaser` + +Nice and easy :) + ![Tanks](http://www.photonstorm.com/wp-content/uploads/2013/10/phaser_tanks-640x480.png) diff --git a/bower.json b/bower.json new file mode 100644 index 00000000..74bd4116 --- /dev/null +++ b/bower.json @@ -0,0 +1,37 @@ +{ + "name": "phaser", + "version": "1.1.3", + "homepage": "http://phaser.io", + "authors": [ + "photonstorm " + ], + "description": "A fun, free and fast 2D game framework for making HTML5 games for desktop and mobile, supporting Canvas and WebGL.", + "main": "build/phaser.min.js", + "keywords": [ + "html5", + "game", + "games", + "framework", + "canvas", + "WebGL", + "tilemaps", + "physics", + "sprites", + "fonts", + "images", + "audio", + "Web", + "Audio", + "touch", + "input", + "mobile" + ], + "license": "MIT", + "ignore": [ + "**/.*", + "node_modules", + "bower_components", + "test", + "tests" + ] +} diff --git a/build/phaser.d.ts b/build/phaser.d.ts index 4d4654ac..4148ead4 100644 --- a/build/phaser.d.ts +++ b/build/phaser.d.ts @@ -260,7 +260,7 @@ declare module Phaser { } class Game { - constructor(width: number, height: number, renderer: number, parent: string, state: Object, transparent: boolean, antialias: boolean); + constructor(width: number, height: number, renderer: number, parent: string, state: Object, transparent?: boolean, antialias?: boolean); id: number; width: number; height: number; @@ -655,7 +655,7 @@ declare module Phaser { onAnimationStart: Phaser.Signal; onAnimationComplete: Phaser.Signal; onAnimationLoop: Phaser.Signal; - } + } class GameObjectFactory { constructor(game: Phaser.Game); @@ -687,7 +687,7 @@ declare module Phaser { type: number; renderOrderID: number; lifespan: number; - events: Phaser.Event[]; + events: Phaser.Events; animations: Phaser.AnimationManager; input: Phaser.InputHandler; key: string; @@ -707,8 +707,6 @@ declare module Phaser { bottomLeft: Phaser.Point; bounds: Phaser.Rectangle; body: Phaser.Physics.Arcade.Body; - velocity: number; - acceleration: number; inWorld: boolean; inWorldThreshold: number; angle: number; @@ -731,6 +729,24 @@ declare module Phaser { getBounds(rect: Phaser.Rectangle): Phaser.Rectangle; } + class Events { + parent: Phaser.Sprite; + onAddedToGroup: Phaser.Signal; + onRemovedFromGroup: Phaser.Signal; + onKilled: Phaser.Signal; + onRevived: Phaser.Signal; + onOutOfBounds: Phaser.Signal; + onInputOver: Phaser.Signal; + onInputOut: Phaser.Signal; + onInputDown: Phaser.Signal; + onInputUp: Phaser.Signal; + onDragStart: Phaser.Signal; + onDragStop: Phaser.Signal; + onAnimationStart: Phaser.Signal; + onAnimationComplete: Phaser.Signal; + onAnimationLoop: Phaser.Signal; + } + class TileSprite { constructor(game: Phaser.Game, x: number, y: number, width: number, height: number, key?: string, frame?: number); texture: Phaser.RenderTexture; @@ -1166,60 +1182,68 @@ declare module Phaser { update(time: number): boolean; } - class Easing { - Linear: { - None: (k: number) => number; - }; - Quadratic: { - In: (k: number) => number; - Out: (k: number) => number; - InOut: (k: number) => number; - }; - Cubic: { - In: (k: number) => number; - Out: (k: number) => number; - InOut: (k: number) => number; - }; - Quartic: { - In: (k: number) => number; - Out: (k: number) => number; - InOut: (k: number) => number; - }; - Quintic: { - In: (k: number) => number; - Out: (k: number) => number; - InOut: (k: number) => number; - }; - Sinusoidal: { - In: (k: number) => number; - Out: (k: number) => number; - InOut: (k: number) => number; - }; - Exponential: { - In: (k: number) => number; - Out: (k: number) => number; - InOut: (k: number) => number; - }; - Circular: { - In: (k: number) => number; - Out: (k: number) => number; - InOut: (k: number) => number; - }; - Elastic: { - In: (k: number) => number; - Out: (k: number) => number; - InOut: (k: number) => number; - }; - Back: { - In: (k: number) => number; - Out: (k: number) => number; - InOut: (k: number) => number; - }; - Bounce: { - In: (k: number) => number; - Out: (k: number) => number; - InOut: (k: number) => number; - }; + class Easing.Linear { + static None(k: number); + } + + class Easing.Quadratic { + static In(k: number); + static Out(k: number); + static InOut(k: number); + } + + class Easing.Cubic { + static In(k: number); + static Out(k: number); + static InOut(k: number); + } + + class Easing.Quartic { + static In(k: number); + static Out(k: number); + static InOut(k: number); + } + + class Easing.Quintic { + static In(k: number); + static Out(k: number); + static InOut(k: number); + } + + class Easing.Sinusoidal { + static In(k: number); + static Out(k: number); + static InOut(k: number); + } + + class Easing.Exponential { + static In(k: number); + static Out(k: number); + static InOut(k: number); + } + + class Easing.Circular { + static In(k: number); + static Out(k: number); + static InOut(k: number); + } + + class Easing.Elastic { + static In(k: number); + static Out(k: number); + static InOut(k: number); + } + + class Easing.Back { + static In(k: number); + static Out(k: number); + static InOut(k: number); + } + + class Easing.Bounce { + static In(k: number); + static Out(k: number); + static InOut(k: number); } class Time { @@ -1286,48 +1310,46 @@ declare module Phaser { onComplete(): void; } - module Animation { - class Frame { - constructor(index: number, x: number, y: number, width: number, height: number, name: string, uuid: string); - index: number; - x: number; - y: number; - width: number; - height: number; - centerX: number; - centerY: number; - distance: number; - name: string; - uuid: string; - rotated: boolean; - rotationDirection: string; - trimmed: boolean; - sourceSizeW: number; - sourceSizeH: number; - spriteSourceSizeX: number; - spriteSourceSizeY: number; - spriteSourceSizeW: number; - spriteSourcesizeH: number; - setTrim(trimmed: boolean, actualWidth: number, actualHeight: number, destX: number, destY: number, destWidth: number, destHeight: number): void; - } + class Frame { + constructor(index: number, x: number, y: number, width: number, height: number, name: string, uuid: string); + index: number; + x: number; + y: number; + width: number; + height: number; + centerX: number; + centerY: number; + distance: number; + name: string; + uuid: string; + rotated: boolean; + rotationDirection: string; + trimmed: boolean; + sourceSizeW: number; + sourceSizeH: number; + spriteSourceSizeX: number; + spriteSourceSizeY: number; + spriteSourceSizeW: number; + spriteSourcesizeH: number; + setTrim(trimmed: boolean, actualWidth: number, actualHeight: number, destX: number, destY: number, destWidth: number, destHeight: number): void; + } - class FrameData { - addFrame(frame: Frame): Frame; - getFrame(index: number): Frame; - getFrameByName(name: string): Frame; - checkFrame(name: string): boolean; - getFrameRange(start: number, end: number, output: Array): Array; - getFrames(frames: Array, useNumericIndex?: boolean, output?: Array): Array; - getFrameIndexes(frames: Array, useNumericIndex?: boolean, output?: Array): Array; - total: number; - } + class FrameData { + addFrame(frame: Frame): Frame; + getFrame(index: number): Frame; + getFrameByName(name: string): Frame; + checkFrame(name: string): boolean; + getFrameRange(start: number, end: number, output: Array): Array; + getFrames(frames: Array, useNumericIndex?: boolean, output?: Array): Array; + getFrameIndexes(frames: Array, useNumericIndex?: boolean, output?: Array): Array; + total: number; + } - class Parser { - spriteSheet(game: Phaser.Game, key: string, frameWidth: number, frameHeight: number, frameMax?: number): Phaser.Animation.FrameData; - JSONData(game: Phaser.Game, json: Object, cacheKey: string): Phaser.Animation.FrameData; - JSONDataHash(game: Phaser.Game, json: Object, cacheKey: string): Phaser.Animation.FrameData; - XMLData(game: Phaser.Game, xml: Object, cacheKey: string): Phaser.Animation.FrameData; - } + class AnimationParser { + spriteSheet(game: Phaser.Game, key: string, frameWidth: number, frameHeight: number, frameMax?: number): Phaser.Animation.FrameData; + JSONData(game: Phaser.Game, json: Object, cacheKey: string): Phaser.Animation.FrameData; + JSONDataHash(game: Phaser.Game, json: Object, cacheKey: string): Phaser.Animation.FrameData; + XMLData(game: Phaser.Game, xml: Object, cacheKey: string): Phaser.Animation.FrameData; } class Cache { @@ -1420,10 +1442,8 @@ declare module Phaser { nextFile(previousKey: string, success: boolean): void; } - module Loader { - class Parser { - bitmapFont(game: Phaser.Game, xml: Object, cacheKey: Phaser.Animation.FrameData): void; - } + class LoaderParser { + bitmapFont(game: Phaser.Game, xml: Object, cacheKey: Phaser.Animation.FrameData): void; } class Sound { diff --git a/examples/assets/tiles/tiles_spritesheet.png b/examples/assets/tiles/tiles_spritesheet.png new file mode 100644 index 0000000000000000000000000000000000000000..57ee83b63cbacf9b18da9001ea83ff8b0f831889 GIT binary patch literal 128835 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 Cosk9r literal 0 HcmV?d00001 diff --git a/examples/display/bitmapdata wobble.js b/examples/display/bitmapdata wobble.js index 53190772..07224ba6 100644 --- a/examples/display/bitmapdata wobble.js +++ b/examples/display/bitmapdata wobble.js @@ -17,7 +17,7 @@ var waveDataCounter; function create() { // Create our BitmapData object at a size of 32x64 - bmd = game.add.bitmapData('ball', 32, 64); + bmd = game.add.bitmapData(32, 64); // And apply it to 100 randomly positioned sprites for (var i = 0; i < 100; i++) @@ -62,7 +62,7 @@ function updateWobblyBall() // Now all the pixel data has been redrawn we render it to the BitmapData object. // In CANVAS mode this doesn't do anything, but on WebGL it pushes the new texture to the GPU. - // If your game is exclusively running under Canvas you ca safely ignore this step. + // If your game is exclusively running under Canvas you can safely ignore this step. bmd.render(); // Cycle through the wave data - this is what causes the image to "undulate" diff --git a/examples/wip/bmd.js b/examples/wip/bmd.js index dda21b51..36d4e42a 100644 --- a/examples/wip/bmd.js +++ b/examples/wip/bmd.js @@ -14,7 +14,7 @@ var bmd; function create() { - bmd = game.add.bitmapData('ball', 32, 64); + bmd = game.add.bitmapData(32, 64); console.log(bmd); diff --git a/examples/wip/tilemap.js b/examples/wip/tilemap.js index 93633159..4df1e197 100644 --- a/examples/wip/tilemap.js +++ b/examples/wip/tilemap.js @@ -3,6 +3,9 @@ var game = new Phaser.Game(800, 600, Phaser.CANVAS, 'phaser-example', { preload: function preload() { + // game.load.tilemap('map', 'assets/maps/mario1.json', null, Phaser.Tilemap.TILED_JSON); + // game.load.tileset('marioTiles', 'assets/maps/mario1.png', 16, 16); + game.load.tilemap('map', 'assets/maps/newtest.json', null, Phaser.Tilemap.TILED_JSON); game.load.tileset('tiles', 'assets/maps/ground_1x1.png', 32, 32); // game.load.image('phaser', 'assets/sprites/phaser-ship.png'); @@ -21,25 +24,20 @@ var sprite; function create() { - game.stage.backgroundColor = '#5c94fc'; + // game.stage.backgroundColor = '#5c94fc'; map = game.add.tilemap('map'); + // map.setCollisionByIndexRange(80, 97); // mario map.setCollisionByIndex(1); // Phaser.TilemapLayer = function (game, x, y, renderWidth, renderHeight, tileset, tilemap, layer) { - - // layer = game.add.tilemapLayer(0, 0, 800, 600, null, map, 0); layer = game.add.tilemapLayer(0, 0, 800, 600, null, map, 0); // layer2 = game.add.tilemapLayer(0, 0, 400, 600, null, map, 0); // layer.cameraOffset.x = 400; // layer.alpha = 0.5; - // tileset = game.add.tileset('tilesNes'); - // layer = game.add.tilemapLayer(0, 0, map.layers[0].width*tilesetNes.tileWidth, 600, tileset, map, 0); - // disable this and you can place anywhere, but will almost certainly screw collision - layer.resizeWorld(); sprite = game.add.sprite(260, 100, 'phaser'); @@ -51,10 +49,6 @@ function create() { // In this case the box is 50px in and 25px down. sprite.body.setSize(16, 16, 8, 8); - - - - // We'll set a lower max angular velocity here to keep it from going totally nuts sprite.body.maxAngular = 500; diff --git a/src/core/Game.js b/src/core/Game.js index 692f6387..b195f6c7 100644 --- a/src/core/Game.js +++ b/src/core/Game.js @@ -17,8 +17,8 @@ * @param {number} [width=800] - The width of your game in game pixels. * @param {number} [height=600] - The height of your game in game pixels. * @param {number} [renderer=Phaser.AUTO] - Which renderer to use: Phaser.AUTO will auto-detect, Phaser.WEBGL, Phaser.CANVAS or Phaser.HEADLESS (no rendering at all). -* @param {HTMLElement} [parent=''] - The Games DOM parent. -* @param {any} [state=null] - Description. +* @param {string|HTMLElement} [parent=''] - The DOM element into which this games canvas will be injected. Either a DOM ID (string) or the element itself. +* @param {object} [state=null] - The default state object. A object consisting of Phaser.State functions (preload, create, update, render) or null. * @param {boolean} [transparent=false] - Use a transparent canvas background or not. * @param {boolean} [antialias=true] - Anti-alias graphics. */ diff --git a/src/core/Group.js b/src/core/Group.js index 0fe18896..6e23200f 100644 --- a/src/core/Group.js +++ b/src/core/Group.js @@ -10,7 +10,7 @@ * @classdesc A Group is a container for display objects that allows for fast pooling, recycling and collision checks. * @constructor * @param {Phaser.Game} game - A reference to the currently running game. -* @param {*} parent - The parent Group or DisplayObjectContainer that will hold this group, if any. If not set, or set to null, it will use game.world. +* @param {*} parent - The parent Group or DisplayObjectContainer that will hold this group, if any. If undefined it will use game.world. * @param {string} [name=group] - A name for this Group. Not used internally but useful for debugging. * @param {boolean} [useStage=false] - Should the DisplayObjectContainer this Group creates be added to the World (default, false) or direct to the Stage (true). */ @@ -21,7 +21,7 @@ Phaser.Group = function (game, parent, name, useStage) { */ this.game = game; - if (typeof parent === 'undefined' || parent === null) + if (typeof parent === 'undefined') { parent = game.world; } diff --git a/src/gameobjects/DOMSprite.js b/src/gameobjects/DOMSprite.js new file mode 100644 index 00000000..d990bc5b --- /dev/null +++ b/src/gameobjects/DOMSprite.js @@ -0,0 +1,88 @@ +/** +* @author Richard Davey +* @copyright 2013 Photon Storm Ltd. +* @license {@link https://github.com/photonstorm/phaser/blob/master/license.txt|MIT License} +*/ + +/** +* Create a new DOMSprite. +* @class Phaser.DOMSprite +* @constructor +* @param {Phaser.Game} game - Current game instance. +* @param {string} id - DOM ID. +* @param {number} x - X position of the new text object. +* @param {number} y - Y position of the new text object. +* @param {string} text - The actual text that will be written. +* @param {object} style - The style object containing style attributes like font, font size , +*/ +Phaser.DOMSprite = function (game, element, x, y, style) { + + x = x || 0; + y = y || 0; + style = style || ''; + + /** + * @property {Phaser.Game} game - A reference to the currently running Game. + */ + this.game = game; + + /** + * @property {boolean} exists - If exists = false then the Text isn't updated by the core game loop. + * @default + */ + this.exists = true; + + /** + * @property {boolean} alive - This is a handy little var your game can use to determine if an object is alive or not, it doesn't effect rendering. + * @default + */ + this.alive = true; + + /** + * @property {Phaser.Group} group - The parent Group of this Text object. + */ + this.group = null; + + /** + * @property {string} name - The user defined name given to this object. + * @default + */ + this.name = ''; + + /** + * @property {number} type - The const type of this object. + * @default + */ + this.type = Phaser.DOMSPRITE; + + /** + * @property {boolean} visible - Sets the visible state of this DOMSprite. + * @default + */ + this.visible = true; + + /* + if (parent) + { + if (typeof parent === 'string') + { + // hopefully an element ID + target = document.getElementById(parent); + } + else if (typeof parent === 'object' && parent.nodeType === 1) + { + // quick test for a HTMLelement + target = parent; + } + + if (overflowHidden) + { + target.style.overflow = 'hidden'; + } + } + */ + +}; + +// Phaser.DOMSprite.prototype = Object.create(PIXI.DOMSprite.prototype); +// Phaser.DOMSprite.prototype.constructor = Phaser.DOMSprite; diff --git a/src/gameobjects/Text.js b/src/gameobjects/Text.js index b5be0621..aa20c490 100644 --- a/src/gameobjects/Text.js +++ b/src/gameobjects/Text.js @@ -5,7 +5,7 @@ */ /** -* Create a new Text. +* Create a new `Text` object. * @class Phaser.Text * @constructor * @param {Phaser.Game} game - Current game instance. diff --git a/src/loader/Cache.js b/src/loader/Cache.js index 40ce6d0a..7d0bad59 100644 --- a/src/loader/Cache.js +++ b/src/loader/Cache.js @@ -415,8 +415,10 @@ Phaser.Cache.prototype = { { return this._canvases[key].canvas; } - - return null; + else + { + console.warn('Phaser.Cache.getCanvas: Invalid key: "' + key + '"'); + } }, @@ -433,8 +435,10 @@ Phaser.Cache.prototype = { { return this._bitmapDatas[key]; } - - return null; + else + { + console.warn('Phaser.Cache.getBitmapData: Invalid key: "' + key + '"'); + } }, @@ -469,8 +473,10 @@ Phaser.Cache.prototype = { { return this._images[key].data; } - - return null; + else + { + console.warn('Phaser.Cache.getImage: Invalid key: "' + key + '"'); + } }, @@ -487,8 +493,10 @@ Phaser.Cache.prototype = { { return this._tilesets[key].data; } - - return null; + else + { + console.warn('Phaser.Cache.getTilesetImage: Invalid key: "' + key + '"'); + } }, @@ -505,8 +513,10 @@ Phaser.Cache.prototype = { { return this._tilesets[key].tileData; } - - return null; + else + { + console.warn('Phaser.Cache.getTileset: Invalid key: "' + key + '"'); + } }, @@ -523,8 +533,11 @@ Phaser.Cache.prototype = { { return this._tilemaps[key]; } + else + { + console.warn('Phaser.Cache.getTilemapData: Invalid key: "' + key + '"'); + } - return null; }, /** @@ -625,8 +638,10 @@ Phaser.Cache.prototype = { { return this._textures[key]; } - - return null; + else + { + console.warn('Phaser.Cache.getTexture: Invalid key: "' + key + '"'); + } }, @@ -643,8 +658,10 @@ Phaser.Cache.prototype = { { return this._sounds[key]; } - - return null; + else + { + console.warn('Phaser.Cache.getSound: Invalid key: "' + key + '"'); + } }, @@ -661,8 +678,10 @@ Phaser.Cache.prototype = { { return this._sounds[key].data; } - - return null; + else + { + console.warn('Phaser.Cache.getSoundData: Invalid key: "' + key + '"'); + } }, @@ -726,8 +745,10 @@ Phaser.Cache.prototype = { { return this._text[key].data; } - - return null; + else + { + console.warn('Phaser.Cache.getText: Invalid key: "' + key + '"'); + } }, diff --git a/src/tilemap/Tilemap.js b/src/tilemap/Tilemap.js index 58dcbbb4..1078fe14 100644 --- a/src/tilemap/Tilemap.js +++ b/src/tilemap/Tilemap.js @@ -21,7 +21,7 @@ Phaser.Tilemap = function (game, key) { this.game = game; /** - * @property {array} layers - An array of Tilemap layers. + * @property {array} layers - An array of Tilemap layer data. */ this.layers = null; @@ -30,7 +30,6 @@ Phaser.Tilemap = function (game, key) { this.key = key; this.layers = game.cache.getTilemapData(key).layers; - this.calculateIndexes(); } else { @@ -45,7 +44,6 @@ Phaser.Tilemap = function (game, key) { /** * @property {array} debugMap - Map data used for debug values only. */ - this.debugMap = []; /** @@ -117,25 +115,40 @@ Phaser.Tilemap.prototype = { data: data, indexes: [], dirty: true + }); this.currentLayer = this.layers.length - 1; }, + createTilemapLayer: function (x, y, renderWidth, renderHeight, tileset, layer) { + + return this.game.world.add(new Phaser.TilemapLayer(this.game, x, y, renderWidth, renderHeight, tileset, this, layer)); + + }, + + setCollisionByIndexRange: function (start, stop, layer) { + + if (start > stop) + { + return; + } + + for (var i = start; i <= stop; i++) + { + this.setCollisionByIndex(i, layer); + } + + }, + /** * Sets collision values on a tile in the set. * * @method Phaser.Tileset#setCollision - * @param {number} index - The index of the tile within the set. - * @param {boolean} left - Should the tile collide on the left? - * @param {boolean} right - Should the tile collide on the right? - * @param {boolean} up - Should the tile collide on the top? - * @param {boolean} down - Should the tile collide on the bottom? + * @param {number} index - The index of the tile on the layer. + * @param {number} layer - The layer to operate on. */ - - // Sets all tiles matching the given index to collide on the given faces - // Recalculates the collision map setCollisionByIndex: function (index, layer) { if (typeof layer === "undefined") { layer = this.currentLayer; } @@ -157,6 +170,9 @@ Phaser.Tilemap.prototype = { } } + // Sets all tiles matching the given index to collide on the given faces + // Recalculates the collision map + // Now re-calculate interesting faces this.calculateFaces(layer); @@ -169,7 +185,7 @@ Phaser.Tilemap.prototype = { var left = null; var right = null; -console.log(this.layers[layer].width, 'x', this.layers[layer].height); + // console.log(this.layers[layer].width, 'x', this.layers[layer].height); for (var y = 0; y < this.layers[layer].height ; y++) { diff --git a/src/tilemap/TilemapLayer.js b/src/tilemap/TilemapLayer.js index 8fb940b3..28da86e7 100644 --- a/src/tilemap/TilemapLayer.js +++ b/src/tilemap/TilemapLayer.js @@ -16,7 +16,7 @@ * @param {number} renderHeight - Height of the layer. * @param {Phaser.Tileset|string} tileset - The tile set used for rendering. * @param {Phaser.Tilemap} tilemap - The tilemap to which this layer belongs. -* @param {number} layer - The layer index within the map. +* @param {number|string} [layer=0] - The layer within the tilemap this TilemapLayer represents. */ Phaser.TilemapLayer = function (game, x, y, renderWidth, renderHeight, tileset, tilemap, layer) { @@ -48,7 +48,7 @@ Phaser.TilemapLayer = function (game, x, y, renderWidth, renderHeight, tileset, /** * @property {Phaser.Frame} textureFrame - Dimensions of the renderable area. */ - this.textureFrame = new Phaser.Frame(0, 0, 0, renderWidth, renderHeight, 'tilemaplayer', game.rnd.uuid()); + this.textureFrame = new Phaser.Frame(0, 0, 0, renderWidth, renderHeight, 'tilemapLayer', game.rnd.uuid()); Phaser.Sprite.call(this, this.game, x, y, this.texture, this.textureFrame); @@ -248,12 +248,12 @@ Phaser.TilemapLayer = function (game, x, y, renderWidth, renderHeight, tileset, this.tilemap = null; /** - * @property {number} layer - Tilemap layer index. + * @property {object} layer - The layer object within the Tilemap that this layer represents. */ this.layer = null; /** - * @property {number} index + * @property {number} index - The index of this layer within the Tilemap. */ this.index = 0; @@ -349,13 +349,13 @@ Phaser.TilemapLayer.prototype.updateTileset = function (tileset) { */ Phaser.TilemapLayer.prototype.updateMapData = function (tilemap, layer) { - if (typeof layer === 'undefined') - { - layer = 0; - } - if (tilemap instanceof Phaser.Tilemap) { + if (typeof layer === 'undefined') + { + layer = 0; + } + this.tilemap = tilemap; this.layer = this.tilemap.layers[layer]; this.tileWidth = this.layer.tileWidth; @@ -690,7 +690,8 @@ Phaser.TilemapLayer.prototype.render = function () { this.dirty = true; } - if (!this.dirty || !this.tileset || !this.tilemap || !this.visible) + // if (!this.dirty || !this.tileset || !this.tilemap || !this.visible) + if (!this.dirty || !this.tilemap || !this.visible) { return; } @@ -710,10 +711,8 @@ Phaser.TilemapLayer.prototype.render = function () { this._ty = this._dy; this.context.clearRect(0, 0, this.canvas.width, this.canvas.height); - // this.context.fillStyle = '#000000'; - // this.context.fillRect(0, 0, this.canvas.width, this.canvas.height); - - this.context.strokeStyle = '#00ff00'; + this.context.fillStyle = 'rgba(0,255,0,0.3)'; + this.context.strokeStyle = 'rgba(0,255,0,0.9)'; for (var y = this._startY, lenY = this._startY + this._maxY; y < lenY; y++) { @@ -725,25 +724,27 @@ Phaser.TilemapLayer.prototype.render = function () { // var tile = this.tileset.tiles[this._column[x] - 1]; - if (tile) - { - // this.context.drawImage( - // this.tileset.image, - // tile.x, - // tile.y, - // this.tileWidth, - // this.tileHeight, - // Math.floor(this._tx), - // Math.floor(this._ty), - // this.tileWidth, - // this.tileHeight - // ); - } + // if (tile && this.tileset) + // { + // this.context.drawImage( + // this.tileset.image, + // tile.x, + // tile.y, + // this.tileWidth, + // this.tileHeight, + // Math.floor(this._tx), + // Math.floor(this._ty), + // this.tileWidth, + // this.tileHeight + // ); + // } if (tile && (tile.faceTop || tile.faceBottom || tile.faceLeft || tile.faceRight)) { this._tx = Math.floor(this._tx); + this.context.fillRect(this._tx, this._ty, this.tileWidth, this.tileHeight); + this.context.beginPath(); if (tile.faceTop) @@ -770,9 +771,9 @@ Phaser.TilemapLayer.prototype.render = function () { this.context.lineTo(this._tx + this.tileWidth, this._ty + this.tileHeight); } + // this.context.closePath(); this.context.stroke(); - // this.context.fillRect(this._tx, this._ty, this.tileWidth, this.tileHeight); // this.context.strokeRect(this._tx, this._ty, this.tileWidth, this.tileHeight); } diff --git a/wip/examples/a_template.php b/wip/examples/a_template.php deleted file mode 100644 index 69811ff7..00000000 --- a/wip/examples/a_template.php +++ /dev/null @@ -1,39 +0,0 @@ - - - - phaser.js - a new beginning - - - - - - - - \ No newline at end of file diff --git a/wip/examples/anchor1.php b/wip/examples/anchor1.php deleted file mode 100644 index f0407a48..00000000 --- a/wip/examples/anchor1.php +++ /dev/null @@ -1,57 +0,0 @@ - - - - phaser.js - a new beginning - - - - - - - - \ No newline at end of file diff --git a/wip/examples/bitmapFont1.php b/wip/examples/bitmapFont1.php deleted file mode 100644 index b76db46a..00000000 --- a/wip/examples/bitmapFont1.php +++ /dev/null @@ -1,39 +0,0 @@ - - - - phaser.js - a new beginning - - - - - - - - \ No newline at end of file diff --git a/wip/examples/body1.php b/wip/examples/body1.php deleted file mode 100644 index 07cf8781..00000000 --- a/wip/examples/body1.php +++ /dev/null @@ -1,62 +0,0 @@ - - - - phaser.js - a new beginning - - - - - - - - \ No newline at end of file diff --git a/wip/examples/body2.php b/wip/examples/body2.php deleted file mode 100644 index c6272e94..00000000 --- a/wip/examples/body2.php +++ /dev/null @@ -1,63 +0,0 @@ - - - - phaser.js - a new beginning - - - - - - - - \ No newline at end of file diff --git a/wip/examples/body3.php b/wip/examples/body3.php deleted file mode 100644 index d07f9c3b..00000000 --- a/wip/examples/body3.php +++ /dev/null @@ -1,69 +0,0 @@ - - - - phaser.js - a new beginning - - - - - - - - \ No newline at end of file diff --git a/wip/examples/bring to top.php b/wip/examples/bring to top.php deleted file mode 100644 index c405cf91..00000000 --- a/wip/examples/bring to top.php +++ /dev/null @@ -1,54 +0,0 @@ - - - - phaser.js - a new beginning - - - - - - - - \ No newline at end of file diff --git a/wip/examples/bring to top2.php b/wip/examples/bring to top2.php deleted file mode 100644 index f693a7a8..00000000 --- a/wip/examples/bring to top2.php +++ /dev/null @@ -1,55 +0,0 @@ - - - - phaser.js - a new beginning - - - - - - - - \ No newline at end of file diff --git a/wip/examples/bringToTop.php b/wip/examples/bringToTop.php deleted file mode 100644 index 26c663be..00000000 --- a/wip/examples/bringToTop.php +++ /dev/null @@ -1,341 +0,0 @@ - - - - phaser.js - a new beginning - - - - - - - - \ No newline at end of file diff --git a/wip/examples/bringToTop2.php b/wip/examples/bringToTop2.php deleted file mode 100644 index 8fba2e33..00000000 --- a/wip/examples/bringToTop2.php +++ /dev/null @@ -1,41 +0,0 @@ - - - - phaser.js - a new beginning - - - - - - - - \ No newline at end of file diff --git a/wip/examples/bringToTop3.php b/wip/examples/bringToTop3.php deleted file mode 100644 index 34796a8b..00000000 --- a/wip/examples/bringToTop3.php +++ /dev/null @@ -1,94 +0,0 @@ - - - - phaser.js - a new beginning - - - - - - - - \ No newline at end of file diff --git a/wip/examples/button1.php b/wip/examples/button1.php deleted file mode 100644 index 08d7b7d2..00000000 --- a/wip/examples/button1.php +++ /dev/null @@ -1,67 +0,0 @@ - - - - phaser.js - a new beginning - - - - - - - - \ No newline at end of file diff --git a/wip/examples/camera1.php b/wip/examples/camera1.php deleted file mode 100644 index 254a027a..00000000 --- a/wip/examples/camera1.php +++ /dev/null @@ -1,72 +0,0 @@ - - - - phaser.js - a new beginning - - - - - - - - \ No newline at end of file diff --git a/wip/examples/camera2.php b/wip/examples/camera2.php deleted file mode 100644 index 87295cec..00000000 --- a/wip/examples/camera2.php +++ /dev/null @@ -1,175 +0,0 @@ - - - - phaser.js - a new beginning - - - - - - - - \ No newline at end of file diff --git a/wip/examples/camera3.php b/wip/examples/camera3.php deleted file mode 100644 index 8b3b46ac..00000000 --- a/wip/examples/camera3.php +++ /dev/null @@ -1,74 +0,0 @@ - - - - phaser.js - a new beginning - - - - - - - - \ No newline at end of file diff --git a/wip/examples/camera4.php b/wip/examples/camera4.php deleted file mode 100644 index 81df4f6a..00000000 --- a/wip/examples/camera4.php +++ /dev/null @@ -1,56 +0,0 @@ - - - - phaser.js - a new beginning - - - - - - - - \ No newline at end of file diff --git a/wip/examples/camera5.php b/wip/examples/camera5.php deleted file mode 100644 index 123dbe0d..00000000 --- a/wip/examples/camera5.php +++ /dev/null @@ -1,70 +0,0 @@ - - - - phaser.js - a new beginning - - - - - - - - \ No newline at end of file diff --git a/wip/examples/camera_cull1.php b/wip/examples/camera_cull1.php deleted file mode 100644 index d332703b..00000000 --- a/wip/examples/camera_cull1.php +++ /dev/null @@ -1,68 +0,0 @@ - - - - phaser.js - a new beginning - - - - - - - - \ No newline at end of file diff --git a/wip/examples/camerafollow.php b/wip/examples/camerafollow.php deleted file mode 100644 index 97e4dcc5..00000000 --- a/wip/examples/camerafollow.php +++ /dev/null @@ -1,79 +0,0 @@ - - - - phaser.js - a new beginning - - - - - - - - \ No newline at end of file diff --git a/wip/examples/circle.html b/wip/examples/circle.html deleted file mode 100644 index 8175a630..00000000 --- a/wip/examples/circle.html +++ /dev/null @@ -1,45 +0,0 @@ - - - - phaser.js - a(nother) new beginning - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/wip/examples/collision_group_vs_group.php b/wip/examples/collision_group_vs_group.php deleted file mode 100644 index 0d921cf9..00000000 --- a/wip/examples/collision_group_vs_group.php +++ /dev/null @@ -1,117 +0,0 @@ - - - - phaser.js - a new beginning - - - - - - - - \ No newline at end of file diff --git a/wip/examples/collision_sprite_vs_group.php b/wip/examples/collision_sprite_vs_group.php deleted file mode 100644 index 4f2fac73..00000000 --- a/wip/examples/collision_sprite_vs_group.php +++ /dev/null @@ -1,100 +0,0 @@ - - - - phaser.js - a new beginning - - - - - - - - \ No newline at end of file diff --git a/wip/examples/collision_sprite_vs_sprite.php b/wip/examples/collision_sprite_vs_sprite.php deleted file mode 100644 index 24c4d55a..00000000 --- a/wip/examples/collision_sprite_vs_sprite.php +++ /dev/null @@ -1,65 +0,0 @@ - - - - phaser.js - a new beginning - - - - - - - - \ No newline at end of file diff --git a/wip/examples/collision_sprite_vs_sprite_custom_process.php b/wip/examples/collision_sprite_vs_sprite_custom_process.php deleted file mode 100644 index f691580c..00000000 --- a/wip/examples/collision_sprite_vs_sprite_custom_process.php +++ /dev/null @@ -1,96 +0,0 @@ - - - - phaser.js - a new beginning - - - - - - - - \ No newline at end of file diff --git a/wip/examples/crop.php b/wip/examples/crop.php deleted file mode 100644 index 598fb4ac..00000000 --- a/wip/examples/crop.php +++ /dev/null @@ -1,46 +0,0 @@ - - - - phaser.js - a new beginning - - - - - - - - \ No newline at end of file diff --git a/wip/examples/crop2.php b/wip/examples/crop2.php deleted file mode 100644 index 7df1b996..00000000 --- a/wip/examples/crop2.php +++ /dev/null @@ -1,46 +0,0 @@ - - - - phaser.js - a new beginning - - - - - - - - \ No newline at end of file diff --git a/wip/examples/crop3.php b/wip/examples/crop3.php deleted file mode 100644 index 45551cd3..00000000 --- a/wip/examples/crop3.php +++ /dev/null @@ -1,48 +0,0 @@ - - - - phaser.js - a new beginning - - - - - - - - \ No newline at end of file diff --git a/wip/examples/drag.php b/wip/examples/drag.php deleted file mode 100644 index 8ee651eb..00000000 --- a/wip/examples/drag.php +++ /dev/null @@ -1,43 +0,0 @@ - - - - phaser.js - a new beginning - - - - - - - - \ No newline at end of file diff --git a/wip/examples/fullscreen.php b/wip/examples/fullscreen.php deleted file mode 100644 index a65cd53a..00000000 --- a/wip/examples/fullscreen.php +++ /dev/null @@ -1,63 +0,0 @@ - - - - phaser.js - a new beginning - - - - - - - - - \ No newline at end of file diff --git a/wip/examples/get_bounds.php b/wip/examples/get_bounds.php deleted file mode 100644 index 415eb9d9..00000000 --- a/wip/examples/get_bounds.php +++ /dev/null @@ -1,47 +0,0 @@ - - - - phaser.js - a new beginning - - - - - - - - \ No newline at end of file diff --git a/wip/examples/graphics.php b/wip/examples/graphics.php deleted file mode 100644 index efac7411..00000000 --- a/wip/examples/graphics.php +++ /dev/null @@ -1,80 +0,0 @@ - - - - phaser.js - a new beginning - - - - - - - - \ No newline at end of file diff --git a/wip/examples/group1.php b/wip/examples/group1.php deleted file mode 100644 index ef2febcf..00000000 --- a/wip/examples/group1.php +++ /dev/null @@ -1,84 +0,0 @@ - - - - phaser.js - a new beginning - - - - - - - - \ No newline at end of file diff --git a/wip/examples/inherit.php b/wip/examples/inherit.php deleted file mode 100644 index 89dc7e2c..00000000 --- a/wip/examples/inherit.php +++ /dev/null @@ -1,167 +0,0 @@ - - - - phaser.js - a new beginning - - - - - - - - \ No newline at end of file diff --git a/wip/examples/input1.php b/wip/examples/input1.php deleted file mode 100644 index fc892ea6..00000000 --- a/wip/examples/input1.php +++ /dev/null @@ -1,48 +0,0 @@ - - - - phaser.js - a new beginning - - - - - - - -
- - - - - \ No newline at end of file diff --git a/wip/examples/input2.php b/wip/examples/input2.php deleted file mode 100644 index 4f8c94f1..00000000 --- a/wip/examples/input2.php +++ /dev/null @@ -1,60 +0,0 @@ - - - - phaser.js - a new beginning - - - - - - - - - \ No newline at end of file diff --git a/wip/examples/invaders.php b/wip/examples/invaders.php deleted file mode 100644 index ae429829..00000000 --- a/wip/examples/invaders.php +++ /dev/null @@ -1,63 +0,0 @@ - - - - phaser.js - a new beginning - - - - - - - - \ No newline at end of file diff --git a/wip/examples/js-physics.php b/wip/examples/js-physics.php deleted file mode 100644 index da3f186b..00000000 --- a/wip/examples/js-physics.php +++ /dev/null @@ -1,26 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - diff --git a/wip/examples/js.php b/wip/examples/js.php deleted file mode 100644 index f49d52a6..00000000 --- a/wip/examples/js.php +++ /dev/null @@ -1,109 +0,0 @@ - -?> - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/wip/examples/js_full.php b/wip/examples/js_full.php deleted file mode 100644 index faf156c2..00000000 --- a/wip/examples/js_full.php +++ /dev/null @@ -1,120 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/wip/examples/linkedlist1.php b/wip/examples/linkedlist1.php deleted file mode 100644 index c877264f..00000000 --- a/wip/examples/linkedlist1.php +++ /dev/null @@ -1,74 +0,0 @@ - - - - phaser.js - a new beginning - - - - - - - - \ No newline at end of file diff --git a/wip/examples/loader 2.html b/wip/examples/loader 2.html deleted file mode 100644 index 048ac00e..00000000 --- a/wip/examples/loader 2.html +++ /dev/null @@ -1,59 +0,0 @@ - - - - phaser.js - a(nother) new beginning - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/wip/examples/loader atlas json.html b/wip/examples/loader atlas json.html deleted file mode 100644 index df0a42f5..00000000 --- a/wip/examples/loader atlas json.html +++ /dev/null @@ -1,57 +0,0 @@ - - - - phaser.js - a(nother) new beginning - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/wip/examples/loader atlas xml.html b/wip/examples/loader atlas xml.html deleted file mode 100644 index e89d70e6..00000000 --- a/wip/examples/loader atlas xml.html +++ /dev/null @@ -1,57 +0,0 @@ - - - - phaser.js - a(nother) new beginning - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/wip/examples/loader audio.html b/wip/examples/loader audio.html deleted file mode 100644 index 137ade9b..00000000 --- a/wip/examples/loader audio.html +++ /dev/null @@ -1,57 +0,0 @@ - - - - phaser.js - a(nother) new beginning - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/wip/examples/loader spritesheet.html b/wip/examples/loader spritesheet.html deleted file mode 100644 index 12cf925f..00000000 --- a/wip/examples/loader spritesheet.html +++ /dev/null @@ -1,57 +0,0 @@ - - - - phaser.js - a(nother) new beginning - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/wip/examples/loader.html b/wip/examples/loader.html deleted file mode 100644 index 955593f6..00000000 --- a/wip/examples/loader.html +++ /dev/null @@ -1,48 +0,0 @@ - - - - phaser.js - a(nother) new beginning - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/wip/examples/mapcollide.php b/wip/examples/mapcollide.php deleted file mode 100644 index 9d8f888b..00000000 --- a/wip/examples/mapcollide.php +++ /dev/null @@ -1,85 +0,0 @@ - - - - phaser.js - a new beginning - - - - - - - - \ No newline at end of file diff --git a/wip/examples/mario.php b/wip/examples/mario.php deleted file mode 100644 index c05900fd..00000000 --- a/wip/examples/mario.php +++ /dev/null @@ -1,60 +0,0 @@ - - - - phaser.js - a new beginning - - - - - - - - \ No newline at end of file diff --git a/wip/examples/mariocombo.php b/wip/examples/mariocombo.php deleted file mode 100644 index 551db25b..00000000 --- a/wip/examples/mariocombo.php +++ /dev/null @@ -1,48 +0,0 @@ - - - - phaser.js - Super Mario Combo - - - - - - - - - \ No newline at end of file diff --git a/wip/examples/mariotogether.php b/wip/examples/mariotogether.php deleted file mode 100644 index 908a2361..00000000 --- a/wip/examples/mariotogether.php +++ /dev/null @@ -1,45 +0,0 @@ - - - - phaser.js - Super Mario Combo - - - - - - - - \ No newline at end of file diff --git a/wip/examples/math sincos.html b/wip/examples/math sincos.html deleted file mode 100644 index 061282ff..00000000 --- a/wip/examples/math sincos.html +++ /dev/null @@ -1,48 +0,0 @@ - - - - phaser.js - a(nother) new beginning - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/wip/examples/motion1.php b/wip/examples/motion1.php deleted file mode 100644 index b3171080..00000000 --- a/wip/examples/motion1.php +++ /dev/null @@ -1,98 +0,0 @@ - - - - phaser.js - a new beginning - - - - - - - - \ No newline at end of file diff --git a/wip/examples/pixi 1.html b/wip/examples/pixi 1.html deleted file mode 100644 index 4bd70188..00000000 --- a/wip/examples/pixi 1.html +++ /dev/null @@ -1,139 +0,0 @@ - - - - phaser.js - a(nother) new beginning - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/wip/examples/point.html b/wip/examples/point.html deleted file mode 100644 index 716778e9..00000000 --- a/wip/examples/point.html +++ /dev/null @@ -1,44 +0,0 @@ - - - - phaser.js - a(nother) new beginning - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/wip/examples/quadtree.php b/wip/examples/quadtree.php deleted file mode 100644 index afee3f4c..00000000 --- a/wip/examples/quadtree.php +++ /dev/null @@ -1,110 +0,0 @@ - - - - phaser.js - a new beginning - - - - - - - - \ No newline at end of file diff --git a/wip/examples/quadtree2.php b/wip/examples/quadtree2.php deleted file mode 100644 index e7746bd0..00000000 --- a/wip/examples/quadtree2.php +++ /dev/null @@ -1,90 +0,0 @@ - - - - phaser.js - a new beginning - - - - - - - - \ No newline at end of file diff --git a/wip/examples/raf.html b/wip/examples/raf.html deleted file mode 100644 index 939844a7..00000000 --- a/wip/examples/raf.html +++ /dev/null @@ -1,45 +0,0 @@ - - - - phaser.js - a(nother) new beginning - - - - - - - - - - - - - - - - - - - - - - - - -
?
-
Game Time: 
- - - - - \ No newline at end of file diff --git a/wip/examples/rect1.php b/wip/examples/rect1.php deleted file mode 100644 index f88d102d..00000000 --- a/wip/examples/rect1.php +++ /dev/null @@ -1,22 +0,0 @@ - - - - phaser.js - a new beginning - - - - - - - - \ No newline at end of file diff --git a/wip/examples/rendertexture.php b/wip/examples/rendertexture.php deleted file mode 100644 index 1746e071..00000000 --- a/wip/examples/rendertexture.php +++ /dev/null @@ -1,42 +0,0 @@ - - - - phaser.js - a new beginning - - - - - - - - \ No newline at end of file diff --git a/wip/examples/rnd.html b/wip/examples/rnd.html deleted file mode 100644 index a6bd1f57..00000000 --- a/wip/examples/rnd.html +++ /dev/null @@ -1,25 +0,0 @@ - - - - phaser.js - a(nother) new beginning - - - - - - - - - - - - \ No newline at end of file diff --git a/wip/examples/signals.html b/wip/examples/signals.html deleted file mode 100644 index 0871cc32..00000000 --- a/wip/examples/signals.html +++ /dev/null @@ -1,35 +0,0 @@ - - - - phaser.js - a(nother) new beginning - - - - - - - - - - - - \ No newline at end of file diff --git a/wip/examples/snap.php b/wip/examples/snap.php deleted file mode 100644 index bbc78ef4..00000000 --- a/wip/examples/snap.php +++ /dev/null @@ -1,54 +0,0 @@ - - - - phaser.js - a new beginning - - - - - - - - \ No newline at end of file diff --git a/wip/examples/sound1.php b/wip/examples/sound1.php deleted file mode 100644 index 4def5f6e..00000000 --- a/wip/examples/sound1.php +++ /dev/null @@ -1,71 +0,0 @@ - - - - - phaser.js - a new beginning - - - - - - - - \ No newline at end of file diff --git a/wip/examples/sprite1.php b/wip/examples/sprite1.php deleted file mode 100644 index 18b58c5f..00000000 --- a/wip/examples/sprite1.php +++ /dev/null @@ -1,43 +0,0 @@ - - - - phaser.js - a new beginning - - - - - - - - \ No newline at end of file diff --git a/wip/examples/sprite2.php b/wip/examples/sprite2.php deleted file mode 100644 index 79244eb2..00000000 --- a/wip/examples/sprite2.php +++ /dev/null @@ -1,55 +0,0 @@ - - - - phaser.js - a new beginning - - - - - - - - \ No newline at end of file diff --git a/wip/examples/sprite3.php b/wip/examples/sprite3.php deleted file mode 100644 index ccaba885..00000000 --- a/wip/examples/sprite3.php +++ /dev/null @@ -1,65 +0,0 @@ - - - - phaser.js - a new beginning - - - - - - - - \ No newline at end of file diff --git a/wip/examples/sprite4.php b/wip/examples/sprite4.php deleted file mode 100644 index 295eed61..00000000 --- a/wip/examples/sprite4.php +++ /dev/null @@ -1,70 +0,0 @@ - - - - phaser.js - a new beginning - - - - - - - - \ No newline at end of file diff --git a/wip/examples/sprite_extend.php b/wip/examples/sprite_extend.php deleted file mode 100644 index 77853a9b..00000000 --- a/wip/examples/sprite_extend.php +++ /dev/null @@ -1,56 +0,0 @@ - - - - phaser.js - a new beginning - - - - - - - - \ No newline at end of file diff --git a/wip/examples/stage 1.php b/wip/examples/stage 1.php deleted file mode 100644 index b90f8c86..00000000 --- a/wip/examples/stage 1.php +++ /dev/null @@ -1,55 +0,0 @@ - - - - phaser.js - a new beginning - - - - - - - - \ No newline at end of file diff --git a/wip/examples/stage 2.php b/wip/examples/stage 2.php deleted file mode 100644 index f30ed092..00000000 --- a/wip/examples/stage 2.php +++ /dev/null @@ -1,66 +0,0 @@ - - - - phaser.js - a new beginning - - - - - - - - \ No newline at end of file diff --git a/wip/examples/stage 3.php b/wip/examples/stage 3.php deleted file mode 100644 index 859fae54..00000000 --- a/wip/examples/stage 3.php +++ /dev/null @@ -1,68 +0,0 @@ - - - - phaser.js - a new beginning - - - - - - - - \ No newline at end of file diff --git a/wip/examples/stagecolor.php b/wip/examples/stagecolor.php deleted file mode 100644 index 2e744126..00000000 --- a/wip/examples/stagecolor.php +++ /dev/null @@ -1,31 +0,0 @@ - - - - phaser.js - a new beginning - - - - - - - - \ No newline at end of file diff --git a/wip/examples/statetest.php b/wip/examples/statetest.php deleted file mode 100644 index 9d8f888b..00000000 --- a/wip/examples/statetest.php +++ /dev/null @@ -1,85 +0,0 @@ - - - - phaser.js - a new beginning - - - - - - - - \ No newline at end of file diff --git a/wip/examples/test.php b/wip/examples/test.php deleted file mode 100644 index d85c2305..00000000 --- a/wip/examples/test.php +++ /dev/null @@ -1,79 +0,0 @@ - - - - phaser.js - a new beginning - - - - - - - \ No newline at end of file diff --git a/wip/examples/text1.php b/wip/examples/text1.php deleted file mode 100644 index 4ccbb80d..00000000 --- a/wip/examples/text1.php +++ /dev/null @@ -1,31 +0,0 @@ - - - - phaser.js - a new beginning - - - - - - - - \ No newline at end of file diff --git a/wip/examples/text2.php b/wip/examples/text2.php deleted file mode 100644 index 64226d1c..00000000 --- a/wip/examples/text2.php +++ /dev/null @@ -1,41 +0,0 @@ - - - - phaser.js - a new beginning - - - - - - - - \ No newline at end of file diff --git a/wip/examples/text3.php b/wip/examples/text3.php deleted file mode 100644 index aec89013..00000000 --- a/wip/examples/text3.php +++ /dev/null @@ -1,82 +0,0 @@ - - - - phaser.js - a new beginning - - - - - - - - \ No newline at end of file diff --git a/wip/examples/tilemap.php b/wip/examples/tilemap.php deleted file mode 100644 index b37e1945..00000000 --- a/wip/examples/tilemap.php +++ /dev/null @@ -1,62 +0,0 @@ - - - - phaser.js - a new beginning - - - - - - - - \ No newline at end of file diff --git a/wip/examples/tilesprite1.php b/wip/examples/tilesprite1.php deleted file mode 100644 index fb522309..00000000 --- a/wip/examples/tilesprite1.php +++ /dev/null @@ -1,63 +0,0 @@ - - - - phaser.js - a new beginning - - - - - - - - \ No newline at end of file diff --git a/wip/examples/tilesprite2.php b/wip/examples/tilesprite2.php deleted file mode 100644 index 5ad09c6f..00000000 --- a/wip/examples/tilesprite2.php +++ /dev/null @@ -1,54 +0,0 @@ - - - - phaser.js - a new beginning - - - - - - - - \ No newline at end of file diff --git a/wip/examples/touch1.php b/wip/examples/touch1.php deleted file mode 100644 index 120b6e4d..00000000 --- a/wip/examples/touch1.php +++ /dev/null @@ -1,62 +0,0 @@ - - - - phaser.js - a new beginning - - - - - - - - \ No newline at end of file diff --git a/wip/examples/tween.html b/wip/examples/tween.html deleted file mode 100644 index 609e4b0b..00000000 --- a/wip/examples/tween.html +++ /dev/null @@ -1,47 +0,0 @@ - - - - phaser.js - a(nother) new beginning - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/wip/examples/tween2.php b/wip/examples/tween2.php deleted file mode 100644 index 8df04a3e..00000000 --- a/wip/examples/tween2.php +++ /dev/null @@ -1,42 +0,0 @@ - - - - phaser.js - a new beginning - - - - - - - - \ No newline at end of file diff --git a/wip/examples/wip1.html b/wip/examples/wip1.html deleted file mode 100644 index aa95919c..00000000 --- a/wip/examples/wip1.html +++ /dev/null @@ -1,141 +0,0 @@ - - - - phaser.js - a(nother) new beginning - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/wip/examples/world.php b/wip/examples/world.php deleted file mode 100644 index 1061a3a8..00000000 --- a/wip/examples/world.php +++ /dev/null @@ -1,106 +0,0 @@ - - - - phaser.js - a new beginning - - - - - - - - \ No newline at end of file diff --git a/wip/phaser clean up/ArcadePhysics.ts b/wip/phaser clean up/ArcadePhysics.ts deleted file mode 100644 index 00ae9b18..00000000 --- a/wip/phaser clean up/ArcadePhysics.ts +++ /dev/null @@ -1,1121 +0,0 @@ -/// -/// -/// -/// -/// - -/** -* Phaser - PhysicsManager -* -* Your game only has one PhysicsManager instance and it's responsible for looking after, creating and colliding -* all of the physics objects in the world. -*/ - - -module Phaser.Physics { - - export class ArcadePhysics { - - constructor(game: Game, width: number, height: number) { - - this.game = game; - - this.gravity = new Vec2; - this.drag = new Vec2; - this.bounce = new Vec2; - this.angularDrag = 0; - - this.bounds = new Rectangle(0, 0, width, height); - - this._distance = new Vec2; - this._tangent = new Vec2; - - this.members = new Group(game); - - } - - /** - * Local private reference to Game. - */ - public game: Game; - - /** - * Physics object pool - */ - public members: Group; - - // Temp calculation vars - private _drag: number; - private _delta: number; - private _velocityDelta: number; - private _length: number = 0; - private _distance: Vec2; - private _tangent: Vec2; - private _separatedX: bool; - private _separatedY: bool; - private _overlap: number; - private _maxOverlap: number; - private _obj1Velocity: number; - private _obj2Velocity: number; - private _obj1NewVelocity: number; - private _obj2NewVelocity: number; - private _average: number; - private _quadTree: QuadTree; - private _quadTreeResult: bool; - - public bounds: Rectangle; - - public gravity: Vec2; - public drag: Vec2; - public bounce: Vec2; - public angularDrag: number; - - /** - * The overlap bias is used when calculating hull overlap before separation - change it if you have especially small or large GameObjects - * @type {number} - */ - static OVERLAP_BIAS: number = 4; - - /** - * The overlap bias is used when calculating hull overlap before separation - change it if you have especially small or large GameObjects - * @type {number} - */ - static TILE_OVERLAP: bool = false; - - /** - * @type {number} - */ - public worldDivisions: number = 6; - - - /* - public update() { - - this._length = this._objects.length; - - for (var i = 0; i < this._length; i++) - { - if (this._objects[i]) - { - this._objects[i].preUpdate(); - this.updateMotion(this._objects[i]); - this.collideWorld(this._objects[i]); - - for (var x = 0; x < this._length; x++) - { - if (this._objects[x] && this._objects[x] !== this._objects[i]) - { - //this.collideShapes(this._objects[i], this._objects[x]); - var r = this.NEWseparate(this._objects[i], this._objects[x]); - //console.log('sep', r); - } - } - - } - } - - } - - public render() { - - // iterate through the objects here, updating and colliding - for (var i = 0; i < this._length; i++) - { - if (this._objects[i]) - { - this._objects[i].render(this.game.stage.context); - } - } - - } -*/ - - public updateMotion(body: Phaser.Physics.Body) { - - if (body.type == Types.BODY_DISABLED) - { - return; - } - - this._velocityDelta = (this.computeVelocity(body.angularVelocity, body.gravity.x, body.angularAcceleration, body.angularDrag, body.maxAngular) - body.angularVelocity) / 2; - body.angularVelocity += this._velocityDelta; - body.sprite.transform.rotation += body.angularVelocity * this.game.time.elapsed; - body.angularVelocity += this._velocityDelta; - - this._velocityDelta = (this.computeVelocity(body.velocity.x, body.gravity.x, body.acceleration.x, body.drag.x) - body.velocity.x) / 2; - body.velocity.x += this._velocityDelta; - this._delta = body.velocity.x * this.game.time.elapsed; - body.velocity.x += this._velocityDelta; - //body.position.x += this._delta; - body.sprite.x += this._delta; - - this._velocityDelta = (this.computeVelocity(body.velocity.y, body.gravity.y, body.acceleration.y, body.drag.y) - body.velocity.y) / 2; - body.velocity.y += this._velocityDelta; - this._delta = body.velocity.y * this.game.time.elapsed; - body.velocity.y += this._velocityDelta; - //body.position.y += this._delta; - body.sprite.y += this._delta; - - } - - /** - * A tween-like function that takes a starting velocity and some other factors and returns an altered velocity. - * - * @param {number} Velocity Any component of velocity (e.g. 20). - * @param {number} Acceleration Rate at which the velocity is changing. - * @param {number} Drag Really kind of a deceleration, this is how much the velocity changes if Acceleration is not set. - * @param {number} Max An absolute value cap for the velocity. - * - * @return {number} The altered Velocity value. - */ - public computeVelocity(velocity: number, gravity: number = 0, acceleration: number = 0, drag: number = 0, max: number = 10000): number { - - if (acceleration !== 0) - { - velocity += (acceleration + gravity) * this.game.time.elapsed; - } - else if (drag !== 0) - { - this._drag = drag * this.game.time.elapsed; - - if (velocity - this._drag > 0) - { - velocity = velocity - this._drag; - } - else if (velocity + this._drag < 0) - { - velocity += this._drag; - } - else - { - velocity = 0; - } - - velocity += gravity; - } - - if ((velocity != 0) && (max != 10000)) - { - if (velocity > max) - { - velocity = max; - } - else if (velocity < -max) - { - velocity = -max; - } - } - - return velocity; - - } - - /** - * The core Collision separation method. - * @param body1 The first Physics.Body to separate - * @param body2 The second Physics.Body to separate - * @returns {boolean} Returns true if the bodies were separated, otherwise false. - */ - public separate(body1: Body, body2: Body): bool { - - this._separatedX = this.separateBodyX(body1, body2); - this._separatedY = this.separateBodyY(body1, body2); - - return this._separatedX || this._separatedY; - - } - - public checkHullIntersection(body1: Body, body2:Body): bool { - return ((body1.hullX + body1.hullWidth > body2.hullX) && (body1.hullX < body2.hullX + body2.hullWidth) && (body1.hullY + body1.hullHeight > body2.hullY) && (body1.hullY < body2.hullY + body2.hullHeight)); - } - - /** - * Separates the two objects on their x axis - * @param object1 The first GameObject to separate - * @param object2 The second GameObject to separate - * @returns {boolean} Whether the objects in fact touched and were separated along the X axis. - */ - public separateBodyX(body1: Body, body2: Body): bool { - - // Can't separate two disabled or static objects - if ((body1.type == Types.BODY_DISABLED || body1.type == Types.BODY_STATIC) && (body2.type == Types.BODY_DISABLED || body2.type == Types.BODY_STATIC)) - { - return false; - } - - // First, get the two object deltas - this._overlap = 0; - - if (body1.deltaX != body2.deltaX) - { - if (RectangleUtils.intersects(body1.bounds, body2.bounds)) - { - this._maxOverlap = body1.deltaXAbs + body2.deltaXAbs + PhysicsManager.OVERLAP_BIAS; - - // If they did overlap (and can), figure out by how much and flip the corresponding flags - if (body1.deltaX > body2.deltaX) - { - this._overlap = body1.bounds.right - body2.bounds.x; - - if ((this._overlap > this._maxOverlap) || !(body1.allowCollisions & Types.RIGHT) || !(body2.allowCollisions & Types.LEFT)) - { - this._overlap = 0; - } - else - { - body1.touching |= Types.RIGHT; - body2.touching |= Types.LEFT; - } - } - else if (body1.deltaX < body2.deltaX) - { - this._overlap = body1.bounds.x - body2.bounds.width - body2.bounds.x; - - if ((-this._overlap > this._maxOverlap) || !(body1.allowCollisions & Types.LEFT) || !(body2.allowCollisions & Types.RIGHT)) - { - this._overlap = 0; - } - else - { - body1.touching |= Types.LEFT; - body2.touching |= Types.RIGHT; - } - } - } - } - - // Then adjust their positions and velocities accordingly (if there was any overlap) - if (this._overlap != 0) - { - this._obj1Velocity = body1.velocity.x; - this._obj2Velocity = body2.velocity.x; - - /** - * Dynamic = gives and receives impacts - * Static = gives but doesn't receive impacts, cannot be moved by physics - * Kinematic = gives impacts, but never receives, can be moved by physics - */ - - // 2 dynamic bodies will exchange velocities - if (body1.type == Types.BODY_DYNAMIC && body2.type == Types.BODY_DYNAMIC) - { - this._overlap *= 0.5; - body1.position.x = body1.position.x - this._overlap; - body2.position.x += this._overlap; - - this._obj1NewVelocity = Math.sqrt((this._obj2Velocity * this._obj2Velocity * body2.mass) / body1.mass) * ((this._obj2Velocity > 0) ? 1 : -1); - this._obj2NewVelocity = Math.sqrt((this._obj1Velocity * this._obj1Velocity * body1.mass) / body2.mass) * ((this._obj1Velocity > 0) ? 1 : -1); - this._average = (this._obj1NewVelocity + this._obj2NewVelocity) * 0.5; - this._obj1NewVelocity -= this._average; - this._obj2NewVelocity -= this._average; - body1.velocity.x = this._average + this._obj1NewVelocity * body1.bounce.x; - body2.velocity.x = this._average + this._obj2NewVelocity * body2.bounce.x; - } - else if (body2.type != Types.BODY_DYNAMIC) - { - // Body 2 is Static or Kinematic - this._overlap *= 2; - body1.position.x -= this._overlap; - body1.velocity.x = this._obj2Velocity - this._obj1Velocity * body1.bounce.x; - } - else if (body1.type != Types.BODY_DYNAMIC) - { - // Body 1 is Static or Kinematic - this._overlap *= 2; - body2.position.x += this._overlap; - body2.velocity.x = this._obj1Velocity - this._obj2Velocity * body2.bounce.x; - } - - return true; - } - else - { - return false; - } - - } - - /** - * Separates the two objects on their y axis - * @param object1 The first GameObject to separate - * @param object2 The second GameObject to separate - * @returns {boolean} Whether the objects in fact touched and were separated along the Y axis. - */ - public separateBodyY(body1: Body, body2: Body): bool { - - // Can't separate two immovable objects - if ((body1.type == Types.BODY_DISABLED || body1.type == Types.BODY_STATIC) && (body2.type == Types.BODY_DISABLED || body2.type == Types.BODY_STATIC)) - { - return false; - } - - // First, get the two object deltas - this._overlap = 0; - - if (body1.deltaY != body2.deltaY) - { - if (RectangleUtils.intersects(body1.bounds, body2.bounds)) - { - // This is the only place to use the DeltaAbs values - this._maxOverlap = body1.deltaYAbs + body2.deltaYAbs + PhysicsManager.OVERLAP_BIAS; - - // If they did overlap (and can), figure out by how much and flip the corresponding flags - if (body1.deltaY > body2.deltaY) - { - this._overlap = body1.bounds.bottom - body2.bounds.y; - - if ((this._overlap > this._maxOverlap) || !(body1.allowCollisions & Types.DOWN) || !(body2.allowCollisions & Types.UP)) - { - this._overlap = 0; - } - else - { - body1.touching |= Types.DOWN; - body2.touching |= Types.UP; - } - } - else if (body1.deltaY < body2.deltaY) - { - this._overlap = body1.bounds.y - body2.bounds.height - body2.bounds.y; - - if ((-this._overlap > this._maxOverlap) || !(body1.allowCollisions & Types.UP) || !(body2.allowCollisions & Types.DOWN)) - { - this._overlap = 0; - } - else - { - body1.touching |= Types.UP; - body2.touching |= Types.DOWN; - } - } - } - } - - // Then adjust their positions and velocities accordingly (if there was any overlap) - if (this._overlap != 0) - { - this._obj1Velocity = body1.velocity.y; - this._obj2Velocity = body2.velocity.y; - - /** - * Dynamic = gives and receives impacts - * Static = gives but doesn't receive impacts, cannot be moved by physics - * Kinematic = gives impacts, but never receives, can be moved by physics - */ - - if (body1.type == Types.BODY_DYNAMIC && body2.type == Types.BODY_DYNAMIC) - { - this._overlap *= 0.5; - body1.position.y = body1.position.y - this._overlap; - body2.position.y += this._overlap; - - this._obj1NewVelocity = Math.sqrt((this._obj2Velocity * this._obj2Velocity * body2.mass) / body1.mass) * ((this._obj2Velocity > 0) ? 1 : -1); - this._obj2NewVelocity = Math.sqrt((this._obj1Velocity * this._obj1Velocity * body1.mass) / body2.mass) * ((this._obj1Velocity > 0) ? 1 : -1); - var average: number = (this._obj1NewVelocity + this._obj2NewVelocity) * 0.5; - this._obj1NewVelocity -= average; - this._obj2NewVelocity -= average; - body1.velocity.y = average + this._obj1NewVelocity * body1.bounce.y; - body2.velocity.y = average + this._obj2NewVelocity * body2.bounce.y; - } - else if (body2.type != Types.BODY_DYNAMIC) - { - this._overlap *= 2; - body1.position.y -= this._overlap; - body1.velocity.y = this._obj2Velocity - this._obj1Velocity * body1.bounce.y; - // This is special case code that handles things like horizontal moving platforms you can ride - //if (body2.parent.active && body2.moves && (body1.deltaY > body2.deltaY)) - if (body2.sprite.active && (body1.deltaY > body2.deltaY)) - { - body1.position.x += body2.position.x - body2.oldPosition.x; - } - } - else if (body1.type != Types.BODY_DYNAMIC) - { - this._overlap *= 2; - body2.position.y += this._overlap; - body2.velocity.y = this._obj1Velocity - this._obj2Velocity * body2.bounce.y; - // This is special case code that handles things like horizontal moving platforms you can ride - //if (object1.active && body1.moves && (body1.deltaY < body2.deltaY)) - if (body1.sprite.active && (body1.deltaY < body2.deltaY)) - { - body2.position.x += body1.position.x - body1.oldPosition.x; - } - } - - return true; - } - else - { - return false; - } - } - - - - - - - - /* - private TILEseparate(shapeA: IPhysicsShape, shapeB: IPhysicsShape, distance: Vec2, tangent: Vec2) { - - if (tangent.x == 1) - { - console.log('1 The left side of ShapeA hit the right side of ShapeB', Math.floor(distance.x)); - shapeA.physics.touching |= Phaser.Types.LEFT; - shapeB.physics.touching |= Phaser.Types.RIGHT; - } - else if (tangent.x == -1) - { - console.log('2 The right side of ShapeA hit the left side of ShapeB', Math.floor(distance.x)); - shapeA.physics.touching |= Phaser.Types.RIGHT; - shapeB.physics.touching |= Phaser.Types.LEFT; - } - - if (tangent.y == 1) - { - console.log('3 The top of ShapeA hit the bottom of ShapeB', Math.floor(distance.y)); - shapeA.physics.touching |= Phaser.Types.UP; - shapeB.physics.touching |= Phaser.Types.DOWN; - } - else if (tangent.y == -1) - { - console.log('4 The bottom of ShapeA hit the top of ShapeB', Math.floor(distance.y)); - shapeA.physics.touching |= Phaser.Types.DOWN; - shapeB.physics.touching |= Phaser.Types.UP; - } - - - // only apply collision response forces if the object is travelling into, and not out of, the collision - var dot = Vec2Utils.dot(shapeA.physics.velocity, tangent); - - if (dot < 0) - { - console.log('in to', dot); - - // Apply horizontal bounce - if (shapeA.physics.bounce.x > 0) - { - shapeA.physics.velocity.x *= -(shapeA.physics.bounce.x); - } - else - { - shapeA.physics.velocity.x = 0; - } - // Apply horizontal bounce - if (shapeA.physics.bounce.y > 0) - { - shapeA.physics.velocity.y *= -(shapeA.physics.bounce.y); - } - else - { - shapeA.physics.velocity.y = 0; - } - } - else - { - console.log('out of', dot); - } - - shapeA.position.x += Math.floor(distance.x); - //shapeA.bounds.x += Math.floor(distance.x); - - shapeA.position.y += Math.floor(distance.y); - //shapeA.bounds.y += distance.y; - - console.log('------------------------------------------------'); - - } - - private collideWorld(shape:IPhysicsShape) { - - // Collide on the x-axis - this._distance.x = shape.world.bounds.x - (shape.position.x - shape.bounds.halfWidth); - - if (0 < this._distance.x) - { - // Hit Left - this._tangent.setTo(1, 0); - this.separateXWall(shape, this._distance, this._tangent); - } - else - { - this._distance.x = (shape.position.x + shape.bounds.halfWidth) - shape.world.bounds.right; - - if (0 < this._distance.x) - { - // Hit Right - this._tangent.setTo(-1, 0); - this._distance.reverse(); - this.separateXWall(shape, this._distance, this._tangent); - } - } - - // Collide on the y-axis - this._distance.y = shape.world.bounds.y - (shape.position.y - shape.bounds.halfHeight); - - if (0 < this._distance.y) - { - // Hit Top - this._tangent.setTo(0, 1); - this.separateYWall(shape, this._distance, this._tangent); - } - else - { - this._distance.y = (shape.position.y + shape.bounds.halfHeight) - shape.world.bounds.bottom; - - if (0 < this._distance.y) - { - // Hit Bottom - this._tangent.setTo(0, -1); - this._distance.reverse(); - this.separateYWall(shape, this._distance, this._tangent); - } - } - - } - - private separateX(shapeA: IPhysicsShape, shapeB: IPhysicsShape, distance: Vec2, tangent: Vec2) { - - if (tangent.x == 1) - { - console.log('The left side of ShapeA hit the right side of ShapeB', distance.x); - shapeA.physics.touching |= Phaser.Types.LEFT; - shapeB.physics.touching |= Phaser.Types.RIGHT; - } - else - { - console.log('The right side of ShapeA hit the left side of ShapeB', distance.x); - shapeA.physics.touching |= Phaser.Types.RIGHT; - shapeB.physics.touching |= Phaser.Types.LEFT; - } - - // collision edges - //shapeA.oH = tangent.x; - - // only apply collision response forces if the object is travelling into, and not out of, the collision - if (Vec2Utils.dot(shapeA.physics.velocity, tangent) < 0) - { - // Apply horizontal bounce - if (shapeA.physics.bounce.x > 0) - { - shapeA.physics.velocity.x *= -(shapeA.physics.bounce.x); - } - else - { - shapeA.physics.velocity.x = 0; - } - } - - shapeA.position.x += distance.x; - shapeA.bounds.x += distance.x; - - } - - private separateY(shapeA: IPhysicsShape, shapeB: IPhysicsShape, distance: Vec2, tangent: Vec2) { - - if (tangent.y == 1) - { - console.log('The top of ShapeA hit the bottom of ShapeB', distance.y); - shapeA.physics.touching |= Phaser.Types.UP; - shapeB.physics.touching |= Phaser.Types.DOWN; - } - else - { - console.log('The bottom of ShapeA hit the top of ShapeB', distance.y); - shapeA.physics.touching |= Phaser.Types.DOWN; - shapeB.physics.touching |= Phaser.Types.UP; - } - - // collision edges - //shapeA.oV = tangent.y; - - // only apply collision response forces if the object is travelling into, and not out of, the collision - if (Vec2Utils.dot(shapeA.physics.velocity, tangent) < 0) - { - // Apply horizontal bounce - if (shapeA.physics.bounce.y > 0) - { - shapeA.physics.velocity.y *= -(shapeA.physics.bounce.y); - } - else - { - shapeA.physics.velocity.y = 0; - } - } - - shapeA.position.y += distance.y; - shapeA.bounds.y += distance.y; - - } - - private separateXWall(shapeA: IPhysicsShape, distance: Vec2, tangent: Vec2) { - - if (tangent.x == 1) - { - console.log('The left side of ShapeA hit the right side of ShapeB', distance.x); - shapeA.physics.touching |= Phaser.Types.LEFT; - } - else - { - console.log('The right side of ShapeA hit the left side of ShapeB', distance.x); - shapeA.physics.touching |= Phaser.Types.RIGHT; - } - - // collision edges - //shapeA.oH = tangent.x; - - // only apply collision response forces if the object is travelling into, and not out of, the collision - if (Vec2Utils.dot(shapeA.physics.velocity, tangent) < 0) - { - // Apply horizontal bounce - if (shapeA.physics.bounce.x > 0) - { - shapeA.physics.velocity.x *= -(shapeA.physics.bounce.x); - } - else - { - shapeA.physics.velocity.x = 0; - } - } - - shapeA.position.x += distance.x; - - } - - private separateYWall(shapeA: IPhysicsShape, distance: Vec2, tangent: Vec2) { - - if (tangent.y == 1) - { - console.log('The top of ShapeA hit the bottom of ShapeB', distance.y); - shapeA.physics.touching |= Phaser.Types.UP; - } - else - { - console.log('The bottom of ShapeA hit the top of ShapeB', distance.y); - shapeA.physics.touching |= Phaser.Types.DOWN; - } - - // collision edges - //shapeA.oV = tangent.y; - - // only apply collision response forces if the object is travelling into, and not out of, the collision - if (Vec2Utils.dot(shapeA.physics.velocity, tangent) < 0) - { - // Apply horizontal bounce - if (shapeA.physics.bounce.y > 0) - { - shapeA.physics.velocity.y *= -(shapeA.physics.bounce.y); - } - else - { - shapeA.physics.velocity.y = 0; - } - } - - shapeA.position.y += distance.y; - - } - */ - - /** - * Checks for overlaps between two objects using the world QuadTree. Can be Sprite vs. Sprite, Sprite vs. Group or Group vs. Group. - * Note: Does not take the objects scrollFactor into account. All overlaps are check in world space. - * @param object1 The first Sprite or Group to check. If null the world.group is used. - * @param object2 The second Sprite or Group to check. - * @param notifyCallback A callback function that is called if the objects overlap. The two objects will be passed to this function in the same order in which you passed them to Collision.overlap. - * @param processCallback A callback function that lets you perform additional checks against the two objects if they overlap. If this is set then notifyCallback will only be called if processCallback returns true. - * @param context The context in which the callbacks will be called - * @returns {boolean} true if the objects overlap, otherwise false. - */ - public overlap(object1 = null, object2 = null, notifyCallback = null, processCallback = null, context = null): bool { - - /* - if (object1 == null) - { - object1 = this.game.world.group; - } - - if (object2 == object1) - { - object2 = null; - } - - QuadTree.divisions = this.worldDivisions; - - this._quadTree = new Phaser.QuadTree(this, this.bounds.x, this.bounds.y, this.bounds.width, this.bounds.height); - - this._quadTree.load(object1, object2, notifyCallback, processCallback, context); - - this._quadTreeResult = this._quadTree.execute(); - - console.log('over', this._quadTreeResult); - - this._quadTree.destroy(); - - this._quadTree = null; - - return this._quadTreeResult; - */ - - return false; - - } - - - - - - - /** - * Collision resolution specifically for GameObjects vs. Tiles. - * @param object The GameObject to separate - * @param tile The Tile to separate - * @returns {boolean} Whether the objects in fact touched and were separated - */ - public separateTile(object:Sprite, x: number, y: number, width: number, height: number, mass: number, collideLeft: bool, collideRight: bool, collideUp: bool, collideDown: bool, separateX: bool, separateY: bool): bool { - - //var separatedX: bool = this.separateTileX(object, x, y, width, height, mass, collideLeft, collideRight, separateX); - //var separatedY: bool = this.separateTileY(object, x, y, width, height, mass, collideUp, collideDown, separateY); - - //return separatedX || separatedY; - - return false; - - } - - /** - * Separates the two objects on their x axis - * @param object The GameObject to separate - * @param tile The Tile to separate - * @returns {boolean} Whether the objects in fact touched and were separated along the X axis. - */ - /* - public separateTileX(object:Sprite, x: number, y: number, width: number, height: number, mass: number, collideLeft: bool, collideRight: bool, separate: bool): bool { - - // Can't separate two immovable objects (tiles are always immovable) - if (object.immovable) - { - return false; - } - - // First, get the object delta - var overlap: number = 0; - var objDelta: number = object.x - object.last.x; - //var objDelta: number = object.collisionMask.deltaX; - - if (objDelta != 0) - { - // Check if the X hulls actually overlap - var objDeltaAbs: number = (objDelta > 0) ? objDelta : -objDelta; - //var objDeltaAbs: number = object.collisionMask.deltaXAbs; - var objBounds: Rectangle = new Rectangle(object.x - ((objDelta > 0) ? objDelta : 0), object.last.y, object.width + ((objDelta > 0) ? objDelta : -objDelta), object.height); - - if ((objBounds.x + objBounds.width > x) && (objBounds.x < x + width) && (objBounds.y + objBounds.height > y) && (objBounds.y < y + height)) - { - var maxOverlap: number = objDeltaAbs + Collision.OVERLAP_BIAS; - - // If they did overlap (and can), figure out by how much and flip the corresponding flags - if (objDelta > 0) - { - overlap = object.x + object.width - x; - - if ((overlap > maxOverlap) || !(object.allowCollisions & Collision.RIGHT) || collideLeft == false) - { - overlap = 0; - } - else - { - object.touching |= Collision.RIGHT; - } - } - else if (objDelta < 0) - { - overlap = object.x - width - x; - - if ((-overlap > maxOverlap) || !(object.allowCollisions & Collision.LEFT) || collideRight == false) - { - overlap = 0; - } - else - { - object.touching |= Collision.LEFT; - } - - } - - } - } - - // Then adjust their positions and velocities accordingly (if there was any overlap) - if (overlap != 0) - { - if (separate == true) - { - //console.log(' - object.x = object.x - overlap; - object.velocity.x = -(object.velocity.x * object.elasticity); - } - - Collision.TILE_OVERLAP = true; - return true; - } - else - { - return false; - } - - } - */ - - /** - * Separates the two objects on their y axis - * @param object The first GameObject to separate - * @param tile The second GameObject to separate - * @returns {boolean} Whether the objects in fact touched and were separated along the Y axis. - */ - /* - public separateTileY(object: Sprite, x: number, y: number, width: number, height: number, mass: number, collideUp: bool, collideDown: bool, separate: bool): bool { - - // Can't separate two immovable objects (tiles are always immovable) - if (object.immovable) - { - return false; - } - - // First, get the two object deltas - var overlap: number = 0; - var objDelta: number = object.y - object.last.y; - - if (objDelta != 0) - { - // Check if the Y hulls actually overlap - var objDeltaAbs: number = (objDelta > 0) ? objDelta : -objDelta; - var objBounds: Rectangle = new Rectangle(object.x, object.y - ((objDelta > 0) ? objDelta : 0), object.width, object.height + objDeltaAbs); - - if ((objBounds.x + objBounds.width > x) && (objBounds.x < x + width) && (objBounds.y + objBounds.height > y) && (objBounds.y < y + height)) - { - var maxOverlap: number = objDeltaAbs + Collision.OVERLAP_BIAS; - - // If they did overlap (and can), figure out by how much and flip the corresponding flags - if (objDelta > 0) - { - overlap = object.y + object.height - y; - - if ((overlap > maxOverlap) || !(object.allowCollisions & Collision.DOWN) || collideUp == false) - { - overlap = 0; - } - else - { - object.touching |= Collision.DOWN; - } - } - else if (objDelta < 0) - { - overlap = object.y - height - y; - - if ((-overlap > maxOverlap) || !(object.allowCollisions & Collision.UP) || collideDown == false) - { - overlap = 0; - } - else - { - object.touching |= Collision.UP; - } - } - } - } - - // TODO - with super low velocities you get lots of stuttering, set some kind of base minimum here - - // Then adjust their positions and velocities accordingly (if there was any overlap) - if (overlap != 0) - { - if (separate == true) - { - object.y = object.y - overlap; - object.velocity.y = -(object.velocity.y * object.elasticity); - } - - Collision.TILE_OVERLAP = true; - return true; - } - else - { - return false; - } - } - */ - - - /** - * Separates the two objects on their x axis - * @param object The GameObject to separate - * @param tile The Tile to separate - * @returns {boolean} Whether the objects in fact touched and were separated along the X axis. - */ - /* - public static NEWseparateTileX(object:Sprite, x: number, y: number, width: number, height: number, mass: number, collideLeft: bool, collideRight: bool, separate: bool): bool { - - // Can't separate two immovable objects (tiles are always immovable) - if (object.immovable) - { - return false; - } - - // First, get the object delta - var overlap: number = 0; - - if (object.collisionMask.deltaX != 0) - { - // Check if the X hulls actually overlap - //var objDeltaAbs: number = (objDelta > 0) ? objDelta : -objDelta; - //var objBounds: Rectangle = new Rectangle(object.x - ((objDelta > 0) ? objDelta : 0), object.last.y, object.width + ((objDelta > 0) ? objDelta : -objDelta), object.height); - - //if ((objBounds.x + objBounds.width > x) && (objBounds.x < x + width) && (objBounds.y + objBounds.height > y) && (objBounds.y < y + height)) - if (object.collisionMask.intersectsRaw(x, x + width, y, y + height)) - { - var maxOverlap: number = object.collisionMask.deltaXAbs + Collision.OVERLAP_BIAS; - - // If they did overlap (and can), figure out by how much and flip the corresponding flags - if (object.collisionMask.deltaX > 0) - { - //overlap = object.x + object.width - x; - overlap = object.collisionMask.right - x; - - if ((overlap > maxOverlap) || !(object.allowCollisions & Collision.RIGHT) || collideLeft == false) - { - overlap = 0; - } - else - { - object.touching |= Collision.RIGHT; - } - } - else if (object.collisionMask.deltaX < 0) - { - //overlap = object.x - width - x; - overlap = object.collisionMask.x - width - x; - - if ((-overlap > maxOverlap) || !(object.allowCollisions & Collision.LEFT) || collideRight == false) - { - overlap = 0; - } - else - { - object.touching |= Collision.LEFT; - } - - } - - } - } - - // Then adjust their positions and velocities accordingly (if there was any overlap) - if (overlap != 0) - { - if (separate == true) - { - object.x = object.x - overlap; - object.velocity.x = -(object.velocity.x * object.elasticity); - } - - Collision.TILE_OVERLAP = true; - return true; - } - else - { - return false; - } - - } - */ - - /** - * Separates the two objects on their y axis - * @param object The first GameObject to separate - * @param tile The second GameObject to separate - * @returns {boolean} Whether the objects in fact touched and were separated along the Y axis. - */ - /* - public NEWseparateTileY(object: Sprite, x: number, y: number, width: number, height: number, mass: number, collideUp: bool, collideDown: bool, separate: bool): bool { - - // Can't separate two immovable objects (tiles are always immovable) - if (object.immovable) - { - return false; - } - - // First, get the two object deltas - var overlap: number = 0; - //var objDelta: number = object.y - object.last.y; - - if (object.collisionMask.deltaY != 0) - { - // Check if the Y hulls actually overlap - //var objDeltaAbs: number = (objDelta > 0) ? objDelta : -objDelta; - //var objBounds: Rectangle = new Rectangle(object.x, object.y - ((objDelta > 0) ? objDelta : 0), object.width, object.height + objDeltaAbs); - - //if ((objBounds.x + objBounds.width > x) && (objBounds.x < x + width) && (objBounds.y + objBounds.height > y) && (objBounds.y < y + height)) - if (object.collisionMask.intersectsRaw(x, x + width, y, y + height)) - { - //var maxOverlap: number = objDeltaAbs + Collision.OVERLAP_BIAS; - var maxOverlap: number = object.collisionMask.deltaYAbs + Collision.OVERLAP_BIAS; - - // If they did overlap (and can), figure out by how much and flip the corresponding flags - if (object.collisionMask.deltaY > 0) - { - //overlap = object.y + object.height - y; - overlap = object.collisionMask.bottom - y; - - if ((overlap > maxOverlap) || !(object.allowCollisions & Collision.DOWN) || collideUp == false) - { - overlap = 0; - } - else - { - object.touching |= Collision.DOWN; - } - } - else if (object.collisionMask.deltaY < 0) - { - //overlap = object.y - height - y; - overlap = object.collisionMask.y - height - y; - - if ((-overlap > maxOverlap) || !(object.allowCollisions & Collision.UP) || collideDown == false) - { - overlap = 0; - } - else - { - object.touching |= Collision.UP; - } - } - } - } - - // TODO - with super low velocities you get lots of stuttering, set some kind of base minimum here - - // Then adjust their positions and velocities accordingly (if there was any overlap) - if (overlap != 0) - { - if (separate == true) - { - object.y = object.y - overlap; - object.velocity.y = -(object.velocity.y * object.elasticity); - } - - Collision.TILE_OVERLAP = true; - return true; - } - else - { - return false; - } - } - */ - - } - -} \ No newline at end of file diff --git a/wip/phaser clean up/Collision.ts b/wip/phaser clean up/Collision.ts deleted file mode 100644 index 13684b81..00000000 --- a/wip/phaser clean up/Collision.ts +++ /dev/null @@ -1,1702 +0,0 @@ -/// -/// -/// -/// -/// -/// -/// -/// -/// - -/** -* Phaser - Collision -* -* A set of extremely useful collision and geometry intersection functions. -*/ - -module Phaser { - - export class Collision { - - /** - * Collision constructor - * @param game A reference to the current Game - */ - constructor(game: Game) { - - this._game = game; - - Collision.T_VECTORS = []; - - for (var i = 0; i < 10; i++) - { - Collision.T_VECTORS.push(new Vec2); - } - - Collision.T_ARRAYS = []; - - for (var i = 0; i < 5; i++) - { - Collision.T_ARRAYS.push([]); - } - - } - - /** - * Local private reference to Game - */ - private _game: Game; - - /** - * Flag used to allow GameObjects to collide on their left side - * @type {number} - */ - public static LEFT: number = 0x0001; - - /** - * Flag used to allow GameObjects to collide on their right side - * @type {number} - */ - public static RIGHT: number = 0x0010; - - /** - * Flag used to allow GameObjects to collide on their top side - * @type {number} - */ - public static UP: number = 0x0100; - - /** - * Flag used to allow GameObjects to collide on their bottom side - * @type {number} - */ - public static DOWN: number = 0x1000; - - /** - * Flag used with GameObjects to disable collision - * @type {number} - */ - public static NONE: number = 0; - - /** - * Flag used to allow GameObjects to collide with a ceiling - * @type {number} - */ - public static CEILING: number = Collision.UP; - - /** - * Flag used to allow GameObjects to collide with a floor - * @type {number} - */ - public static FLOOR: number = Collision.DOWN; - - /** - * Flag used to allow GameObjects to collide with a wall (same as LEFT+RIGHT) - * @type {number} - */ - public static WALL: number = Collision.LEFT | Collision.RIGHT; - - /** - * Flag used to allow GameObjects to collide on any face - * @type {number} - */ - public static ANY: number = Collision.LEFT | Collision.RIGHT | Collision.UP | Collision.DOWN; - - /** - * The overlap bias is used when calculating hull overlap before separation - change it if you have especially small or large GameObjects - * @type {number} - */ - public static OVERLAP_BIAS: number = 4; - - /** - * This holds the result of the tile separation check, true if the object was moved, otherwise false - * @type {boolean} - */ - public static TILE_OVERLAP: bool = false; - - /** - * A temporary Rectangle used in the separation process to help avoid gc spikes - * @type {Rectangle} - */ - public static _tempBounds: Rectangle; - - /** - * Checks for Line to Line intersection and returns an IntersectResult object containing the results of the intersection. - * @param line1 The first Line object to check - * @param line2 The second Line object to check - * @param [output] An optional IntersectResult object to store the intersection values in. One is created if none given. - * @returns {IntersectResult=} An IntersectResult object containing the results of the intersection - */ - public static lineToLine(line1: Line, line2: Line, output?: IntersectResult = new IntersectResult): IntersectResult { - - var denominator = (line1.x1 - line1.x2) * (line2.y1 - line2.y2) - (line1.y1 - line1.y2) * (line2.x1 - line2.x2); - - if (denominator !== 0) - { - output.result = true; - output.x = ((line1.x1 * line1.y2 - line1.y1 * line1.x2) * (line2.x1 - line2.x2) - (line1.x1 - line1.x2) * (line2.x1 * line2.y2 - line2.y1 * line2.x2)) / denominator; - output.y = ((line1.x1 * line1.y2 - line1.y1 * line1.x2) * (line2.y1 - line2.y2) - (line1.y1 - line1.y2) * (line2.x1 * line2.y2 - line2.y1 * line2.x2)) / denominator; - } - - return output; - } - - /** - * Checks for Line to Line Segment intersection and returns an IntersectResult object containing the results of the intersection. - * @param line The Line object to check - * @param seg The Line segment object to check - * @param [output] An optional IntersectResult object to store the intersection values in. One is created if none given. - * @returns {IntersectResult=} An IntersectResult object containing the results of the intersection - */ - public static lineToLineSegment(line: Line, seg: Line, output?: IntersectResult = new IntersectResult): IntersectResult { - - var denominator = (line.x1 - line.x2) * (seg.y1 - seg.y2) - (line.y1 - line.y2) * (seg.x1 - seg.x2); - - if (denominator !== 0) - { - output.x = ((line.x1 * line.y2 - line.y1 * line.x2) * (seg.x1 - seg.x2) - (line.x1 - line.x2) * (seg.x1 * seg.y2 - seg.y1 * seg.x2)) / denominator; - output.y = ((line.x1 * line.y2 - line.y1 * line.x2) * (seg.y1 - seg.y2) - (line.y1 - line.y2) * (seg.x1 * seg.y2 - seg.y1 * seg.x2)) / denominator; - - var maxX = Math.max(seg.x1, seg.x2); - var minX = Math.min(seg.x1, seg.x2); - var maxY = Math.max(seg.y1, seg.y2); - var minY = Math.min(seg.y1, seg.y2); - - if ((output.x <= maxX && output.x >= minX) === true || (output.y <= maxY && output.y >= minY) === true) - { - output.result = true; - } - - } - - return output; - - } - - /** - * Checks for Line to Raw Line Segment intersection and returns the result in the IntersectResult object. - * @param line The Line object to check - * @param x1 The start x coordinate of the raw segment - * @param y1 The start y coordinate of the raw segment - * @param x2 The end x coordinate of the raw segment - * @param y2 The end y coordinate of the raw segment - * @param [output] An optional IntersectResult object to store the intersection values in. One is created if none given. - * @returns {IntersectResult=} An IntersectResult object containing the results of the intersection - */ - public static lineToRawSegment(line: Line, x1: number, y1: number, x2: number, y2: number, output?: IntersectResult = new IntersectResult): IntersectResult { - - var denominator = (line.x1 - line.x2) * (y1 - y2) - (line.y1 - line.y2) * (x1 - x2); - - if (denominator !== 0) - { - output.x = ((line.x1 * line.y2 - line.y1 * line.x2) * (x1 - x2) - (line.x1 - line.x2) * (x1 * y2 - y1 * x2)) / denominator; - output.y = ((line.x1 * line.y2 - line.y1 * line.x2) * (y1 - y2) - (line.y1 - line.y2) * (x1 * y2 - y1 * x2)) / denominator; - - var maxX = Math.max(x1, x2); - var minX = Math.min(x1, x2); - var maxY = Math.max(y1, y2); - var minY = Math.min(y1, y2); - - if ((output.x <= maxX && output.x >= minX) === true || (output.y <= maxY && output.y >= minY) === true) - { - output.result = true; - } - - } - - return output; - - } - - /** - * Checks for Line to Ray intersection and returns the result in an IntersectResult object. - * @param line1 The Line object to check - * @param ray The Ray object to check - * @param [output] An optional IntersectResult object to store the intersection values in. One is created if none given. - * @returns {IntersectResult=} An IntersectResult object containing the results of the intersection - */ - public static lineToRay(line1: Line, ray: Line, output?: IntersectResult = new IntersectResult): IntersectResult { - - var denominator = (line1.x1 - line1.x2) * (ray.y1 - ray.y2) - (line1.y1 - line1.y2) * (ray.x1 - ray.x2); - - if (denominator !== 0) - { - output.x = ((line1.x1 * line1.y2 - line1.y1 * line1.x2) * (ray.x1 - ray.x2) - (line1.x1 - line1.x2) * (ray.x1 * ray.y2 - ray.y1 * ray.x2)) / denominator; - output.y = ((line1.x1 * line1.y2 - line1.y1 * line1.x2) * (ray.y1 - ray.y2) - (line1.y1 - line1.y2) * (ray.x1 * ray.y2 - ray.y1 * ray.x2)) / denominator; - output.result = true; // true unless either of the 2 following conditions are met - - if (!(ray.x1 >= ray.x2) && output.x < ray.x1) - { - output.result = false; - } - - if (!(ray.y1 >= ray.y2) && output.y < ray.y1) - { - output.result = false; - } - } - - return output; - - } - - - /** - * Check if the Line and Circle objects intersect - * @param line The Line object to check - * @param circle The Circle object to check - * @param [output] An optional IntersectResult object to store the intersection values in. One is created if none given. - * @returns {IntersectResult=} An IntersectResult object containing the results of the intersection - */ - public static lineToCircle(line: Line, circle: Circle, output?: IntersectResult = new IntersectResult): IntersectResult { - - // Get a perpendicular line running to the center of the circle - if (line.perp(circle.x, circle.y).length <= circle.radius) - { - output.result = true; - } - - return output; - - } - - /** - * Check if the Line intersects each side of the Rectangle - * @param line The Line object to check - * @param rect The Rectangle object to check - * @param [output] An optional IntersectResult object to store the intersection values in. One is created if none given. - * @returns {IntersectResult=} An IntersectResult object containing the results of the intersection - */ - public static lineToRectangle(line: Line, rect: Rectangle, output?: IntersectResult = new IntersectResult): IntersectResult { - - // Top of the Rectangle vs the Line - Collision.lineToRawSegment(line, rect.x, rect.y, rect.right, rect.y, output); - - if (output.result === true) - { - return output; - } - - // Left of the Rectangle vs the Line - Collision.lineToRawSegment(line, rect.x, rect.y, rect.x, rect.bottom, output); - - if (output.result === true) - { - return output; - } - - // Bottom of the Rectangle vs the Line - Collision.lineToRawSegment(line, rect.x, rect.bottom, rect.right, rect.bottom, output); - - if (output.result === true) - { - return output; - } - - // Right of the Rectangle vs the Line - Collision.lineToRawSegment(line, rect.right, rect.y, rect.right, rect.bottom, output); - - return output; - - } - - /** - * Check if the two Line Segments intersect and returns the result in an IntersectResult object. - * @param line1 The first Line Segment to check - * @param line2 The second Line Segment to check - * @param [output] An optional IntersectResult object to store the intersection values in. One is created if none given. - * @returns {IntersectResult=} An IntersectResult object containing the results of the intersection - */ - public static lineSegmentToLineSegment(line1: Line, line2: Line, output?: IntersectResult = new IntersectResult): IntersectResult { - - Collision.lineToLineSegment(line1, line2); - - if (output.result === true) - { - if (!(output.x >= Math.min(line1.x1, line1.x2) && output.x <= Math.max(line1.x1, line1.x2) - && output.y >= Math.min(line1.y1, line1.y2) && output.y <= Math.max(line1.y1, line1.y2))) - { - output.result = false; - } - } - - return output; - } - - /** - * Check if the Line Segment intersects with the Ray and returns the result in an IntersectResult object. - * @param line The Line Segment to check. - * @param ray The Ray to check. - * @param [output] An optional IntersectResult object to store the intersection values in. One is created if none given. - * @returns {IntersectResult=} An IntersectResult object containing the results of the intersection - */ - public static lineSegmentToRay(line: Line, ray: Line, output?: IntersectResult = new IntersectResult): IntersectResult { - - Collision.lineToRay(line, ray, output); - - if (output.result === true) - { - if (!(output.x >= Math.min(line.x1, line.x2) && output.x <= Math.max(line.x1, line.x2) - && output.y >= Math.min(line.y1, line.y2) && output.y <= Math.max(line.y1, line.y2))) - { - output.result = false; - } - } - - return output; - - } - - /** - * Check if the Line Segment intersects with the Circle and returns the result in an IntersectResult object. - * @param seg The Line Segment to check. - * @param circle The Circle to check - * @param [output] An optional IntersectResult object to store the intersection values in. One is created if none given. - * @returns {IntersectResult=} An IntersectResult object containing the results of the intersection - */ - public static lineSegmentToCircle(seg: Line, circle: Circle, output?: IntersectResult = new IntersectResult): IntersectResult { - - var perp = seg.perp(circle.x, circle.y); - - if (perp.length <= circle.radius) - { - // Line intersects circle - check if segment does - var maxX = Math.max(seg.x1, seg.x2); - var minX = Math.min(seg.x1, seg.x2); - var maxY = Math.max(seg.y1, seg.y2); - var minY = Math.min(seg.y1, seg.y2); - - if ((perp.x2 <= maxX && perp.x2 >= minX) && (perp.y2 <= maxY && perp.y2 >= minY)) - { - output.result = true; - } - else - { - // Worst case - segment doesn't traverse center, so no perpendicular connection. - if (Collision.circleContainsPoint(circle, { x: seg.x1, y: seg.y1 }) || Collision.circleContainsPoint(circle, { x: seg.x2, y: seg.y2 })) - { - output.result = true; - } - } - - } - - return output; - } - - /** - * Check if the Line Segment intersects with the Rectangle and returns the result in an IntersectResult object. - * @param seg The Line Segment to check. - * @param rect The Rectangle to check. - * @param [output] An optional IntersectResult object to store the intersection values in. One is created if none given. - * @returns {IntersectResult=} An IntersectResult object containing the results of the intersection - */ - public static lineSegmentToRectangle(seg: Line, rect: Rectangle, output?: IntersectResult = new IntersectResult): IntersectResult { - - if (rect.contains(seg.x1, seg.y1) && rect.contains(seg.x2, seg.y2)) - { - output.result = true; - } - else - { - // Top of the Rectangle vs the Line - Collision.lineToRawSegment(seg, rect.x, rect.y, rect.right, rect.bottom, output); - - if (output.result === true) - { - return output; - } - - // Left of the Rectangle vs the Line - Collision.lineToRawSegment(seg, rect.x, rect.y, rect.x, rect.bottom, output); - - if (output.result === true) - { - return output; - } - - // Bottom of the Rectangle vs the Line - Collision.lineToRawSegment(seg, rect.x, rect.bottom, rect.right, rect.bottom, output); - - if (output.result === true) - { - return output; - } - - // Right of the Rectangle vs the Line - Collision.lineToRawSegment(seg, rect.right, rect.y, rect.right, rect.bottom, output); - - return output; - - } - - return output; - - } - - /** - * Check for Ray to Rectangle intersection and returns the result in an IntersectResult object. - * @param ray The Ray to check. - * @param rect The Rectangle to check. - * @param [output] An optional IntersectResult object to store the intersection values in. One is created if none given. - * @returns {IntersectResult=} An IntersectResult object containing the results of the intersection - */ - public static rayToRectangle(ray: Line, rect: Rectangle, output?: IntersectResult = new IntersectResult): IntersectResult { - - // Currently just finds first intersection - might not be closest to ray pt1 - Collision.lineToRectangle(ray, rect, output); - - return output; - - } - - - /** - * Check whether a Ray intersects a Line segment and returns the parametric value where the intersection occurs in an IntersectResult object. - * @param rayX1 - * @param rayY1 - * @param rayX2 - * @param rayY2 - * @param lineX1 - * @param lineY1 - * @param lineX2 - * @param lineY2 - * @param [output] An optional IntersectResult object to store the intersection values in. One is created if none given. - * @returns {IntersectResult=} An IntersectResult object containing the results of the intersection - */ - public static rayToLineSegment(rayX1, rayY1, rayX2, rayY2, lineX1, lineY1, lineX2, lineY2, output?: IntersectResult = new IntersectResult): IntersectResult { - - var r:number; - var s:number; - var d:number; - - // Check lines are not parallel - if ((rayY2 - rayY1) / (rayX2 - rayX1) != (lineY2 - lineY1) / (lineX2 - lineX1)) - { - d = (((rayX2 - rayX1) * (lineY2 - lineY1)) - (rayY2 - rayY1) * (lineX2 - lineX1)); - - if (d != 0) - { - r = (((rayY1 - lineY1) * (lineX2 - lineX1)) - (rayX1 - lineX1) * (lineY2 - lineY1)) / d; - s = (((rayY1 - lineY1) * (rayX2 - rayX1)) - (rayX1 - lineX1) * (rayY2 - rayY1)) / d; - - if (r >= 0) - { - if (s >= 0 && s <= 1) - { - output.result = true; - output.x = rayX1 + r * (rayX2 - rayX1); - output.y = rayY1 + r * (rayY2 - rayY1); - } - } - } - } - - return output; - - } - - /** - * Determines whether the specified point is contained within the rectangular region defined by the Rectangle object and returns the result in an IntersectResult object. - * @param point The Point or Point object to check, or any object with x and y properties. - * @param rect The Rectangle object to check the point against - * @param [output] An optional IntersectResult object to store the intersection values in. One is created if none given. - * @returns {IntersectResult=} An IntersectResult object containing the results of the intersection - */ - public static pointToRectangle(point, rect: Rectangle, output?: IntersectResult = new IntersectResult): IntersectResult { - - output.setTo(point.x, point.y); - - //output.result = rect.containsPoint(point); - - - return output; - - } - - /** - * Check whether two axis aligned Rectangles intersect and returns the intersecting rectangle dimensions in an IntersectResult object if they do. - * @param rect1 The first Rectangle object. - * @param rect2 The second Rectangle object. - * @param [output] An optional IntersectResult object to store the intersection values in. One is created if none given. - * @returns {IntersectResult=} An IntersectResult object containing the results of the intersection - */ - public static rectangleToRectangle(rect1: Rectangle, rect2: Rectangle, output?: IntersectResult = new IntersectResult): IntersectResult { - - var leftX = Math.max(rect1.x, rect2.x); - var rightX = Math.min(rect1.right, rect2.right); - var topY = Math.max(rect1.y, rect2.y); - var bottomY = Math.min(rect1.bottom, rect2.bottom); - - output.setTo(leftX, topY, rightX - leftX, bottomY - topY, rightX - leftX, bottomY - topY); - - var cx = output.x + output.width * .5; - var cy = output.y + output.height * .5; - - if ((cx > rect1.x && cx < rect1.right) && (cy > rect1.y && cy < rect1.bottom)) - { - output.result = true; - } - - return output; - - } - - /** - * Checks if the Rectangle and Circle objects intersect and returns the result in an IntersectResult object. - * @param rect The Rectangle object to check - * @param circle The Circle object to check - * @param [output] An optional IntersectResult object to store the intersection values in. One is created if none given. - * @returns {IntersectResult=} An IntersectResult object containing the results of the intersection - */ - public static rectangleToCircle(rect: Rectangle, circle: Circle, output?: IntersectResult = new IntersectResult): IntersectResult { - - return Collision.circleToRectangle(circle, rect, output); - - } - - /** - * Checks if the two Circle objects intersect and returns the result in an IntersectResult object. - * @param circle1 The first Circle object to check - * @param circle2 The second Circle object to check - * @param [output] An optional IntersectResult object to store the intersection values in. One is created if none given. - * @returns {IntersectResult=} An IntersectResult object containing the results of the intersection - */ - public static circleToCircle(circle1: Circle, circle2: Circle, output?: IntersectResult = new IntersectResult): IntersectResult { - - output.result = ((circle1.radius + circle2.radius) * (circle1.radius + circle2.radius)) >= Collision.distanceSquared(circle1.x, circle1.y, circle2.x, circle2.y); - - return output; - - } - - /** - * Checks if the Circle object intersects with the Rectangle and returns the result in an IntersectResult object. - * @param circle The Circle object to check - * @param rect The Rectangle object to check - * @param [output] An optional IntersectResult object to store the intersection values in. One is created if none given. - * @returns {IntersectResult=} An IntersectResult object containing the results of the intersection - */ - public static circleToRectangle(circle: Circle, rect: Rectangle, output?: IntersectResult = new IntersectResult): IntersectResult { - - var inflatedRect: Rectangle = rect.clone(); - - inflatedRect.inflate(circle.radius, circle.radius); - - output.result = inflatedRect.contains(circle.x, circle.y); - - return output; - - } - - /** - * Checks if the Point object is contained within the Circle and returns the result in an IntersectResult object. - * @param circle The Circle object to check - * @param point A Point or Point object to check, or any object with x and y properties - * @param [output] An optional IntersectResult object to store the intersection values in. One is created if none given. - * @returns {IntersectResult=} An IntersectResult object containing the results of the intersection - */ - public static circleContainsPoint(circle: Circle, point, output?: IntersectResult = new IntersectResult): IntersectResult { - - output.result = circle.radius * circle.radius >= Collision.distanceSquared(circle.x, circle.y, point.x, point.y); - - return output; - - } - - /** - * Checks for overlaps between two objects using the world QuadTree. Can be GameObject vs. GameObject, GameObject vs. Group or Group vs. Group. - * Note: Does not take the objects scrollFactor into account. All overlaps are check in world space. - * @param object1 The first GameObject or Group to check. If null the world.group is used. - * @param object2 The second GameObject or Group to check. - * @param notifyCallback A callback function that is called if the objects overlap. The two objects will be passed to this function in the same order in which you passed them to Collision.overlap. - * @param processCallback A callback function that lets you perform additional checks against the two objects if they overlap. If this is set then notifyCallback will only be called if processCallback returns true. - * @param context The context in which the callbacks will be called - * @returns {boolean} true if the objects overlap, otherwise false. - */ - public overlap(object1: Basic = null, object2: Basic = null, notifyCallback = null, processCallback = null, context = null): bool { - - if (object1 == null) - { - object1 = this._game.world.group; - } - - if (object2 == object1) - { - object2 = null; - } - - QuadTree.divisions = this._game.world.worldDivisions; - - var quadTree: QuadTree = new QuadTree(this._game.world.bounds.x, this._game.world.bounds.y, this._game.world.bounds.width, this._game.world.bounds.height); - - quadTree.load(object1, object2, notifyCallback, processCallback, context); - - var result: bool = quadTree.execute(); - - quadTree.destroy(); - - quadTree = null; - - return result; - - } - - /** - * The core Collision separation function used by Collision.overlap. - * @param object1 The first GameObject to separate - * @param object2 The second GameObject to separate - * @returns {boolean} Returns true if the objects were separated, otherwise false. - */ - public static separate(object1, object2): bool { - - object1.collisionMask.update(); - object2.collisionMask.update(); - - var separatedX: bool = Collision.separateX(object1, object2); - var separatedY: bool = Collision.separateY(object1, object2); - - return separatedX || separatedY; - - } - - /** - * Collision resolution specifically for GameObjects vs. Tiles. - * @param object The GameObject to separate - * @param tile The Tile to separate - * @returns {boolean} Whether the objects in fact touched and were separated - */ - public static separateTile(object:GameObject, x: number, y: number, width: number, height: number, mass: number, collideLeft: bool, collideRight: bool, collideUp: bool, collideDown: bool, separateX: bool, separateY: bool): bool { - - object.collisionMask.update(); - - var separatedX: bool = Collision.separateTileX(object, x, y, width, height, mass, collideLeft, collideRight, separateX); - var separatedY: bool = Collision.separateTileY(object, x, y, width, height, mass, collideUp, collideDown, separateY); - - return separatedX || separatedY; - - } - - /** - * Separates the two objects on their x axis - * @param object The GameObject to separate - * @param tile The Tile to separate - * @returns {boolean} Whether the objects in fact touched and were separated along the X axis. - */ - public static separateTileX(object:GameObject, x: number, y: number, width: number, height: number, mass: number, collideLeft: bool, collideRight: bool, separate: bool): bool { - - // Can't separate two immovable objects (tiles are always immovable) - if (object.immovable) - { - return false; - } - - // First, get the object delta - var overlap: number = 0; - var objDelta: number = object.x - object.last.x; - //var objDelta: number = object.collisionMask.deltaX; - - if (objDelta != 0) - { - // Check if the X hulls actually overlap - var objDeltaAbs: number = (objDelta > 0) ? objDelta : -objDelta; - //var objDeltaAbs: number = object.collisionMask.deltaXAbs; - var objBounds: Rectangle = new Rectangle(object.x - ((objDelta > 0) ? objDelta : 0), object.last.y, object.width + ((objDelta > 0) ? objDelta : -objDelta), object.height); - - if ((objBounds.x + objBounds.width > x) && (objBounds.x < x + width) && (objBounds.y + objBounds.height > y) && (objBounds.y < y + height)) - { - var maxOverlap: number = objDeltaAbs + Collision.OVERLAP_BIAS; - - // If they did overlap (and can), figure out by how much and flip the corresponding flags - if (objDelta > 0) - { - overlap = object.x + object.width - x; - - if ((overlap > maxOverlap) || !(object.allowCollisions & Collision.RIGHT) || collideLeft == false) - { - overlap = 0; - } - else - { - object.touching |= Collision.RIGHT; - } - } - else if (objDelta < 0) - { - overlap = object.x - width - x; - - if ((-overlap > maxOverlap) || !(object.allowCollisions & Collision.LEFT) || collideRight == false) - { - overlap = 0; - } - else - { - object.touching |= Collision.LEFT; - } - - } - - } - } - - // Then adjust their positions and velocities accordingly (if there was any overlap) - if (overlap != 0) - { - if (separate == true) - { - //console.log(' - object.x = object.x - overlap; - object.velocity.x = -(object.velocity.x * object.elasticity); - } - - Collision.TILE_OVERLAP = true; - return true; - } - else - { - return false; - } - - } - - /** - * Separates the two objects on their y axis - * @param object The first GameObject to separate - * @param tile The second GameObject to separate - * @returns {boolean} Whether the objects in fact touched and were separated along the Y axis. - */ - public static separateTileY(object: GameObject, x: number, y: number, width: number, height: number, mass: number, collideUp: bool, collideDown: bool, separate: bool): bool { - - // Can't separate two immovable objects (tiles are always immovable) - if (object.immovable) - { - return false; - } - - // First, get the two object deltas - var overlap: number = 0; - var objDelta: number = object.y - object.last.y; - - if (objDelta != 0) - { - // Check if the Y hulls actually overlap - var objDeltaAbs: number = (objDelta > 0) ? objDelta : -objDelta; - var objBounds: Rectangle = new Rectangle(object.x, object.y - ((objDelta > 0) ? objDelta : 0), object.width, object.height + objDeltaAbs); - - if ((objBounds.x + objBounds.width > x) && (objBounds.x < x + width) && (objBounds.y + objBounds.height > y) && (objBounds.y < y + height)) - { - var maxOverlap: number = objDeltaAbs + Collision.OVERLAP_BIAS; - - // If they did overlap (and can), figure out by how much and flip the corresponding flags - if (objDelta > 0) - { - overlap = object.y + object.height - y; - - if ((overlap > maxOverlap) || !(object.allowCollisions & Collision.DOWN) || collideUp == false) - { - overlap = 0; - } - else - { - object.touching |= Collision.DOWN; - } - } - else if (objDelta < 0) - { - overlap = object.y - height - y; - - if ((-overlap > maxOverlap) || !(object.allowCollisions & Collision.UP) || collideDown == false) - { - overlap = 0; - } - else - { - object.touching |= Collision.UP; - } - } - } - } - - // TODO - with super low velocities you get lots of stuttering, set some kind of base minimum here - - // Then adjust their positions and velocities accordingly (if there was any overlap) - if (overlap != 0) - { - if (separate == true) - { - object.y = object.y - overlap; - object.velocity.y = -(object.velocity.y * object.elasticity); - } - - Collision.TILE_OVERLAP = true; - return true; - } - else - { - return false; - } - } - - - /** - * Separates the two objects on their x axis - * @param object The GameObject to separate - * @param tile The Tile to separate - * @returns {boolean} Whether the objects in fact touched and were separated along the X axis. - */ - public static NEWseparateTileX(object:GameObject, x: number, y: number, width: number, height: number, mass: number, collideLeft: bool, collideRight: bool, separate: bool): bool { - - // Can't separate two immovable objects (tiles are always immovable) - if (object.immovable) - { - return false; - } - - // First, get the object delta - var overlap: number = 0; - - if (object.collisionMask.deltaX != 0) - { - // Check if the X hulls actually overlap - //var objDeltaAbs: number = (objDelta > 0) ? objDelta : -objDelta; - //var objBounds: Rectangle = new Rectangle(object.x - ((objDelta > 0) ? objDelta : 0), object.last.y, object.width + ((objDelta > 0) ? objDelta : -objDelta), object.height); - - //if ((objBounds.x + objBounds.width > x) && (objBounds.x < x + width) && (objBounds.y + objBounds.height > y) && (objBounds.y < y + height)) - if (object.collisionMask.intersectsRaw(x, x + width, y, y + height)) - { - var maxOverlap: number = object.collisionMask.deltaXAbs + Collision.OVERLAP_BIAS; - - // If they did overlap (and can), figure out by how much and flip the corresponding flags - if (object.collisionMask.deltaX > 0) - { - //overlap = object.x + object.width - x; - overlap = object.collisionMask.right - x; - - if ((overlap > maxOverlap) || !(object.allowCollisions & Collision.RIGHT) || collideLeft == false) - { - overlap = 0; - } - else - { - object.touching |= Collision.RIGHT; - } - } - else if (object.collisionMask.deltaX < 0) - { - //overlap = object.x - width - x; - overlap = object.collisionMask.x - width - x; - - if ((-overlap > maxOverlap) || !(object.allowCollisions & Collision.LEFT) || collideRight == false) - { - overlap = 0; - } - else - { - object.touching |= Collision.LEFT; - } - - } - - } - } - - // Then adjust their positions and velocities accordingly (if there was any overlap) - if (overlap != 0) - { - if (separate == true) - { - object.x = object.x - overlap; - object.velocity.x = -(object.velocity.x * object.elasticity); - } - - Collision.TILE_OVERLAP = true; - return true; - } - else - { - return false; - } - - } - - /** - * Separates the two objects on their y axis - * @param object The first GameObject to separate - * @param tile The second GameObject to separate - * @returns {boolean} Whether the objects in fact touched and were separated along the Y axis. - */ - public static NEWseparateTileY(object: GameObject, x: number, y: number, width: number, height: number, mass: number, collideUp: bool, collideDown: bool, separate: bool): bool { - - // Can't separate two immovable objects (tiles are always immovable) - if (object.immovable) - { - return false; - } - - // First, get the two object deltas - var overlap: number = 0; - //var objDelta: number = object.y - object.last.y; - - if (object.collisionMask.deltaY != 0) - { - // Check if the Y hulls actually overlap - //var objDeltaAbs: number = (objDelta > 0) ? objDelta : -objDelta; - //var objBounds: Rectangle = new Rectangle(object.x, object.y - ((objDelta > 0) ? objDelta : 0), object.width, object.height + objDeltaAbs); - - //if ((objBounds.x + objBounds.width > x) && (objBounds.x < x + width) && (objBounds.y + objBounds.height > y) && (objBounds.y < y + height)) - if (object.collisionMask.intersectsRaw(x, x + width, y, y + height)) - { - //var maxOverlap: number = objDeltaAbs + Collision.OVERLAP_BIAS; - var maxOverlap: number = object.collisionMask.deltaYAbs + Collision.OVERLAP_BIAS; - - // If they did overlap (and can), figure out by how much and flip the corresponding flags - if (object.collisionMask.deltaY > 0) - { - //overlap = object.y + object.height - y; - overlap = object.collisionMask.bottom - y; - - if ((overlap > maxOverlap) || !(object.allowCollisions & Collision.DOWN) || collideUp == false) - { - overlap = 0; - } - else - { - object.touching |= Collision.DOWN; - } - } - else if (object.collisionMask.deltaY < 0) - { - //overlap = object.y - height - y; - overlap = object.collisionMask.y - height - y; - - if ((-overlap > maxOverlap) || !(object.allowCollisions & Collision.UP) || collideDown == false) - { - overlap = 0; - } - else - { - object.touching |= Collision.UP; - } - } - } - } - - // TODO - with super low velocities you get lots of stuttering, set some kind of base minimum here - - // Then adjust their positions and velocities accordingly (if there was any overlap) - if (overlap != 0) - { - if (separate == true) - { - object.y = object.y - overlap; - object.velocity.y = -(object.velocity.y * object.elasticity); - } - - Collision.TILE_OVERLAP = true; - return true; - } - else - { - return false; - } - } - - /** - * Separates the two objects on their x axis - * @param object1 The first GameObject to separate - * @param object2 The second GameObject to separate - * @returns {boolean} Whether the objects in fact touched and were separated along the X axis. - */ - public static separateX(object1, object2): bool { - - // Can't separate two immovable objects - if (object1.immovable && object2.immovable) - { - return false; - } - - // First, get the two object deltas - var overlap: number = 0; - - if (object1.collisionMask.deltaX != object2.collisionMask.deltaX) - { - if (object1.collisionMask.intersects(object2.collisionMask)) - { - var maxOverlap: number = object1.collisionMask.deltaXAbs + object2.collisionMask.deltaXAbs + Collision.OVERLAP_BIAS; - - // If they did overlap (and can), figure out by how much and flip the corresponding flags - if (object1.collisionMask.deltaX > object2.collisionMask.deltaX) - { - overlap = object1.collisionMask.right - object2.collisionMask.x; - - if ((overlap > maxOverlap) || !(object1.allowCollisions & Collision.RIGHT) || !(object2.allowCollisions & Collision.LEFT)) - { - overlap = 0; - } - else - { - object1.touching |= Collision.RIGHT; - object2.touching |= Collision.LEFT; - } - } - else if (object1.collisionMask.deltaX < object2.collisionMask.deltaX) - { - overlap = object1.collisionMask.x - object2.collisionMask.width - object2.collisionMask.x; - - if ((-overlap > maxOverlap) || !(object1.allowCollisions & Collision.LEFT) || !(object2.allowCollisions & Collision.RIGHT)) - { - overlap = 0; - } - else - { - object1.touching |= Collision.LEFT; - object2.touching |= Collision.RIGHT; - } - - } - - } - } - - // Then adjust their positions and velocities accordingly (if there was any overlap) - if (overlap != 0) - { - var obj1Velocity: number = object1.velocity.x; - var obj2Velocity: number = object2.velocity.x; - - if (!object1.immovable && !object2.immovable) - { - overlap *= 0.5; - object1.x = object1.x - overlap; - object2.x += overlap; - - var obj1NewVelocity: number = Math.sqrt((obj2Velocity * obj2Velocity * object2.mass) / object1.mass) * ((obj2Velocity > 0) ? 1 : -1); - var obj2NewVelocity: number = Math.sqrt((obj1Velocity * obj1Velocity * object1.mass) / object2.mass) * ((obj1Velocity > 0) ? 1 : -1); - var average: number = (obj1NewVelocity + obj2NewVelocity) * 0.5; - obj1NewVelocity -= average; - obj2NewVelocity -= average; - object1.velocity.x = average + obj1NewVelocity * object1.elasticity; - object2.velocity.x = average + obj2NewVelocity * object2.elasticity; - } - else if (!object1.immovable) - { - object1.x = object1.x - overlap; - object1.velocity.x = obj2Velocity - obj1Velocity * object1.elasticity; - } - else if (!object2.immovable) - { - object2.x += overlap; - object2.velocity.x = obj1Velocity - obj2Velocity * object2.elasticity; - } - - return true; - } - else - { - return false; - } - - } - - /** - * Separates the two objects on their y axis - * @param object1 The first GameObject to separate - * @param object2 The second GameObject to separate - * @returns {boolean} Whether the objects in fact touched and were separated along the Y axis. - */ - public static separateY(object1, object2): bool { - - // Can't separate two immovable objects - if (object1.immovable && object2.immovable) { - return false; - } - - // First, get the two object deltas - var overlap: number = 0; - - if (object1.collisionMask.deltaY != object2.collisionMask.deltaY) - { - if (object1.collisionMask.intersects(object2.collisionMask)) - { - // This is the only place to use the DeltaAbs values - var maxOverlap: number = object1.collisionMask.deltaYAbs + object2.collisionMask.deltaYAbs + Collision.OVERLAP_BIAS; - - // If they did overlap (and can), figure out by how much and flip the corresponding flags - if (object1.collisionMask.deltaY > object2.collisionMask.deltaY) - { - overlap = object1.collisionMask.bottom - object2.collisionMask.y; - - if ((overlap > maxOverlap) || !(object1.allowCollisions & Collision.DOWN) || !(object2.allowCollisions & Collision.UP)) - { - overlap = 0; - } - else - { - object1.touching |= Collision.DOWN; - object2.touching |= Collision.UP; - } - } - else if (object1.collisionMask.deltaY < object2.collisionMask.deltaY) - { - overlap = object1.collisionMask.y - object2.collisionMask.height - object2.collisionMask.y; - - if ((-overlap > maxOverlap) || !(object1.allowCollisions & Collision.UP) || !(object2.allowCollisions & Collision.DOWN)) - { - overlap = 0; - } - else - { - object1.touching |= Collision.UP; - object2.touching |= Collision.DOWN; - } - } - } - } - - // Then adjust their positions and velocities accordingly (if there was any overlap) - if (overlap != 0) - { - var obj1Velocity: number = object1.velocity.y; - var obj2Velocity: number = object2.velocity.y; - - if (!object1.immovable && !object2.immovable) - { - overlap *= 0.5; - object1.y = object1.y - overlap; - object2.y += overlap; - - var obj1NewVelocity: number = Math.sqrt((obj2Velocity * obj2Velocity * object2.mass) / object1.mass) * ((obj2Velocity > 0) ? 1 : -1); - var obj2NewVelocity: number = Math.sqrt((obj1Velocity * obj1Velocity * object1.mass) / object2.mass) * ((obj1Velocity > 0) ? 1 : -1); - var average: number = (obj1NewVelocity + obj2NewVelocity) * 0.5; - obj1NewVelocity -= average; - obj2NewVelocity -= average; - object1.velocity.y = average + obj1NewVelocity * object1.elasticity; - object2.velocity.y = average + obj2NewVelocity * object2.elasticity; - } - else if (!object1.immovable) - { - object1.y = object1.y - overlap; - object1.velocity.y = obj2Velocity - obj1Velocity * object1.elasticity; - // This is special case code that handles things like horizontal moving platforms you can ride - if (object2.active && object2.moves && (object1.collisionMask.deltaY > object2.collisionMask.deltaY)) - { - object1.x += object2.x - object2.last.x; - } - } - else if (!object2.immovable) - { - object2.y += overlap; - object2.velocity.y = obj1Velocity - obj2Velocity * object2.elasticity; - // This is special case code that handles things like horizontal moving platforms you can ride - if (object1.active && object1.moves && (object1.collisionMask.deltaY < object2.collisionMask.deltaY)) - { - object2.x += object1.x - object1.last.x; - } - } - - return true; - } - else - { - return false; - } - } - - /** - * Returns the distance between the two given coordinates. - * @param x1 The X value of the first coordinate - * @param y1 The Y value of the first coordinate - * @param x2 The X value of the second coordinate - * @param y2 The Y value of the second coordinate - * @returns {number} The distance between the two coordinates - */ - public static distance(x1: number, y1: number, x2: number, y2: number) { - return Math.sqrt((x2 - x1) * (x2 - x1) + (y2 - y1) * (y2 - y1)); - } - - /** - * Returns the distanced squared between the two given coordinates. - * @param x1 The X value of the first coordinate - * @param y1 The Y value of the first coordinate - * @param x2 The X value of the second coordinate - * @param y2 The Y value of the second coordinate - * @returns {number} The distance between the two coordinates - */ - public static distanceSquared(x1: number, y1: number, x2: number, y2: number) { - return (x2 - x1) * (x2 - x1) + (y2 - y1) * (y2 - y1); - } - - - - // SAT - - /** - * Flattens the specified array of points onto a unit vector axis, - * resulting in a one dimensional range of the minimum and - * maximum value on that axis. - * - * @param {Array.} points The points to flatten. - * @param {Vector} normal The unit vector axis to flatten on. - * @param {Array.} result An array. After calling this function, - * result[0] will be the minimum value, - * result[1] will be the maximum value. - */ - public static flattenPointsOn(points, normal, result) { - - var min = Number.MAX_VALUE; - var max = -Number.MAX_VALUE; - var len = points.length; - - for (var i = 0; i < len; i++) - { - // Get the magnitude of the projection of the point onto the normal - var dot = points[i].dot(normal); - if (dot < min) { min = dot; } - if (dot > max) { max = dot; } - } - - result[0] = min; result[1] = max; - - } - - /** - * Pool of Vectors used in calculations. - * - * @type {Array.} - */ - public static T_VECTORS: Vec2[]; - - /** - * Pool of Arrays used in calculations. - * - * @type {Array.>} - */ - public static T_ARRAYS; - - /** - * Check whether two convex clockwise polygons are separated by the specified - * axis (must be a unit vector). - * - * @param {Vector} aPos The position of the first polygon. - * @param {Vector} bPos The position of the second polygon. - * @param {Array.} aPoints The points in the first polygon. - * @param {Array.} bPoints The points in the second polygon. - * @param {Vector} axis The axis (unit sized) to test against. The points of both polygons - * will be projected onto this axis. - * @param {Response=} response A Response object (optional) which will be populated - * if the axis is not a separating axis. - * @return {boolean} true if it is a separating axis, false otherwise. If false, - * and a response is passed in, information about how much overlap and - * the direction of the overlap will be populated. - */ - public static isSeparatingAxis(aPos, bPos, aPoints, bPoints, axis, response?:Response = null): bool { - - var rangeA = Collision.T_ARRAYS.pop(); - var rangeB = Collision.T_ARRAYS.pop(); - - // Get the magnitude of the offset between the two polygons - var offsetV = Collision.T_VECTORS.pop().copyFrom(bPos).sub(aPos); - var projectedOffset = offsetV.dot(axis); - - // Project the polygons onto the axis. - Collision.flattenPointsOn(aPoints, axis, rangeA); - Collision.flattenPointsOn(bPoints, axis, rangeB); - - // Move B's range to its position relative to A. - rangeB[0] += projectedOffset; - rangeB[1] += projectedOffset; - - // Check if there is a gap. If there is, this is a separating axis and we can stop - if (rangeA[0] > rangeB[1] || rangeB[0] > rangeA[1]) - { - Collision.T_VECTORS.push(offsetV); - Collision.T_ARRAYS.push(rangeA); - Collision.T_ARRAYS.push(rangeB); - return true; - } - - // If we're calculating a response, calculate the overlap. - if (response) - { - var overlap = 0; - - // A starts further left than B - if (rangeA[0] < rangeB[0]) - { - response.aInB = false; - - // A ends before B does. We have to pull A out of B - if (rangeA[1] < rangeB[1]) - { - overlap = rangeA[1] - rangeB[0]; - response.bInA = false; - // B is fully inside A. Pick the shortest way out. - } - else - { - var option1 = rangeA[1] - rangeB[0]; - var option2 = rangeB[1] - rangeA[0]; - overlap = option1 < option2 ? option1 : -option2; - } - // B starts further left than A - } - else - { - response.bInA = false; - - // B ends before A ends. We have to push A out of B - if (rangeA[1] > rangeB[1]) - { - overlap = rangeA[0] - rangeB[1]; - response.aInB = false; - // A is fully inside B. Pick the shortest way out. - } - else - { - var option1 = rangeA[1] - rangeB[0]; - var option2 = rangeB[1] - rangeA[0]; - overlap = option1 < option2 ? option1 : -option2; - } - } - - // If this is the smallest amount of overlap we've seen so far, set it as the minimum overlap. - var absOverlap = Math.abs(overlap); - - if (absOverlap < response.overlap) - { - response.overlap = absOverlap; - response.overlapN.copyFrom(axis); - - if (overlap < 0) - { - response.overlapN.reverse(); - } - } - } - - Collision.T_VECTORS.push(offsetV); - Collision.T_ARRAYS.push(rangeA); - Collision.T_ARRAYS.push(rangeB); - - return false; - - } - - public static LEFT_VORNOI_REGION:number = -1; - public static MIDDLE_VORNOI_REGION:number = 0; - public static RIGHT_VORNOI_REGION:number = 1; - - /** - * Calculates which Vornoi region a point is on a line segment. - * It is assumed that both the line and the point are relative to (0, 0) - * - * | (0) | - * (-1) [0]--------------[1] (1) - * | (0) | - * - * @param {Vector} line The line segment. - * @param {Vector} point The point. - * @return {number} LEFT_VORNOI_REGION (-1) if it is the left region, - * MIDDLE_VORNOI_REGION (0) if it is the middle region, - * RIGHT_VORNOI_REGION (1) if it is the right region. - */ - public static vornoiRegion(line: Vec2, point: Vec2): number { - - var len2 = line.length2(); - var dp = point.dot(line); - - if (dp < 0) { return Collision.LEFT_VORNOI_REGION; } - else if (dp > len2) { return Collision.RIGHT_VORNOI_REGION; } - else { return Collision.MIDDLE_VORNOI_REGION; } - - } - - /** - * Check if two circles intersect. - * - * @param {Circle} a The first circle. - * @param {Circle} b The second circle. - * @param {Response=} response Response object (optional) that will be populated if - * the circles intersect. - * @return {boolean} true if the circles intersect, false if they don't. - */ - public static testCircleCircle(a: Circle, b: Circle, response?: Response = null): bool { - - var differenceV = Collision.T_VECTORS.pop().copyFrom(b.pos).sub(a.pos); - var totalRadius = a.radius + b.radius; - var totalRadiusSq = totalRadius * totalRadius; - var distanceSq = differenceV.length2(); - - if (distanceSq > totalRadiusSq) - { - // They do not intersect - Collision.T_VECTORS.push(differenceV); - return false; - } - - // They intersect. If we're calculating a response, calculate the overlap. - if (response) - { - var dist = Math.sqrt(distanceSq); - response.a = a; - response.b = b; - response.overlap = totalRadius - dist; - response.overlapN.copyFrom(differenceV.normalize()); - response.overlapV.copyFrom(differenceV).scale(response.overlap); - response.aInB = a.radius <= b.radius && dist <= b.radius - a.radius; - response.bInA = b.radius <= a.radius && dist <= a.radius - b.radius; - } - - Collision.T_VECTORS.push(differenceV); - return true; - - } - - /** - * Check if a polygon and a circle intersect. - * - * @param {Polygon} polygon The polygon. - * @param {Circle} circle The circle. - * @param {Response=} response Response object (optional) that will be populated if - * they interset. - * @return {boolean} true if they intersect, false if they don't. - */ - public static testPolygonCircle(polygon: Polygon, circle: Circle, response?: Response = null): bool { - - var circlePos = Collision.T_VECTORS.pop().copyFrom(circle.pos).sub(polygon.pos); - var radius = circle.radius; - var radius2 = radius * radius; - var points = polygon.points; - var len = points.length; - var edge = T_VECTORS.pop(); - var point = T_VECTORS.pop(); - - // For each edge in the polygon - for (var i = 0; i < len; i++) - { - var next = i === len - 1 ? 0 : i + 1; - var prev = i === 0 ? len - 1 : i - 1; - var overlap = 0; - var overlapN = null; - - // Get the edge - edge.copyFrom(polygon.edges[i]); - // Calculate the center of the cirble relative to the starting point of the edge - point.copyFrom(circlePos).sub(points[i]); - - // If the distance between the center of the circle and the point - // is bigger than the radius, the polygon is definitely not fully in - // the circle. - if (response && point.length2() > radius2) - { - response.aInB = false; - } - - // Calculate which Vornoi region the center of the circle is in. - var region = vornoiRegion(edge, point); - - if (region === Collision.LEFT_VORNOI_REGION) - { - // Need to make sure we're in the RIGHT_VORNOI_REGION of the previous edge. - edge.copyFrom(polygon.edges[prev]); - - // Calculate the center of the circle relative the starting point of the previous edge - var point2 = Collision.T_VECTORS.pop().copyFrom(circlePos).sub(points[prev]); - region = vornoiRegion(edge, point2); - - if (region === Collision.RIGHT_VORNOI_REGION) - { - // It's in the region we want. Check if the circle intersects the point. - var dist = point.length2(); - - if (dist > radius) - { - // No intersection - Collision.T_VECTORS.push(circlePos); - Collision.T_VECTORS.push(edge); - Collision.T_VECTORS.push(point); - Collision.T_VECTORS.push(point2); - return false; - } - else if (response) - { - // It intersects, calculate the overlap - response.bInA = false; - overlapN = point.normalize(); - overlap = radius - dist; - } - } - - Collision.T_VECTORS.push(point2); - - } - else if (region === Collision.RIGHT_VORNOI_REGION) - { - // Need to make sure we're in the left region on the next edge - edge.copyFrom(polygon.edges[next]); - - // Calculate the center of the circle relative to the starting point of the next edge - point.copyFrom(circlePos).sub(points[next]); - region = vornoiRegion(edge, point); - - if (region === Collision.LEFT_VORNOI_REGION) - { - // It's in the region we want. Check if the circle intersects the point. - var dist = point.length2(); - - if (dist > radius) - { - // No intersection - Collision.T_VECTORS.push(circlePos); - Collision.T_VECTORS.push(edge); - Collision.T_VECTORS.push(point); - return false; - } - else if (response) - { - // It intersects, calculate the overlap - response.bInA = false; - overlapN = point.normalize(); - overlap = radius - dist; - } - } - // MIDDLE_VORNOI_REGION - } - else - { - // Need to check if the circle is intersecting the edge, - // Change the edge into its "edge normal". - var normal = edge.perp().normalize(); - - // Find the perpendicular distance between the center of the - // circle and the edge. - var dist = point.dot(normal); - var distAbs = Math.abs(dist); - - // If the circle is on the outside of the edge, there is no intersection - if (dist > 0 && distAbs > radius) - { - Collision.T_VECTORS.push(circlePos); - Collision.T_VECTORS.push(normal); - Collision.T_VECTORS.push(point); - return false; - } - else if (response) - { - // It intersects, calculate the overlap. - overlapN = normal; - overlap = radius - dist; - // If the center of the circle is on the outside of the edge, or part of the - // circle is on the outside, the circle is not fully inside the polygon. - if (dist >= 0 || overlap < 2 * radius) - { - response.bInA = false; - } - } - } - - // If this is the smallest overlap we've seen, keep it. - // (overlapN may be null if the circle was in the wrong Vornoi region) - if (overlapN && response && Math.abs(overlap) < Math.abs(response.overlap)) - { - response.overlap = overlap; - response.overlapN.copyFrom(overlapN); - } - } - - // Calculate the final overlap vector - based on the smallest overlap. - if (response) - { - response.a = polygon; - response.b = circle; - response.overlapV.copyFrom(response.overlapN).scale(response.overlap); - } - - Collision.T_VECTORS.push(circlePos); - Collision.T_VECTORS.push(edge); - Collision.T_VECTORS.push(point); - - return true; - } - - /** - * Check if a circle and a polygon intersect. - * - * NOTE: This runs slightly slower than polygonCircle as it just - * runs polygonCircle and reverses everything at the end. - * - * @param {Circle} circle The circle. - * @param {Polygon} polygon The polygon. - * @param {Response=} response Response object (optional) that will be populated if - * they interset. - * @return {boolean} true if they intersect, false if they don't. - */ - public static testCirclePolygon(circle: Circle, polygon: Polygon, response?: Response = null): bool { - - var result = Collision.testPolygonCircle(polygon, circle, response); - - if (result && response) - { - // Swap A and B in the response. - var a = response.a; - var aInB = response.aInB; - response.overlapN.reverse(); - response.overlapV.reverse(); - response.a = response.b; - response.b = a; - response.aInB = response.bInA; - response.bInA = aInB; - } - - return result; - } - - /** - * Checks whether two convex, clockwise polygons intersect. - * - * @param {Polygon} a The first polygon. - * @param {Polygon} b The second polygon. - * @param {Response=} response Response object (optional) that will be populated if - * they interset. - * @return {boolean} true if they intersect, false if they don't. - */ - public static testPolygonPolygon(a: Polygon, b: Polygon, response?: Response = null): bool { - - var aPoints = a.points; - var aLen = aPoints.length; - var bPoints = b.points; - var bLen = bPoints.length; - - // If any of the edge normals of A is a separating axis, no intersection. - for (var i = 0; i < aLen; i++) - { - if (Collision.isSeparatingAxis(a.pos, b.pos, aPoints, bPoints, a.normals[i], response)) - { - return false; - } - } - - // If any of the edge normals of B is a separating axis, no intersection. - for (var i = 0; i < bLen; i++) - { - if (Collision.isSeparatingAxis(a.pos, b.pos, aPoints, bPoints, b.normals[i], response)) - { - return false; - } - } - - // Since none of the edge normals of A or B are a separating axis, there is an intersection - // and we've already calculated the smallest overlap (in isSeparatingAxis). Calculate the - // final overlap vector. - if (response) - { - response.a = a; - response.b = b; - response.overlapV.copyFrom(response.overlapN).scale(response.overlap); - } - - return true; - } - - } - -} \ No newline at end of file diff --git a/wip/phaser clean up/CollisionMask.ts b/wip/phaser clean up/CollisionMask.ts deleted file mode 100644 index f0186ae0..00000000 --- a/wip/phaser clean up/CollisionMask.ts +++ /dev/null @@ -1,501 +0,0 @@ -/// - -/** -* Phaser - CollisionMask -*/ - -module Phaser { - - export class CollisionMask { - - /** - * CollisionMask constructor. Creates a new CollisionMask for the given GameObject. - * - * @param game {Phaser.Game} Current game instance. - * @param parent {Phaser.GameObject} The GameObject this CollisionMask belongs to. - * @param x {number} The initial x position of the CollisionMask. - * @param y {number} The initial y position of the CollisionMask. - * @param width {number} The width of the CollisionMask. - * @param height {number} The height of the CollisionMask. - */ - constructor(game: Game, parent: GameObject, x: number, y: number, width: number, height: number) { - - this._game = game; - this._parent = parent; - - // By default the CollisionMask is a quad - this.type = CollisionMask.QUAD; - - this.quad = new Phaser.Quad(this._parent.x, this._parent.y, this._parent.width, this._parent.height); - this.offset = new MicroPoint(0, 0); - this.last = new MicroPoint(0, 0); - - this._ref = this.quad; - - return this; - - } - - private _game; - private _parent; - - // An internal reference to the active collision shape - private _ref; - - /** - * Geom type of this sprite. (available: QUAD, POINT, CIRCLE, LINE, RECTANGLE, POLYGON) - * @type {number} - */ - public type: number = 0; - - /** - * Quad (a smaller version of Rectangle). - * @type {number} - */ - public static QUAD: number = 0; - - /** - * Point. - * @type {number} - */ - public static POINT: number = 1; - - /** - * Circle. - * @type {number} - */ - public static CIRCLE: number = 2; - - /** - * Line. - * @type {number} - */ - public static LINE: number = 3; - - /** - * Rectangle. - * @type {number} - */ - public static RECTANGLE: number = 4; - - /** - * Polygon. - * @type {number} - */ - public static POLYGON: number = 5; - - /** - * Rectangle shape container. A Rectangle instance. - * @type {Rectangle} - */ - public quad: Quad; - - /** - * Point shape container. A Point instance. - * @type {Point} - */ - public point: Point; - - /** - * Circle shape container. A Circle instance. - * @type {Circle} - */ - public circle: Circle; - - /** - * Line shape container. A Line instance. - * @type {Line} - */ - public line: Line; - - /** - * Rectangle shape container. A Rectangle instance. - * @type {Rectangle} - */ - public rect: Rectangle; - - /** - * A value from the top-left of the GameObject frame that this collisionMask is offset to. - * If the CollisionMask is a Quad/Rectangle the offset relates to the top-left of that Quad. - * If the CollisionMask is a Circle the offset relates to the center of the circle. - * @type {MicroPoint} - */ - public offset: MicroPoint; - - /** - * The previous x/y coordinates of the CollisionMask, used for hull calculations - * @type {MicroPoint} - */ - public last: MicroPoint; - - /** - * Create a circle shape with specific diameter. - * @param diameter {number} Diameter of the circle. - * @return {CollisionMask} This - */ - createCircle(diameter: number): CollisionMask { - - this.type = CollisionMask.CIRCLE; - this.circle = new Circle(this.last.x, this.last.y, diameter); - this._ref = this.circle; - - return this; - - } - - /** - * Pre-update is called right before update() on each object in the game loop. - */ - public preUpdate() { - - this.last.x = this.x; - this.last.y = this.y; - - } - - public update() { - - this._ref.x = this._parent.x + this.offset.x; - this._ref.y = this._parent.y + this.offset.y; - - } - - /** - * Renders the bounding box around this Sprite and the contact points. Useful for visually debugging. - * @param camera {Camera} Camera the bound will be rendered to. - * @param cameraOffsetX {number} X offset of bound to the camera. - * @param cameraOffsetY {number} Y offset of bound to the camera. - */ - public render(camera:Camera, cameraOffsetX:number, cameraOffsetY:number) { - - var _dx = cameraOffsetX + (this.x - camera.worldView.x); - var _dy = cameraOffsetY + (this.y - camera.worldView.y); - - this._parent.context.fillStyle = this._parent.renderDebugColor; - - if (this.type == CollisionMask.QUAD) - { - this._parent.context.fillRect(_dx, _dy, this.width, this.height); - } - else if (this.type == CollisionMask.CIRCLE) - { - this._parent.context.beginPath(); - this._parent.context.arc(_dx, _dy, this.circle.radius, 0, Math.PI * 2); - this._parent.context.fill(); - this._parent.context.closePath(); - } - - } - - /** - * Destroy all objects and references belonging to this CollisionMask - */ - public destroy() { - - this._game = null; - this._parent = null; - this._ref = null; - this.quad = null; - this.point = null; - this.circle = null; - this.rect = null; - this.line = null; - this.offset = null; - - } - - public intersectsRaw(left: number, right: number, top: number, bottom: number): bool { - -//if ((objBounds.x + objBounds.width > x) && (objBounds.x < x + width) && (objBounds.y + objBounds.height > y) && (objBounds.y < y + height)) - - return true; - - } - - public intersectsVector(vector: Phaser.Vector2): bool { - - if (this.type == CollisionMask.QUAD) - { - return this.quad.contains(vector.x, vector.y); - } - - } - - /** - * Gives a basic boolean response to a geometric collision. - * If you need the details of the collision use the Collision functions instead and inspect the IntersectResult object. - * @param source {GeomSprite} Sprite you want to check. - * @return {boolean} Whether they overlaps or not. - */ - public intersects(source: CollisionMask): bool { - - // Quad vs. Quad - if (this.type == CollisionMask.QUAD && source.type == CollisionMask.QUAD) - { - return this.quad.intersects(source.quad); - } - - // Circle vs. Circle - if (this.type == CollisionMask.CIRCLE && source.type == CollisionMask.CIRCLE) - { - return Collision.circleToCircle(this.circle, source.circle).result; - } - - // Circle vs. Rect - if (this.type == CollisionMask.CIRCLE && source.type == CollisionMask.RECTANGLE) - { - return Collision.circleToRectangle(this.circle, source.rect).result; - } - - // Circle vs. Point - if (this.type == CollisionMask.CIRCLE && source.type == CollisionMask.POINT) - { - return Collision.circleContainsPoint(this.circle, source.point).result; - } - - // Circle vs. Line - if (this.type == CollisionMask.CIRCLE && source.type == CollisionMask.LINE) - { - return Collision.lineToCircle(source.line, this.circle).result; - } - - // Rect vs. Rect - if (this.type == CollisionMask.RECTANGLE && source.type == CollisionMask.RECTANGLE) - { - return Collision.rectangleToRectangle(this.rect, source.rect).result; - } - - // Rect vs. Circle - if (this.type == CollisionMask.RECTANGLE && source.type == CollisionMask.CIRCLE) - { - return Collision.circleToRectangle(source.circle, this.rect).result; - } - - // Rect vs. Point - if (this.type == CollisionMask.RECTANGLE && source.type == CollisionMask.POINT) - { - return Collision.pointToRectangle(source.point, this.rect).result; - } - - // Rect vs. Line - if (this.type == CollisionMask.RECTANGLE && source.type == CollisionMask.LINE) - { - return Collision.lineToRectangle(source.line, this.rect).result; - } - - // Point vs. Point - if (this.type == CollisionMask.POINT && source.type == CollisionMask.POINT) - { - return this.point.equals(source.point); - } - - // Point vs. Circle - if (this.type == CollisionMask.POINT && source.type == CollisionMask.CIRCLE) - { - return Collision.circleContainsPoint(source.circle, this.point).result; - } - - // Point vs. Rect - if (this.type == CollisionMask.POINT && source.type == CollisionMask.RECTANGLE) - { - return Collision.pointToRectangle(this.point, source.rect).result; - } - - // Point vs. Line - if (this.type == CollisionMask.POINT && source.type == CollisionMask.LINE) - { - return source.line.isPointOnLine(this.point.x, this.point.y); - } - - // Line vs. Line - if (this.type == CollisionMask.LINE && source.type == CollisionMask.LINE) - { - return Collision.lineSegmentToLineSegment(this.line, source.line).result; - } - - // Line vs. Circle - if (this.type == CollisionMask.LINE && source.type == CollisionMask.CIRCLE) - { - return Collision.lineToCircle(this.line, source.circle).result; - } - - // Line vs. Rect - if (this.type == CollisionMask.LINE && source.type == CollisionMask.RECTANGLE) - { - return Collision.lineSegmentToRectangle(this.line, source.rect).result; - } - - // Line vs. Point - if (this.type == CollisionMask.LINE && source.type == CollisionMask.POINT) - { - return this.line.isPointOnLine(source.point.x, source.point.y); - } - - return false; - - } - - public checkHullIntersection(mask: CollisionMask): bool { - - if ((this.hullX + this.hullWidth > mask.hullX) && (this.hullX < mask.hullX + mask.width) && (this.hullY + this.hullHeight > mask.hullY) && (this.hullY < mask.hullY + mask.hullHeight)) - { - return true; - } - else - { - return false; - } - - } - - public get hullWidth(): number { - - if (this.deltaX > 0) - { - return this.width + this.deltaX; - } - else - { - return this.width - this.deltaX; - } - - } - - public get hullHeight(): number { - - if (this.deltaY > 0) - { - return this.height + this.deltaY; - } - else - { - return this.height - this.deltaY; - } - - } - - public get hullX(): number { - - if (this.x < this.last.x) - { - return this.x; - } - else - { - return this.last.x; - } - - } - - public get hullY(): number { - - if (this.y < this.last.y) - { - return this.y; - } - else - { - return this.last.y; - } - - } - - public get deltaXAbs(): number { - return (this.deltaX > 0 ? this.deltaX : -this.deltaX); - } - - public get deltaYAbs(): number { - return (this.deltaY > 0 ? this.deltaY : -this.deltaY); - } - - public get deltaX(): number { - return this.x - this.last.x; - } - - public get deltaY(): number { - return this.y - this.last.y; - } - - public get x(): number { - return this._ref.x; - //return this.quad.x; - } - - public set x(value: number) { - this._ref.x = value; - //this.quad.x = value; - } - - public get y(): number { - return this._ref.y; - //return this.quad.y; - } - - public set y(value: number) { - this._ref.y = value; - //this.quad.y = value; - } - - //public get rotation(): number { - // return this._angle; - //} - - //public set rotation(value: number) { - // this._angle = this._game.math.wrap(value, 360, 0); - //} - - //public get angle(): number { - // return this._angle; - //} - - //public set angle(value: number) { - // this._angle = this._game.math.wrap(value, 360, 0); - //} - - public set width(value:number) { - //this.quad.width = value; - this._ref.width = value; - } - - public set height(value:number) { - //this.quad.height = value; - this._ref.height = value; - } - - public get width(): number { - //return this.quad.width; - return this._ref.width; - } - - public get height(): number { - //return this.quad.height; - return this._ref.height; - } - - public get left(): number { - return this.x; - } - - public get right(): number { - return this.x + this.width; - } - - public get top(): number { - return this.y; - } - - public get bottom(): number { - return this.y + this.height; - } - - public get halfWidth(): number { - return this.width / 2; - } - - public get halfHeight(): number { - return this.height / 2; - } - - } - -} \ No newline at end of file diff --git a/wip/phaser clean up/Debug.ts b/wip/phaser clean up/Debug.ts deleted file mode 100644 index 5ffa22eb..00000000 --- a/wip/phaser clean up/Debug.ts +++ /dev/null @@ -1,90 +0,0 @@ -/** -* Phaser - Components - Debug -* -* -*/ - -module Phaser.Components { - - export class Debug { - - /** - * Render bound of this sprite for debugging? (default to false) - * @type {boolean} - */ - public renderDebug: bool = false; - - /** - * Color of the Sprite when no image is present. Format is a css color string. - * @type {string} - */ - public fillColor: string = 'rgb(255,255,255)'; - - /** - * Color of bound when render debug. (see renderDebug) Format is a css color string. - * @type {string} - */ - public renderDebugColor: string = 'rgba(0,255,0,0.5)'; - - /** - * Color of points when render debug. (see renderDebug) Format is a css color string. - * @type {string} - */ - public renderDebugPointColor: string = 'rgba(255,255,255,1)'; - - /** - * Renders the bounding box around this Sprite and the contact points. Useful for visually debugging. - * @param camera {Camera} Camera the bound will be rendered to. - * @param cameraOffsetX {number} X offset of bound to the camera. - * @param cameraOffsetY {number} Y offset of bound to the camera. - */ - /* - private renderBounds(camera: Camera, cameraOffsetX: number, cameraOffsetY: number) { - - this._dx = cameraOffsetX + (this.frameBounds.topLeft.x - camera.worldView.x); - this._dy = cameraOffsetY + (this.frameBounds.topLeft.y - camera.worldView.y); - - this.context.fillStyle = this.renderDebugColor; - this.context.fillRect(this._dx, this._dy, this.frameBounds.width, this.frameBounds.height); - - //this.context.fillStyle = this.renderDebugPointColor; - - //var hw = this.frameBounds.halfWidth * this.scale.x; - //var hh = this.frameBounds.halfHeight * this.scale.y; - //var sw = (this.frameBounds.width * this.scale.x) - 1; - //var sh = (this.frameBounds.height * this.scale.y) - 1; - - //this.context.fillRect(this._dx, this._dy, 1, 1); // top left - //this.context.fillRect(this._dx + hw, this._dy, 1, 1); // top center - //this.context.fillRect(this._dx + sw, this._dy, 1, 1); // top right - //this.context.fillRect(this._dx, this._dy + hh, 1, 1); // left center - //this.context.fillRect(this._dx + hw, this._dy + hh, 1, 1); // center - //this.context.fillRect(this._dx + sw, this._dy + hh, 1, 1); // right center - //this.context.fillRect(this._dx, this._dy + sh, 1, 1); // bottom left - //this.context.fillRect(this._dx + hw, this._dy + sh, 1, 1); // bottom center - //this.context.fillRect(this._dx + sw, this._dy + sh, 1, 1); // bottom right - - } - */ - - /** - * Render debug infos. (including name, bounds info, position and some other properties) - * @param x {number} X position of the debug info to be rendered. - * @param y {number} Y position of the debug info to be rendered. - * @param [color] {number} color of the debug info to be rendered. (format is css color string) - */ - /* - public renderDebugInfo(x: number, y: number, color?: string = 'rgb(255,255,255)') { - - this.context.fillStyle = color; - this.context.fillText('Sprite: ' + this.name + ' (' + this.frameBounds.width + ' x ' + this.frameBounds.height + ')', x, y); - this.context.fillText('x: ' + this.frameBounds.x.toFixed(1) + ' y: ' + this.frameBounds.y.toFixed(1) + ' rotation: ' + this.angle.toFixed(1), x, y + 14); - this.context.fillText('dx: ' + this._dx.toFixed(1) + ' dy: ' + this._dy.toFixed(1) + ' dw: ' + this._dw.toFixed(1) + ' dh: ' + this._dh.toFixed(1), x, y + 28); - this.context.fillText('sx: ' + this._sx.toFixed(1) + ' sy: ' + this._sy.toFixed(1) + ' sw: ' + this._sw.toFixed(1) + ' sh: ' + this._sh.toFixed(1), x, y + 42); - - } - */ - - } - -} \ No newline at end of file diff --git a/wip/phaser clean up/DebugPhysics.ts b/wip/phaser clean up/DebugPhysics.ts deleted file mode 100644 index 4dee9ba2..00000000 --- a/wip/phaser clean up/DebugPhysics.ts +++ /dev/null @@ -1,78 +0,0 @@ - static renderPhysicsBody(body: Phaser.Physics.Body, lineWidth: number = 1, fillStyle: string = 'rgba(0,255,0,0.2)', sleepStyle: string = 'rgba(100,100,100,0.2)') { - - for (var s = 0; s < body.shapesLength; s++) - { - DebugUtils.context.beginPath(); - - if (body.shapes[s].type == Phaser.Physics.AdvancedPhysics.SHAPE_TYPE_POLY) - { - var verts = body.shapes[s].tverts; - - // DebugUtils.context.moveTo(body.position.x * 50 + verts[0].x, body.position.y * 50 + verts[0].y); - DebugUtils.context.moveTo(verts[0].x * 50, verts[0].y * 50); - - for (var i = 1; i < verts.length; i++) - { - // DebugUtils.context.lineTo(body.position.x * 50 + verts[i].x, body.position.y * 50 + verts[i].y); - DebugUtils.context.lineTo(verts[i].x * 50, verts[i].y * 50); - } - - // DebugUtils.context.lineTo(body.position.x * 50 + verts[0].x, body.position.y * 50 + verts[0].y); - DebugUtils.context.lineTo(verts[0].x * 50, verts[0].y * 50); - } - else if (body.shapes[s].type == Phaser.Physics.AdvancedPhysics.SHAPE_TYPE_CIRCLE) - { - var circle = body.shapes[s]; - DebugUtils.context.arc(circle.tc.x * 50, circle.tc.y * 50, circle.radius * 50, 0, Math.PI * 2, false); - } - - DebugUtils.context.closePath(); - - if (body.isAwake) - { - DebugUtils.context.fillStyle = fillStyle; - } - else - { - DebugUtils.context.fillStyle = sleepStyle; - } - - DebugUtils.context.fill(); - - } - - } - - /** - * Render debug infos. (including name, bounds info, position and some other properties) - * @param x {number} X position of the debug info to be rendered. - * @param y {number} Y position of the debug info to be rendered. - * @param [color] {number} color of the debug info to be rendered. (format is css color string) - */ - /* - static renderPhysicsBodyInfo(body: Phaser.Physics.Body, x: number, y: number, color?: string = 'rgb(255,255,255)') { - - DebugUtils.context.fillStyle = color; - DebugUtils.context.fillText('Body ID: ' + body.name, x, y); - DebugUtils.context.fillText('Position x: ' + body.position.x.toFixed(1) + ' y: ' + body.position.y.toFixed(1) + ' rotation: ' + body.angle.toFixed(1), x, y + 14); - DebugUtils.context.fillText('World x: ' + (body.position.x * 50).toFixed(1) + ' y: ' + (body.position.y * 50).toFixed(1), x, y + 28); - DebugUtils.context.fillText('Velocity x: ' + body.velocity.x.toFixed(1) + ' y: ' + body.velocity.y.toFixed(1), x, y + 42); - - if (body.shapes[0].verts.length > 0) - { - DebugUtils.context.fillText('Vert 1 x: ' + (body.shapes[0].verts[0].x * 50) + ' y: ' + (body.shapes[0].verts[0].y * 50), x, y + 56); - DebugUtils.context.fillText('Vert 2 x: ' + (body.shapes[0].verts[1].x * 50) + ' y: ' + (body.shapes[0].verts[1].y * 50), x, y + 70); - DebugUtils.context.fillText('Vert 3 x: ' + (body.shapes[0].tverts[2].x * 50) + ' y: ' + (body.shapes[0].tverts[2].y * 50), x, y + 84); - DebugUtils.context.fillText('Vert 4 x: ' + (body.shapes[0].tverts[3].x * 50) + ' y: ' + (body.shapes[0].tverts[3].y * 50), x, y + 98); - - // - // DebugUtils.context.fillText('Vert 1 x: ' + body.shapes[0].verts[0].x.toFixed(1) + ' y: ' + body.shapes[0].verts[0].y.toFixed(1), x, y + 56); - // DebugUtils.context.fillText('Vert 2 x: ' + body.shapes[0].verts[1].x.toFixed(1) + ' y: ' + body.shapes[0].verts[1].y.toFixed(1), x, y + 70); - // DebugUtils.context.fillText('Vert 3 x: ' + body.shapes[0].verts[2].x.toFixed(1) + ' y: ' + body.shapes[0].verts[2].y.toFixed(1), x, y + 84); - // DebugUtils.context.fillText('Vert 4 x: ' + body.shapes[0].verts[3].x.toFixed(1) + ' y: ' + body.shapes[0].verts[3].y.toFixed(1), x, y + 98); - // - - } - - } - */ diff --git a/wip/phaser clean up/Emitter.ts b/wip/phaser clean up/Emitter.ts deleted file mode 100644 index 1aa5d2b1..00000000 --- a/wip/phaser clean up/Emitter.ts +++ /dev/null @@ -1,454 +0,0 @@ -/// -/// - -/** -* Phaser - Emitter -* -* Emitter is a lightweight particle emitter. It can be used for one-time explosions or for -* continuous effects like rain and fire. All it really does is launch Particle objects out -* at set intervals, and fixes their positions and velocities accorindgly. -*/ - -module Phaser { - - export class Emitter extends Group { - - /** - * Creates a new Emitter object at a specific position. - * Does NOT automatically generate or attach particles! - * - * @param x {number} The X position of the emitter. - * @param y {number} The Y position of the emitter. - * @param [size] {number} Specifies a maximum capacity for this emitter. - */ - constructor(game: Game, x: number = 0, y: number = 0, size: number = 0) { - - super(game, size); - - this.x = x; - this.y = y; - this.width = 0; - this.height = 0; - this.minParticleSpeed = new MicroPoint(-100, -100); - this.maxParticleSpeed = new MicroPoint(100, 100); - this.minRotation = -360; - this.maxRotation = 360; - this.gravity = 0; - this.particleClass = null; - this.particleDrag = new MicroPoint(); - this.frequency = 0.1; - this.lifespan = 3; - this.bounce = 0; - this._quantity = 0; - this._counter = 0; - this._explode = true; - this.on = false; - this._point = new MicroPoint(); - - } - - /** - * The X position of the top left corner of the emitter in world space. - */ - public x: number; - - /** - * The Y position of the top left corner of emitter in world space. - */ - public y: number; - - /** - * The width of the emitter. Particles can be randomly generated from anywhere within this box. - */ - public width: number; - - /** - * The height of the emitter. Particles can be randomly generated from anywhere within this box. - */ - public height: number; - - /** - * The minimum possible velocity of a particle. - * The default value is (-100,-100). - */ - public minParticleSpeed: MicroPoint; - - /** - * The maximum possible velocity of a particle. - * The default value is (100,100). - */ - public maxParticleSpeed: MicroPoint; - - /** - * The X and Y drag component of particles launched from the emitter. - */ - public particleDrag: MicroPoint; - - /** - * The minimum possible angular velocity of a particle. The default value is -360. - * NOTE: rotating particles are more expensive to draw than non-rotating ones! - */ - public minRotation: number; - - /** - * The maximum possible angular velocity of a particle. The default value is 360. - * NOTE: rotating particles are more expensive to draw than non-rotating ones! - */ - public maxRotation: number; - - /** - * Sets the acceleration.y member of each particle to this value on launch. - */ - public gravity: number; - - /** - * Determines whether the emitter is currently emitting particles. - * It is totally safe to directly toggle this. - */ - public on: bool; - - /** - * How often a particle is emitted (if emitter is started with Explode == false). - */ - public frequency: number; - - /** - * How long each particle lives once it is emitted. - * Set lifespan to 'zero' for particles to live forever. - */ - public lifespan: number; - - /** - * How much each particle should bounce. 1 = full bounce, 0 = no bounce. - */ - public bounce: number; - - /** - * Set your own particle class type here. - * Default is Particle. - */ - public particleClass; - - /** - * Internal helper for deciding how many particles to launch. - */ - private _quantity: number; - - /** - * Internal helper for the style of particle emission (all at once, or one at a time). - */ - private _explode: bool; - - /** - * Internal helper for deciding when to launch particles or kill them. - */ - private _timer: number; - - /** - * Internal counter for figuring out how many particles to launch. - */ - private _counter: number; - - /** - * Internal point object, handy for reusing for memory mgmt purposes. - */ - private _point: MicroPoint; - - /** - * Clean up memory. - */ - public destroy() { - this.minParticleSpeed = null; - this.maxParticleSpeed = null; - this.particleDrag = null; - this.particleClass = null; - this._point = null; - super.destroy(); - } - - /** - * This function generates a new array of particle sprites to attach to the emitter. - * - * @param graphics If you opted to not pre-configure an array of Sprite objects, you can simply pass in a particle image or sprite sheet. - * @param quantity {number} The number of particles to generate when using the "create from image" option. - * @param multiple {boolean} Whether the image in the Graphics param is a single particle or a bunch of particles (if it's a bunch, they need to be square!). - * @param collide {number} Whether the particles should be flagged as not 'dead' (non-colliding particles are higher performance). 0 means no collisions, 0-1 controls scale of particle's bounding box. - * - * @return This Emitter instance (nice for chaining stuff together, if you're into that). - */ - public makeParticles(graphics, quantity: number = 50, multiple: bool = false, collide: number = 0): Emitter { - - this.maxSize = quantity; - - var totalFrames: number = 1; - - /* - if(Multiple) - { - var sprite:Sprite = new Sprite(this._game); - sprite.loadGraphic(Graphics,true); - totalFrames = sprite.frames; - sprite.destroy(); - } - */ - - var randomFrame: number; - var particle: Particle; - var i: number = 0; - - while (i < quantity) - { - if (this.particleClass == null) - { - particle = new Particle(this._game); - } - else - { - particle = new this.particleClass(this._game); - } - - if (multiple) - { - /* - randomFrame = this._game.math.random()*totalFrames; - if(BakedRotations > 0) - particle.loadRotatedGraphic(Graphics,BakedRotations,randomFrame); - else - { - particle.loadGraphic(Graphics,true); - particle.frame = randomFrame; - } - */ - } - else - { - /* - if (BakedRotations > 0) - particle.loadRotatedGraphic(Graphics,BakedRotations); - else - particle.loadGraphic(Graphics); - */ - - if (graphics) - { - particle.loadGraphic(graphics); - } - - } - - if (collide > 0) - { - particle.allowCollisions = Collision.ANY; - particle.width *= collide; - particle.height *= collide; - //particle.centerOffsets(); - } - else - { - particle.allowCollisions = Collision.NONE; - } - - particle.exists = false; - - this.add(particle); - - i++; - } - - return this; - } - - /** - * Called automatically by the game loop, decides when to launch particles and when to "die". - */ - public update() { - - if (this.on) - { - if (this._explode) - { - this.on = false; - - var i: number = 0; - var l: number = this._quantity; - - if ((l <= 0) || (l > this.length)) - { - l = this.length; - } - - while (i < l) - { - this.emitParticle(); - i++; - } - - this._quantity = 0; - } - else - { - this._timer += this._game.time.elapsed; - - while ((this.frequency > 0) && (this._timer > this.frequency) && this.on) - { - this._timer -= this.frequency; - this.emitParticle(); - - if ((this._quantity > 0) && (++this._counter >= this._quantity)) - { - this.on = false; - this._quantity = 0; - } - } - } - } - - super.update(); - - } - - /** - * Call this function to turn off all the particles and the emitter. - */ - public kill() { - - this.on = false; - - super.kill(); - - } - - /** - * Call this function to start emitting particles. - * - * @param explode {boolean} Whether the particles should all burst out at once. - * @param lifespan {number} How long each particle lives once emitted. 0 = forever. - * @param frequency {number} Ignored if Explode is set to true. Frequency is how often to emit a particle. 0 = never emit, 0.1 = 1 particle every 0.1 seconds, 5 = 1 particle every 5 seconds. - * @param quantity {number} How many particles to launch. 0 = "all of the particles". - */ - public start(explode: bool = true, lifespan: number = 0, frequency: number = 0.1, quantity: number = 0) { - - this.revive(); - - this.visible = true; - this.on = true; - - this._explode = explode; - this.lifespan = lifespan; - this.frequency = frequency; - this._quantity += quantity; - - this._counter = 0; - this._timer = 0; - - } - - /** - * This function can be used both internally and externally to emit the next particle. - */ - public emitParticle() { - - var particle: Particle = this.recycle(Particle); - - particle.lifespan = this.lifespan; - particle.elasticity = this.bounce; - particle.reset(this.x - (particle.width >> 1) + this._game.math.random() * this.width, this.y - (particle.height >> 1) + this._game.math.random() * this.height); - particle.visible = true; - - if (this.minParticleSpeed.x != this.maxParticleSpeed.x) - { - particle.velocity.x = this.minParticleSpeed.x + this._game.math.random() * (this.maxParticleSpeed.x - this.minParticleSpeed.x); - } - else - { - particle.velocity.x = this.minParticleSpeed.x; - } - - if (this.minParticleSpeed.y != this.maxParticleSpeed.y) - { - particle.velocity.y = this.minParticleSpeed.y + this._game.math.random() * (this.maxParticleSpeed.y - this.minParticleSpeed.y); - } - else - { - particle.velocity.y = this.minParticleSpeed.y; - } - - particle.acceleration.y = this.gravity; - - if (this.minRotation != this.maxRotation && this.minRotation !== 0 && this.maxRotation !== 0) - { - particle.angularVelocity = this.minRotation + this._game.math.random() * (this.maxRotation - this.minRotation); - } - else - { - particle.angularVelocity = this.minRotation; - } - - if (particle.angularVelocity != 0) - { - particle.angle = this._game.math.random() * 360 - 180; - } - - particle.drag.x = this.particleDrag.x; - particle.drag.y = this.particleDrag.y; - particle.onEmit(); - - } - - /** - * A more compact way of setting the width and height of the emitter. - * - * @param width {number} The desired width of the emitter (particles are spawned randomly within these dimensions). - * @param height {number} The desired height of the emitter. - */ - public setSize(width: number, height: number) { - this.width = width; - this.height = height; - } - - /** - * A more compact way of setting the X velocity range of the emitter. - * - * @param Min {number} The minimum value for this range. - * @param Max {number} The maximum value for this range. - */ - public setXSpeed(min: number = 0, max: number = 0) { - this.minParticleSpeed.x = min; - this.maxParticleSpeed.x = max; - } - - /** - * A more compact way of setting the Y velocity range of the emitter. - * - * @param Min {number} The minimum value for this range. - * @param Max {number} The maximum value for this range. - */ - public setYSpeed(min: number = 0, max: number = 0) { - this.minParticleSpeed.y = min; - this.maxParticleSpeed.y = max; - } - - /** - * A more compact way of setting the angular velocity constraints of the emitter. - * - * @param Min {number} The minimum value for this range. - * @param Max {number} The maximum value for this range. - */ - public setRotation(min: number = 0, max: number = 0) { - this.minRotation = min; - this.maxRotation = max; - } - - /** - * Change the emitter's midpoint to match the midpoint of a Object. - * - * @param Object {object} The Object that you want to sync up with. - */ - public at(object) { - object.getMidpoint(this._point); - this.x = this._point.x - (this.width >> 1); - this.y = this._point.y - (this.height >> 1); - } - } - -} \ No newline at end of file diff --git a/wip/phaser clean up/Input.ts b/wip/phaser clean up/Input.ts deleted file mode 100644 index 75a23b04..00000000 --- a/wip/phaser clean up/Input.ts +++ /dev/null @@ -1,29 +0,0 @@ -/** -* Phaser - Components - Input -* -* -*/ - -module Phaser.Components { - - export class Input { - - // Input - //public inputEnabled: bool = false; - //private _inputOver: bool = false; - - //public onInputOver: Phaser.Signal; - //public onInputOut: Phaser.Signal; - //public onInputDown: Phaser.Signal; - //public onInputUp: Phaser.Signal; - - /** - * Update input. - */ - private updateInput() { - } - - - } - -} \ No newline at end of file diff --git a/wip/phaser clean up/Motion.ts b/wip/phaser clean up/Motion.ts deleted file mode 100644 index 6d91c688..00000000 --- a/wip/phaser clean up/Motion.ts +++ /dev/null @@ -1,409 +0,0 @@ -/// -/// - -/** -* Phaser - Motion -* -* The Motion class contains lots of useful functions for moving game objects around in world space. -*/ - -module Phaser { - - export class Motion { - - constructor(game: Game) { - - this._game = game; - - } - - private _game: Game; - - /** - * A tween-like function that takes a starting velocity and some other factors and returns an altered velocity. - * - * @param {number} Velocity Any component of velocity (e.g. 20). - * @param {number} Acceleration Rate at which the velocity is changing. - * @param {number} Drag Really kind of a deceleration, this is how much the velocity changes if Acceleration is not set. - * @param {number} Max An absolute value cap for the velocity. - * - * @return {number} The altered Velocity value. - */ - public computeVelocity(Velocity: number, Acceleration: number = 0, Drag: number = 0, Max: number = 10000): number { - - if (Acceleration !== 0) - { - Velocity += Acceleration * this._game.time.elapsed; - } - else if (Drag !== 0) - { - var drag: number = Drag * this._game.time.elapsed; - - if (Velocity - drag > 0) - { - Velocity = Velocity - drag; - } - else if (Velocity + drag < 0) - { - Velocity += drag; - } - else - { - Velocity = 0; - } - } - - if ((Velocity != 0) && (Max != 10000)) - { - if (Velocity > Max) - { - Velocity = Max; - } - else if (Velocity < -Max) - { - Velocity = -Max; - } - } - - return Velocity; - - } - - /** - * Given the angle and speed calculate the velocity and return it as a Point - * - * @param {number} angle The angle (in degrees) calculated in clockwise positive direction (down = 90 degrees positive, right = 0 degrees positive, up = 90 degrees negative) - * @param {number} speed The speed it will move, in pixels per second sq - * - * @return {Point} A Point where Point.x contains the velocity x value and Point.y contains the velocity y value - */ - public velocityFromAngle(angle: number, speed: number): Point { - - if (isNaN(speed)) - { - speed = 0; - } - - var a: number = this._game.math.degreesToRadians(angle); - - return new Point((Math.cos(a) * speed), (Math.sin(a) * speed)); - - } - - /** - * Sets the source Sprite x/y velocity so it will move directly towards the destination Sprite at the speed given (in pixels per second)
- * If you specify a maxTime then it will adjust the speed (over-writing what you set) so it arrives at the destination in that number of seconds.
- * Timings are approximate due to the way Flash timers work, and irrespective of SWF frame rate. Allow for a variance of +- 50ms.
- * The source object doesn't stop moving automatically should it ever reach the destination coordinates.
- * If you need the object to accelerate, see accelerateTowardsObject() instead - * Note: Doesn't take into account acceleration, maxVelocity or drag (if you set drag or acceleration too high this object may not move at all) - * - * @param {GameObject} source The Sprite on which the velocity will be set - * @param {GameObject} dest The Sprite where the source object will move to - * @param {number} speed The speed it will move, in pixels per second (default is 60 pixels/sec) - * @param {number} maxTime Time given in milliseconds (1000 = 1 sec). If set the speed is adjusted so the source will arrive at destination in the given number of ms - */ - public moveTowardsObject(source:GameObject, dest:GameObject, speed:number= 60, maxTime:number = 0) - { - var a:number = this.angleBetween(source, dest); - - if (maxTime > 0) - { - var d:number = this.distanceBetween(source, dest); - - // We know how many pixels we need to move, but how fast? - speed = d / (maxTime / 1000); - } - - source.velocity.x = Math.cos(a) * speed; - source.velocity.y = Math.sin(a) * speed; - - } - - /** - * Sets the x/y acceleration on the source Sprite so it will move towards the destination Sprite at the speed given (in pixels per second)
- * You must give a maximum speed value, beyond which the Sprite won't go any faster.
- * If you don't need acceleration look at moveTowardsObject() instead. - * - * @param {GameObject} source The Sprite on which the acceleration will be set - * @param {GameObject} dest The Sprite where the source object will move towards - * @param {number} speed The speed it will accelerate in pixels per second - * @param {number} xSpeedMax The maximum speed in pixels per second in which the sprite can move horizontally - * @param {number} ySpeedMax The maximum speed in pixels per second in which the sprite can move vertically - */ - public accelerateTowardsObject(source:GameObject, dest:GameObject, speed:number, xSpeedMax:number, ySpeedMax:number) - { - var a:number = this.angleBetween(source, dest); - - source.velocity.x = 0; - source.velocity.y = 0; - - source.acceleration.x = Math.cos(a) * speed; - source.acceleration.y = Math.sin(a) * speed; - - source.maxVelocity.x = xSpeedMax; - source.maxVelocity.y = ySpeedMax; - - } - - /** - * Move the given Sprite towards the mouse pointer coordinates at a steady velocity - * If you specify a maxTime then it will adjust the speed (over-writing what you set) so it arrives at the destination in that number of seconds.
- * Timings are approximate due to the way Flash timers work, and irrespective of SWF frame rate. Allow for a variance of +- 50ms.
- * The source object doesn't stop moving automatically should it ever reach the destination coordinates.
- * - * @param {GameObject} source The Sprite to move - * @param {number} speed The speed it will move, in pixels per second (default is 60 pixels/sec) - * @param {number} maxTime Time given in milliseconds (1000 = 1 sec). If set the speed is adjusted so the source will arrive at destination in the given number of ms - */ - public moveTowardsMouse(source:GameObject, speed:number = 60, maxTime:number = 0) - { - var a:number = this.angleBetweenMouse(source); - - if (maxTime > 0) - { - var d:number = this.distanceToMouse(source); - - // We know how many pixels we need to move, but how fast? - speed = d / (maxTime / 1000); - } - - source.velocity.x = Math.cos(a) * speed; - source.velocity.y = Math.sin(a) * speed; - - } - - /** - * Sets the x/y acceleration on the source Sprite so it will move towards the mouse coordinates at the speed given (in pixels per second)
- * You must give a maximum speed value, beyond which the Sprite won't go any faster.
- * If you don't need acceleration look at moveTowardsMouse() instead. - * - * @param {GameObject} source The Sprite on which the acceleration will be set - * @param {number} speed The speed it will accelerate in pixels per second - * @param {number} xSpeedMax The maximum speed in pixels per second in which the sprite can move horizontally - * @param {number} ySpeedMax The maximum speed in pixels per second in which the sprite can move vertically - */ - public accelerateTowardsMouse(source:GameObject, speed:number, xSpeedMax:number, ySpeedMax:number) - { - var a:number = this.angleBetweenMouse(source); - - source.velocity.x = 0; - source.velocity.y = 0; - - source.acceleration.x = Math.cos(a) * speed; - source.acceleration.y = Math.sin(a) * speed; - - source.maxVelocity.x = xSpeedMax; - source.maxVelocity.y = ySpeedMax; - } - - /** - * Sets the x/y velocity on the source Sprite so it will move towards the target coordinates at the speed given (in pixels per second)
- * If you specify a maxTime then it will adjust the speed (over-writing what you set) so it arrives at the destination in that number of seconds.
- * Timings are approximate due to the way Flash timers work, and irrespective of SWF frame rate. Allow for a variance of +- 50ms.
- * The source object doesn't stop moving automatically should it ever reach the destination coordinates.
- * - * @param {GameObject} source The Sprite to move - * @param {Point} target The Point coordinates to move the source Sprite towards - * @param {number} speed The speed it will move, in pixels per second (default is 60 pixels/sec) - * @param {number} maxTime Time given in milliseconds (1000 = 1 sec). If set the speed is adjusted so the source will arrive at destination in the given number of ms - */ - public moveTowardsPoint(source:GameObject, target:Point, speed:number = 60, maxTime:number = 0) - { - var a:number = this.angleBetweenPoint(source, target); - - if (maxTime > 0) - { - var d:number = this.distanceToPoint(source, target); - - // We know how many pixels we need to move, but how fast? - speed = d / (maxTime / 1000); - } - - source.velocity.x = Math.cos(a) * speed; - source.velocity.y = Math.sin(a) * speed; - } - - /** - * Sets the x/y acceleration on the source Sprite so it will move towards the target coordinates at the speed given (in pixels per second)
- * You must give a maximum speed value, beyond which the Sprite won't go any faster.
- * If you don't need acceleration look at moveTowardsPoint() instead. - * - * @param {GameObject} source The Sprite on which the acceleration will be set - * @param {Point} target The Point coordinates to move the source Sprite towards - * @param {number} speed The speed it will accelerate in pixels per second - * @param {number} xSpeedMax The maximum speed in pixels per second in which the sprite can move horizontally - * @param {number} ySpeedMax The maximum speed in pixels per second in which the sprite can move vertically - */ - public accelerateTowardsPoint(source:GameObject, target:Point, speed:number, xSpeedMax:number, ySpeedMax:number) - { - var a:number = this.angleBetweenPoint(source, target); - - source.velocity.x = 0; - source.velocity.y = 0; - - source.acceleration.x = Math.cos(a) * speed; - source.acceleration.y = Math.sin(a) * speed; - - source.maxVelocity.x = xSpeedMax; - source.maxVelocity.y = ySpeedMax; - } - - /** - * Find the distance (in pixels, rounded) between two Sprites, taking their origin into account - * - * @param {GameObject} a The first Sprite - * @param {GameObject} b The second Sprite - * @return {number} int Distance (in pixels) - */ - public distanceBetween(a:GameObject, b:GameObject):number - { - var dx:number = (a.x + a.origin.x) - (b.x + b.origin.x); - var dy:number = (a.y + a.origin.y) - (b.y + b.origin.y); - - return this._game.math.vectorLength(dx, dy); - - } - - /** - * Find the distance (in pixels, rounded) from an Sprite to the given Point, taking the source origin into account - * - * @param {GameObject} a The Sprite - * @param {Point} target The Point - * @return {number} Distance (in pixels) - */ - public distanceToPoint(a:GameObject, target:Point):number - { - var dx:number = (a.x + a.origin.x) - (target.x); - var dy:number = (a.y + a.origin.y) - (target.y); - - return this._game.math.vectorLength(dx, dy); - } - - /** - * Find the distance (in pixels, rounded) from the object x/y and the mouse x/y - * - * @param {GameObject} a Sprite to test against - * @return {number} The distance between the given sprite and the mouse coordinates - */ - public distanceToMouse(a:GameObject):number - { - var dx: number = (a.x + a.origin.x) - this._game.input.x; - var dy: number = (a.y + a.origin.y) - this._game.input.y; - - return this._game.math.vectorLength(dx, dy); - } - - /** - * Find the angle (in radians) between an Sprite and an Point. The source sprite takes its x/y and origin into account. - * The angle is calculated in clockwise positive direction (down = 90 degrees positive, right = 0 degrees positive, up = 90 degrees negative) - * - * @param {GameObject} a The Sprite to test from - * @param {Point} target The Point to angle the Sprite towards - * @param {boolean} asDegrees If you need the value in degrees instead of radians, set to true - * - * @return {number} The angle (in radians unless asDegrees is true) - */ - public angleBetweenPoint(a:GameObject, target:Point, asDegrees:bool = false):number - { - var dx:number = (target.x) - (a.x + a.origin.x); - var dy:number = (target.y) - (a.y + a.origin.y); - - if (asDegrees) - { - return this._game.math.radiansToDegrees(Math.atan2(dy, dx)); - } - else - { - return Math.atan2(dy, dx); - } - } - - /** - * Find the angle (in radians) between the two Sprite, taking their x/y and origin into account. - * The angle is calculated in clockwise positive direction (down = 90 degrees positive, right = 0 degrees positive, up = 90 degrees negative) - * - * @param {GameObject} a The Sprite to test from - * @param {GameObject} b The Sprite to test to - * @param {boolean} asDegrees If you need the value in degrees instead of radians, set to true - * - * @return {number} The angle (in radians unless asDegrees is true) - */ - public angleBetween(a:GameObject, b:GameObject, asDegrees:bool = false):number - { - var dx:number = (b.x + b.origin.x) - (a.x + a.origin.x); - var dy:number = (b.y + b.origin.y) - (a.y + a.origin.y); - - if (asDegrees) - { - return this._game.math.radiansToDegrees(Math.atan2(dy, dx)); - } - else - { - return Math.atan2(dy, dx); - } - } - - /** - * Given the GameObject and speed calculate the velocity and return it as an Point based on the direction the sprite is facing - * - * @param {GameObject} parent The Sprite to get the facing value from - * @param {number} speed The speed it will move, in pixels per second sq - * - * @return {Point} An Point where Point.x contains the velocity x value and Point.y contains the velocity y value - */ - public velocityFromFacing(parent:GameObject, speed:number):Point - { - var a:number; - - if (parent.facing == Collision.LEFT) - { - a = this._game.math.degreesToRadians(180); - } - else if (parent.facing == Collision.RIGHT) - { - a = this._game.math.degreesToRadians(0); - } - else if (parent.facing == Collision.UP) - { - a = this._game.math.degreesToRadians(-90); - } - else if (parent.facing == Collision.DOWN) - { - a = this._game.math.degreesToRadians(90); - } - - return new Point(Math.cos(a) * speed, Math.sin(a) * speed); - - } - - /** - * Find the angle (in radians) between an Sprite and the mouse, taking their x/y and origin into account. - * The angle is calculated in clockwise positive direction (down = 90 degrees positive, right = 0 degrees positive, up = 90 degrees negative) - * - * @param {GameObject} a The Object to test from - * @param {boolean} asDegrees If you need the value in degrees instead of radians, set to true - * - * @return {number} The angle (in radians unless asDegrees is true) - */ - public angleBetweenMouse(a:GameObject, asDegrees:bool = false):number - { - // In order to get the angle between the object and mouse, we need the objects screen coordinates (rather than world coordinates) - var p:MicroPoint = a.getScreenXY(); - - var dx:number = a._game.input.x - p.x; - var dy:number = a._game.input.y - p.y; - - if (asDegrees) - { - return this._game.math.radiansToDegrees(Math.atan2(dy, dx)); - } - else - { - return Math.atan2(dy, dx); - } - } - - } - -} diff --git a/wip/phaser clean up/OldSprite.ts b/wip/phaser clean up/OldSprite.ts deleted file mode 100644 index 1e28f513..00000000 --- a/wip/phaser clean up/OldSprite.ts +++ /dev/null @@ -1,355 +0,0 @@ -/// -/// -/// -/// - -/** -* Phaser - Sprite -* -* The Sprite GameObject is an extension of the core GameObject that includes support for animation and dynamic textures. -* It's probably the most used GameObject of all. -*/ - -module Phaser { - - export class OldSprite { - - /** - * Sprite constructor - * Create a new Sprite. - * - * @param game {Phaser.Game} Current game instance. - * @param [x] {number} the initial x position of the sprite. - * @param [y] {number} the initial y position of the sprite. - * @param [key] {string} Key of the graphic you want to load for this sprite. - * @param [width] {number} The width of the object. - * @param [height] {number} The height of the object. - */ - constructor(game: Game, x?: number = 0, y?: number = 0, key?: string = null, width?: number = 16, height?: number = 16) { - - this.canvas = game.stage.canvas; - this.context = game.stage.context; - - this.frameBounds = new Rectangle(x, y, width, height); - this.exists = true; - this.active = true; - this.visible = true; - this.alive = true; - this.isGroup = false; - this.alpha = 1; - this.scale = new MicroPoint(1, 1); - - this.last = new MicroPoint(x, y); - this.align = GameObject.ALIGN_TOP_LEFT; - this.mass = 1; - this.elasticity = 0; - this.health = 1; - this.immovable = false; - this.moves = true; - this.worldBounds = null; - - this.touching = Collision.NONE; - this.wasTouching = Collision.NONE; - this.allowCollisions = Collision.ANY; - - this.velocity = new MicroPoint(); - this.acceleration = new MicroPoint(); - this.drag = new MicroPoint(); - this.maxVelocity = new MicroPoint(10000, 10000); - - this.angle = 0; - this.angularVelocity = 0; - this.angularAcceleration = 0; - this.angularDrag = 0; - this.maxAngular = 10000; - - this.cameraBlacklist = []; - this.scrollFactor = new MicroPoint(1, 1); - this.collisionMask = new CollisionMask(game, this, x, y, width, height); - - - this._texture = null; - - this.animations = new AnimationManager(this._game, this); - - if (key !== null) - { - this.cacheKey = key; - this.loadGraphic(key); - } - else - { - this.frameBounds.width = 16; - this.frameBounds.height = 16; - } - - } - - /** - * The essential reference to the main game object - */ - public _game: Game; - - /** - * Controls whether update() is automatically called by State/Group. - */ - public active: bool; - - /** - * Controls whether draw() is automatically called by State/Group. - */ - public visible: bool; - - /** - * Setting this to true will prevent the object from being updated during the main game loop (you will have to call update on it yourself) - */ - public ignoreGlobalUpdate: bool; - - /** - * Setting this to true will prevent the object from being rendered during the main game loop (you will have to call render on it yourself) - */ - public ignoreGlobalRender: bool; - - /** - * An Array of Cameras to which this GameObject won't render - * @type {Array} - */ - public cameraBlacklist: number[]; - - /** - * Orientation of the object. - * @type {number} - */ - public facing: number; - - /** - * Set alpha to a number between 0 and 1 to change the opacity. - * @type {number} - */ - public alpha: number; - - /** - * Scale factor of the object. - * @type {MicroPoint} - */ - public scale: MicroPoint; - - /** - * Controls if the GameObject is rendered rotated or not. - * If renderRotation is false then the object can still rotate but it will never be rendered rotated. - * @type {boolean} - */ - public renderRotation: bool = true; - - /** - * A point that can store numbers from 0 to 1 (for X and Y independently) - * which governs how much this object is affected by the camera . - * @type {MicroPoint} - */ - public scrollFactor: MicroPoint; - - /** - * Rectangle container of this object. - * @type {Rectangle} - */ - public frameBounds: Rectangle; - - /** - * This objects CollisionMask - * @type {CollisionMask} - */ - public collisionMask: CollisionMask; - - /** - * A rectangular area which is object is allowed to exist within. If it travels outside of this area it will perform the outOfBoundsAction. - * @type {Quad} - */ - public worldBounds: Quad; - - /** - * A reference to the Canvas this GameObject will render to - * @type {HTMLCanvasElement} - */ - public canvas: HTMLCanvasElement; - - /** - * A reference to the Canvas Context2D this GameObject will render to - * @type {CanvasRenderingContext2D} - */ - public context: CanvasRenderingContext2D; - - /** - * Texture of this sprite to be rendered. - */ - private _texture; - - /** - * Texture of this sprite is DynamicTexture? (default to false) - * @type {boolean} - */ - private _dynamicTexture: bool = false; - - - /** - * This manages animations of the sprite. You can modify animations though it. (see AnimationManager) - * @type AnimationManager - */ - public animations: AnimationManager; - - /** - * The cache key that was used for this texture (if any) - */ - public cacheKey: string; - - - /** - * Flip the graphic horizontally? (defaults to false) - * @type {boolean} - */ - public flipped: bool = false; - - - - /** - * Pre-update is called right before update() on each object in the game loop. - */ - public preUpdate() { - - this.last.x = this.frameBounds.x; - this.last.y = this.frameBounds.y; - - this.collisionMask.preUpdate(); - - } - - /** - * Override this function to update your class's position and appearance. - */ - public update() { - } - - /** - * Automatically called after update() by the game loop. - */ - public postUpdate() { - - this.animations.update(); - - if (this.moves) - { - this.updateMotion(); - } - - if (this.worldBounds != null) - { - if (this.outOfBoundsAction == GameObject.OUT_OF_BOUNDS_KILL) - { - if (this.x < this.worldBounds.x || this.x > this.worldBounds.right || this.y < this.worldBounds.y || this.y > this.worldBounds.bottom) - { - this.kill(); - } - } - else - { - if (this.x < this.worldBounds.x) - { - this.x = this.worldBounds.x; - } - else if (this.x > this.worldBounds.right) - { - this.x = this.worldBounds.right; - } - - if (this.y < this.worldBounds.y) - { - this.y = this.worldBounds.y; - } - else if (this.y > this.worldBounds.bottom) - { - this.y = this.worldBounds.bottom; - } - } - } - - this.collisionMask.update(); - - if (this.inputEnabled) - { - this.updateInput(); - } - - this.wasTouching = this.touching; - this.touching = Collision.NONE; - - } - - /** - * Clean up memory. - */ - public destroy() { - } - - public set width(value:number) { - this.frameBounds.width = value; - } - - public set height(value:number) { - this.frameBounds.height = value; - } - - public get width(): number { - return this.frameBounds.width; - } - - public get height(): number { - return this.frameBounds.height; - } - - public set frame(value: number) { - this.animations.frame = value; - } - - public get frame(): number { - return this.animations.frame; - } - - public set frameName(value: string) { - this.animations.frameName = value; - } - - public get frameName(): string { - return this.animations.frameName; - } - - /** - * Handy for "killing" game objects. - * Default behavior is to flag them as nonexistent AND dead. - * However, if you want the "corpse" to remain in the game, - * like to animate an effect or whatever, you should override this, - * setting only alive to false, and leaving exists true. - */ - public kill() { - this.alive = false; - this.exists = false; - } - - /** - * Handy for bringing game objects "back to life". Just sets alive and exists back to true. - * In practice, this is most often called by Object.reset(). - */ - public revive() { - this.alive = true; - this.exists = true; - } - - /** - * Convert object to readable string name. Useful for debugging, save games, etc. - */ - public toString(): string { - return ""; - } - - - } - -} \ No newline at end of file diff --git a/wip/phaser clean up/PixelUtils.ts b/wip/phaser clean up/PixelUtils.ts deleted file mode 100644 index dfd1f8d3..00000000 --- a/wip/phaser clean up/PixelUtils.ts +++ /dev/null @@ -1,49 +0,0 @@ -/// -/// -/// -/// - -/** -* Phaser - PixelUtils -* -* A collection of methods useful for manipulating pixels. -*/ - -module Phaser { - - export class PixelUtils { - - static boot() { - - PixelUtils.pixelCanvas = document.createElement('canvas'); - PixelUtils.pixelCanvas.width = 1; - PixelUtils.pixelCanvas.height = 1; - PixelUtils.pixelContext = PixelUtils.pixelCanvas.getContext('2d'); - - } - - /** - * Canvas element used in 1x1 pixel checks. - * @type {HTMLCanvasElement} - */ - static pixelCanvas: HTMLCanvasElement; - - /** - * Render context of pixelCanvas - * @type {CanvasRenderingContext2D} - */ - static pixelContext: CanvasRenderingContext2D; - - static getPixel(key: string, x: number, y: number): number { - - // write out a single pixel (won't help with rotated sprites though.. hmm) - - var imageData = PixelUtils.pixelContext.getImageData(0, 0, 1, 1); - - return ColorUtils.getColor32(imageData.data[3], imageData.data[0], imageData.data[1], imageData.data[2]); - - } - - } - -} \ No newline at end of file diff --git a/wip/phaser clean up/Properties.ts b/wip/phaser clean up/Properties.ts deleted file mode 100644 index 3171afe9..00000000 --- a/wip/phaser clean up/Properties.ts +++ /dev/null @@ -1,36 +0,0 @@ -/** -* Phaser - Components - Properties -* -* -*/ - -module Phaser.Components { - - export class Properties { - - /** - * Handy for storing health percentage or armor points or whatever. - * @type {number} - */ - public health: number; - - /** - * Reduces the "health" variable of this sprite by the amount specified in Damage. - * Calls kill() if health drops to or below zero. - * - * @param Damage {number} How much health to take away (use a negative number to give a health bonus). - */ - public hurt(damage: number) { - - this.health = this.health - damage; - - if (this.health <= 0) - { - //this.kill(); - } - - } - - } - -} \ No newline at end of file diff --git a/wip/phaser clean up/Tilemap.ts b/wip/phaser clean up/Tilemap.ts deleted file mode 100644 index 746d7df5..00000000 --- a/wip/phaser clean up/Tilemap.ts +++ /dev/null @@ -1,434 +0,0 @@ -/// -/// -/// - -/** -* Phaser - Tilemap -* -* This GameObject allows for the display of a tilemap within the game world. Tile maps consist of an image, tile data and a size. -* Internally it creates a TilemapLayer for each layer in the tilemap. -*/ - -module Phaser { - - export class Tilemap { - - /** - * Tilemap constructor - * Create a new Tilemap. - * - * @param game {Phaser.Game} Current game instance. - * @param key {string} Asset key for this map. - * @param mapData {string} Data of this map. (a big 2d array, normally in csv) - * @param format {number} Format of this map data, available: Tilemap.FORMAT_CSV or Tilemap.FORMAT_TILED_JSON. - * @param resizeWorld {boolean} Resize the world bound automatically based on this tilemap? - * @param tileWidth {number} Width of tiles in this map. - * @param tileHeight {number} Height of tiles in this map. - */ - constructor(game: Game, key: string, mapData: string, format: number, resizeWorld: bool = true, tileWidth?: number = 0, tileHeight?: number = 0) { - - //super(game); - - this.isGroup = false; - - this.tiles = []; - this.layers = []; - - this.mapFormat = format; - - switch (format) - { - case Tilemap.FORMAT_CSV: - this.parseCSV(game.cache.getText(mapData), key, tileWidth, tileHeight); - break; - - case Tilemap.FORMAT_TILED_JSON: - this.parseTiledJSON(game.cache.getText(mapData), key); - break; - } - - if (this.currentLayer && resizeWorld) - { - this._game.world.setSize(this.currentLayer.widthInPixels, this.currentLayer.heightInPixels, true); - } - - } - - private _tempCollisionData; - - /** - * Tilemap data format enum: CSV. - * @type {number} - */ - public static FORMAT_CSV: number = 0; - /** - * Tilemap data format enum: Tiled JSON. - * @type {number} - */ - public static FORMAT_TILED_JSON: number = 1; - - /** - * Array contains tile objects of this map. - * @type {Tile[]} - */ - public tiles : Tile[]; - /** - * Array contains tilemap layer objects of this map. - * @type {TilemapLayer[]} - */ - public layers : TilemapLayer[]; - /** - * Current tilemap layer. - * @type {TilemapLayer} - */ - public currentLayer: TilemapLayer; - /** - * The tilemap layer for collision. - * @type {TilemapLayer} - */ - public collisionLayer: TilemapLayer; - /** - * Tilemap collision callback. - * @type {function} - */ - public collisionCallback = null; - /** - * Context for the collision callback called with. - */ - public collisionCallbackContext; - /** - * Format of this tilemap data. Available values: Tilemap.FORMAT_CSV or Tilemap.FORMAT_TILED_JSON. - * @type {number} - */ - public mapFormat: number; - - /** - * Inherited update method. - */ - public update() { - } - - /** - * Render this tilemap to a specific camera with specific offset. - * @param camera {Camera} The camera this tilemap will be rendered to. - * @param cameraOffsetX {number} X offset of the camera. - * @param cameraOffsetY {number} Y offset of the camera. - */ - public render(camera: Camera, cameraOffsetX: number, cameraOffsetY: number) { - - if (this.cameraBlacklist.indexOf(camera.ID) == -1) - { - // Loop through the layers - for (var i = 0; i < this.layers.length; i++) - { - this.layers[i].render(camera, cameraOffsetX, cameraOffsetY); - } - } - - } - - /** - * Parset csv map data and generate tiles. - * @param data {string} CSV map data. - * @param key {string} Asset key for tileset image. - * @param tileWidth {number} Width of its tile. - * @param tileHeight {number} Height of its tile. - */ - private parseCSV(data: string, key: string, tileWidth: number, tileHeight: number) { - - var layer: TilemapLayer = new TilemapLayer(this._game, this, key, Tilemap.FORMAT_CSV, 'TileLayerCSV' + this.layers.length.toString(), tileWidth, tileHeight); - - // Trim any rogue whitespace from the data - data = data.trim(); - - var rows = data.split("\n"); - - for (var i = 0; i < rows.length; i++) - { - var column = rows[i].split(","); - - if (column.length > 0) - { - layer.addColumn(column); - } - } - - layer.updateBounds(); - var tileQuantity = layer.parseTileOffsets(); - - this.currentLayer = layer; - this.collisionLayer = layer; - - this.layers.push(layer); - - this.generateTiles(tileQuantity); - - } - - /** - * Parset JSON map data and generate tiles. - * @param data {string} JSON map data. - * @param key {string} Asset key for tileset image. - */ - private parseTiledJSON(data: string, key: string) { - - // Trim any rogue whitespace from the data - data = data.trim(); - - var json = JSON.parse(data); - - for (var i = 0; i < json.layers.length; i++) - { - var layer: TilemapLayer = new TilemapLayer(this._game, this, key, Tilemap.FORMAT_TILED_JSON, json.layers[i].name, json.tilewidth, json.tileheight); - - layer.alpha = json.layers[i].opacity; - layer.visible = json.layers[i].visible; - layer.tileMargin = json.tilesets[0].margin; - layer.tileSpacing = json.tilesets[0].spacing; - - var c = 0; - var row; - - for (var t = 0; t < json.layers[i].data.length; t++) - { - if (c == 0) - { - row = []; - } - - row.push(json.layers[i].data[t]); - - c++; - - if (c == json.layers[i].width) - { - layer.addColumn(row); - c = 0; - } - } - - layer.updateBounds(); - - var tileQuantity = layer.parseTileOffsets(); - - this.currentLayer = layer; - this.collisionLayer = layer; - - this.layers.push(layer); - - } - - this.generateTiles(tileQuantity); - - } - - /** - * Create tiles of given quantity. - * @param qty {number} Quentity of tiles to be generated. - */ - private generateTiles(qty:number) { - - for (var i = 0; i < qty; i++) - { - this.tiles.push(new Tile(this._game, this, i, this.currentLayer.tileWidth, this.currentLayer.tileHeight)); - } - - } - - public get widthInPixels(): number { - return this.currentLayer.widthInPixels; - } - - public get heightInPixels(): number { - return this.currentLayer.heightInPixels; - } - - // Tile Collision - - /** - * Set callback to be called when this tilemap collides. - * @param context {object} Callback will be called with this context. - * @param callback {function} Callback function. - */ - public setCollisionCallback(context, callback) { - - this.collisionCallbackContext = context; - this.collisionCallback = callback; - - } - - /** - * Set collision configs of tiles in a range index. - * @param start {number} First index of tiles. - * @param end {number} Last index of tiles. - * @param collision {number} Bit field of flags. (see Tile.allowCollision) - * @param resetCollisions {boolean} Reset collision flags before set. - * @param separateX {boolean} Enable seprate at x-axis. - * @param separateY {boolean} Enable seprate at y-axis. - */ - public setCollisionRange(start: number, end: number, collision?:number = Collision.ANY, resetCollisions?: bool = false, separateX?: bool = true, separateY?: bool = true) { - - for (var i = start; i < end; i++) - { - this.tiles[i].setCollision(collision, resetCollisions, separateX, separateY); - } - - } - - /** - * Set collision configs of tiles with given index. - * @param values {number[]} Index array which contains all tile indexes. The tiles with those indexes will be setup with rest parameters. - * @param collision {number} Bit field of flags. (see Tile.allowCollision) - * @param resetCollisions {boolean} Reset collision flags before set. - * @param separateX {boolean} Enable seprate at x-axis. - * @param separateY {boolean} Enable seprate at y-axis. - */ - public setCollisionByIndex(values:number[], collision?:number = Collision.ANY, resetCollisions?: bool = false, separateX?: bool = true, separateY?: bool = true) { - - for (var i = 0; i < values.length; i++) - { - this.tiles[values[i]].setCollision(collision, resetCollisions, separateX, separateY); - } - - } - - // Tile Management - - /** - * Get the tile by its index. - * @param value {number} Index of the tile you want to get. - * @return {Tile} The tile with given index. - */ - public getTileByIndex(value: number):Tile { - - if (this.tiles[value]) - { - return this.tiles[value]; - } - - return null; - - } - - /** - * Get the tile located at specific position and layer. - * @param x {number} X position of this tile located. - * @param y {number} Y position of this tile located. - * @param [layer] {number} layer of this tile located. - * @return {Tile} The tile with specific properties. - */ - public getTile(x: number, y: number, layer?: number = 0):Tile { - - return this.tiles[this.layers[layer].getTileIndex(x, y)]; - - } - - /** - * Get the tile located at specific position (in world coordinate) and layer. (thus you give a position of a point which is within the tile) - * @param x {number} X position of the point in target tile. - * @param x {number} Y position of the point in target tile. - * @param [layer] {number} layer of this tile located. - * @return {Tile} The tile with specific properties. - */ - public getTileFromWorldXY(x: number, y: number, layer?: number = 0):Tile { - - return this.tiles[this.layers[layer].getTileFromWorldXY(x, y)]; - - } - - public getTileFromInputXY(layer?: number = 0):Tile { - - return this.tiles[this.layers[layer].getTileFromWorldXY(this._game.input.getWorldX(), this._game.input.getWorldY())]; - - } - - /** - * Get tiles overlaps the given object. - * @param object {GameObject} Tiles you want to get that overlaps this. - * @return {array} Array with tiles informations. (Each contains x, y and the tile.) - */ - public getTileOverlaps(object: GameObject) { - - return this.currentLayer.getTileOverlaps(object); - - } - - // COLLIDE - /** - * Check whether this tilemap collides with the given game object or group of objects. - * @param objectOrGroup {function} Target object of group you want to check. - * @param callback {function} This is called if objectOrGroup collides the tilemap. - * @param context {object} Callback will be called with this context. - * @return {boolean} Return true if this collides with given object, otherwise return false. - */ - public collide(objectOrGroup = null, callback = null, context = null) { - - if (callback !== null && context !== null) - { - this.collisionCallback = callback; - this.collisionCallbackContext = context; - } - - if (objectOrGroup == null) - { - objectOrGroup = this._game.world.group; - } - - // Group? - if (objectOrGroup.isGroup == false) - { - this.collideGameObject(objectOrGroup); - } - else - { - objectOrGroup.forEachAlive(this, this.collideGameObject, true); - } - - } - - /** - * Check whether this tilemap collides with the given game object. - * @param object {GameObject} Target object you want to check. - * @return {boolean} Return true if this collides with given object, otherwise return false. - */ - public collideGameObject(object: GameObject): bool { - - if (object !== this && object.immovable == false && object.exists == true && object.allowCollisions != Collision.NONE) - { - this._tempCollisionData = this.collisionLayer.getTileOverlaps(object); - - if (this.collisionCallback !== null && this._tempCollisionData.length > 0) - { - this.collisionCallback.call(this.collisionCallbackContext, object, this._tempCollisionData); - } - - return true; - } - else - { - return false; - } - - } - - /** - * Set a tile to a specific layer. - * @param x {number} X position of this tile. - * @param y {number} Y position of this tile. - * @param index {number} The index of this tile type in the core map data. - * @param [layer] {number} which layer you want to set the tile to. - */ - public putTile(x: number, y: number, index: number, layer?: number = 0) { - - this.layers[layer].putTile(x, y, index); - - } - - // Set current layer - // Set layer order? - // Delete tiles of certain type - // Erase tiles - - } - -} \ No newline at end of file diff --git a/wip/phaser clean up/aabb 1.ts b/wip/phaser clean up/aabb 1.ts deleted file mode 100644 index df5ac64b..00000000 --- a/wip/phaser clean up/aabb 1.ts +++ /dev/null @@ -1,64 +0,0 @@ -/// - -(function () { - - var game = new Phaser.Game(this, 'game', 800, 600, init, create, update, render); - - function init() { - - // Using Phasers asset loader we load up a PNG from the assets folder - game.load.image('atari', 'assets/sprites/atari800xl.png'); - game.load.start(); - - } - - var atari: Phaser.Sprite; - - function create() { - - atari = game.add.sprite(200, 300, 'atari'); - atari.texture.alpha = 0.5; - //atari.scale.setTo(1.5, 1.5); - - //atari.physics.shape.setSize(150, 50); - //atari.physics.shape.offset.setTo(50, 25); - - //atari.physics.gravity.setTo(0, 2); - atari.body.bounce.setTo(0.7, 0.7); - atari.body.drag.setTo(10, 10); - - } - - function update() { - - atari.body.acceleration.x = 0; - atari.body.acceleration.y = 0; - - if (game.input.keyboard.isDown(Phaser.Keyboard.LEFT)) - { - atari.body.acceleration.x = -150; - } - else if (game.input.keyboard.isDown(Phaser.Keyboard.RIGHT)) - { - atari.body.acceleration.x = 150; - } - - if (game.input.keyboard.isDown(Phaser.Keyboard.UP)) - { - atari.body.acceleration.y = -150; - } - else if (game.input.keyboard.isDown(Phaser.Keyboard.DOWN)) - { - atari.body.acceleration.y = 150; - } - - } - - function render() { - - atari.body.renderDebugInfo(16, 16); - Phaser.DebugUtils.renderSpritePhysicsBody(atari); - - } - -})(); diff --git a/wip/phaser clean up/aabb vs aabb 1.ts b/wip/phaser clean up/aabb vs aabb 1.ts deleted file mode 100644 index bbaf3f5e..00000000 --- a/wip/phaser clean up/aabb vs aabb 1.ts +++ /dev/null @@ -1,81 +0,0 @@ -/// - -(function () { - - var game = new Phaser.Game(this, 'game', 800, 600, init, create, update, render); - - function init() { - - // Using Phasers asset loader we load up a PNG from the assets folder - game.load.image('atari', 'assets/sprites/atari800xl.png'); - game.load.image('card', 'assets/sprites/mana_card.png'); - game.load.start(); - - } - - var atari: Phaser.Sprite; - var card: Phaser.Sprite; - - function create() { - - //atari = game.add.sprite(350, 100, 'atari'); - //atari = game.add.sprite(350, 500, 'atari'); - atari = game.add.sprite(0, 310, 'atari'); - card = game.add.sprite(400, 300, 'card'); - - //card.body.immovable = true; - - //atari.texture.alpha = 0.5; - //atari.scale.setTo(1.5, 1.5); - - //atari.body.shape.setSize(150, 50); - //atari.body.shape.offset.setTo(50, 25); - - //atari.body.gravity.setTo(0, 2); - atari.body.bounce.setTo(1, 1); - //atari.body.drag.setTo(10, 10); - - card.body.bounce.setTo(0.7, 0.7); - //card.body.velocity.x = -50; - - } - - function update() { - - atari.body.acceleration.x = 0; - atari.body.acceleration.y = 0; - - if (game.input.keyboard.isDown(Phaser.Keyboard.LEFT)) - { - atari.body.acceleration.x = -150; - } - else if (game.input.keyboard.isDown(Phaser.Keyboard.RIGHT)) - { - atari.body.acceleration.x = 150; - } - - if (game.input.keyboard.isDown(Phaser.Keyboard.UP)) - { - atari.body.acceleration.y = -150; - } - else if (game.input.keyboard.isDown(Phaser.Keyboard.DOWN)) - { - atari.body.acceleration.y = 150; - } - - // collide? - game.collide(atari, card); - - } - - function render() { - - atari.body.renderDebugInfo(16, 16); - card.body.renderDebugInfo(200, 16); - - Phaser.DebugUtils.renderSpritePhysicsBody(atari); - Phaser.DebugUtils.renderSpritePhysicsBody(card); - - } - -})(); diff --git a/wip/phaser clean up/body1.ts b/wip/phaser clean up/body1.ts deleted file mode 100644 index 86cb69df..00000000 --- a/wip/phaser clean up/body1.ts +++ /dev/null @@ -1,206 +0,0 @@ -/// -/// -/// -/// - -(function () { - - var game = new Phaser.Game(this, 'game', 800, 600, init, create, update, render); - - function init() { - - game.load.image('xatari', 'assets/sprites/atari800xl.png'); - game.load.image('card', 'assets/sprites/mana_card.png'); - game.load.image('atari', 'assets/sprites/shinyball.png'); - game.load.start(); - - } - - var debug: HTMLTextAreaElement; - var atari: Phaser.Sprite; - var card: Phaser.Sprite; - var physics: Phaser.Physics.Advanced.Manager; - var circle: Phaser.Physics.Advanced.Body; - var walls: Phaser.Physics.Advanced.Body; - var t: Phaser.Physics.Advanced.Body; - - function create() { - - //atari = game.add.sprite(200, 100, 'atari'); - - // need to get the physics bounds around the sprite center, regardless of origin - //atari.transform.origin.setTo(0.5, 0.5); - //card = game.add.sprite(500, 300, 'card'); - - physics = new Phaser.Physics.Advanced.Manager(game); - //Phaser.Physics.Advanced.Manager.debug = debug; - - walls = new Phaser.Physics.Advanced.Body(null, Phaser.Types.BODY_STATIC, 0, 0); - walls.game = game; - - //walls.addBox(250, 200, 500, 20, 0, 1, 1); - - //staticBody.addShape(new ShapeBox(0, 0.2, 20.48, 0.4)); - - // * 0.02 p2m - // * 50 m2p - - walls.addBox(0, 500, 1024, 20, 0, 1, 1); - - //walls.transform.setRotation(game.math.degreesToRadians(4)); - //walls.fixedRotation = true; - - // position is in relation to the containing body! don't forget this - //ground = walls.addShape(new Phaser.Physics.Advanced.Shapes.Box(400, 500, 500, 20)); - - //walls.addShape(new Phaser.Physics.Advanced.ShapeBox(0, 0.2, 20.48, 0.4)); - //walls.addShape(new Phaser.Physics.Advanced.ShapeBox(0, 15.16, 20.48, 0.4)); - //walls.addShape(new Phaser.Physics.Advanced.ShapeBox(-10.04, 7.68, 0.4, 14.56)); - //walls.addShape(new Phaser.Physics.Advanced.ShapeBox(10.04, 7.68, 0.4, 14.56)); - //walls.resetMassData(); - - physics.space.addBody(walls); - - // Add a circle - //circle = new Phaser.Physics.Advanced.Body(null, Phaser.Types.BODY_DYNAMIC, 200, 100); - //circle.game = game; - //circle.addCircle(32, 0, 0, 0.5); - //physics.space.addBody(circle); - - t = new Phaser.Physics.Advanced.Body(null, Phaser.Types.BODY_DYNAMIC, 300, 100); - //t.fixedRotation = true; - t.game = game; - t.addBox(0, 0, 20, 20, 0.5, 1, 1); - //t.addCircle(32, 0, 0, 0.8); - //t.addTriangle(0, 0, 1, 1, 2, 2); - //t.addPoly([{ x: -0.8, y: 0.48 }, { x: -0.8, y: 0 }, { x: 0.8, y: 0 }, { x: 0.8, y: 0.32 }, { x: 0, y: 0.84 }, { x: -0.56, y: 0.84 }], 1, 1, 6); - //t.addPoly([{ x: -0.8, y: 0.48 }, { x: -0.8, y: 0 }, { x: 0.8, y: 0 }, { x: 0.8, y: 0.32 }, { x: 0, y: 0.84 }, { x: -0.56, y: 0.84 }], 0.5, 1, 1); - //t.transform.setRotation(game.math.degreesToRadians(45)); - //t.fixedRotation = true; - physics.space.addBody(t); - - game.input.onTap.add(step, this); - - } - - function step() { - - physics.update(); - } - - function update() { - - //if (physics.space.stepCount < 90) - //{ - physics.update(); - //} - - //atari.x = physics.metersToPixels(circle.position.x); - //atari.y = physics.metersToPixels(circle.position.y); - //atari.rotation = physics.metersToPixels(circle.angle); - - // force moves without rotating - - if (game.input.keyboard.isDown(Phaser.Keyboard.LEFT)) - { - circle.applyAngularImpulse(-0.02); - } - else if (game.input.keyboard.isDown(Phaser.Keyboard.RIGHT)) - { - circle.applyAngularImpulse(0.02); - } - - - /* - if (game.input.keyboard.isDown(Phaser.Keyboard.LEFT)) - { - circle.applyForceToCenter(new Phaser.Vec2(-8, 0)); - } - else if (game.input.keyboard.isDown(Phaser.Keyboard.RIGHT)) - { - circle.applyForceToCenter(new Phaser.Vec2(8, 0)); - } - */ - - if (game.input.keyboard.isDown(Phaser.Keyboard.UP)) - { - circle.applyForceToCenter(new Phaser.Vec2(0, -10)); - } - else if (game.input.keyboard.isDown(Phaser.Keyboard.DOWN)) - { - circle.applyForceToCenter(new Phaser.Vec2(0, 5)); - } - - //console.log(circle.velocity.x, circle.velocity.y); - //console.log('p', circle.position.x, circle.position.y); - } - - function renderBounds(body: Phaser.Physics.Advanced.Body) { - - game.stage.context.fillStyle = 'rgba(0,255,200,0.2)'; - - game.stage.context.fillRect(body.bounds.x, body.bounds.y, body.bounds.width, body.bounds.height); - - } - - function renderCircle(shape) { - - game.stage.context.beginPath(); - game.stage.context.arc(shape.tc.x * 50, shape.tc.y * 50, shape.radius * 50, 0, Math.PI * 2, false); - - if (shape.body.isAwake) - { - game.stage.context.fillStyle = 'rgba(0,255,0, 0.3)'; - } - else - { - game.stage.context.fillStyle = 'rgba(100,100,100, 0.1)'; - } - - game.stage.context.fill(); - game.stage.context.closePath(); - - } - - function drawPolygon(ctx, shape, lineWidth, fillStyle) { - - var verts = shape.tverts; - var body = shape.body; - - ctx.beginPath(); - ctx.moveTo(body.position.x + verts[0].x * 50, body.position.y + verts[0].y * 50); - - for (var i = 0; i < verts.length; i++) { - ctx.lineTo(body.position.x + verts[i].x * 50, body.position.y + verts[i].y * 50); - } - - ctx.lineTo(body.position.x + verts[verts.length - 1].x * 50, body.position.y + verts[verts.length - 1].y * 50); - - ctx.closePath(); - - ctx.fillStyle = fillStyle; - ctx.fill(); - - } - - function render() { - - //game.stage.context.fillStyle = 'rgb(255,255,0)'; - //game.stage.context.fillText('x: ' + t.position.x + ' y: ' + t.position.y, 32, 32); - //game.stage.context.fillText('vx: ' + t.velocity.x + ' vy: ' + t.velocity.y, 32, 64); - //game.stage.context.fillText('x: ' + t.bounds.x + ' y: ' + t.bounds.y, 32, 32); - //game.stage.context.fillText('vx: ' + t.velocity.x + ' vy: ' + t.velocity.y, 32, 64); - - //renderCircle(circle.shapes[0]); - //renderBounds(circle); - - drawPolygon(game.stage.context, walls.shapes[0], 1, 'rgb(0,255,255)'); - //drawPolygon(game.stage.context, walls.shapes[1], 1, 'rgb(0,255,255)'); - - //renderCircle(t.shapes[0]); - drawPolygon(game.stage.context, t.shapes[0], 1, 'rgb(255,255,255)'); - //renderBounds(t); - - } - -})(); diff --git a/wip/phaser clean up/obb vs obb.ts b/wip/phaser clean up/obb vs obb.ts deleted file mode 100644 index 5b22bebf..00000000 --- a/wip/phaser clean up/obb vs obb.ts +++ /dev/null @@ -1,54 +0,0 @@ -/// - -(function () { - - var game = new Phaser.Game(this, 'game', 800, 600, init, create, update, render); - - function init() { - - // Using Phasers asset loader we load up a PNG from the assets folder - game.load.image('atari', 'assets/sprites/atari800xl.png'); - game.load.image('card', 'assets/sprites/mana_card.png'); - game.load.start(); - - } - - var atari: Phaser.Sprite; - var card: Phaser.Sprite; - - function create() { - - atari = game.add.sprite(200, 310, 'atari'); - - card = game.add.sprite(500, 300, 'card'); - - atari.input.start(0); - atari.input.enableDrag(); - - card.input.start(0); - card.events.onInputDown.add(rotateIt, this); - - } - - function rotateIt() { - card.rotation += 10; - } - - function update() { - } - - function render() { - - game.stage.context.save(); - - game.stage.context.strokeStyle = 'rgb(255,255,0)'; - game.stage.context.strokeRect(atari.cameraView.x, atari.cameraView.y, atari.cameraView.width, atari.cameraView.height); - - game.stage.context.strokeStyle = 'rgb(255,0,255)'; - game.stage.context.strokeRect(card.cameraView.x, card.cameraView.y, card.cameraView.width, card.cameraView.height); - - game.stage.context.restore(); - - } - -})();