littlejsengine 1.7.12 → 1.7.21
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/build/littlejs.d.ts +153 -19
- package/build/littlejs.esm.js +164 -117
- package/build/littlejs.esm.min.js +1 -1
- package/build/littlejs.js +164 -117
- package/build/littlejs.min.js +1 -1
- package/build/littlejs.release.js +159 -116
- package/examples/breakout/index.html +3 -3
- package/examples/breakoutTutorial/index.html +2 -2
- package/examples/electron/build.js +2 -0
- package/examples/electron/index.html +2 -2
- package/examples/js13k/build.js +2 -0
- package/examples/js13k/index.html +13 -13
- package/examples/module/index.html +1 -1
- package/examples/particles/index.html +1 -1
- package/examples/platformer/index.html +6 -6
- package/examples/puzzle/index.html +2 -2
- package/examples/screenshot.jpg +0 -0
- package/examples/starter/build.js +2 -0
- package/examples/starter/game.js +1 -1
- package/examples/starter/index.html +13 -13
- package/examples/stress/index.html +1 -1
- package/examples/typescript/build.js +2 -0
- package/examples/typescript/index.html +1 -1
- package/package.json +1 -1
- package/src/engine.js +10 -7
- package/src/engineAudio.js +67 -41
- package/src/engineBuild.js +23 -7
- package/src/engineDebug.js +5 -1
- package/src/engineDraw.js +3 -3
- package/src/engineInput.js +22 -10
- package/src/engineWebGL.js +43 -43
package/build/littlejs.esm.js
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
// LittleJS - MIT License - Copyright 2021 Frank Force
|
|
2
2
|
|
|
3
|
+
'use strict';
|
|
4
|
+
|
|
3
5
|
/**
|
|
4
6
|
* LittleJS Debug System
|
|
5
7
|
* - Press Esc to show debug overlay with mouse pick
|
|
@@ -10,7 +12,7 @@
|
|
|
10
12
|
* @namespace Debug
|
|
11
13
|
*/
|
|
12
14
|
|
|
13
|
-
|
|
15
|
+
|
|
14
16
|
|
|
15
17
|
/** True if debug is enabled
|
|
16
18
|
* @type {Boolean}
|
|
@@ -213,7 +215,11 @@ function debugRender()
|
|
|
213
215
|
mainContext.drawImage(overlayCanvas, 0, 0);
|
|
214
216
|
overlayCanvas.width |= 0;
|
|
215
217
|
|
|
216
|
-
|
|
218
|
+
// remove alpha and save
|
|
219
|
+
const w = mainCanvas.width, h = mainCanvas.height;
|
|
220
|
+
overlayContext.fillRect(0,0,w,h);
|
|
221
|
+
overlayContext.drawImage(mainCanvas, 0, 0);
|
|
222
|
+
debugSaveCanvas(overlayCanvas);
|
|
217
223
|
debugTakeScreenshot = 0;
|
|
218
224
|
}
|
|
219
225
|
|
|
@@ -414,7 +420,7 @@ function debugRender()
|
|
|
414
420
|
* @namespace Utilities
|
|
415
421
|
*/
|
|
416
422
|
|
|
417
|
-
|
|
423
|
+
|
|
418
424
|
|
|
419
425
|
/** A shortcut to get Math.PI
|
|
420
426
|
* @type {Number}
|
|
@@ -1069,7 +1075,7 @@ class Timer
|
|
|
1069
1075
|
* @namespace Settings
|
|
1070
1076
|
*/
|
|
1071
1077
|
|
|
1072
|
-
|
|
1078
|
+
|
|
1073
1079
|
|
|
1074
1080
|
///////////////////////////////////////////////////////////////////////////////
|
|
1075
1081
|
// Camera settings
|
|
@@ -1317,7 +1323,7 @@ let medalsPreventUnlock;
|
|
|
1317
1323
|
* LittleJS Object System
|
|
1318
1324
|
*/
|
|
1319
1325
|
|
|
1320
|
-
|
|
1326
|
+
|
|
1321
1327
|
|
|
1322
1328
|
/**
|
|
1323
1329
|
* LittleJS Object Base Object Class
|
|
@@ -1713,7 +1719,7 @@ class EngineObject
|
|
|
1713
1719
|
* @namespace Draw
|
|
1714
1720
|
*/
|
|
1715
1721
|
|
|
1716
|
-
|
|
1722
|
+
|
|
1717
1723
|
|
|
1718
1724
|
/** The primary 2D canvas visible to the user
|
|
1719
1725
|
* @type {HTMLCanvasElement}
|
|
@@ -1884,7 +1890,7 @@ function drawPoly(points, color=new Color, useWebGL=glEnable, screenSpace)
|
|
|
1884
1890
|
* @param {Boolean} [useWebGL=glEnable]
|
|
1885
1891
|
* @param {Boolean} [screenSpace=0]
|
|
1886
1892
|
* @memberof Draw */
|
|
1887
|
-
function drawLine(posA, posB, thickness=.1, color, useWebGL)
|
|
1893
|
+
function drawLine(posA, posB, thickness=.1, color, useWebGL, screenSpace)
|
|
1888
1894
|
{
|
|
1889
1895
|
const halfDelta = vec2((posB.x - posA.x)/2, (posB.y - posA.y)/2);
|
|
1890
1896
|
const size = vec2(thickness, halfDelta.length()*2);
|
|
@@ -1904,12 +1910,12 @@ function drawCanvas2D(pos, size, angle, mirror, drawFunction, context = mainCont
|
|
|
1904
1910
|
{
|
|
1905
1911
|
if (!screenSpace)
|
|
1906
1912
|
{
|
|
1907
|
-
//
|
|
1913
|
+
// transform from world space to screen space
|
|
1908
1914
|
pos = worldToScreen(pos);
|
|
1909
1915
|
size = size.scale(cameraScale);
|
|
1910
1916
|
}
|
|
1911
1917
|
context.save();
|
|
1912
|
-
context.translate(pos.x+.5
|
|
1918
|
+
context.translate(pos.x+.5, pos.y+.5);
|
|
1913
1919
|
context.rotate(angle);
|
|
1914
1920
|
context.scale(mirror ? -size.x : size.x, size.y);
|
|
1915
1921
|
drawFunction(context);
|
|
@@ -2094,7 +2100,7 @@ function toggleFullscreen()
|
|
|
2094
2100
|
* @namespace Input
|
|
2095
2101
|
*/
|
|
2096
2102
|
|
|
2097
|
-
|
|
2103
|
+
|
|
2098
2104
|
|
|
2099
2105
|
/** Returns true if device key is down
|
|
2100
2106
|
* @param {Number} key
|
|
@@ -2278,18 +2284,26 @@ function mouseToScreen(mousePos)
|
|
|
2278
2284
|
const stickData = [];
|
|
2279
2285
|
function gamepadsUpdate()
|
|
2280
2286
|
{
|
|
2281
|
-
|
|
2287
|
+
// update touch gamepad if enabled
|
|
2288
|
+
if (touchGamepadEnable && isTouchDevice)
|
|
2282
2289
|
{
|
|
2283
|
-
//
|
|
2284
|
-
|
|
2285
|
-
|
|
2290
|
+
// create the touch gamepad if it doesn't exist
|
|
2291
|
+
if (!touchGamepadButtons)
|
|
2292
|
+
createTouchGamepad();
|
|
2286
2293
|
|
|
2287
|
-
|
|
2288
|
-
const data = inputData[1] || (inputData[1] = []);
|
|
2289
|
-
for (let i=10; i--;)
|
|
2294
|
+
if (touchGamepadTimer.isSet())
|
|
2290
2295
|
{
|
|
2291
|
-
|
|
2292
|
-
|
|
2296
|
+
// read virtual analog stick
|
|
2297
|
+
const sticks = stickData[0] || (stickData[0] = []);
|
|
2298
|
+
sticks[0] = vec2(touchGamepadStick.x, -touchGamepadStick.y); // flip vertical
|
|
2299
|
+
|
|
2300
|
+
// read virtual gamepad buttons
|
|
2301
|
+
const data = inputData[1] || (inputData[1] = []);
|
|
2302
|
+
for (let i=10; i--;)
|
|
2303
|
+
{
|
|
2304
|
+
const j = i == 3 ? 2 : i == 2 ? 3 : i; // fix button locations
|
|
2305
|
+
data[j] = touchGamepadButtons[i] ? 1 + 2*!gamepadIsDown(j,0) : 4*gamepadIsDown(j,0);
|
|
2306
|
+
}
|
|
2293
2307
|
}
|
|
2294
2308
|
}
|
|
2295
2309
|
|
|
@@ -2386,6 +2400,10 @@ if (isTouchDevice)
|
|
|
2386
2400
|
// set was touching
|
|
2387
2401
|
wasTouching = touching;
|
|
2388
2402
|
|
|
2403
|
+
// prevent default handling like copy and magnifier lens
|
|
2404
|
+
if (document.hasFocus()) // allow document to get focus
|
|
2405
|
+
e.preventDefault();
|
|
2406
|
+
|
|
2389
2407
|
// must return true so the document will get focus
|
|
2390
2408
|
return true;
|
|
2391
2409
|
}
|
|
@@ -2398,7 +2416,7 @@ if (isTouchDevice)
|
|
|
2398
2416
|
let touchGamepadTimer = new Timer, touchGamepadButtons, touchGamepadStick;
|
|
2399
2417
|
|
|
2400
2418
|
// create the touch gamepad, called automatically by the engine
|
|
2401
|
-
|
|
2419
|
+
function createTouchGamepad()
|
|
2402
2420
|
{
|
|
2403
2421
|
// touch input internal variables
|
|
2404
2422
|
touchGamepadButtons = [];
|
|
@@ -2532,7 +2550,7 @@ function touchGamepadRender()
|
|
|
2532
2550
|
* @namespace Audio
|
|
2533
2551
|
*/
|
|
2534
2552
|
|
|
2535
|
-
|
|
2553
|
+
|
|
2536
2554
|
|
|
2537
2555
|
/**
|
|
2538
2556
|
* Sound Object - Stores a zzfx sound for later use and can be played positionally
|
|
@@ -2568,8 +2586,9 @@ class Sound
|
|
|
2568
2586
|
if (zzfxSound)
|
|
2569
2587
|
{
|
|
2570
2588
|
// generate zzfx sound now for fast playback
|
|
2571
|
-
this.randomness = zzfxSound[1] || (zzfxSound[1] = 0);
|
|
2572
|
-
this.
|
|
2589
|
+
this.randomness = zzfxSound[1] || (zzfxSound[1] = 0);
|
|
2590
|
+
this.sampleChannels = [zzfxG(...zzfxSound)];
|
|
2591
|
+
this.sampleRate = zzfxR;
|
|
2573
2592
|
}
|
|
2574
2593
|
}
|
|
2575
2594
|
|
|
@@ -2578,11 +2597,12 @@ class Sound
|
|
|
2578
2597
|
* @param {Number} [volume=1] - How much to scale volume by (in addition to range fade)
|
|
2579
2598
|
* @param {Number} [pitch=1] - How much to scale pitch by (also adjusted by this.randomness)
|
|
2580
2599
|
* @param {Number} [randomnessScale=1] - How much to scale randomness
|
|
2581
|
-
* @
|
|
2600
|
+
* @param {Boolean} [loop=0] - Should the sound loop
|
|
2601
|
+
* @return {AudioBufferSourceNode} - The audio source node
|
|
2582
2602
|
*/
|
|
2583
|
-
play(pos, volume=1, pitch=1, randomnessScale=1)
|
|
2603
|
+
play(pos, volume=1, pitch=1, randomnessScale=1, loop=0)
|
|
2584
2604
|
{
|
|
2585
|
-
if (!soundEnable || !this.
|
|
2605
|
+
if (!soundEnable || !this.sampleChannels) return;
|
|
2586
2606
|
|
|
2587
2607
|
let pan;
|
|
2588
2608
|
if (pos)
|
|
@@ -2605,43 +2625,80 @@ class Sound
|
|
|
2605
2625
|
|
|
2606
2626
|
// play the sound
|
|
2607
2627
|
const playbackRate = pitch + pitch * this.randomness*randomnessScale*rand(-1,1);
|
|
2608
|
-
return playSamples(
|
|
2628
|
+
return this.source = playSamples(this.sampleChannels, volume, playbackRate, pan, loop, this.sampleRate);
|
|
2629
|
+
}
|
|
2630
|
+
|
|
2631
|
+
/** Stop the last instance of this sound that was played */
|
|
2632
|
+
stop()
|
|
2633
|
+
{
|
|
2634
|
+
if (this.source)
|
|
2635
|
+
this.source.stop();
|
|
2636
|
+
this.source = 0;
|
|
2609
2637
|
}
|
|
2610
2638
|
|
|
2611
2639
|
/** Play the sound as a note with a semitone offset
|
|
2612
2640
|
* @param {Number} semitoneOffset - How many semitones to offset pitch
|
|
2613
2641
|
* @param {Vector2} [pos] - World space position to play the sound, sound is not attenuated if null
|
|
2614
2642
|
* @param {Number} [volume=1] - How much to scale volume by (in addition to range fade)
|
|
2615
|
-
* @return {AudioBufferSourceNode} - The audio
|
|
2643
|
+
* @return {AudioBufferSourceNode} - The audio source node
|
|
2616
2644
|
*/
|
|
2617
2645
|
playNote(semitoneOffset, pos, volume)
|
|
2618
2646
|
{ return this.play(pos, volume, 2**(semitoneOffset/12), 0); }
|
|
2647
|
+
|
|
2648
|
+
/** Get how long this sound is in seconds
|
|
2649
|
+
* @return {Number} - How long the sound is in seconds (undefined if loading)
|
|
2650
|
+
*/
|
|
2651
|
+
getDuration()
|
|
2652
|
+
{ return this.sampleChannels && this.sampleChannels[0].length / this.sampleRate; }
|
|
2653
|
+
|
|
2654
|
+
/** Check if the last instance of this sound is playing
|
|
2655
|
+
* @return {Boolean} - True if the sound is playing
|
|
2656
|
+
*/
|
|
2657
|
+
isPlaying() { return this.source && !this.source.ended; }
|
|
2658
|
+
|
|
2659
|
+
/** Check if sound is loading, for sounds fetched from a url
|
|
2660
|
+
* @return {Boolean} - True if sound is loading and not ready to play
|
|
2661
|
+
*/
|
|
2662
|
+
isLoading() { return !this.sampleChannels; }
|
|
2619
2663
|
}
|
|
2620
2664
|
|
|
2621
2665
|
/**
|
|
2622
2666
|
* Sound Wave Object - Stores a wave sound for later use and can be played positionally
|
|
2667
|
+
* - this can be used to play wave, mp3, and ogg files
|
|
2668
|
+
* @example
|
|
2669
|
+
* // create a sound
|
|
2670
|
+
* const sound_example = new SoundWave('sound.mp3');
|
|
2671
|
+
*
|
|
2672
|
+
* // play the sound
|
|
2673
|
+
* sound_example.play();
|
|
2623
2674
|
*/
|
|
2624
2675
|
class SoundWave extends Sound
|
|
2625
2676
|
{
|
|
2626
2677
|
/** Create a sound object and cache the wave file for later use
|
|
2627
|
-
* @param {String}
|
|
2628
|
-
* @param {Number} [randomness
|
|
2678
|
+
* @param {String} filename - Filename of audio file to load
|
|
2679
|
+
* @param {Number} [randomness=0] - How much to randomize frequency each time sound plays
|
|
2629
2680
|
* @param {Number} [range=soundDefaultRange] - World space max range of sound, will not play if camera is farther away
|
|
2630
2681
|
* @param {Number} [taper=soundDefaultTaper] - At what percentage of range should it start tapering off
|
|
2631
2682
|
*/
|
|
2632
|
-
constructor(
|
|
2683
|
+
constructor(filename, randomness=0, range, taper)
|
|
2633
2684
|
{
|
|
2634
2685
|
super(0, range, taper);
|
|
2635
2686
|
this.randomness = randomness;
|
|
2636
2687
|
|
|
2637
2688
|
if (!soundEnable) return;
|
|
2638
|
-
if (!
|
|
2689
|
+
if (!soundDecoderContext)
|
|
2639
2690
|
soundDecoderContext = new AudioContext;
|
|
2640
2691
|
|
|
2641
|
-
fetch(
|
|
2692
|
+
fetch(filename)
|
|
2642
2693
|
.then(response => response.arrayBuffer())
|
|
2643
|
-
.then(arrayBuffer =>
|
|
2644
|
-
.then(audioBuffer =>
|
|
2694
|
+
.then(arrayBuffer => soundDecoderContext.decodeAudioData(arrayBuffer))
|
|
2695
|
+
.then(audioBuffer =>
|
|
2696
|
+
{
|
|
2697
|
+
this.sampleChannels = [];
|
|
2698
|
+
for (let i = audioBuffer.numberOfChannels; i--;)
|
|
2699
|
+
this.sampleChannels[i] = audioBuffer.getChannelData(i);
|
|
2700
|
+
this.sampleRate = audioBuffer.sampleRate;
|
|
2701
|
+
});
|
|
2645
2702
|
}
|
|
2646
2703
|
}
|
|
2647
2704
|
let soundDecoderContext; // audio context used only to decode audio files
|
|
@@ -2676,48 +2733,34 @@ let soundDecoderContext; // audio context used only to decode audio files
|
|
|
2676
2733
|
* // play the music
|
|
2677
2734
|
* music_example.play();
|
|
2678
2735
|
*/
|
|
2679
|
-
class Music
|
|
2736
|
+
class Music extends Sound
|
|
2680
2737
|
{
|
|
2681
2738
|
/** Create a music object and cache the zzfx music samples for later use
|
|
2682
2739
|
* @param {Array} zzfxMusic - Array of zzfx music parameters
|
|
2683
2740
|
*/
|
|
2684
2741
|
constructor(zzfxMusic)
|
|
2685
2742
|
{
|
|
2686
|
-
|
|
2743
|
+
super();
|
|
2687
2744
|
|
|
2688
|
-
|
|
2745
|
+
if (!soundEnable) return;
|
|
2746
|
+
this.randomness = 0;
|
|
2747
|
+
this.sampleChannels = zzfxM(...zzfxMusic);
|
|
2748
|
+
this.sampleRate = zzfxR;
|
|
2689
2749
|
}
|
|
2690
2750
|
|
|
2691
2751
|
/** Play the music
|
|
2692
2752
|
* @param {Number} [volume=1] - How much to scale volume by
|
|
2693
|
-
* @param {Boolean} [loop=1] - True if the music should loop
|
|
2694
|
-
* @return {AudioBufferSourceNode} - The audio node
|
|
2753
|
+
* @param {Boolean} [loop=1] - True if the music should loop
|
|
2754
|
+
* @return {AudioBufferSourceNode} - The audio source node
|
|
2695
2755
|
*/
|
|
2696
2756
|
play(volume, loop = 1)
|
|
2697
|
-
{
|
|
2698
|
-
if (!soundEnable) return;
|
|
2699
|
-
|
|
2700
|
-
return this.source = playSamples(this.cachedSamples, volume, 1, 0, loop);
|
|
2701
|
-
}
|
|
2702
|
-
|
|
2703
|
-
/** Stop the music */
|
|
2704
|
-
stop()
|
|
2705
|
-
{
|
|
2706
|
-
if (this.source)
|
|
2707
|
-
this.source.stop();
|
|
2708
|
-
this.source = 0;
|
|
2709
|
-
}
|
|
2710
|
-
|
|
2711
|
-
/** Check if music is playing
|
|
2712
|
-
* @return {Boolean}
|
|
2713
|
-
*/
|
|
2714
|
-
isPlaying() { return this.source; }
|
|
2757
|
+
{ return super.play(0, volume, 1, 1, loop); }
|
|
2715
2758
|
}
|
|
2716
2759
|
|
|
2717
|
-
/** Play an mp3 or wav audio from a local file or url
|
|
2760
|
+
/** Play an mp3, ogg, or wav audio from a local file or url
|
|
2718
2761
|
* @param {String} url - Location of sound file to play
|
|
2719
2762
|
* @param {Number} [volume=1] - How much to scale volume by
|
|
2720
|
-
* @param {Boolean} [loop=1] - True if the music should loop
|
|
2763
|
+
* @param {Boolean} [loop=1] - True if the music should loop
|
|
2721
2764
|
* @return {HTMLAudioElement} - The audio element for this sound
|
|
2722
2765
|
* @memberof Audio */
|
|
2723
2766
|
function playAudioFile(url, volume=1, loop=1)
|
|
@@ -2781,9 +2824,10 @@ let audioContext;
|
|
|
2781
2824
|
* @param {Number} [rate=1] - The playback rate to use
|
|
2782
2825
|
* @param {Number} [pan=0] - How much to apply stereo panning
|
|
2783
2826
|
* @param {Boolean} [loop=0] - True if the sound should loop when it reaches the end
|
|
2827
|
+
* @param {Number} [sampleRate=44100] - Sample rate for the sound
|
|
2784
2828
|
* @return {AudioBufferSourceNode} - The audio node of the sound played
|
|
2785
2829
|
* @memberof Audio */
|
|
2786
|
-
function playSamples(sampleChannels, volume=1, rate=1, pan=0, loop=0)
|
|
2830
|
+
function playSamples(sampleChannels, volume=1, rate=1, pan=0, loop=0, sampleRate=zzfxR)
|
|
2787
2831
|
{
|
|
2788
2832
|
if (!soundEnable) return;
|
|
2789
2833
|
|
|
@@ -2800,7 +2844,7 @@ function playSamples(sampleChannels, volume=1, rate=1, pan=0, loop=0)
|
|
|
2800
2844
|
}
|
|
2801
2845
|
|
|
2802
2846
|
// create buffer and source
|
|
2803
|
-
const buffer = audioContext.createBuffer(sampleChannels.length, sampleChannels[0].length,
|
|
2847
|
+
const buffer = audioContext.createBuffer(sampleChannels.length, sampleChannels[0].length, sampleRate),
|
|
2804
2848
|
source = audioContext.createBufferSource();
|
|
2805
2849
|
|
|
2806
2850
|
// copy samples to buffer and setup source
|
|
@@ -3059,7 +3103,7 @@ function zzfxM(instruments, patterns, sequence, BPM = 125)
|
|
|
3059
3103
|
* @namespace TileCollision
|
|
3060
3104
|
*/
|
|
3061
3105
|
|
|
3062
|
-
|
|
3106
|
+
|
|
3063
3107
|
|
|
3064
3108
|
/** The tile collision layer array, use setTileCollisionData and getTileCollisionData to access
|
|
3065
3109
|
* @type {Array}
|
|
@@ -3412,7 +3456,7 @@ constructor(pos, size=tileCollisionSize, tileSize=tileSizeDefault, scale=vec2(1)
|
|
|
3412
3456
|
* LittleJS Particle System
|
|
3413
3457
|
*/
|
|
3414
3458
|
|
|
3415
|
-
|
|
3459
|
+
|
|
3416
3460
|
|
|
3417
3461
|
/**
|
|
3418
3462
|
* Particle Emitter - Spawns particles with the given settings
|
|
@@ -3722,7 +3766,7 @@ class Particle extends EngineObject
|
|
|
3722
3766
|
* @namespace Medals
|
|
3723
3767
|
*/
|
|
3724
3768
|
|
|
3725
|
-
|
|
3769
|
+
|
|
3726
3770
|
|
|
3727
3771
|
/** List of all medals
|
|
3728
3772
|
* @type {Array}
|
|
@@ -4046,7 +4090,7 @@ class Newgrounds
|
|
|
4046
4090
|
* @namespace WebGL
|
|
4047
4091
|
*/
|
|
4048
4092
|
|
|
4049
|
-
|
|
4093
|
+
|
|
4050
4094
|
|
|
4051
4095
|
/** The WebGL canvas which appears above the main canvas and below the overlay canvas
|
|
4052
4096
|
* @type {HTMLCanvasElement}
|
|
@@ -4068,7 +4112,7 @@ let glActiveTexture, glShader, glArrayBuffer, glPositionData, glColorData, glBat
|
|
|
4068
4112
|
|
|
4069
4113
|
///////////////////////////////////////////////////////////////////////////////
|
|
4070
4114
|
|
|
4071
|
-
//
|
|
4115
|
+
// Initalize WebGL, called automatically by the engine
|
|
4072
4116
|
function glInit()
|
|
4073
4117
|
{
|
|
4074
4118
|
// create the canvas and tile texture
|
|
@@ -4107,6 +4151,48 @@ function glInit()
|
|
|
4107
4151
|
glBatchCount = 0;
|
|
4108
4152
|
}
|
|
4109
4153
|
|
|
4154
|
+
// Setup render each frame, called automatically by engine
|
|
4155
|
+
function glPreRender()
|
|
4156
|
+
{
|
|
4157
|
+
// clear and set to same size as main canvas
|
|
4158
|
+
glContext.viewport(0, 0, glCanvas.width = mainCanvas.width, glCanvas.height = mainCanvas.height);
|
|
4159
|
+
glContext.clear(gl_COLOR_BUFFER_BIT);
|
|
4160
|
+
|
|
4161
|
+
// set up the shader
|
|
4162
|
+
glContext.useProgram(glShader);
|
|
4163
|
+
glContext.activeTexture(gl_TEXTURE0);
|
|
4164
|
+
glContext.bindTexture(gl_TEXTURE_2D, glActiveTexture = glTileTexture);
|
|
4165
|
+
glContext.bindBuffer(gl_ARRAY_BUFFER, glArrayBuffer);
|
|
4166
|
+
glContext.bufferData(gl_ARRAY_BUFFER, gl_VERTEX_BUFFER_SIZE, gl_DYNAMIC_DRAW);
|
|
4167
|
+
glSetBlendMode();
|
|
4168
|
+
|
|
4169
|
+
// set vertex attributes
|
|
4170
|
+
let offset = 0;
|
|
4171
|
+
const initVertexAttribArray = (name, type, typeSize, size, normalize=0)=>
|
|
4172
|
+
{
|
|
4173
|
+
const location = glContext.getAttribLocation(glShader, name);
|
|
4174
|
+
glContext.enableVertexAttribArray(location);
|
|
4175
|
+
glContext.vertexAttribPointer(location, size, type, normalize, gl_VERTEX_BYTE_STRIDE, offset);
|
|
4176
|
+
offset += size*typeSize;
|
|
4177
|
+
}
|
|
4178
|
+
initVertexAttribArray('p', gl_FLOAT, 4, 2); // position
|
|
4179
|
+
initVertexAttribArray('t', gl_FLOAT, 4, 2); // texture coords
|
|
4180
|
+
initVertexAttribArray('c', gl_UNSIGNED_BYTE, 1, 4, 1); // color
|
|
4181
|
+
initVertexAttribArray('a', gl_UNSIGNED_BYTE, 1, 4, 1); // additiveColor
|
|
4182
|
+
|
|
4183
|
+
// build the transform matrix
|
|
4184
|
+
const sx = 2 * cameraScale / mainCanvas.width;
|
|
4185
|
+
const sy = 2 * cameraScale / mainCanvas.height;
|
|
4186
|
+
glContext.uniformMatrix4fv(glContext.getUniformLocation(glShader, 'm'), 0,
|
|
4187
|
+
new Float32Array([
|
|
4188
|
+
sx, 0, 0, 0,
|
|
4189
|
+
0, sy, 0, 0,
|
|
4190
|
+
1, 1, -1, 1,
|
|
4191
|
+
-1-sx*cameraPos.x, -1-sy*cameraPos.y, 0, 0
|
|
4192
|
+
])
|
|
4193
|
+
);
|
|
4194
|
+
}
|
|
4195
|
+
|
|
4110
4196
|
/** Set the WebGl blend mode, normally you should call setBlendMode instead
|
|
4111
4197
|
* @param {Boolean} [additive=0]
|
|
4112
4198
|
* @memberof WebGL */
|
|
@@ -4186,48 +4272,6 @@ function glCreateTexture(image)
|
|
|
4186
4272
|
return texture;
|
|
4187
4273
|
}
|
|
4188
4274
|
|
|
4189
|
-
// called automatically by engine before render
|
|
4190
|
-
function glPreRender()
|
|
4191
|
-
{
|
|
4192
|
-
// clear and set to same size as main canvas
|
|
4193
|
-
glContext.viewport(0, 0, glCanvas.width = mainCanvas.width, glCanvas.height = mainCanvas.height);
|
|
4194
|
-
glContext.clear(gl_COLOR_BUFFER_BIT);
|
|
4195
|
-
|
|
4196
|
-
// set up the shader
|
|
4197
|
-
glContext.useProgram(glShader);
|
|
4198
|
-
glContext.activeTexture(gl_TEXTURE0);
|
|
4199
|
-
glContext.bindTexture(gl_TEXTURE_2D, glActiveTexture = glTileTexture);
|
|
4200
|
-
glContext.bindBuffer(gl_ARRAY_BUFFER, glArrayBuffer);
|
|
4201
|
-
glContext.bufferData(gl_ARRAY_BUFFER, gl_VERTEX_BUFFER_SIZE, gl_DYNAMIC_DRAW);
|
|
4202
|
-
glSetBlendMode();
|
|
4203
|
-
|
|
4204
|
-
// set vertex attributes
|
|
4205
|
-
let offset = 0;
|
|
4206
|
-
const initVertexAttribArray = (name, type, typeSize, size, normalize=0)=>
|
|
4207
|
-
{
|
|
4208
|
-
const location = glContext.getAttribLocation(glShader, name);
|
|
4209
|
-
glContext.enableVertexAttribArray(location);
|
|
4210
|
-
glContext.vertexAttribPointer(location, size, type, normalize, gl_VERTEX_BYTE_STRIDE, offset);
|
|
4211
|
-
offset += size*typeSize;
|
|
4212
|
-
}
|
|
4213
|
-
initVertexAttribArray('p', gl_FLOAT, 4, 2); // position
|
|
4214
|
-
initVertexAttribArray('t', gl_FLOAT, 4, 2); // texture coords
|
|
4215
|
-
initVertexAttribArray('c', gl_UNSIGNED_BYTE, 1, 4, 1); // color
|
|
4216
|
-
initVertexAttribArray('a', gl_UNSIGNED_BYTE, 1, 4, 1); // additiveColor
|
|
4217
|
-
|
|
4218
|
-
// build the transform matrix
|
|
4219
|
-
const sx = 2 * cameraScale / mainCanvas.width;
|
|
4220
|
-
const sy = 2 * cameraScale / mainCanvas.height;
|
|
4221
|
-
glContext.uniformMatrix4fv(glContext.getUniformLocation(glShader, 'm'), 0,
|
|
4222
|
-
new Float32Array([
|
|
4223
|
-
sx, 0, 0, 0,
|
|
4224
|
-
0, sy, 0, 0,
|
|
4225
|
-
1, 1, -1, 1,
|
|
4226
|
-
-1-sx*cameraPos.x, -1-sy*cameraPos.y, 0, 0
|
|
4227
|
-
])
|
|
4228
|
-
);
|
|
4229
|
-
}
|
|
4230
|
-
|
|
4231
4275
|
/** Draw all sprites and clear out the buffer, called automatically by the system whenever necessary
|
|
4232
4276
|
* @memberof WebGL */
|
|
4233
4277
|
function glFlush()
|
|
@@ -4485,7 +4529,7 @@ gl_VERTEX_BUFFER_SIZE = gl_MAX_BATCH * gl_VERTEX_BYTE_STRIDE;
|
|
|
4485
4529
|
* @namespace Engine
|
|
4486
4530
|
*/
|
|
4487
4531
|
|
|
4488
|
-
|
|
4532
|
+
|
|
4489
4533
|
|
|
4490
4534
|
/** Name of engine
|
|
4491
4535
|
* @type {String}
|
|
@@ -4497,7 +4541,7 @@ const engineName = 'LittleJS';
|
|
|
4497
4541
|
* @type {String}
|
|
4498
4542
|
* @default
|
|
4499
4543
|
* @memberof Engine */
|
|
4500
|
-
const engineVersion = '1.7.
|
|
4544
|
+
const engineVersion = '1.7.21';
|
|
4501
4545
|
|
|
4502
4546
|
/** Frames per second to update objects
|
|
4503
4547
|
* @type {Number}
|
|
@@ -4567,11 +4611,13 @@ function engineInit(gameInit, gameUpdate, gameUpdatePost, gameRender, gameRender
|
|
|
4567
4611
|
debug && (tileImage.onload=()=>ASSERT(1)); // tile sheet can not reloaded
|
|
4568
4612
|
|
|
4569
4613
|
// setup html
|
|
4570
|
-
const styleBody =
|
|
4571
|
-
'
|
|
4572
|
-
'
|
|
4573
|
-
'
|
|
4574
|
-
'
|
|
4614
|
+
const styleBody =
|
|
4615
|
+
'margin:0;overflow:hidden;' + // fill the window
|
|
4616
|
+
'background:#000;' + // set background color
|
|
4617
|
+
'touch-action:none;' + // prevent mobile pinch to resize
|
|
4618
|
+
'user-select:none;' + // prevent mobile hold to select
|
|
4619
|
+
'-webkit-user-select:none;' + // compatibility for ios
|
|
4620
|
+
'-webkit-touch-callout:none'; // compatibility for ios
|
|
4575
4621
|
document.body.style = styleBody;
|
|
4576
4622
|
document.body.appendChild(mainCanvas = document.createElement('canvas'));
|
|
4577
4623
|
mainContext = mainCanvas.getContext('2d');
|
|
@@ -4585,7 +4631,8 @@ function engineInit(gameInit, gameUpdate, gameUpdatePost, gameRender, gameRender
|
|
|
4585
4631
|
overlayContext = overlayCanvas.getContext('2d');
|
|
4586
4632
|
|
|
4587
4633
|
// set canvas style
|
|
4588
|
-
const styleCanvas =
|
|
4634
|
+
const styleCanvas =
|
|
4635
|
+
'position:absolute;' + // position canvas
|
|
4589
4636
|
'top:50%;left:50%;transform:translate(-50%,-50%);' + // center the canvas
|
|
4590
4637
|
(canvasPixelated?'image-rendering:pixelated':''); // set pixelated rendering
|
|
4591
4638
|
(glCanvas||mainCanvas).style = mainCanvas.style = overlayCanvas.style = styleCanvas;
|