littlejsengine 1.14.19 → 1.14.23
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 +118 -18
- package/dist/littlejs.esm.js +506 -199
- package/dist/littlejs.esm.min.js +1 -1
- package/dist/littlejs.js +493 -199
- package/dist/littlejs.min.js +1 -1
- package/dist/littlejs.release.js +468 -200
- package/examples/electron/game.js +1 -41
- package/examples/electron/index.html +2 -2
- package/examples/htmlMenu/game.js +1 -1
- package/examples/index.html +12 -6
- package/examples/module/game.js +5 -7
- package/examples/particles/index.html +6 -9
- package/examples/platformer/gameCharacter.js +1 -1
- package/examples/platformer/gameEffects.js +7 -2
- package/examples/platformer/gameObjects.js +3 -3
- package/examples/shorts/animation.js +2 -2
- package/examples/shorts/base.html +4 -1
- package/examples/shorts/cameraDrag.js +22 -0
- package/examples/shorts/debugDraw.js +38 -0
- package/examples/shorts/fps.js +90 -0
- package/examples/shorts/helloWorld.js +1 -1
- package/examples/shorts/hillGlideGame.js +1 -1
- package/examples/shorts/landerGame.js +1 -1
- package/examples/shorts/postProcess.js +1 -1
- package/examples/shorts/sequencer.js +3 -3
- package/examples/shorts/spaceGame.js +1 -1
- package/examples/shorts/spriteAtlas.js +6 -6
- package/examples/shorts/starfield.js +1 -1
- package/examples/shorts/tileRaycast.js +39 -0
- package/examples/shorts/tiles.png +0 -0
- package/examples/shorts/tiltedView.js +1 -1
- package/examples/starter/game.js +5 -7
- package/examples/starter/index.html +2 -2
- package/examples/typescript/game.js +3 -2
- package/examples/typescript/game.ts +5 -7
- package/examples/uiSystem/game.js +2 -2
- package/package.json +1 -1
- package/src/engine.js +3 -3
- package/src/engineAudio.js +13 -13
- package/src/engineDebug.js +27 -0
- package/src/engineDraw.js +68 -20
- package/src/engineExport.js +13 -0
- package/src/engineInput.js +14 -16
- package/src/engineObject.js +31 -6
- package/src/engineParticles.js +36 -15
- package/src/engineRelease.js +2 -1
- package/src/engineSettings.js +20 -1
- package/src/engineTileLayer.js +62 -59
- package/src/engineUtilities.js +206 -58
- package/src/engineWebGL.js +13 -8
- package/examples/shorts/raycasting.js +0 -79
package/examples/starter/game.js
CHANGED
|
@@ -65,16 +65,17 @@ function gameInit()
|
|
|
65
65
|
// create particle emitter
|
|
66
66
|
particleEmitter = new ParticleEmitter(
|
|
67
67
|
vec2(16,9), 0, // emitPos, emitAngle
|
|
68
|
-
0, 0, 500,
|
|
68
|
+
0, 0, 500, 3.14, // emitSize, emitTime, rate, cone
|
|
69
69
|
tile(0, 16), // tileIndex, tileSize
|
|
70
70
|
hsl(1,1,1), hsl(0,0,0), // colorStartA, colorStartB
|
|
71
71
|
hsl(0,0,0,0), hsl(0,0,0,0), // colorEndA, colorEndB
|
|
72
|
-
1, .2, .2, .1, .05,
|
|
73
|
-
.99, 1, 1,
|
|
74
|
-
.05, .5, true, true
|
|
72
|
+
1, .2, .2, .1, .05, // time, sizeStart, sizeEnd, speed, angleSpeed
|
|
73
|
+
.99, 1, 1, 3.14, // damping, angleDamping, gravityScale, cone
|
|
74
|
+
.05, .5, true, true // fadeRate, randomness, collide, additive
|
|
75
75
|
);
|
|
76
76
|
particleEmitter.restitution = .3; // bounce when it collides
|
|
77
77
|
particleEmitter.trailScale = 2; // stretch as it moves
|
|
78
|
+
particleEmitter.velocityInheritance = .3; // inherit emitter velocity
|
|
78
79
|
}
|
|
79
80
|
|
|
80
81
|
///////////////////////////////////////////////////////////////////////////////
|
|
@@ -104,10 +105,7 @@ function gameUpdate()
|
|
|
104
105
|
|
|
105
106
|
// move particles to mouse location if on screen
|
|
106
107
|
if (mousePosScreen.x)
|
|
107
|
-
{
|
|
108
108
|
particleEmitter.pos = mousePos;
|
|
109
|
-
particleEmitter.velocity = mouseDelta.scale(.3*timeDelta);
|
|
110
|
-
}
|
|
111
109
|
}
|
|
112
110
|
|
|
113
111
|
///////////////////////////////////////////////////////////////////////////////
|
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
</head><body>
|
|
8
8
|
|
|
9
9
|
<!-- LittleJS Engine -->
|
|
10
|
-
<script src=../../dist/littlejs.js?
|
|
10
|
+
<script src=../../dist/littlejs.js?1.14.24></script>
|
|
11
11
|
|
|
12
12
|
<!-- LittleJS Engine Source
|
|
13
13
|
<script src=../../src/engineDebug.js></script>
|
|
@@ -31,4 +31,4 @@
|
|
|
31
31
|
-->
|
|
32
32
|
|
|
33
33
|
<!-- Add your game scripts here -->
|
|
34
|
-
<script src=game.js?
|
|
34
|
+
<script src=game.js?1.14.24></script>
|
|
@@ -52,16 +52,17 @@ function gameInit() {
|
|
|
52
52
|
LJS.setGravity(vec2(0, -.01));
|
|
53
53
|
// create particle emitter
|
|
54
54
|
particleEmitter = new LJS.ParticleEmitter(vec2(16, 9), 0, // emitPos, emitAngle
|
|
55
|
-
|
|
55
|
+
0, 0, 500, 3.14, // emitSize, emitTime, rate, cone
|
|
56
56
|
tile(0, 16), // tileIndex, tileSize
|
|
57
57
|
hsl(1, 1, 1), hsl(0, 0, 0), // colorStartA, colorStartB
|
|
58
58
|
hsl(0, 0, 0, 0), hsl(0, 0, 0, 0), // colorEndA, colorEndB
|
|
59
59
|
1, .2, .2, .1, .05, // time, sizeStart, sizeEnd, speed, angleSpeed
|
|
60
|
-
.99, 1, 1,
|
|
60
|
+
.99, 1, 1, 3.14, // damping, angleDamping, gravityScale, cone
|
|
61
61
|
.05, .5, true, true // fadeRate, randomness, collide, additive
|
|
62
62
|
);
|
|
63
63
|
particleEmitter.restitution = .3; // bounce when it collides
|
|
64
64
|
particleEmitter.trailScale = 2; // stretch as it moves
|
|
65
|
+
particleEmitter.velocityInheritance = .3; // inherit emitter velocity
|
|
65
66
|
}
|
|
66
67
|
///////////////////////////////////////////////////////////////////////////////
|
|
67
68
|
function gameUpdate() {
|
|
@@ -69,16 +69,17 @@ function gameInit()
|
|
|
69
69
|
// create particle emitter
|
|
70
70
|
particleEmitter = new LJS.ParticleEmitter(
|
|
71
71
|
vec2(16,9), 0, // emitPos, emitAngle
|
|
72
|
-
|
|
72
|
+
0, 0, 500, 3.14, // emitSize, emitTime, rate, cone
|
|
73
73
|
tile(0, 16), // tileIndex, tileSize
|
|
74
74
|
hsl(1,1,1), hsl(0,0,0), // colorStartA, colorStartB
|
|
75
75
|
hsl(0,0,0,0), hsl(0,0,0,0), // colorEndA, colorEndB
|
|
76
|
-
1, .2, .2, .1, .05,
|
|
77
|
-
.99, 1, 1,
|
|
78
|
-
.05, .5, true, true
|
|
76
|
+
1, .2, .2, .1, .05, // time, sizeStart, sizeEnd, speed, angleSpeed
|
|
77
|
+
.99, 1, 1, 3.14, // damping, angleDamping, gravityScale, cone
|
|
78
|
+
.05, .5, true, true // fadeRate, randomness, collide, additive
|
|
79
79
|
);
|
|
80
80
|
particleEmitter.restitution = .3; // bounce when it collides
|
|
81
81
|
particleEmitter.trailScale = 2; // stretch stretch as it moves
|
|
82
|
+
particleEmitter.velocityInheritance = .3; // inherit emitter velocity
|
|
82
83
|
}
|
|
83
84
|
|
|
84
85
|
///////////////////////////////////////////////////////////////////////////////
|
|
@@ -101,10 +102,7 @@ function gameUpdate()
|
|
|
101
102
|
|
|
102
103
|
// move particles to mouse location if on screen
|
|
103
104
|
if (LJS.mousePosScreen.x)
|
|
104
|
-
{
|
|
105
105
|
particleEmitter.pos = LJS.mousePos;
|
|
106
|
-
particleEmitter.velocity = LJS.mouseDelta.scale(.3*LJS.timeDelta);
|
|
107
|
-
}
|
|
108
106
|
}
|
|
109
107
|
|
|
110
108
|
///////////////////////////////////////////////////////////////////////////////
|
|
@@ -64,7 +64,7 @@ function createUI()
|
|
|
64
64
|
// example button
|
|
65
65
|
const button1 = new LJS.UIButton(vec2(0,140), vec2(350, 50), 'Test Button');
|
|
66
66
|
uiMenu.addChild(button1);
|
|
67
|
-
button1.onClick = ()=> uiBackground.color = hsl(
|
|
67
|
+
button1.onClick = ()=> uiBackground.color = hsl(LJS.rand(),1,.7);
|
|
68
68
|
|
|
69
69
|
// example checkbox
|
|
70
70
|
const checkbox = new LJS.UICheckbox(vec2(-140,-20), vec2(40));
|
|
@@ -114,7 +114,7 @@ function gameRender()
|
|
|
114
114
|
// test game rendering
|
|
115
115
|
for (let i=0; i<1e3; ++i)
|
|
116
116
|
{
|
|
117
|
-
const pos = vec2(30*
|
|
117
|
+
const pos = vec2(30*LJS.sin(i+LJS.time/9),20*LJS.sin(i*i+LJS.time/9));
|
|
118
118
|
LJS.drawTile(pos, vec2(2), tile(3,128), hsl(i/9,1,.4), LJS.time+i, !(i%2), hsl(i/9,1,.1,0));
|
|
119
119
|
}
|
|
120
120
|
}
|
package/package.json
CHANGED
package/src/engine.js
CHANGED
|
@@ -30,7 +30,7 @@ const engineName = 'LittleJS';
|
|
|
30
30
|
* @type {string}
|
|
31
31
|
* @default
|
|
32
32
|
* @memberof Engine */
|
|
33
|
-
const engineVersion = '1.14.
|
|
33
|
+
const engineVersion = '1.14.24';
|
|
34
34
|
|
|
35
35
|
/** Frames per second to update
|
|
36
36
|
* @type {number}
|
|
@@ -513,7 +513,7 @@ function engineObjectsCollect(pos, size, objects=engineObjects)
|
|
|
513
513
|
|
|
514
514
|
/**
|
|
515
515
|
* @callback ObjectCallbackFunction - Function that processes an object
|
|
516
|
-
* @param {EngineObject}
|
|
516
|
+
* @param {EngineObject} object
|
|
517
517
|
* @memberof Engine
|
|
518
518
|
*/
|
|
519
519
|
|
|
@@ -561,7 +561,7 @@ function drawEngineSplashScreen(t)
|
|
|
561
561
|
// background
|
|
562
562
|
const p3 = percent(t, 1, .8);
|
|
563
563
|
const p4 = percent(t, 0, .5);
|
|
564
|
-
const g = x.createRadialGradient(w/2,h/2,0,w/2,h/2,
|
|
564
|
+
const g = x.createRadialGradient(w/2,h/2,0,w/2,h/2,hypot(w,h)*.7);
|
|
565
565
|
g.addColorStop(0,hsl(0,0,lerp(0,p3/2,p4),p3).toString());
|
|
566
566
|
g.addColorStop(1,hsl(0,0,0,p3).toString());
|
|
567
567
|
x.save();
|
package/src/engineAudio.js
CHANGED
|
@@ -605,10 +605,10 @@ function zzfxG
|
|
|
605
605
|
|
|
606
606
|
// biquad LP/HP filter
|
|
607
607
|
quality = 2, w = PI2 * abs(filter) * 2 / sampleRate,
|
|
608
|
-
|
|
609
|
-
a0 = 1 + alpha, a1 = -2*
|
|
610
|
-
b0 = (1 + sign(filter) *
|
|
611
|
-
b1 = -(sign(filter) +
|
|
608
|
+
cosw = cos(w), alpha = sin(w) / 2 / quality,
|
|
609
|
+
a0 = 1 + alpha, a1 = -2*cosw / a0, a2 = (1 - alpha) / a0,
|
|
610
|
+
b0 = (1 + sign(filter) * cosw) / 2 / a0,
|
|
611
|
+
b1 = -(sign(filter) + cosw) / a0, b2 = b0,
|
|
612
612
|
x2 = 0, x1 = 0, y2 = 0, y1 = 0;
|
|
613
613
|
|
|
614
614
|
// scale by sample rate
|
|
@@ -631,15 +631,15 @@ function zzfxG
|
|
|
631
631
|
if (!(++crush%(bitCrush*100|0))) // bit crush
|
|
632
632
|
{
|
|
633
633
|
s = shape? shape>1? shape>2? shape>3? shape>4? // wave shape
|
|
634
|
-
(t/PI2%1 < shapeCurve/2? 1 : -1) :
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
1-(2*t/PI2%2+2)%2:
|
|
638
|
-
1-4*abs(
|
|
639
|
-
|
|
634
|
+
(t/PI2%1 < shapeCurve/2? 1 : -1) : // 5 square duty
|
|
635
|
+
sin(t**3) : // 4 noise
|
|
636
|
+
max(min(tan(t),1),-1): // 3 tan
|
|
637
|
+
1-(2*t/PI2%2+2)%2: // 2 saw
|
|
638
|
+
1-4*abs(round(t/PI2)-t/PI2): // 1 triangle
|
|
639
|
+
sin(t); // 0 sin
|
|
640
640
|
|
|
641
641
|
s = (repeatTime ?
|
|
642
|
-
1 - tremolo + tremolo*
|
|
642
|
+
1 - tremolo + tremolo*sin(PI2*i/repeatTime) // tremolo
|
|
643
643
|
: 1) *
|
|
644
644
|
(shape>4?s:sign(s)*abs(s)**shapeCurve) * // shape curve
|
|
645
645
|
(i < attack ? i/attack : // attack
|
|
@@ -661,8 +661,8 @@ function zzfxG
|
|
|
661
661
|
}
|
|
662
662
|
|
|
663
663
|
f = (frequency += slide += deltaSlide) *// frequency
|
|
664
|
-
|
|
665
|
-
t += f + f*noise*
|
|
664
|
+
cos(modulation*modOffset++); // modulation
|
|
665
|
+
t += f + f*noise*sin(i**5); // noise
|
|
666
666
|
|
|
667
667
|
if (jump && ++jump > pitchJumpTime) // pitch jump
|
|
668
668
|
{
|
package/src/engineDebug.js
CHANGED
|
@@ -622,4 +622,31 @@ function debugVideoCaptureUpdate()
|
|
|
622
622
|
combineCanvases();
|
|
623
623
|
debugVideoCaptureTrack.requestFrame();
|
|
624
624
|
debugVideoCaptureIcon.textContent = '● REC ' + formatTime(debugVideoCaptureTimer);
|
|
625
|
+
}
|
|
626
|
+
|
|
627
|
+
// make color constants immutable with debug assertions
|
|
628
|
+
function debugProtectConstant(obj)
|
|
629
|
+
{
|
|
630
|
+
if (debug)
|
|
631
|
+
{
|
|
632
|
+
// get properties and store original values
|
|
633
|
+
const props = Object.keys(obj), values = {};
|
|
634
|
+
props.forEach(prop => values[prop] = obj[prop]);
|
|
635
|
+
|
|
636
|
+
// replace with getters/setters that assert
|
|
637
|
+
props.forEach(prop =>
|
|
638
|
+
{
|
|
639
|
+
Object.defineProperty(obj, prop, {
|
|
640
|
+
get: () => values[prop],
|
|
641
|
+
set: (value) =>
|
|
642
|
+
{
|
|
643
|
+
ASSERT(false, `Cannot modify engine constant. Attempted to set constant (${obj}) property '${prop}' to '${value}'.`);
|
|
644
|
+
},
|
|
645
|
+
enumerable: true
|
|
646
|
+
});
|
|
647
|
+
});
|
|
648
|
+
}
|
|
649
|
+
|
|
650
|
+
// freeze the object to prevent adding new properties
|
|
651
|
+
return Object.freeze(obj);
|
|
625
652
|
}
|
package/src/engineDraw.js
CHANGED
|
@@ -252,6 +252,7 @@ function drawTile(pos, size=new Vector2(1), tileInfo, color=WHITE,
|
|
|
252
252
|
const bleedScale = tileInfo ? tileInfo.bleedScale : 0;
|
|
253
253
|
if (useWebGL)
|
|
254
254
|
{
|
|
255
|
+
ASSERT(!!glContext, 'WebGL is not enabled!');
|
|
255
256
|
if (screenSpace)
|
|
256
257
|
{
|
|
257
258
|
// convert to world space
|
|
@@ -347,6 +348,7 @@ function drawRectGradient(pos, size, colorTop=WHITE, colorBottom=BLACK, angle=0,
|
|
|
347
348
|
ASSERT(!context || !useWebGL, 'context only supported in canvas 2D mode');
|
|
348
349
|
if (useWebGL)
|
|
349
350
|
{
|
|
351
|
+
ASSERT(!!glContext, 'WebGL is not enabled!');
|
|
350
352
|
if (screenSpace)
|
|
351
353
|
{
|
|
352
354
|
// convert to world space
|
|
@@ -358,7 +360,7 @@ function drawRectGradient(pos, size, colorTop=WHITE, colorBottom=BLACK, angle=0,
|
|
|
358
360
|
const halfSizeX = size.x/2, halfSizeY = size.y/2;
|
|
359
361
|
const colorTopInt = colorTop.rgbaInt();
|
|
360
362
|
const colorBottomInt = colorBottom.rgbaInt();
|
|
361
|
-
const c =
|
|
363
|
+
const c = cos(-angle), s = sin(-angle);
|
|
362
364
|
for (let i=4; i--;)
|
|
363
365
|
{
|
|
364
366
|
const x = i & 1 ? halfSizeX : -halfSizeX;
|
|
@@ -409,6 +411,7 @@ function drawLineList(points, width=.1, color, wrap=false, pos=vec2(), angle=0,
|
|
|
409
411
|
ASSERT(!context || !useWebGL, 'context only supported in canvas 2D mode');
|
|
410
412
|
if (useWebGL)
|
|
411
413
|
{
|
|
414
|
+
ASSERT(!!glContext, 'WebGL is not enabled!');
|
|
412
415
|
let scale = 1;
|
|
413
416
|
if (screenSpace)
|
|
414
417
|
{
|
|
@@ -458,6 +461,8 @@ function drawLine(posA, posB, width=.1, color, pos=vec2(), angle=0, useWebGL, sc
|
|
|
458
461
|
const halfDelta = vec2((posB.x - posA.x)/2, (posB.y - posA.y)/2);
|
|
459
462
|
const size = vec2(width, halfDelta.length()*2);
|
|
460
463
|
pos = pos.add(posA.add(halfDelta));
|
|
464
|
+
if (screenSpace)
|
|
465
|
+
halfDelta.y *= -1; // flip angle Y if screen space
|
|
461
466
|
angle += halfDelta.angle();
|
|
462
467
|
drawRect(pos, size, color, angle, useWebGL, screenSpace, context);
|
|
463
468
|
}
|
|
@@ -485,7 +490,7 @@ function drawRegularPoly(pos, size=vec2(1), sides=3, color=WHITE, lineWidth=0, l
|
|
|
485
490
|
for (let i=sides; i--;)
|
|
486
491
|
{
|
|
487
492
|
const a = (i/sides)*PI*2;
|
|
488
|
-
points.push(vec2(
|
|
493
|
+
points.push(vec2(sin(a)*sizeX, cos(a)*sizeY));
|
|
489
494
|
}
|
|
490
495
|
drawPoly(points, color, lineWidth, lineColor, pos, angle, useWebGL, screenSpace, context);
|
|
491
496
|
}
|
|
@@ -512,6 +517,7 @@ function drawPoly(points, color=WHITE, lineWidth=0, lineColor=BLACK, pos=vec2(),
|
|
|
512
517
|
|
|
513
518
|
if (useWebGL)
|
|
514
519
|
{
|
|
520
|
+
ASSERT(!!glContext, 'WebGL is not enabled!');
|
|
515
521
|
let scale = 1;
|
|
516
522
|
if (screenSpace)
|
|
517
523
|
{
|
|
@@ -733,18 +739,18 @@ function drawTextScreen(text, pos, size=1, color=WHITE, lineWidth=0, lineColor=B
|
|
|
733
739
|
* @memberof Draw */
|
|
734
740
|
function screenToWorld(screenPos)
|
|
735
741
|
{
|
|
736
|
-
let
|
|
737
|
-
let
|
|
742
|
+
let x = (screenPos.x - mainCanvasSize.x/2 + .5) / cameraScale;
|
|
743
|
+
let y = (screenPos.y - mainCanvasSize.y/2 + .5) / -cameraScale;
|
|
738
744
|
if (cameraAngle)
|
|
739
745
|
{
|
|
740
746
|
// apply camera rotation
|
|
741
|
-
const
|
|
742
|
-
const rotatedX =
|
|
743
|
-
const rotatedY =
|
|
744
|
-
|
|
745
|
-
|
|
747
|
+
const c = cos(-cameraAngle), s = sin(-cameraAngle);
|
|
748
|
+
const rotatedX = x * c - y * s;
|
|
749
|
+
const rotatedY = x * s + y * c;
|
|
750
|
+
x = rotatedX;
|
|
751
|
+
y = rotatedY;
|
|
746
752
|
}
|
|
747
|
-
return new Vector2(
|
|
753
|
+
return new Vector2(x + cameraPos.x, y + cameraPos.y);
|
|
748
754
|
}
|
|
749
755
|
|
|
750
756
|
/** Convert from world to screen space coordinates
|
|
@@ -753,24 +759,64 @@ function screenToWorld(screenPos)
|
|
|
753
759
|
* @memberof Draw */
|
|
754
760
|
function worldToScreen(worldPos)
|
|
755
761
|
{
|
|
756
|
-
let
|
|
757
|
-
let
|
|
762
|
+
let x = worldPos.x - cameraPos.x;
|
|
763
|
+
let y = worldPos.y - cameraPos.y;
|
|
758
764
|
if (cameraAngle)
|
|
759
765
|
{
|
|
760
766
|
// apply inverse camera rotation
|
|
761
|
-
const
|
|
762
|
-
const rotatedX =
|
|
763
|
-
const rotatedY =
|
|
764
|
-
|
|
765
|
-
|
|
767
|
+
const c = cos(cameraAngle), s = sin(cameraAngle);
|
|
768
|
+
const rotatedX = x * c - y * s;
|
|
769
|
+
const rotatedY = x * s + y * c;
|
|
770
|
+
x = rotatedX;
|
|
771
|
+
y = rotatedY;
|
|
766
772
|
}
|
|
767
773
|
return new Vector2
|
|
768
774
|
(
|
|
769
|
-
|
|
770
|
-
|
|
775
|
+
x * cameraScale + mainCanvasSize.x/2 - .5,
|
|
776
|
+
y * -cameraScale + mainCanvasSize.y/2 - .5
|
|
771
777
|
);
|
|
772
778
|
}
|
|
773
779
|
|
|
780
|
+
/** Convert from screen to world space coordinates for a directional vector (no translation)
|
|
781
|
+
* @param {Vector2} screenDelta
|
|
782
|
+
* @return {Vector2}
|
|
783
|
+
* @memberof Draw */
|
|
784
|
+
function screenToWorldDelta(screenDelta)
|
|
785
|
+
{
|
|
786
|
+
let x = screenDelta.x / cameraScale;
|
|
787
|
+
let y = screenDelta.y / -cameraScale;
|
|
788
|
+
if (cameraAngle)
|
|
789
|
+
{
|
|
790
|
+
// apply camera rotation
|
|
791
|
+
const c = cos(-cameraAngle), s = sin(-cameraAngle);
|
|
792
|
+
const rotatedX = x * c - y * s;
|
|
793
|
+
const rotatedY = x * s + y * c;
|
|
794
|
+
x = rotatedX;
|
|
795
|
+
y = rotatedY;
|
|
796
|
+
}
|
|
797
|
+
return new Vector2(x, y);
|
|
798
|
+
}
|
|
799
|
+
|
|
800
|
+
/** Convert from screen to world space coordinates for a directional vector (no translation)
|
|
801
|
+
* @param {Vector2} worldDelta
|
|
802
|
+
* @return {Vector2}
|
|
803
|
+
* @memberof Draw */
|
|
804
|
+
function worldToScreenDelta(worldDelta)
|
|
805
|
+
{
|
|
806
|
+
let x = worldDelta.x;
|
|
807
|
+
let y = worldDelta.y;
|
|
808
|
+
if (cameraAngle)
|
|
809
|
+
{
|
|
810
|
+
// apply inverse camera rotation
|
|
811
|
+
const c = cos(cameraAngle), s = sin(cameraAngle);
|
|
812
|
+
const rotatedX = x * c - y * s;
|
|
813
|
+
const rotatedY = x * s + y * c;
|
|
814
|
+
x = rotatedX;
|
|
815
|
+
y = rotatedY;
|
|
816
|
+
}
|
|
817
|
+
return new Vector2(x * cameraScale, y * -cameraScale);
|
|
818
|
+
}
|
|
819
|
+
|
|
774
820
|
/** Get the camera's visible area in world space
|
|
775
821
|
* @return {Vector2}
|
|
776
822
|
* @memberof Draw */
|
|
@@ -823,6 +869,8 @@ function drawImageColor(context, image, sx, sy, sWidth, sHeight, dx, dy, dWidth,
|
|
|
823
869
|
function isBlack(c) { return c.r <= 0 && c.g <= 0 && c.b <= 0 && c.a <= 0; }
|
|
824
870
|
const sx2 = bleedScale;
|
|
825
871
|
const sy2 = bleedScale;
|
|
872
|
+
sWidth = max(1,sWidth|0);
|
|
873
|
+
sHeight = max(1,sHeight|0);
|
|
826
874
|
const sWidth2 = sWidth - 2*bleedScale;
|
|
827
875
|
const sHeight2 = sHeight - 2*bleedScale;
|
|
828
876
|
if (!canvasColorTiles || (additiveColor ? isWhite(color.add(additiveColor)) && additiveColor.a <= 0 : isWhite(color)))
|
|
@@ -837,7 +885,7 @@ function drawImageColor(context, image, sx, sy, sWidth, sHeight, dx, dy, dWidth,
|
|
|
837
885
|
// copy to offscreen canvas
|
|
838
886
|
workCanvas.width = sWidth;
|
|
839
887
|
workCanvas.height = sHeight;
|
|
840
|
-
workContext.drawImage(image, sx, sy, sWidth, sHeight, 0, 0, sWidth, sHeight);
|
|
888
|
+
workContext.drawImage(image, sx|0, sy|0, sWidth, sHeight, 0, 0, sWidth, sHeight);
|
|
841
889
|
|
|
842
890
|
// tint image using offscreen work context
|
|
843
891
|
const imageData = workContext.getImageData(0, 0, sWidth, sHeight);
|
package/src/engineExport.js
CHANGED
|
@@ -79,6 +79,7 @@ export
|
|
|
79
79
|
gamepadDirectionEmulateStick,
|
|
80
80
|
inputWASDEmulateDirection,
|
|
81
81
|
touchGamepadEnable,
|
|
82
|
+
touchGamepadCenterButton,
|
|
82
83
|
touchGamepadAnalog,
|
|
83
84
|
touchGamepadSize,
|
|
84
85
|
touchGamepadAlpha,
|
|
@@ -122,6 +123,7 @@ export
|
|
|
122
123
|
setGamepadDirectionEmulateStick,
|
|
123
124
|
setInputWASDEmulateDirection,
|
|
124
125
|
setTouchGamepadEnable,
|
|
126
|
+
setTouchGamepadCenterButton,
|
|
125
127
|
setTouchGamepadAnalog,
|
|
126
128
|
setTouchGamepadSize,
|
|
127
129
|
setTouchGamepadAlpha,
|
|
@@ -140,9 +142,18 @@ export
|
|
|
140
142
|
// Utilities
|
|
141
143
|
PI,
|
|
142
144
|
abs,
|
|
145
|
+
floor,
|
|
146
|
+
ceil,
|
|
147
|
+
round,
|
|
143
148
|
min,
|
|
144
149
|
max,
|
|
145
150
|
sign,
|
|
151
|
+
hypot,
|
|
152
|
+
log2,
|
|
153
|
+
sin,
|
|
154
|
+
cos,
|
|
155
|
+
tan,
|
|
156
|
+
atan2,
|
|
146
157
|
mod,
|
|
147
158
|
clamp,
|
|
148
159
|
percent,
|
|
@@ -210,6 +221,8 @@ export
|
|
|
210
221
|
drawCount,
|
|
211
222
|
screenToWorld,
|
|
212
223
|
worldToScreen,
|
|
224
|
+
screenToWorldDelta,
|
|
225
|
+
worldToScreenDelta,
|
|
213
226
|
drawTile,
|
|
214
227
|
drawRect,
|
|
215
228
|
drawRectGradient,
|
package/src/engineInput.js
CHANGED
|
@@ -182,9 +182,9 @@ function inputUpdate()
|
|
|
182
182
|
if(!(touchInputEnable && isTouchDevice) && !document.hasFocus())
|
|
183
183
|
inputClear();
|
|
184
184
|
|
|
185
|
-
// update mouse world space position
|
|
185
|
+
// update mouse world space position and delta
|
|
186
186
|
mousePos = screenToWorld(mousePosScreen);
|
|
187
|
-
mouseDelta = mouseDeltaScreen
|
|
187
|
+
mouseDelta = screenToWorldDelta(mouseDeltaScreen);
|
|
188
188
|
|
|
189
189
|
// update gamepads if enabled
|
|
190
190
|
gamepadsUpdate();
|
|
@@ -216,7 +216,6 @@ function inputInit()
|
|
|
216
216
|
document.addEventListener('wheel', onMouseWheel);
|
|
217
217
|
document.addEventListener('contextmenu', onContextMenu);
|
|
218
218
|
document.addEventListener('blur', onBlur);
|
|
219
|
-
document.addEventListener('mouseleave', onMouseLeave);
|
|
220
219
|
|
|
221
220
|
// init touch input
|
|
222
221
|
if (isTouchDevice && touchInputEnable)
|
|
@@ -258,7 +257,10 @@ function inputInit()
|
|
|
258
257
|
|
|
259
258
|
isUsingGamepad = false;
|
|
260
259
|
inputData[0][e.button] = 3;
|
|
260
|
+
|
|
261
|
+
let mousePosScreenLast = mousePosScreen;
|
|
261
262
|
mousePosScreen = mouseEventToScreen(vec2(e.x,e.y));
|
|
263
|
+
mouseDeltaScreen = mouseDeltaScreen.add(mousePosScreen.subtract(mousePosScreenLast));
|
|
262
264
|
inputPreventDefault && e.button && e.preventDefault();
|
|
263
265
|
}
|
|
264
266
|
function onMouseUp(e)
|
|
@@ -269,18 +271,13 @@ function inputInit()
|
|
|
269
271
|
}
|
|
270
272
|
function onMouseMove(e)
|
|
271
273
|
{
|
|
274
|
+
let mousePosScreenLast = mousePosScreen;
|
|
272
275
|
mousePosScreen = mouseEventToScreen(vec2(e.x,e.y));
|
|
273
|
-
mouseDeltaScreen = mouseDeltaScreen.add(
|
|
276
|
+
mouseDeltaScreen = mouseDeltaScreen.add(mousePosScreen.subtract(mousePosScreenLast));
|
|
274
277
|
}
|
|
275
278
|
function onMouseWheel(e) { mouseWheel = e.ctrlKey ? 0 : sign(e.deltaY); }
|
|
276
279
|
function onContextMenu(e) { e.preventDefault(); } // prevent right click menu
|
|
277
280
|
function onBlur() { inputClear(); } // reset input when focus is lost
|
|
278
|
-
function onMouseLeave()
|
|
279
|
-
{
|
|
280
|
-
// set mouse position and delta when leaving canvas
|
|
281
|
-
mousePosScreen = vec2(-1);
|
|
282
|
-
mouseDeltaScreen = vec2(0);
|
|
283
|
-
}
|
|
284
281
|
}
|
|
285
282
|
|
|
286
283
|
// convert a mouse or touch event position to screen space
|
|
@@ -324,8 +321,8 @@ function gamepadsUpdate()
|
|
|
324
321
|
else if (touchGamepadStick.lengthSquared() > .3)
|
|
325
322
|
{
|
|
326
323
|
// convert to 8 way dpad
|
|
327
|
-
sticks[0].x =
|
|
328
|
-
sticks[0].y = -
|
|
324
|
+
sticks[0].x = round(touchGamepadStick.x);
|
|
325
|
+
sticks[0].y = -round(touchGamepadStick.y);
|
|
329
326
|
sticks[0] = sticks[0].clampLength();
|
|
330
327
|
}
|
|
331
328
|
|
|
@@ -453,11 +450,11 @@ function touchInputInit()
|
|
|
453
450
|
{
|
|
454
451
|
// set event pos and pass it along
|
|
455
452
|
const pos = vec2(e.touches[0].clientX, e.touches[0].clientY);
|
|
456
|
-
const
|
|
453
|
+
const mousePosScreenLast = mousePosScreen;
|
|
457
454
|
mousePosScreen = mouseEventToScreen(pos);
|
|
458
455
|
if (wasTouching)
|
|
459
456
|
{
|
|
460
|
-
mouseDeltaScreen = mouseDeltaScreen.add(mousePosScreen.subtract(
|
|
457
|
+
mouseDeltaScreen = mouseDeltaScreen.add(mousePosScreen.subtract(mousePosScreenLast));
|
|
461
458
|
isUsingGamepad = touchGamepadEnable;
|
|
462
459
|
}
|
|
463
460
|
else
|
|
@@ -489,7 +486,7 @@ function touchInputInit()
|
|
|
489
486
|
if (touching)
|
|
490
487
|
{
|
|
491
488
|
touchGamepadTimer.set();
|
|
492
|
-
if (
|
|
489
|
+
if (touchGamepadCenterButton && !wasTouching && paused)
|
|
493
490
|
{
|
|
494
491
|
// touch anywhere to press start when paused
|
|
495
492
|
touchGamepadButtons[9] = 1;
|
|
@@ -528,7 +525,8 @@ function touchInputInit()
|
|
|
528
525
|
if (button < touchGamepadButtonCount)
|
|
529
526
|
touchGamepadButtons[button] = 1;
|
|
530
527
|
}
|
|
531
|
-
else if (
|
|
528
|
+
else if (touchGamepadCenterButton && !wasTouching &&
|
|
529
|
+
startCenter.distance(touchPos) < touchGamepadSize)
|
|
532
530
|
{
|
|
533
531
|
// virtual start button in center
|
|
534
532
|
touchGamepadButtons[9] = 1;
|
package/src/engineObject.js
CHANGED
|
@@ -287,6 +287,27 @@ class EngineObject
|
|
|
287
287
|
// test which side we bounced off (or both if a corner)
|
|
288
288
|
const blockedLayerY = tileCollisionTest(vec2(oldPos.x, this.pos.y), this.size, this);
|
|
289
289
|
const blockedLayerX = tileCollisionTest(vec2(this.pos.x, oldPos.y), this.size, this);
|
|
290
|
+
|
|
291
|
+
if (blockedLayerX)
|
|
292
|
+
{
|
|
293
|
+
// try to move up a tiny bit
|
|
294
|
+
const epsilon = 1e-3;
|
|
295
|
+
const maxMoveUp = .1;
|
|
296
|
+
const y = floor(oldPos.y-this.size.y/2+1) +
|
|
297
|
+
this.size.y/2 + epsilon;
|
|
298
|
+
const delta = y - this.pos.y;
|
|
299
|
+
if (delta < maxMoveUp)
|
|
300
|
+
if (!tileCollisionTest(vec2(this.pos.x, y), this.size, this))
|
|
301
|
+
{
|
|
302
|
+
this.pos.y = y;
|
|
303
|
+
debugPhysics && debugRect(this.pos, this.size, '#ff0');
|
|
304
|
+
return;
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
// move to previous position and bounce
|
|
308
|
+
this.pos.x = oldPos.x;
|
|
309
|
+
this.velocity.x *= -this.restitution;
|
|
310
|
+
}
|
|
290
311
|
if (blockedLayerY || !blockedLayerX)
|
|
291
312
|
{
|
|
292
313
|
// bounce velocity
|
|
@@ -310,12 +331,6 @@ class EngineObject
|
|
|
310
331
|
this.groundObject = undefined;
|
|
311
332
|
}
|
|
312
333
|
}
|
|
313
|
-
if (blockedLayerX)
|
|
314
|
-
{
|
|
315
|
-
// move to previous position and bounce
|
|
316
|
-
this.pos.x = oldPos.x;
|
|
317
|
-
this.velocity.x *= -this.restitution;
|
|
318
|
-
}
|
|
319
334
|
debugPhysics && debugRect(this.pos, this.size, '#f00');
|
|
320
335
|
}
|
|
321
336
|
}
|
|
@@ -373,6 +388,16 @@ class EngineObject
|
|
|
373
388
|
*/
|
|
374
389
|
collideWithObject(object) { return true; }
|
|
375
390
|
|
|
391
|
+
/** Get this object's up vector
|
|
392
|
+
* @param {number} [scale] - length of the vector
|
|
393
|
+
* @return {Vector2} */
|
|
394
|
+
getUp(scale=1) { return vec2().setAngle(this.angle, scale); }
|
|
395
|
+
|
|
396
|
+
/** Get this object's right vector
|
|
397
|
+
* @param {number} [scale] - length of the vector
|
|
398
|
+
* @return {Vector2} */
|
|
399
|
+
getRight(scale=1) { return vec2().setAngle(this.angle+PI/2, scale); }
|
|
400
|
+
|
|
376
401
|
/** How long since the object was created
|
|
377
402
|
* @return {number} */
|
|
378
403
|
getAliveTime() { return time - this.spawnTime; }
|