littlejsengine 1.17.1 → 1.17.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/littlejs.d.ts +94 -83
- package/dist/littlejs.esm.js +302 -198
- package/dist/littlejs.esm.min.js +1 -1
- package/dist/littlejs.js +302 -197
- package/dist/littlejs.min.js +1 -1
- package/dist/littlejs.release.js +298 -193
- package/examples/box2d/gameObjects.js +3 -3
- package/examples/electron/index.html +2 -2
- package/examples/index.html +8 -7
- package/examples/module/build.bat +7 -0
- package/examples/module/build.js +121 -0
- package/examples/platformer/game.js +1 -1
- package/examples/platformer/gameEffects.js +2 -7
- package/examples/platformer/gameLevel.js +3 -3
- package/examples/puzzle/game.js +2 -2
- package/examples/shorts/base.html +4 -1
- package/examples/shorts/box2dPool.js +2 -2
- package/examples/shorts/box2dTileLayer.js +48 -0
- package/examples/shorts/music.js +2 -2
- package/examples/shorts/musicPlayer.js +2 -2
- package/examples/starter/index.html +2 -2
- package/package.json +5 -5
- package/plugins/box2d.js +167 -63
- package/plugins/uiSystem.js +3 -3
- package/plugins/zzfxm.js +2 -2
- package/reference.md +3 -2
- package/src/engine.js +10 -9
- package/src/engineAudio.js +44 -58
- package/src/engineDebug.js +4 -4
- package/src/engineDraw.js +5 -2
- package/src/engineExport.js +0 -1
- package/src/engineInput.js +4 -4
- package/src/engineMath.js +9 -9
- package/src/engineObject.js +1 -1
- package/src/engineTileLayer.js +51 -40
- package/src/engineWebGL.js +2 -2
package/dist/littlejs.release.js
CHANGED
|
@@ -33,7 +33,7 @@ const engineName = 'LittleJS';
|
|
|
33
33
|
* @type {string}
|
|
34
34
|
* @default
|
|
35
35
|
* @memberof Engine */
|
|
36
|
-
const engineVersion = '1.17.
|
|
36
|
+
const engineVersion = '1.17.5';
|
|
37
37
|
|
|
38
38
|
/** Frames per second to update
|
|
39
39
|
* @type {number}
|
|
@@ -88,8 +88,9 @@ function getPaused() { return paused; }
|
|
|
88
88
|
* @memberof Engine */
|
|
89
89
|
function setPaused(isPaused=true) { paused = isPaused; }
|
|
90
90
|
|
|
91
|
-
//
|
|
91
|
+
// Engine internal variables
|
|
92
92
|
let frameTimeLastMS = 0, frameTimeBufferMS = 0, averageFPS = 0;
|
|
93
|
+
let showEngineVersion = true;
|
|
93
94
|
|
|
94
95
|
///////////////////////////////////////////////////////////////////////////////
|
|
95
96
|
// plugin hooks
|
|
@@ -153,16 +154,17 @@ function engineAddPlugin(update, render, glContextLost, glContextRestored)
|
|
|
153
154
|
* @example
|
|
154
155
|
* // Basic engine startup
|
|
155
156
|
* engineInit(
|
|
156
|
-
* ()
|
|
157
|
-
* ()
|
|
158
|
-
* ()
|
|
159
|
-
* ()
|
|
160
|
-
* ()
|
|
157
|
+
* ()=> { LOG('Game initialized!'); }, // gameInit
|
|
158
|
+
* ()=> { updateGameLogic(); }, // gameUpdate
|
|
159
|
+
* ()=> { updateUI(); }, // gameUpdatePost
|
|
160
|
+
* ()=> { drawBackground(); }, // gameRender
|
|
161
|
+
* ()=> { drawHUD(); }, // gameRenderPost
|
|
161
162
|
* ['tiles.png', 'tilesLevel.png'] // images to load
|
|
162
163
|
* );
|
|
163
164
|
* @memberof Engine */
|
|
164
165
|
async function engineInit(gameInit, gameUpdate, gameUpdatePost, gameRender, gameRenderPost, imageSources=[], rootElement=document.body)
|
|
165
166
|
{
|
|
167
|
+
showEngineVersion && console.log(`${engineName} Engine v${engineVersion}`);
|
|
166
168
|
ASSERT(!mainContext, 'engine already initialized');
|
|
167
169
|
ASSERT(isArray(imageSources), 'pass in images as array');
|
|
168
170
|
|
|
@@ -431,7 +433,6 @@ async function engineInit(gameInit, gameUpdate, gameUpdatePost, gameRender, game
|
|
|
431
433
|
promises.push(new Promise(resolve =>
|
|
432
434
|
{
|
|
433
435
|
let t = 0;
|
|
434
|
-
console.log(`${engineName} Engine v${engineVersion}`);
|
|
435
436
|
updateSplash();
|
|
436
437
|
function updateSplash()
|
|
437
438
|
{
|
|
@@ -608,7 +609,7 @@ function drawEngineLogo(t)
|
|
|
608
609
|
x.fillStyle = C;
|
|
609
610
|
C ? x.fill() : x.stroke();
|
|
610
611
|
};
|
|
611
|
-
const color = (c=0, l=0)
|
|
612
|
+
const color = (c=0, l=0)=>
|
|
612
613
|
hsl([.98,.3,.57,.14][c%4],.9,[0,.3,.5,.8,.9][l]).toString();
|
|
613
614
|
const alpha = wave(1,1,t);
|
|
614
615
|
const p = percent(alpha, .1, .5);
|
|
@@ -782,25 +783,25 @@ function debugProtectConstant(o){ return o; }
|
|
|
782
783
|
const PI = Math.PI;
|
|
783
784
|
|
|
784
785
|
/** Returns absolute value of value passed in
|
|
785
|
-
* @param {number}
|
|
786
|
+
* @param {number} x
|
|
786
787
|
* @return {number}
|
|
787
788
|
* @memberof Math */
|
|
788
789
|
const abs = Math.abs;
|
|
789
790
|
|
|
790
791
|
/** Returns floored value of value passed in
|
|
791
|
-
* @param {number}
|
|
792
|
+
* @param {number} x
|
|
792
793
|
* @return {number}
|
|
793
794
|
* @memberof Math */
|
|
794
795
|
const floor = Math.floor;
|
|
795
796
|
|
|
796
797
|
/** Returns ceiled value of value passed in
|
|
797
|
-
* @param {number}
|
|
798
|
+
* @param {number} x
|
|
798
799
|
* @return {number}
|
|
799
800
|
* @memberof Math */
|
|
800
801
|
const ceil = Math.ceil;
|
|
801
802
|
|
|
802
803
|
/** Returns rounded value passed in
|
|
803
|
-
* @param {number}
|
|
804
|
+
* @param {number} x
|
|
804
805
|
* @return {number}
|
|
805
806
|
* @memberof Math */
|
|
806
807
|
const round = Math.round;
|
|
@@ -818,7 +819,7 @@ const min = Math.min;
|
|
|
818
819
|
const max = Math.max;
|
|
819
820
|
|
|
820
821
|
/** Returns the sign of value passed in
|
|
821
|
-
* @param {number}
|
|
822
|
+
* @param {number} x
|
|
822
823
|
* @return {number}
|
|
823
824
|
* @memberof Math */
|
|
824
825
|
const sign = Math.sign;
|
|
@@ -830,25 +831,25 @@ const sign = Math.sign;
|
|
|
830
831
|
const hypot = Math.hypot;
|
|
831
832
|
|
|
832
833
|
/** Returns log2 of value passed in
|
|
833
|
-
* @param {number}
|
|
834
|
+
* @param {number} x
|
|
834
835
|
* @return {number}
|
|
835
836
|
* @memberof Math */
|
|
836
837
|
const log2 = Math.log2;
|
|
837
838
|
|
|
838
839
|
/** Returns sin of value passed in
|
|
839
|
-
* @param {number}
|
|
840
|
+
* @param {number} x
|
|
840
841
|
* @return {number}
|
|
841
842
|
* @memberof Math */
|
|
842
843
|
const sin = Math.sin;
|
|
843
844
|
|
|
844
845
|
/** Returns cos of value passed in
|
|
845
|
-
* @param {number}
|
|
846
|
+
* @param {number} x
|
|
846
847
|
* @return {number}
|
|
847
848
|
* @memberof Math */
|
|
848
849
|
const cos = Math.cos;
|
|
849
850
|
|
|
850
851
|
/** Returns tan of value passed in
|
|
851
|
-
* @param {number}
|
|
852
|
+
* @param {number} x
|
|
852
853
|
* @return {number}
|
|
853
854
|
* @memberof Math */
|
|
854
855
|
const tan = Math.tan;
|
|
@@ -2920,7 +2921,7 @@ class EngineObject
|
|
|
2920
2921
|
if (this.collideTiles)
|
|
2921
2922
|
{
|
|
2922
2923
|
// check collision against tiles
|
|
2923
|
-
const hitLayer = tileCollisionTest(this.pos, this.size, this)
|
|
2924
|
+
const hitLayer = tileCollisionTest(this.pos, this.size, this);
|
|
2924
2925
|
if (hitLayer)
|
|
2925
2926
|
{
|
|
2926
2927
|
// if already was stuck in collision, don't do anything
|
|
@@ -3424,7 +3425,7 @@ function drawTile(pos, size=vec2(1), tileInfo, color=WHITE,
|
|
|
3424
3425
|
ASSERT(!additiveColor || isColor(additiveColor), 'additiveColor must be a color');
|
|
3425
3426
|
ASSERT(!context || !useWebGL, 'context only supported in canvas 2D mode');
|
|
3426
3427
|
|
|
3427
|
-
const textureInfo = tileInfo
|
|
3428
|
+
const textureInfo = tileInfo?.textureInfo;
|
|
3428
3429
|
const bleed = tileInfo?.bleed ?? 0;
|
|
3429
3430
|
if (useWebGL && glEnable)
|
|
3430
3431
|
{
|
|
@@ -3730,9 +3731,12 @@ function drawEllipse(pos, size=vec2(1), color=WHITE, angle=0, lineWidth=0, lineC
|
|
|
3730
3731
|
ASSERT(isColor(color) && isColor(lineColor), 'color is invalid');
|
|
3731
3732
|
ASSERT(isNumber(angle), 'angle must be a number');
|
|
3732
3733
|
ASSERT(isNumber(lineWidth), 'lineWidth must be a number');
|
|
3733
|
-
ASSERT(lineWidth >= 0
|
|
3734
|
+
ASSERT(lineWidth >= 0, 'lineWidth must be a positive value or 0');
|
|
3734
3735
|
ASSERT(!context || !useWebGL, 'context only supported in canvas 2D mode');
|
|
3735
3736
|
|
|
3737
|
+
// clamp line width to prevent artifacts
|
|
3738
|
+
lineWidth = clamp(lineWidth, 0, Math.min(size.x, size.y));
|
|
3739
|
+
|
|
3736
3740
|
if (useWebGL && glEnable)
|
|
3737
3741
|
{
|
|
3738
3742
|
// draw as a regular polygon
|
|
@@ -4698,9 +4702,9 @@ function inputInit()
|
|
|
4698
4702
|
function touchInputInit()
|
|
4699
4703
|
{
|
|
4700
4704
|
// add non passive touch event listeners
|
|
4701
|
-
document.addEventListener('touchstart', (e)
|
|
4702
|
-
document.addEventListener('touchmove', (e)
|
|
4703
|
-
document.addEventListener('touchend', (e)
|
|
4705
|
+
document.addEventListener('touchstart', (e)=> handleTouch(e), { passive: false });
|
|
4706
|
+
document.addEventListener('touchmove', (e)=> handleTouch(e), { passive: false });
|
|
4707
|
+
document.addEventListener('touchend', (e)=> handleTouch(e), { passive: false });
|
|
4704
4708
|
|
|
4705
4709
|
// handle all touch events the same way
|
|
4706
4710
|
let wasTouching;
|
|
@@ -4906,7 +4910,7 @@ function inputUpdate()
|
|
|
4906
4910
|
// poll gamepads
|
|
4907
4911
|
const maxGamepads = 8;
|
|
4908
4912
|
const gamepads = navigator.getGamepads();
|
|
4909
|
-
const gamepadCount = min(maxGamepads, gamepads.length)
|
|
4913
|
+
const gamepadCount = min(maxGamepads, gamepads.length);
|
|
4910
4914
|
for (let i=0; i<gamepadCount; ++i)
|
|
4911
4915
|
{
|
|
4912
4916
|
// get or create gamepad data
|
|
@@ -5111,29 +5115,46 @@ function audioInit()
|
|
|
5111
5115
|
///////////////////////////////////////////////////////////////////////////////
|
|
5112
5116
|
|
|
5113
5117
|
/**
|
|
5114
|
-
* Sound Object - Stores a sound for later
|
|
5118
|
+
* Sound Object - Stores a sound for later
|
|
5119
|
+
* - this can be used to load and play wave, mp3, and ogg files
|
|
5120
|
+
* - it can also create sounds using the ZzFX sound generator
|
|
5121
|
+
* - can attenuate and apply stereo panning to sounds
|
|
5122
|
+
* - sound instance control with pause/resume capability
|
|
5115
5123
|
*
|
|
5116
5124
|
* <a href=https://killedbyapixel.github.io/ZzFX/>Create sounds using the ZzFX Sound Designer.</a>
|
|
5117
5125
|
* @memberof Audio
|
|
5118
5126
|
* @example
|
|
5119
|
-
* //
|
|
5127
|
+
* // load an audio asset file
|
|
5128
|
+
* const sound_example = new Sound('sound.mp3');
|
|
5129
|
+
*
|
|
5130
|
+
* // create a zzfx sound
|
|
5120
5131
|
* const sound_example = new Sound([.5,.5]);
|
|
5121
5132
|
*
|
|
5122
|
-
* // play
|
|
5133
|
+
* // play a sound
|
|
5123
5134
|
* sound_example.play();
|
|
5124
5135
|
*/
|
|
5125
5136
|
class Sound
|
|
5126
5137
|
{
|
|
5127
|
-
/**
|
|
5128
|
-
*
|
|
5138
|
+
/**
|
|
5139
|
+
* @callback SoundLoadCallback - Function called when sound is loaded
|
|
5140
|
+
* @param {Sound} sound
|
|
5141
|
+
* @memberof Audio
|
|
5142
|
+
*/
|
|
5143
|
+
|
|
5144
|
+
/** Create a sound object and cache the audio for later use
|
|
5145
|
+
* @param {string|Array} [asset] - Filename of audio file or zzfx array
|
|
5146
|
+
* @param {number} [randomness] - How much to randomize frequency each time sound plays, for zzfx sounds the zzfx default is used if undefined
|
|
5129
5147
|
* @param {number} [range=soundDefaultRange] - World space max range of sound
|
|
5130
5148
|
* @param {number} [taper=soundDefaultTaper] - At what percentage of range should it start tapering
|
|
5149
|
+
* @param {SoundLoadCallback} [onloadCallback] - callback function to call when sound is loaded
|
|
5131
5150
|
*/
|
|
5132
|
-
constructor(
|
|
5151
|
+
constructor(asset, randomness, range=soundDefaultRange, taper=soundDefaultTaper, onloadCallback)
|
|
5133
5152
|
{
|
|
5134
5153
|
if (!soundEnable || headlessMode) return;
|
|
5135
5154
|
|
|
5136
|
-
ASSERT(!
|
|
5155
|
+
ASSERT(!asset || isArray(asset) || isString(asset), 'asset must be a file name or zzfx array');
|
|
5156
|
+
ASSERT(randomness === undefined || isNumber(randomness), 'randomness must be a number');
|
|
5157
|
+
ASSERT(randomness === undefined || randomness >= 0 && randomness <=1, 'randomness must be between 0 and 1');
|
|
5137
5158
|
ASSERT(isNumber(range), 'range must be a number');
|
|
5138
5159
|
ASSERT(isNumber(taper), 'taper must be a number');
|
|
5139
5160
|
|
|
@@ -5142,23 +5163,35 @@ class Sound
|
|
|
5142
5163
|
/** @property {number} - At what percentage of range should it start tapering */
|
|
5143
5164
|
this.taper = taper;
|
|
5144
5165
|
/** @property {number} - How much to randomize frequency each time sound plays */
|
|
5145
|
-
this.randomness = 0;
|
|
5166
|
+
this.randomness = randomness ?? 0;
|
|
5146
5167
|
/** @property {number} - Sample rate for this sound */
|
|
5147
5168
|
this.sampleRate = audioDefaultSampleRate;
|
|
5148
5169
|
/** @property {number} - Percentage of this sound currently loaded */
|
|
5149
5170
|
this.loadedPercent = 0;
|
|
5171
|
+
/** @property {SoundLoadCallback} - function to call when sound is loaded */
|
|
5172
|
+
this.onloadCallback = onloadCallback;
|
|
5150
5173
|
|
|
5151
|
-
|
|
5152
|
-
if (zzfxSound)
|
|
5174
|
+
if (Array.isArray(asset))
|
|
5153
5175
|
{
|
|
5176
|
+
// generate zzfx sound
|
|
5177
|
+
const zzfxSound = asset;
|
|
5178
|
+
|
|
5154
5179
|
// remove randomness so it can be applied on playback
|
|
5155
|
-
const
|
|
5180
|
+
const defaultRandomness = randomness ?? .05;
|
|
5181
|
+
const randomnessIndex = 1;
|
|
5156
5182
|
this.randomness = zzfxSound[randomnessIndex] ?? defaultRandomness;
|
|
5157
5183
|
zzfxSound[randomnessIndex] = 0;
|
|
5158
5184
|
|
|
5159
5185
|
// generate the zzfx samples
|
|
5160
5186
|
this.sampleChannels = [zzfxG(...zzfxSound)];
|
|
5161
5187
|
this.loadedPercent = 1;
|
|
5188
|
+
onloadCallback?.(this);
|
|
5189
|
+
}
|
|
5190
|
+
else if (typeof asset === 'string')
|
|
5191
|
+
{
|
|
5192
|
+
// load the audio file
|
|
5193
|
+
const filename = asset;
|
|
5194
|
+
this.loadSound(filename);
|
|
5162
5195
|
}
|
|
5163
5196
|
}
|
|
5164
5197
|
|
|
@@ -5210,7 +5243,7 @@ class Sound
|
|
|
5210
5243
|
* @param {number} [volume] - Volume to play the music at
|
|
5211
5244
|
* @param {boolean} [loop] - Should the music loop?
|
|
5212
5245
|
* @param {boolean} [paused] - Should the music start paused
|
|
5213
|
-
* @return {SoundInstance} - The
|
|
5246
|
+
* @return {SoundInstance} - The sound instance
|
|
5214
5247
|
*/
|
|
5215
5248
|
playMusic(volume=1, loop=true, paused=false)
|
|
5216
5249
|
{ return this.play(undefined, volume, 1, 0, loop, paused); }
|
|
@@ -5220,7 +5253,7 @@ class Sound
|
|
|
5220
5253
|
* @param {number} [semitoneOffset=0] - How many semitones to offset pitch
|
|
5221
5254
|
* @param {Vector2} [pos] - World space position to play the sound if any
|
|
5222
5255
|
* @param {number} [volume=1] - How much to scale volume by
|
|
5223
|
-
* @return {SoundInstance} - The
|
|
5256
|
+
* @return {SoundInstance} - The sound instance
|
|
5224
5257
|
*/
|
|
5225
5258
|
playNote(semitoneOffset=0, pos, volume)
|
|
5226
5259
|
{
|
|
@@ -5233,57 +5266,14 @@ class Sound
|
|
|
5233
5266
|
* @return {number} - How long the sound is in seconds (undefined if loading)
|
|
5234
5267
|
*/
|
|
5235
5268
|
getDuration()
|
|
5236
|
-
{ return this.sampleChannels?.[0]
|
|
5269
|
+
{ return this.sampleChannels?.[0]?.length / this.sampleRate || 0; }
|
|
5237
5270
|
|
|
5238
5271
|
/** Check if sound is loaded, for sounds fetched from a url
|
|
5239
5272
|
* @return {boolean} - True if sound is loaded and ready to play
|
|
5240
5273
|
*/
|
|
5241
5274
|
isLoaded() { return this.loadedPercent === 1; }
|
|
5242
|
-
}
|
|
5243
|
-
|
|
5244
|
-
///////////////////////////////////////////////////////////////////////////////
|
|
5245
|
-
|
|
5246
|
-
/**
|
|
5247
|
-
* Sound Wave Object - Loads and stores an audio file for later use
|
|
5248
|
-
* - this can be used to load and play wave, mp3, and ogg files
|
|
5249
|
-
* @extends Sound
|
|
5250
|
-
* @memberof Audio
|
|
5251
|
-
* @example
|
|
5252
|
-
* // load an audio asset file
|
|
5253
|
-
* const sound_example = new SoundWave('sound.mp3');
|
|
5254
|
-
*
|
|
5255
|
-
* // play the sound
|
|
5256
|
-
* sound_example.play();
|
|
5257
|
-
*/
|
|
5258
|
-
class SoundWave extends Sound
|
|
5259
|
-
{
|
|
5260
|
-
/**
|
|
5261
|
-
* @callback SoundLoadCallback - Function called when sound is loaded
|
|
5262
|
-
* @param {SoundWave} sound
|
|
5263
|
-
* @memberof Audio
|
|
5264
|
-
*/
|
|
5265
5275
|
|
|
5266
|
-
/**
|
|
5267
|
-
* @param {string} filename - Filename of audio file to load
|
|
5268
|
-
* @param {number} [randomness] - How much to randomize frequency each time sound plays
|
|
5269
|
-
* @param {number} [range=soundDefaultRange] - World space max range of sound
|
|
5270
|
-
* @param {number} [taper=soundDefaultTaper] - At what percentage of range should it start tapering
|
|
5271
|
-
* @param {SoundLoadCallback} [onloadCallback] - callback function to call when sound is loaded
|
|
5272
|
-
*/
|
|
5273
|
-
constructor(filename, randomness=0, range, taper, onloadCallback)
|
|
5274
|
-
{
|
|
5275
|
-
super(undefined, range, taper);
|
|
5276
|
-
if (!soundEnable || headlessMode) return;
|
|
5277
|
-
ASSERT(!filename || isString(filename), 'filename must be a string');
|
|
5278
|
-
ASSERT(isNumber(randomness), 'randomness must be a number');
|
|
5279
|
-
|
|
5280
|
-
/** @property {SoundLoadCallback} - callback function to call when sound is loaded */
|
|
5281
|
-
this.onloadCallback = onloadCallback;
|
|
5282
|
-
this.randomness = randomness;
|
|
5283
|
-
filename && this.loadSound(filename);
|
|
5284
|
-
}
|
|
5285
|
-
|
|
5286
|
-
/** Loads a sound from a URL and decodes it into sample data. Must be used with await!
|
|
5276
|
+
/** Loads a sound from a URL and decodes it into sample data.
|
|
5287
5277
|
* @param {string} filename
|
|
5288
5278
|
* @return {Promise<void>} */
|
|
5289
5279
|
async loadSound(filename)
|
|
@@ -5938,17 +5928,17 @@ class TileLayerData
|
|
|
5938
5928
|
class CanvasLayer extends EngineObject
|
|
5939
5929
|
{
|
|
5940
5930
|
/** Create a canvas layer object
|
|
5941
|
-
* @param {Vector2} [
|
|
5931
|
+
* @param {Vector2} [pos] - World space position of the layer
|
|
5942
5932
|
* @param {Vector2} [size] - World space size of the layer
|
|
5943
5933
|
* @param {number} [angle] - Angle the layer is rotated by
|
|
5944
5934
|
* @param {number} [renderOrder] - Objects sorted by renderOrder
|
|
5945
5935
|
* @param {Vector2} [canvasSize] - Default size of canvas, can be changed later
|
|
5946
5936
|
* @param {boolean} [useWebGL] - Should this layer use WebGL for rendering
|
|
5947
5937
|
*/
|
|
5948
|
-
constructor(
|
|
5938
|
+
constructor(pos, size, angle=0, renderOrder=0, canvasSize=vec2(512), useWebGL=true)
|
|
5949
5939
|
{
|
|
5950
5940
|
ASSERT(isVector2(canvasSize), 'canvasSize must be a Vector2');
|
|
5951
|
-
super(
|
|
5941
|
+
super(pos, size, undefined, angle, WHITE, renderOrder);
|
|
5952
5942
|
|
|
5953
5943
|
/** @property {HTMLCanvasElement} - The canvas used by this layer */
|
|
5954
5944
|
this.canvas = headlessMode ? undefined : new OffscreenCanvas(canvasSize.x, canvasSize.y);
|
|
@@ -6052,16 +6042,16 @@ class CanvasLayer extends EngineObject
|
|
|
6052
6042
|
class TileLayer extends CanvasLayer
|
|
6053
6043
|
{
|
|
6054
6044
|
/** Create a tile layer object
|
|
6055
|
-
* @param {Vector2}
|
|
6045
|
+
* @param {Vector2} pos - World space position
|
|
6056
6046
|
* @param {Vector2} size - World space size
|
|
6057
6047
|
* @param {TileInfo} [tileInfo] - Default tile info for layer (used for size and texture)
|
|
6058
6048
|
* @param {number} [renderOrder] - Objects are sorted by renderOrder
|
|
6049
|
+
* @param {boolean} [useWebGL] - Should this layer use WebGL for rendering
|
|
6059
6050
|
*/
|
|
6060
|
-
constructor(
|
|
6051
|
+
constructor(pos, size, tileInfo=tile(), renderOrder=0, useWebGL=true)
|
|
6061
6052
|
{
|
|
6062
6053
|
const canvasSize = tileInfo ? size.multiply(tileInfo.size) : size;
|
|
6063
|
-
|
|
6064
|
-
super(position, size, 0, renderOrder, canvasSize, useWebGL);
|
|
6054
|
+
super(pos, size, 0, renderOrder, canvasSize, useWebGL);
|
|
6065
6055
|
|
|
6066
6056
|
/** @property {TileInfo} - Default tile info for layer */
|
|
6067
6057
|
this.tileInfo = undefined;
|
|
@@ -6073,15 +6063,15 @@ class TileLayer extends CanvasLayer
|
|
|
6073
6063
|
if (headlessMode)
|
|
6074
6064
|
{
|
|
6075
6065
|
// disable rendering in headless mode
|
|
6076
|
-
this.render = ()
|
|
6077
|
-
this.redraw = ()
|
|
6078
|
-
this.redrawStart = ()
|
|
6079
|
-
this.redrawEnd = ()
|
|
6080
|
-
this.drawTileData = ()
|
|
6081
|
-
this.redrawTileData = ()
|
|
6082
|
-
this.drawLayerTile = ()
|
|
6083
|
-
this.drawLayerRect = ()
|
|
6084
|
-
this.clearLayerRect = ()
|
|
6066
|
+
this.render = ()=> {};
|
|
6067
|
+
this.redraw = ()=> {};
|
|
6068
|
+
this.redrawStart = ()=> {};
|
|
6069
|
+
this.redrawEnd = ()=> {};
|
|
6070
|
+
this.drawTileData = ()=> {};
|
|
6071
|
+
this.redrawTileData = ()=> {};
|
|
6072
|
+
this.drawLayerTile = ()=> {};
|
|
6073
|
+
this.drawLayerRect = ()=> {};
|
|
6074
|
+
this.clearLayerRect = ()=> {};
|
|
6085
6075
|
return;
|
|
6086
6076
|
}
|
|
6087
6077
|
|
|
@@ -6115,6 +6105,12 @@ class TileLayer extends CanvasLayer
|
|
|
6115
6105
|
isRedraw ? this.drawTileData(layerPos) : this.redrawTileData(layerPos);
|
|
6116
6106
|
}
|
|
6117
6107
|
|
|
6108
|
+
/** Clear data at a given position in the array
|
|
6109
|
+
* @param {Vector2} layerPos - Local position in array
|
|
6110
|
+
* @param {boolean} [redraw] - Force the tile to redraw if true */
|
|
6111
|
+
clearData(layerPos, redraw=false)
|
|
6112
|
+
{ this.setData(layerPos, new TileLayerData, redraw) }
|
|
6113
|
+
|
|
6118
6114
|
/** Get data at a given position in the array
|
|
6119
6115
|
* @param {Vector2} layerPos - Local position in array
|
|
6120
6116
|
* @return {TileLayerData} */
|
|
@@ -6156,7 +6152,7 @@ class TileLayer extends CanvasLayer
|
|
|
6156
6152
|
for (let x = this.size.x; x--;)
|
|
6157
6153
|
for (let y = this.size.y; y--;)
|
|
6158
6154
|
this.drawTileData(vec2(x,y), false);
|
|
6159
|
-
this.
|
|
6155
|
+
this.isUsingWebGL && glFlush();
|
|
6160
6156
|
this.onRedraw();
|
|
6161
6157
|
this.redrawEnd();
|
|
6162
6158
|
}
|
|
@@ -6205,14 +6201,13 @@ class TileLayer extends CanvasLayer
|
|
|
6205
6201
|
if (!this.context) return;
|
|
6206
6202
|
ASSERT(drawContext === this.context);
|
|
6207
6203
|
|
|
6208
|
-
if (glEnable && this.textureInfo.glTexture)
|
|
6209
|
-
glSetRenderTarget();
|
|
6210
|
-
|
|
6211
6204
|
// set stuff back to normal
|
|
6205
|
+
if (this.isUsingWebGL)
|
|
6206
|
+
glSetRenderTarget();
|
|
6212
6207
|
[drawContext, mainCanvasSize, cameraPos, cameraScale, canvasClearColor] = this.savedRenderSettings;
|
|
6213
6208
|
}
|
|
6214
6209
|
|
|
6215
|
-
/** Draw the tile at a given position in the tile
|
|
6210
|
+
/** Draw the tile at a given position in the tile layer
|
|
6216
6211
|
* This can be used to clear out tiles when they are destroyed
|
|
6217
6212
|
* Tiles can also be redrawn if inside a redrawStart/End block
|
|
6218
6213
|
* @param {Vector2} layerPos
|
|
@@ -6236,7 +6231,7 @@ class TileLayer extends CanvasLayer
|
|
|
6236
6231
|
this.drawLayerTile(drawPos, drawSize, tileInfo, d.color, d.direction*PI/2, d.mirror);
|
|
6237
6232
|
}
|
|
6238
6233
|
|
|
6239
|
-
/** Draw the tile at a given position in the tile
|
|
6234
|
+
/** Draw the tile at a given position in the tile layer
|
|
6240
6235
|
* This can be used to clear tiles when they are destroyed
|
|
6241
6236
|
* For better performance use drawTileData inside a redrawStart/End block
|
|
6242
6237
|
* @param {Vector2} layerPos
|
|
@@ -6264,7 +6259,7 @@ class TileLayer extends CanvasLayer
|
|
|
6264
6259
|
angle=0, mirror, additiveColor)
|
|
6265
6260
|
{
|
|
6266
6261
|
const drawPos = pos.add(size.scale(.5));
|
|
6267
|
-
drawTile(drawPos, size, tileInfo, color, angle, mirror, additiveColor);
|
|
6262
|
+
drawTile(drawPos, size, tileInfo, color, angle, mirror, additiveColor, this.isUsingWebGL);
|
|
6268
6263
|
}
|
|
6269
6264
|
|
|
6270
6265
|
/** Clear a rectangle in layer space
|
|
@@ -6304,14 +6299,15 @@ class TileLayer extends CanvasLayer
|
|
|
6304
6299
|
class TileCollisionLayer extends TileLayer
|
|
6305
6300
|
{
|
|
6306
6301
|
/** Create a tile layer object
|
|
6307
|
-
* @param {Vector2}
|
|
6308
|
-
* @param {Vector2} size
|
|
6309
|
-
* @param {TileInfo} [tileInfo]
|
|
6302
|
+
* @param {Vector2} pos - World space position
|
|
6303
|
+
* @param {Vector2} size - World space size
|
|
6304
|
+
* @param {TileInfo} [tileInfo] - Tile info for layer
|
|
6310
6305
|
* @param {number} [renderOrder] - Objects are sorted by renderOrder
|
|
6306
|
+
* @param {boolean} [useWebGL] - Should this layer use WebGL for rendering
|
|
6311
6307
|
*/
|
|
6312
|
-
constructor(
|
|
6308
|
+
constructor(pos, size, tileInfo=tile(), renderOrder=0, useWebGL=true)
|
|
6313
6309
|
{
|
|
6314
|
-
super(
|
|
6310
|
+
super(pos, size.floor(), tileInfo, renderOrder, useWebGL);
|
|
6315
6311
|
|
|
6316
6312
|
/** @property {Array<number>} - The tile collision grid */
|
|
6317
6313
|
this.collisionData = [];
|
|
@@ -6347,24 +6343,29 @@ class TileCollisionLayer extends TileLayer
|
|
|
6347
6343
|
this.collisionData.fill(0);
|
|
6348
6344
|
}
|
|
6349
6345
|
|
|
6350
|
-
/** Set tile collision data for a given cell in the
|
|
6351
|
-
* @param {Vector2}
|
|
6346
|
+
/** Set tile collision data for a given cell in the layer
|
|
6347
|
+
* @param {Vector2} layerPos
|
|
6352
6348
|
* @param {number} [data] */
|
|
6353
|
-
setCollisionData(
|
|
6349
|
+
setCollisionData(layerPos, data=1)
|
|
6354
6350
|
{
|
|
6355
|
-
ASSERT(isVector2(
|
|
6356
|
-
const i = (
|
|
6357
|
-
|
|
6351
|
+
ASSERT(isVector2(layerPos), 'layerPos must be a Vector2');
|
|
6352
|
+
const i = (layerPos.y|0)*this.size.x + layerPos.x|0;
|
|
6353
|
+
layerPos.arrayCheck(this.size) && (this.collisionData[i] = data);
|
|
6358
6354
|
}
|
|
6359
6355
|
|
|
6360
|
-
/**
|
|
6361
|
-
* @param {Vector2}
|
|
6356
|
+
/** Clear tile collision data for a given cell in the layer
|
|
6357
|
+
* @param {Vector2} layerPos */
|
|
6358
|
+
clearCollisionData(layerPos)
|
|
6359
|
+
{ this.setCollisionData(layerPos, 0); }
|
|
6360
|
+
|
|
6361
|
+
/** Get tile collision data for a given cell in the layer
|
|
6362
|
+
* @param {Vector2} layerPos
|
|
6362
6363
|
* @return {number} */
|
|
6363
|
-
getCollisionData(
|
|
6364
|
+
getCollisionData(layerPos)
|
|
6364
6365
|
{
|
|
6365
|
-
ASSERT(isVector2(
|
|
6366
|
-
const i = (
|
|
6367
|
-
return
|
|
6366
|
+
ASSERT(isVector2(layerPos), 'layerPos must be a Vector2');
|
|
6367
|
+
const i = (layerPos.y|0)*this.size.x + layerPos.x|0;
|
|
6368
|
+
return layerPos.arrayCheck(this.size) ? this.collisionData[i] : 0;
|
|
6368
6369
|
}
|
|
6369
6370
|
|
|
6370
6371
|
/** Check if collision with another object should occur
|
|
@@ -7282,7 +7283,7 @@ function glPreRender(clear=true)
|
|
|
7282
7283
|
p.x, p.y, 0, 1];
|
|
7283
7284
|
|
|
7284
7285
|
// set the same transform matrix for both shaders
|
|
7285
|
-
const initUniform = (program, uniform, value)
|
|
7286
|
+
const initUniform = (program, uniform, value)=>
|
|
7286
7287
|
{
|
|
7287
7288
|
glContext.useProgram(program);
|
|
7288
7289
|
const location = glContext.getUniformLocation(program, uniform);
|
|
@@ -7676,7 +7677,7 @@ function glSetRenderTarget(texture, clear=false)
|
|
|
7676
7677
|
glContext.bindFramebuffer(glContext.FRAMEBUFFER, glFramebuffer);
|
|
7677
7678
|
glContext.framebufferTexture2D(glContext.FRAMEBUFFER,
|
|
7678
7679
|
glContext.COLOR_ATTACHMENT0, glContext.TEXTURE_2D, texture, 0);
|
|
7679
|
-
|
|
7680
|
+
glPreRender(clear);
|
|
7680
7681
|
}
|
|
7681
7682
|
else
|
|
7682
7683
|
{
|
|
@@ -8295,7 +8296,7 @@ class ZzFXMusic extends Sound
|
|
|
8295
8296
|
/** Play the music that loops by default
|
|
8296
8297
|
* @param {number} [volume] - Volume to play the music at
|
|
8297
8298
|
* @param {boolean} [loop] - Should the music loop?
|
|
8298
|
-
* @return {
|
|
8299
|
+
* @return {SoundInstance} - The sound instance
|
|
8299
8300
|
*/
|
|
8300
8301
|
playMusic(volume=1, loop=true)
|
|
8301
8302
|
{ return super.play(undefined, volume, 1, 0, loop); }
|
|
@@ -8342,7 +8343,7 @@ function zzfxM(instruments, patterns, sequence, BPM = 125)
|
|
|
8342
8343
|
sampleBuffer = [hasMore = notFirstBeat = outSampleOffset = 0];
|
|
8343
8344
|
|
|
8344
8345
|
// for each pattern in sequence
|
|
8345
|
-
sequence.forEach((patternIndex, sequenceIndex)
|
|
8346
|
+
sequence.forEach((patternIndex, sequenceIndex)=> {
|
|
8346
8347
|
// get pattern for current channel, use empty 1 note pattern if none found
|
|
8347
8348
|
patternChannel = patterns[patternIndex][channelIndex] || [0, 0, 0];
|
|
8348
8349
|
|
|
@@ -8435,7 +8436,7 @@ let uiDebug = 0;
|
|
|
8435
8436
|
|
|
8436
8437
|
/** Enable UI system debug drawing
|
|
8437
8438
|
* 0=off, 1=normal, 2=show invisible
|
|
8438
|
-
* @param {number|boolean}
|
|
8439
|
+
* @param {number|boolean} debugMode
|
|
8439
8440
|
* @memberof UISystem */
|
|
8440
8441
|
function uiSetDebug(debugMode)
|
|
8441
8442
|
{ uiDebug = typeof debugMode === 'boolean' ? (debugMode ? 1 : 0) : debugMode; }
|
|
@@ -9636,8 +9637,8 @@ class UIScrollbar extends UIObject
|
|
|
9636
9637
|
class UIVideo extends UIObject
|
|
9637
9638
|
{
|
|
9638
9639
|
/** Create a video player UI object
|
|
9639
|
-
* @param {Vector2}
|
|
9640
|
-
* @param {Vector2}
|
|
9640
|
+
* @param {Vector2} pos
|
|
9641
|
+
* @param {Vector2} size
|
|
9641
9642
|
* @param {string} src - Video file path or URL
|
|
9642
9643
|
* @param {boolean} [autoplay=false] - Start playing immediately?
|
|
9643
9644
|
* @param {boolean} [loop=false] - Loop the video?
|
|
@@ -9777,6 +9778,7 @@ class UIVideo extends UIObject
|
|
|
9777
9778
|
* - Contact begin and end callbacks
|
|
9778
9779
|
* - Wraps b2Vec2 type to/from Vector2
|
|
9779
9780
|
* - Raycasting and querying
|
|
9781
|
+
* - Box2dTileLayer for grid based collision
|
|
9780
9782
|
* - Every type of joint
|
|
9781
9783
|
* - Debug physics drawing
|
|
9782
9784
|
* @namespace Box2D
|
|
@@ -9826,21 +9828,24 @@ class Box2dObject extends EngineObject
|
|
|
9826
9828
|
bodyDef.set_type(bodyType);
|
|
9827
9829
|
bodyDef.set_position(box2d.vec2dTo(pos));
|
|
9828
9830
|
bodyDef.set_angle(-angle);
|
|
9831
|
+
|
|
9832
|
+
/** @property {Object} - The Box2d body */
|
|
9829
9833
|
this.body = box2d.world.CreateBody(bodyDef);
|
|
9830
|
-
|
|
9834
|
+
/** @property {Color} - Line color used for default box2d drawing */
|
|
9831
9835
|
this.lineColor = BLACK;
|
|
9832
|
-
box2d
|
|
9833
|
-
|
|
9834
|
-
// edge lists and loops for drawing
|
|
9836
|
+
/** @property {Array<Object>} - List of all edges for default box2d drawing */
|
|
9835
9837
|
this.edgeLists = [];
|
|
9838
|
+
/** @property {Array<Object>} - List of all edge loops for default box2d drawing */
|
|
9836
9839
|
this.edgeLoops = [];
|
|
9840
|
+
|
|
9841
|
+
this.body.object = this; // link body to this object
|
|
9842
|
+
box2d.objects.push(this); // keep track of all box2d objects
|
|
9837
9843
|
}
|
|
9838
9844
|
|
|
9839
9845
|
/** Destroy this object and its physics body */
|
|
9840
9846
|
destroy()
|
|
9841
9847
|
{
|
|
9842
|
-
if (this.destroyed)
|
|
9843
|
-
return;
|
|
9848
|
+
if (this.destroyed) return;
|
|
9844
9849
|
|
|
9845
9850
|
// destroy physics body, fixtures, and joints
|
|
9846
9851
|
ASSERT(this.body, 'Box2dObject has no body to destroy');
|
|
@@ -9871,11 +9876,12 @@ class Box2dObject extends EngineObject
|
|
|
9871
9876
|
}
|
|
9872
9877
|
|
|
9873
9878
|
/** Draws all this object's fixtures
|
|
9874
|
-
* @param {Color}
|
|
9875
|
-
* @param {Color}
|
|
9876
|
-
* @param {number}
|
|
9879
|
+
* @param {Color} [color]
|
|
9880
|
+
* @param {Color} [lineColor]
|
|
9881
|
+
* @param {number} [lineWidth]
|
|
9882
|
+
* @param {boolean} [useWebGL=glEnable]
|
|
9877
9883
|
* @param {CanvasRenderingContext2D} [context] */
|
|
9878
|
-
drawFixtures(color=WHITE, lineColor=BLACK, lineWidth=.1, context)
|
|
9884
|
+
drawFixtures(color=WHITE, lineColor=BLACK, lineWidth=.1, useWebGL, context)
|
|
9879
9885
|
{
|
|
9880
9886
|
// draw non-edge fixtures
|
|
9881
9887
|
this.getFixtureList().forEach((fixture)=>
|
|
@@ -9883,7 +9889,7 @@ class Box2dObject extends EngineObject
|
|
|
9883
9889
|
const shape = box2d.castObjectType(fixture.GetShape());
|
|
9884
9890
|
if (shape.GetType() !== box2d.instance.b2Shape.e_edge)
|
|
9885
9891
|
{
|
|
9886
|
-
box2d.drawFixture(fixture, this.pos, this.angle, color, lineColor, lineWidth, context);
|
|
9892
|
+
box2d.drawFixture(fixture, this.pos, this.angle, color, lineColor, lineWidth, useWebGL, context);
|
|
9887
9893
|
}
|
|
9888
9894
|
});
|
|
9889
9895
|
|
|
@@ -10109,6 +10115,14 @@ class Box2dObject extends EngineObject
|
|
|
10109
10115
|
return fixtures;
|
|
10110
10116
|
}
|
|
10111
10117
|
|
|
10118
|
+
/** Destroy a fixture from the body
|
|
10119
|
+
* @param {Object} [fixture] */
|
|
10120
|
+
destroyFixture(fixture) { this.body.DestroyFixture(fixture); }
|
|
10121
|
+
|
|
10122
|
+
/** Destroy all fixture from the body */
|
|
10123
|
+
destroyAllFixtures()
|
|
10124
|
+
{ this.getFixtureList().forEach(fixture=>this.destroyFixture(fixture)); }
|
|
10125
|
+
|
|
10112
10126
|
///////////////////////////////////////////////////////////////////////////////
|
|
10113
10127
|
// physics get functions
|
|
10114
10128
|
|
|
@@ -10341,6 +10355,136 @@ class Box2dObject extends EngineObject
|
|
|
10341
10355
|
}
|
|
10342
10356
|
}
|
|
10343
10357
|
|
|
10358
|
+
///////////////////////////////////////////////////////////////////////////////
|
|
10359
|
+
/**
|
|
10360
|
+
* Box2D Static Object - Box2d with a static physics body
|
|
10361
|
+
* @extends Box2dObject
|
|
10362
|
+
* @memberof Box2D
|
|
10363
|
+
*/
|
|
10364
|
+
class Box2dStaticObject extends Box2dObject
|
|
10365
|
+
{
|
|
10366
|
+
/** Create a LittleJS object with Box2d physics
|
|
10367
|
+
* @param {Vector2} [pos]
|
|
10368
|
+
* @param {Vector2} [size]
|
|
10369
|
+
* @param {TileInfo} [tileInfo]
|
|
10370
|
+
* @param {number} [angle]
|
|
10371
|
+
* @param {Color} [color]
|
|
10372
|
+
* @param {number} [renderOrder] */
|
|
10373
|
+
constructor(pos, size, tileInfo, angle=0, color, renderOrder=0)
|
|
10374
|
+
{
|
|
10375
|
+
const bodyType = box2d.bodyTypeStatic;
|
|
10376
|
+
super(pos, size, tileInfo, angle, color, bodyType, renderOrder);
|
|
10377
|
+
}
|
|
10378
|
+
}
|
|
10379
|
+
|
|
10380
|
+
///////////////////////////////////////////////////////////////////////////////
|
|
10381
|
+
/**
|
|
10382
|
+
* Box2D Kiematic Object - Box2d with a kinematic physics body
|
|
10383
|
+
* @extends Box2dObject
|
|
10384
|
+
* @memberof Box2D
|
|
10385
|
+
*/
|
|
10386
|
+
class Box2dKiematicObject extends Box2dObject
|
|
10387
|
+
{
|
|
10388
|
+
/** Create a LittleJS object with Box2d physics
|
|
10389
|
+
* @param {Vector2} [pos]
|
|
10390
|
+
* @param {Vector2} [size]
|
|
10391
|
+
* @param {TileInfo} [tileInfo]
|
|
10392
|
+
* @param {number} [angle]
|
|
10393
|
+
* @param {Color} [color]
|
|
10394
|
+
* @param {number} [renderOrder] */
|
|
10395
|
+
constructor(pos, size, tileInfo, angle=0, color, renderOrder=0)
|
|
10396
|
+
{
|
|
10397
|
+
const bodyType = box2d.bodyTypeKinematic;
|
|
10398
|
+
super(pos, size, tileInfo, angle, color, bodyType, renderOrder);
|
|
10399
|
+
}
|
|
10400
|
+
}
|
|
10401
|
+
|
|
10402
|
+
///////////////////////////////////////////////////////////////////////////////
|
|
10403
|
+
/**
|
|
10404
|
+
* Box2d Tile Layer
|
|
10405
|
+
* - adds Box2d support to tile layers
|
|
10406
|
+
* - creates static box2d fixtures for solid tiles
|
|
10407
|
+
* @extends Box2dObject
|
|
10408
|
+
* @memberof Box2D
|
|
10409
|
+
*/
|
|
10410
|
+
class Box2dTileLayer extends Box2dStaticObject
|
|
10411
|
+
{
|
|
10412
|
+
/** Create a Box2d tile layer object
|
|
10413
|
+
* @param {TileCollisionLayer} tileLayer - Tile layer for this object */
|
|
10414
|
+
constructor(tileLayer)
|
|
10415
|
+
{
|
|
10416
|
+
ASSERT(tileLayer instanceof TileCollisionLayer, 'tileLayer must be a TileCollisionLayer');
|
|
10417
|
+
super(tileLayer.pos, tileLayer.size);
|
|
10418
|
+
|
|
10419
|
+
/** @property {TileLayer} - The tile layer */
|
|
10420
|
+
this.tileLayer = tileLayer;
|
|
10421
|
+
this.addChild(tileLayer);
|
|
10422
|
+
}
|
|
10423
|
+
|
|
10424
|
+
render()
|
|
10425
|
+
{
|
|
10426
|
+
// do not render fixtures, tile layer handles rendering
|
|
10427
|
+
}
|
|
10428
|
+
|
|
10429
|
+
/** Create box2d collision fixtures for solid tiles
|
|
10430
|
+
* @param {number} [friction]
|
|
10431
|
+
* @param {number} [restitution] */
|
|
10432
|
+
buildCollision(friction=.2, restitution=0)
|
|
10433
|
+
{
|
|
10434
|
+
// destroy all fixtures and create new ones
|
|
10435
|
+
this.destroyAllFixtures();
|
|
10436
|
+
|
|
10437
|
+
// create box2d object for this layer
|
|
10438
|
+
const size = this.tileLayer.size;
|
|
10439
|
+
this.pos = this.tileLayer.pos.copy();
|
|
10440
|
+
this.size = size.copy();
|
|
10441
|
+
|
|
10442
|
+
// track which tiles have been processed
|
|
10443
|
+
const processed = [];
|
|
10444
|
+
const getIndex = (x, y)=> x + y * size.x;
|
|
10445
|
+
const isSolidUnprocessed = (x, y)=>
|
|
10446
|
+
!processed[getIndex(x, y)] &&
|
|
10447
|
+
this.tileLayer.getCollisionData(vec2(x, y)) > 0;
|
|
10448
|
+
|
|
10449
|
+
// combine tiles into larger boxes
|
|
10450
|
+
for (let x = 0; x < size.x; ++x)
|
|
10451
|
+
for (let y = 0; y < size.y; ++y)
|
|
10452
|
+
{
|
|
10453
|
+
if (!isSolidUnprocessed(x, y)) continue;
|
|
10454
|
+
|
|
10455
|
+
// find max width by scanning right
|
|
10456
|
+
let width = 1, height = 1, canExpand = true;
|
|
10457
|
+
while (isSolidUnprocessed(x + width, y))
|
|
10458
|
+
++width;
|
|
10459
|
+
|
|
10460
|
+
// find max height by scanning up, ensuring all rows have the same width
|
|
10461
|
+
while (canExpand)
|
|
10462
|
+
{
|
|
10463
|
+
for (let checkX = 0; checkX < width; ++checkX)
|
|
10464
|
+
{
|
|
10465
|
+
if (!isSolidUnprocessed(x + checkX, y + height))
|
|
10466
|
+
{
|
|
10467
|
+
canExpand = false;
|
|
10468
|
+
break;
|
|
10469
|
+
}
|
|
10470
|
+
}
|
|
10471
|
+
if (canExpand)
|
|
10472
|
+
++height;
|
|
10473
|
+
}
|
|
10474
|
+
|
|
10475
|
+
// mark all tiles in this rectangle as processed
|
|
10476
|
+
for (let rectX = width; rectX--;)
|
|
10477
|
+
for (let rectY = height; rectY--;)
|
|
10478
|
+
processed[getIndex(x + rectX, y + rectY)] = true;
|
|
10479
|
+
|
|
10480
|
+
// create a single fixture for the entire rectangle
|
|
10481
|
+
const shapeSize = vec2(width, height);
|
|
10482
|
+
const offset = vec2(x + width/2, y + height/2);
|
|
10483
|
+
this.addBox(shapeSize, offset, 0, 0, friction, restitution);
|
|
10484
|
+
}
|
|
10485
|
+
}
|
|
10486
|
+
}
|
|
10487
|
+
|
|
10344
10488
|
///////////////////////////////////////////////////////////////////////////////
|
|
10345
10489
|
/**
|
|
10346
10490
|
* Box2D Raycast Result
|
|
@@ -10382,6 +10526,7 @@ class Box2dJoint
|
|
|
10382
10526
|
* @param {Object} jointDef */
|
|
10383
10527
|
constructor(jointDef)
|
|
10384
10528
|
{
|
|
10529
|
+
/** @property {Object} - The Box2d joint */
|
|
10385
10530
|
this.box2dJoint = box2d.castObjectType(box2d.world.CreateJoint(jointDef));
|
|
10386
10531
|
}
|
|
10387
10532
|
|
|
@@ -10728,7 +10873,7 @@ class Box2dGearJoint extends Box2dJoint
|
|
|
10728
10873
|
* @param {Box2dObject} objectB
|
|
10729
10874
|
* @param {Box2dJoint} joint1
|
|
10730
10875
|
* @param {Box2dJoint} joint2
|
|
10731
|
-
* @param {
|
|
10876
|
+
* @param {number} [ratio] */
|
|
10732
10877
|
constructor(objectA, objectB, joint1, joint2, ratio=1)
|
|
10733
10878
|
{
|
|
10734
10879
|
const jointDef = new box2d.instance.b2GearJointDef();
|
|
@@ -10870,50 +11015,6 @@ class Box2dPrismaticJoint extends Box2dJoint
|
|
|
10870
11015
|
getMotorForce(time) { return this.box2dJoint.GetMotorForce(1/time); }
|
|
10871
11016
|
}
|
|
10872
11017
|
|
|
10873
|
-
///////////////////////////////////////////////////////////////////////////////
|
|
10874
|
-
/**
|
|
10875
|
-
* Box2D Static Object - Box2d with a static physics body
|
|
10876
|
-
* @extends Box2dObject
|
|
10877
|
-
* @memberof Box2D
|
|
10878
|
-
*/
|
|
10879
|
-
class Box2dStaticObject extends Box2dObject
|
|
10880
|
-
{
|
|
10881
|
-
/** Create a LittleJS object with Box2d physics
|
|
10882
|
-
* @param {Vector2} [pos]
|
|
10883
|
-
* @param {Vector2} [size]
|
|
10884
|
-
* @param {TileInfo} [tileInfo]
|
|
10885
|
-
* @param {number} [angle]
|
|
10886
|
-
* @param {Color} [color]
|
|
10887
|
-
* @param {number} [renderOrder] */
|
|
10888
|
-
constructor(pos, size, tileInfo, angle=0, color, renderOrder=0)
|
|
10889
|
-
{
|
|
10890
|
-
const bodyType = box2d.bodyTypeStatic;
|
|
10891
|
-
super(pos, size, tileInfo, angle, color, bodyType, renderOrder);
|
|
10892
|
-
}
|
|
10893
|
-
}
|
|
10894
|
-
|
|
10895
|
-
///////////////////////////////////////////////////////////////////////////////
|
|
10896
|
-
/**
|
|
10897
|
-
* Box2D Kiematic Object - Box2d with a kinematic physics body
|
|
10898
|
-
* @extends Box2dObject
|
|
10899
|
-
* @memberof Box2D
|
|
10900
|
-
*/
|
|
10901
|
-
class Box2dKiematicObject extends Box2dObject
|
|
10902
|
-
{
|
|
10903
|
-
/** Create a LittleJS object with Box2d physics
|
|
10904
|
-
* @param {Vector2} [pos]
|
|
10905
|
-
* @param {Vector2} [size]
|
|
10906
|
-
* @param {TileInfo} [tileInfo]
|
|
10907
|
-
* @param {number} [angle]
|
|
10908
|
-
* @param {Color} [color]
|
|
10909
|
-
* @param {number} [renderOrder] */
|
|
10910
|
-
constructor(pos, size, tileInfo, angle=0, color, renderOrder=0)
|
|
10911
|
-
{
|
|
10912
|
-
const bodyType = box2d.bodyTypeKinematic;
|
|
10913
|
-
super(pos, size, tileInfo, angle, color, bodyType, renderOrder);
|
|
10914
|
-
}
|
|
10915
|
-
}
|
|
10916
|
-
|
|
10917
11018
|
///////////////////////////////////////////////////////////////////////////////
|
|
10918
11019
|
/**
|
|
10919
11020
|
* Box2D Wheel Joint
|
|
@@ -11274,10 +11375,13 @@ class Box2dPlugin
|
|
|
11274
11375
|
{
|
|
11275
11376
|
ASSERT(!box2d, 'Box2D already initialized');
|
|
11276
11377
|
box2d = this;
|
|
11378
|
+
|
|
11379
|
+
/** @property {Object} - The Box2d instance */
|
|
11277
11380
|
this.instance = instance;
|
|
11381
|
+
/** @property {Object} - The Box2d world */
|
|
11278
11382
|
this.world = new box2d.instance.b2World();
|
|
11383
|
+
/** @property {Array<Box2dObject>} - List of all Box2d objects */
|
|
11279
11384
|
this.objects = [];
|
|
11280
|
-
|
|
11281
11385
|
/** @property {number} - Velocity iterations per update*/
|
|
11282
11386
|
this.velocityIterations = 8;
|
|
11283
11387
|
/** @property {number} - Position iterations per update*/
|
|
@@ -11476,8 +11580,9 @@ class Box2dPlugin
|
|
|
11476
11580
|
* @param {Color} [color]
|
|
11477
11581
|
* @param {Color} [lineColor]
|
|
11478
11582
|
* @param {number} [lineWidth]
|
|
11583
|
+
* @param {boolean} [useWebGL=glEnable]
|
|
11479
11584
|
* @param {CanvasRenderingContext2D} [context] */
|
|
11480
|
-
drawFixture(fixture, pos, angle, color=WHITE, lineColor=BLACK, lineWidth=.1, context)
|
|
11585
|
+
drawFixture(fixture, pos, angle, color=WHITE, lineColor=BLACK, lineWidth=.1, useWebgl, context)
|
|
11481
11586
|
{
|
|
11482
11587
|
const shape = box2d.castObjectType(fixture.GetShape());
|
|
11483
11588
|
switch (shape.GetType())
|
|
@@ -11487,20 +11592,20 @@ class Box2dPlugin
|
|
|
11487
11592
|
let points = [];
|
|
11488
11593
|
for (let i=shape.GetVertexCount(); i--;)
|
|
11489
11594
|
points.push(box2d.vec2From(shape.GetVertex(i)));
|
|
11490
|
-
drawPoly(points, color, lineWidth, lineColor, pos, angle);
|
|
11595
|
+
drawPoly(points, color, lineWidth, lineColor, pos, angle, useWebgl, false, context);
|
|
11491
11596
|
break;
|
|
11492
11597
|
}
|
|
11493
11598
|
case box2d.instance.b2Shape.e_circle:
|
|
11494
11599
|
{
|
|
11495
11600
|
const radius = shape.get_m_radius();
|
|
11496
|
-
drawCircle(pos, radius*2, color, lineWidth, lineColor);
|
|
11601
|
+
drawCircle(pos, radius*2, color, lineWidth, lineColor, useWebgl, false, context);
|
|
11497
11602
|
break;
|
|
11498
11603
|
}
|
|
11499
11604
|
case box2d.instance.b2Shape.e_edge:
|
|
11500
11605
|
{
|
|
11501
11606
|
const v1 = box2d.vec2From(shape.get_m_vertex1());
|
|
11502
11607
|
const v2 = box2d.vec2From(shape.get_m_vertex2());
|
|
11503
|
-
drawLine(v1, v2, lineWidth, lineColor, pos, angle);
|
|
11608
|
+
drawLine(v1, v2, lineWidth, lineColor, pos, angle, useWebgl, false, context);
|
|
11504
11609
|
break;
|
|
11505
11610
|
}
|
|
11506
11611
|
}
|
|
@@ -11518,7 +11623,7 @@ class Box2dPlugin
|
|
|
11518
11623
|
}
|
|
11519
11624
|
|
|
11520
11625
|
/** converts a box2d vec2 pointer to a Vector2
|
|
11521
|
-
* @param {Object}
|
|
11626
|
+
* @param {Object} vp */
|
|
11522
11627
|
vec2FromPointer(vp)
|
|
11523
11628
|
{
|
|
11524
11629
|
const v = box2d.instance.wrapPointer(vp, box2d.instance.b2Vec2);
|
|
@@ -11630,7 +11735,7 @@ async function box2dInit()
|
|
|
11630
11735
|
const box2dColorPointer = (c)=>
|
|
11631
11736
|
box2dColor(box2d.instance.wrapPointer(c, box2d.instance.b2Color));
|
|
11632
11737
|
const getDebugColor = (color)=>box2dColorPointer(color).scale(1,.8);
|
|
11633
|
-
const getPointsList = (vertices, vertexCount)
|
|
11738
|
+
const getPointsList = (vertices, vertexCount)=>
|
|
11634
11739
|
{
|
|
11635
11740
|
const points = [];
|
|
11636
11741
|
for (let i=vertexCount; i--;)
|