pxt-common-packages 12.3.1 → 12.3.3
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/built/common-sim.d.ts +4 -0
- package/built/common-sim.js +275 -0
- package/libs/azureiot/built/debug/binary.js +461 -461
- package/libs/browser-events/browserEvents.ts +9 -9
- package/libs/color/built/debug/binary.js +8 -8
- package/libs/color-sensor/built/debug/binary.js +8 -8
- package/libs/controller/built/debug/binary.js +9969 -8494
- package/libs/controller---none/built/debug/binary.js +9948 -8473
- package/libs/datalogger/built/debug/binary.js +63 -63
- package/libs/edge-connector/built/debug/binary.js +8 -8
- package/libs/esp32/built/debug/binary.js +462 -462
- package/libs/game/_locales/game-strings.json +4 -0
- package/libs/game/built/debug/binary.js +9843 -8368
- package/libs/game/docs/reference/sprites/on-overlap.md +32 -109
- package/libs/game/docs/reference/sprites/sprite/say.md +43 -18
- package/libs/game/hitbox.ts +13 -9
- package/libs/game/pxt.json +1 -0
- package/libs/game/rotation.ts +194 -0
- package/libs/game/sprite.ts +102 -8
- package/libs/lcd/built/debug/binary.js +8 -8
- package/libs/light-spectrum-sensor/built/debug/binary.js +9 -9
- package/libs/lora/built/debug/binary.js +8 -8
- package/libs/matrix-keypad/built/debug/binary.js +8 -8
- package/libs/mqtt/built/debug/binary.js +176 -176
- package/libs/net/built/debug/binary.js +176 -176
- package/libs/net-game/built/debug/binary.js +11768 -10293
- package/libs/palette/built/debug/binary.js +9860 -8385
- package/libs/pixel/built/debug/binary.js +8 -8
- package/libs/power/built/debug/binary.js +8 -8
- package/libs/proximity/built/debug/binary.js +8 -8
- package/libs/radio/built/debug/binary.js +8 -8
- package/libs/radio-broadcast/built/debug/binary.js +8 -8
- package/libs/rotary-encoder/built/debug/binary.js +8 -8
- package/libs/screen/built/debug/binary.js +50 -50
- package/libs/screen/image.cpp +374 -0
- package/libs/screen/image.ts +42 -0
- package/libs/screen/sim/image.ts +406 -0
- package/libs/servo/built/debug/binary.js +8 -8
- package/libs/sprite-scaling/built/debug/binary.js +9860 -8385
- package/libs/storyboard/built/debug/binary.js +9860 -8385
- package/package.json +1 -1
|
@@ -12,7 +12,7 @@ An overlap with a sprite of a different or the same kind is detected. If you wan
|
|
|
12
12
|
|
|
13
13
|
When an overlap is detected the sprite of the first kind is given to you in the **sprite** parameter of **handler**. The sprite for the second kind is in **otherSprite**.
|
|
14
14
|
|
|
15
|
-
An overlap of two sprites is
|
|
15
|
+
An overlap of two sprites is detected when the first non-transparent pixel in the image of the first sprite overlaps the first non-transparent pixel of the second sprite. If a sprite has it's ``ghost`` flag set, any overlap with another sprite won't be noticed. Also, an overlap occurs even when the values of **Z** for the sprites are different.
|
|
16
16
|
|
|
17
17
|
## Parameters
|
|
18
18
|
|
|
@@ -24,95 +24,22 @@ An overlap of two sprites is dectected when the first non-transparent pixel in t
|
|
|
24
24
|
|
|
25
25
|
## Example #example
|
|
26
26
|
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
Create a ``Ghost`` sprite that is blasted by green balls. Let the balls go through the sprite until it's ``kind`` is changed to ``Mortal`` by pressing the **A** button. When the ``Ghost`` sprite is changed to ``Mortal``, any contact with the balls is detected in ``||sprites:on overlaps||``. Make the balls push the ``Mortal`` sprite off the screen.
|
|
27
|
+
Create a ``Ghost`` sprite that is blasted by yellow balls. Let the balls go through the sprite until it's ghost status is removed by pressing the **A** button. When the ``Ghost`` sprite is exposed, any contact with the balls is detected in ``||sprites:on overlap||``. Make the balls push the ``Ghost`` sprite off the screen.
|
|
30
28
|
|
|
31
29
|
```blocks
|
|
32
|
-
namespace SpriteKind {
|
|
33
|
-
export const Mortal = SpriteKind.create()
|
|
34
|
-
export const Ghost = SpriteKind.create()
|
|
35
|
-
export const Ball = SpriteKind.create()
|
|
36
|
-
}
|
|
37
|
-
let ghost: Sprite = null
|
|
38
|
-
let projectile: Sprite = null
|
|
39
|
-
ghost = sprites.create(img`
|
|
40
|
-
. . . . . . d d d d d . . . . .
|
|
41
|
-
. . . d d d d 1 1 1 d d d . . .
|
|
42
|
-
. . d d 1 1 1 1 1 1 1 1 d d . .
|
|
43
|
-
. . d 1 1 1 1 1 1 1 1 1 1 d . .
|
|
44
|
-
. . d 1 1 1 1 1 1 1 1 1 1 d d .
|
|
45
|
-
. d d 1 1 1 f 1 1 1 f 1 1 1 d .
|
|
46
|
-
. d 1 1 1 1 1 1 1 1 1 1 1 1 d d
|
|
47
|
-
. d 1 1 1 1 1 1 1 1 1 1 1 1 1 d
|
|
48
|
-
. d 1 1 1 1 1 1 1 1 1 1 1 1 1 d
|
|
49
|
-
d d 1 1 1 1 1 1 f f 1 1 1 1 1 d
|
|
50
|
-
d 1 1 1 1 1 1 1 f f 1 1 1 1 1 d
|
|
51
|
-
d 1 1 1 1 1 1 1 1 1 1 1 1 1 1 d
|
|
52
|
-
d 1 1 1 1 1 1 1 d 1 1 1 1 1 1 d
|
|
53
|
-
d 1 d d d 1 1 d d d d 1 d 1 1 d
|
|
54
|
-
d d d . d d d d . . d d d d d d
|
|
55
|
-
d d . . . d d . . . . d . . d d
|
|
56
|
-
`, SpriteKind.Ghost)
|
|
57
|
-
ghost.x = 40
|
|
58
|
-
ghost.setFlag(SpriteFlag.AutoDestroy, true)
|
|
59
|
-
game.onUpdateInterval(400, function () {
|
|
60
|
-
projectile = sprites.createProjectile(img`
|
|
61
|
-
. . 7 7 7 7 . .
|
|
62
|
-
. 7 7 7 7 7 7 .
|
|
63
|
-
7 7 7 7 7 7 7 7
|
|
64
|
-
7 7 7 7 7 7 7 7
|
|
65
|
-
7 7 7 7 7 7 7 7
|
|
66
|
-
7 7 7 7 7 7 7 7
|
|
67
|
-
. 7 7 7 7 7 7 .
|
|
68
|
-
. . 7 7 7 7 . .
|
|
69
|
-
`, -400, 0, SpriteKind.Ball)
|
|
70
|
-
projectile.z = -1
|
|
71
|
-
})
|
|
72
|
-
sprites.onOverlap(SpriteKind.Mortal, SpriteKind.Ball, function (sprite, otherSprite) {
|
|
73
|
-
sprite.say("Ouch!", 200)
|
|
74
|
-
otherSprite.vx = otherSprite.vx * -1
|
|
75
|
-
otherSprite.vy = Math.randomRange(-100, 100)
|
|
76
|
-
sprite.x += -1
|
|
77
|
-
})
|
|
78
30
|
controller.A.onEvent(ControllerButtonEvent.Pressed, function () {
|
|
79
|
-
ghost.
|
|
31
|
+
ghost.setFlag(SpriteFlag.Ghost, false)
|
|
80
32
|
})
|
|
81
|
-
sprites.
|
|
82
|
-
|
|
33
|
+
sprites.onOverlap(SpriteKind.Player, SpriteKind.Projectile, function (sprite, otherSprite) {
|
|
34
|
+
otherSprite.setVelocity(-50, 50)
|
|
35
|
+
sprite.sayText("Ouch!", 200, false)
|
|
36
|
+
sprite.setPosition(sprite.x + 2, sprite.y + 2)
|
|
37
|
+
})
|
|
38
|
+
sprites.onDestroyed(SpriteKind.Player, function (sprite) {
|
|
39
|
+
game.gameOver(false)
|
|
83
40
|
})
|
|
84
|
-
```
|
|
85
|
-
|
|
86
|
-
## Ghosting mode #ex2
|
|
87
|
-
|
|
88
|
-
Use the **A** to blast a green ball at a ``Ghost`` sprite. Set the flag for the sprite to ``Ghost``. In the ``||sprites:on overlaps||`` block, try to detect the contact of the ball with the ghost. When button **B** is pressed, switch the value of the ``ghost`` flag and see if the ball hits the ghost sprite.
|
|
89
|
-
|
|
90
|
-
```blocks
|
|
91
|
-
namespace SpriteKind {
|
|
92
|
-
export const Mortal = SpriteKind.create()
|
|
93
|
-
export const Ghost = SpriteKind.create()
|
|
94
|
-
export const Ball = SpriteKind.create()
|
|
95
|
-
}
|
|
96
41
|
let projectile: Sprite = null
|
|
97
42
|
let ghost: Sprite = null
|
|
98
|
-
let mySprite = sprites.create(img`
|
|
99
|
-
. . . . . . . . . . . . . . . .
|
|
100
|
-
. . . . . . . . . . . . . . . .
|
|
101
|
-
. . . . . . . . . . . . . . . .
|
|
102
|
-
. . . . . . . . . . . . . . . .
|
|
103
|
-
. . . . . . . . . . . . . . . .
|
|
104
|
-
. . . . . . . . . . . . . . . .
|
|
105
|
-
. . . . . . . . . . . . . . . .
|
|
106
|
-
. . . . . . . . . . . . . . . .
|
|
107
|
-
. . . . . . . . . . . . . . . .
|
|
108
|
-
. . . . . . . . . . . . . . . .
|
|
109
|
-
. . . . . . . . . . . . . . . .
|
|
110
|
-
. . . . . . . . . . . . . . . .
|
|
111
|
-
. . . . . . . . . . . . . . . .
|
|
112
|
-
. . . . . . . . . . . . . . . .
|
|
113
|
-
. . . . . . . . . . . . . . . .
|
|
114
|
-
. . . . . . . . . . . . . . . .
|
|
115
|
-
`, SpriteKind.Ball)
|
|
116
43
|
ghost = sprites.create(img`
|
|
117
44
|
. . . . . . d d d d d . . . . .
|
|
118
45
|
. . . d d d d 1 1 1 d d d . . .
|
|
@@ -130,33 +57,29 @@ ghost = sprites.create(img`
|
|
|
130
57
|
d 1 d d d 1 1 d d d d 1 d 1 1 d
|
|
131
58
|
d d d . d d d d . . d d d d d d
|
|
132
59
|
d d . . . d d . . . . d . . d d
|
|
133
|
-
`, SpriteKind.
|
|
134
|
-
ghost.
|
|
60
|
+
`, SpriteKind.Player)
|
|
61
|
+
ghost.setPosition(60, 60)
|
|
135
62
|
ghost.setFlag(SpriteFlag.AutoDestroy, true)
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
.
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
.
|
|
145
|
-
. .
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
sprite.say("Ouch!", 200)
|
|
157
|
-
otherSprite.vx = otherSprite.vx * -1
|
|
158
|
-
otherSprite.vy = Math.randomRange(-100, 100)
|
|
159
|
-
sprite.x += -1
|
|
63
|
+
ghost.setFlag(SpriteFlag.Ghost, true)
|
|
64
|
+
game.onUpdateInterval(1000, function () {
|
|
65
|
+
projectile = sprites.createProjectileFromSide(img`
|
|
66
|
+
. . . . . . . . . . . . . . . .
|
|
67
|
+
. . . . . . . . . . . . . . . .
|
|
68
|
+
. . . . . . . . . . . . . . . .
|
|
69
|
+
. . . . . . . . . . . . . . . .
|
|
70
|
+
. . . . . . . . . . . . . . . .
|
|
71
|
+
. . . . . . . 5 5 . . . . . . .
|
|
72
|
+
. . . . . . 5 5 5 5 . . . . . .
|
|
73
|
+
. . . . . 5 5 5 5 5 5 . . . . .
|
|
74
|
+
. . . . . 5 5 5 5 5 5 . . . . .
|
|
75
|
+
. . . . . . 5 5 5 5 . . . . . .
|
|
76
|
+
. . . . . . . 5 5 . . . . . . .
|
|
77
|
+
. . . . . . . . . . . . . . . .
|
|
78
|
+
. . . . . . . . . . . . . . . .
|
|
79
|
+
. . . . . . . . . . . . . . . .
|
|
80
|
+
. . . . . . . . . . . . . . . .
|
|
81
|
+
. . . . . . . . . . . . . . . .
|
|
82
|
+
`, 50, 50)
|
|
160
83
|
})
|
|
161
84
|
```
|
|
162
85
|
|
|
@@ -42,28 +42,53 @@ f e e e e e f f f f f e e e e f
|
|
|
42
42
|
smiley.say("Hello!")
|
|
43
43
|
```
|
|
44
44
|
|
|
45
|
-
###
|
|
45
|
+
### Yellow message
|
|
46
46
|
|
|
47
|
-
|
|
47
|
+
Make a square yellow sprite in the middle of the screen. Send a moving person sprite around the screen. When the person sprite crosses the yellow square, make the person say "Yellow!".
|
|
48
48
|
|
|
49
49
|
```blocks
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
let shield: Image = null
|
|
53
|
-
let greenBox: Image = null
|
|
54
|
-
greenBox = image.create(32, 32)
|
|
55
|
-
greenBox.fill(7)
|
|
56
|
-
shield = image.create(4, 64)
|
|
57
|
-
shield.fill(10)
|
|
58
|
-
barrier = sprites.createObstacle(shield)
|
|
59
|
-
barrier.x = scene.screenWidth() - 4
|
|
60
|
-
greenBoxGo = sprites.create(greenBox)
|
|
61
|
-
greenBoxGo.x = 16
|
|
62
|
-
greenBoxGo.ax = 80
|
|
63
|
-
greenBoxGo.onCollision(CollisionDirection.Right, function (wall) {
|
|
64
|
-
greenBoxGo.x = 16
|
|
65
|
-
greenBoxGo.say("Bounce!", 400)
|
|
50
|
+
sprites.onOverlap(SpriteKind.Player, SpriteKind.Player, function (sprite, otherSprite) {
|
|
51
|
+
sprite.sayText("Yellow!", 100, false)
|
|
66
52
|
})
|
|
53
|
+
scene.setBackgroundColor(13)
|
|
54
|
+
let box = sprites.create(img`
|
|
55
|
+
5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5
|
|
56
|
+
5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5
|
|
57
|
+
5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5
|
|
58
|
+
5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5
|
|
59
|
+
5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5
|
|
60
|
+
5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5
|
|
61
|
+
5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5
|
|
62
|
+
5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5
|
|
63
|
+
5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5
|
|
64
|
+
5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5
|
|
65
|
+
5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5
|
|
66
|
+
5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5
|
|
67
|
+
5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5
|
|
68
|
+
5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5
|
|
69
|
+
5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5
|
|
70
|
+
5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5
|
|
71
|
+
`, SpriteKind.Player)
|
|
72
|
+
let person = sprites.create(img`
|
|
73
|
+
. . . . . . f f f f . . . . . .
|
|
74
|
+
. . . . f f f 2 2 f f f . . . .
|
|
75
|
+
. . . f f f 2 2 2 2 f f f . . .
|
|
76
|
+
. . f f f e e e e e e f f f . .
|
|
77
|
+
. . f f e 2 2 2 2 2 2 e e f . .
|
|
78
|
+
. . f e 2 f f f f f f 2 e f . .
|
|
79
|
+
. . f f f f e e e e f f f f . .
|
|
80
|
+
. f f e f b f 4 4 f b f e f f .
|
|
81
|
+
. f e e 4 1 f d d f 1 4 e e f .
|
|
82
|
+
. . f e e d d d d d d e e f . .
|
|
83
|
+
. . . f e e 4 4 4 4 e e f . . .
|
|
84
|
+
. . e 4 f 2 2 2 2 2 2 f 4 e . .
|
|
85
|
+
. . 4 d f 2 2 2 2 2 2 f d 4 . .
|
|
86
|
+
. . 4 4 f 4 4 5 5 4 4 f 4 4 . .
|
|
87
|
+
. . . . . f f f f f f . . . . .
|
|
88
|
+
. . . . . f f . . f f . . . . .
|
|
89
|
+
`, SpriteKind.Player)
|
|
90
|
+
person.setBounceOnWall(true)
|
|
91
|
+
person.setVelocity(50, 50)
|
|
67
92
|
```
|
|
68
93
|
|
|
69
94
|
## #seealso
|
package/libs/game/hitbox.ts
CHANGED
|
@@ -88,15 +88,15 @@ namespace game {
|
|
|
88
88
|
overlapsWith(other: Hitbox): boolean {
|
|
89
89
|
this.updateIfInvalid();
|
|
90
90
|
other.updateIfInvalid();
|
|
91
|
-
if (
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
return
|
|
91
|
+
if (
|
|
92
|
+
this.left > other.right ||
|
|
93
|
+
this.top > other.bottom ||
|
|
94
|
+
this.right < other.left ||
|
|
95
|
+
this.bottom < other.top
|
|
96
|
+
) {
|
|
97
|
+
return false;
|
|
98
|
+
}
|
|
99
|
+
return true;
|
|
100
100
|
}
|
|
101
101
|
}
|
|
102
102
|
|
|
@@ -105,6 +105,10 @@ namespace game {
|
|
|
105
105
|
if (s._hitbox && s._hitbox.isValid())
|
|
106
106
|
return s._hitbox;
|
|
107
107
|
|
|
108
|
+
if (s._rotatedBBox) {
|
|
109
|
+
return new Hitbox(s, Fx8(s._rotatedBBox.width), Fx8(s._rotatedBBox.height), Fx.zeroFx8, Fx.zeroFx8);
|
|
110
|
+
}
|
|
111
|
+
|
|
108
112
|
const i = s.image;
|
|
109
113
|
let minX = Fx8(i.width);
|
|
110
114
|
let minY = Fx8(i.height);
|
package/libs/game/pxt.json
CHANGED
|
@@ -0,0 +1,194 @@
|
|
|
1
|
+
namespace sprites {
|
|
2
|
+
let aabbPoints: number[];
|
|
3
|
+
|
|
4
|
+
export class RotatedBoundingBox {
|
|
5
|
+
protected _rotation: number;
|
|
6
|
+
protected _width: number;
|
|
7
|
+
protected _height: number;
|
|
8
|
+
|
|
9
|
+
protected points: number[];
|
|
10
|
+
protected cornerDistance: number;
|
|
11
|
+
protected cornerAngle: number;
|
|
12
|
+
|
|
13
|
+
public get x0(): number {
|
|
14
|
+
return this.points[0];
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
public get y0(): number {
|
|
18
|
+
return this.points[1];
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
public get x1(): number {
|
|
22
|
+
return this.points[2];
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
public get y1(): number {
|
|
26
|
+
return this.points[3];
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
public get x2(): number {
|
|
30
|
+
return this.points[4];
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
public get y2(): number {
|
|
34
|
+
return this.points[5];
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
public get x3(): number {
|
|
38
|
+
return this.points[6];
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
public get y3(): number {
|
|
42
|
+
return this.points[7];
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
public get rotation() {
|
|
46
|
+
return this._rotation;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
public set rotation(value: number) {
|
|
50
|
+
this.setRotation(value);
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
public get width() {
|
|
54
|
+
return this._width;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
public get height() {
|
|
58
|
+
return this._height;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
constructor(
|
|
62
|
+
public anchor: Sprite,
|
|
63
|
+
width: number,
|
|
64
|
+
height: number
|
|
65
|
+
) {
|
|
66
|
+
this.points = [];
|
|
67
|
+
this._rotation = 0;
|
|
68
|
+
this.setDimensions(width, height);
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
setDimensions(width: number, height: number) {
|
|
72
|
+
width /= 2;
|
|
73
|
+
height /= 2;
|
|
74
|
+
|
|
75
|
+
this.cornerDistance = Math.sqrt(
|
|
76
|
+
width * width + height * height
|
|
77
|
+
);
|
|
78
|
+
this.cornerAngle = Math.atan2(height, width);
|
|
79
|
+
this.setRotation(this._rotation);
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
setRotation(angle: number) {
|
|
83
|
+
this._rotation = angle;
|
|
84
|
+
this.points[0] = Math.cos(this.cornerAngle + angle) * this.cornerDistance;
|
|
85
|
+
this.points[1] = Math.sin(this.cornerAngle + angle) * this.cornerDistance;
|
|
86
|
+
this.points[2] = Math.cos(Math.PI - this.cornerAngle + angle) * this.cornerDistance;
|
|
87
|
+
this.points[3] = Math.sin(Math.PI - this.cornerAngle + angle) * this.cornerDistance;
|
|
88
|
+
this.points[4] = Math.cos(Math.PI + this.cornerAngle + angle) * this.cornerDistance;
|
|
89
|
+
this.points[5] = Math.sin(Math.PI + this.cornerAngle + angle) * this.cornerDistance;
|
|
90
|
+
this.points[6] = Math.cos(angle - this.cornerAngle) * this.cornerDistance;
|
|
91
|
+
this.points[7] = Math.sin(angle - this.cornerAngle) * this.cornerDistance;
|
|
92
|
+
this.updateWidthHeight();
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
overlaps(other: RotatedBoundingBox): boolean {
|
|
96
|
+
return doRectanglesIntersect(
|
|
97
|
+
this.points,
|
|
98
|
+
this.anchor.x,
|
|
99
|
+
this.anchor.y,
|
|
100
|
+
other.points,
|
|
101
|
+
other.anchor.x,
|
|
102
|
+
other.anchor.y
|
|
103
|
+
);
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
overlapsAABB(left: number, top: number, right: number, bottom: number) {
|
|
107
|
+
if (!aabbPoints) {
|
|
108
|
+
aabbPoints = [];
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
aabbPoints[0] = left;
|
|
112
|
+
aabbPoints[1] = top;
|
|
113
|
+
aabbPoints[2] = right;
|
|
114
|
+
aabbPoints[3] = top;
|
|
115
|
+
aabbPoints[4] = right;
|
|
116
|
+
aabbPoints[5] = bottom;
|
|
117
|
+
aabbPoints[6] = left;
|
|
118
|
+
aabbPoints[7] = bottom;
|
|
119
|
+
return doRectanglesIntersect(
|
|
120
|
+
this.points,
|
|
121
|
+
this.anchor.x,
|
|
122
|
+
this.anchor.y,
|
|
123
|
+
aabbPoints,
|
|
124
|
+
0,
|
|
125
|
+
0
|
|
126
|
+
);
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
protected updateWidthHeight() {
|
|
130
|
+
let minX = this.points[0];
|
|
131
|
+
let maxX = minX;
|
|
132
|
+
let minY = this.points[1];
|
|
133
|
+
let maxY = minY;
|
|
134
|
+
|
|
135
|
+
for (let i = 2; i < 8; i += 2) {
|
|
136
|
+
minX = Math.min(minX, this.points[i]);
|
|
137
|
+
maxX = Math.max(maxX, this.points[i]);
|
|
138
|
+
minY = Math.min(minY, this.points[i + 1]);
|
|
139
|
+
maxY = Math.max(maxY, this.points[i + 1]);
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
this._width = (maxX - minX) | 0;
|
|
143
|
+
this._height = (maxY - minY) | 0;
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
// adapted from https://stackoverflow.com/questions/10962379/how-to-check-intersection-between-2-rotated-rectangles
|
|
148
|
+
// but optimized for rectangles
|
|
149
|
+
function doRectanglesIntersect(a: number[], ax: number, ay: number, b: number[], bx: number, by: number) {
|
|
150
|
+
return !(checkForNonIntersection(a, ax, ay, b, bx, by) || checkForNonIntersection(b, bx, by, a, ax, ay));
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
function checkForNonIntersection(a: number[], ax: number, ay: number, b: number[], bx: number, by: number) {
|
|
154
|
+
// we only need to check the first two sides because the
|
|
155
|
+
// normals are the same for the other two
|
|
156
|
+
for (let pointIndex = 0; pointIndex < 4; pointIndex += 2) {
|
|
157
|
+
const normalX = a[pointIndex + 3] - a[pointIndex + 1];
|
|
158
|
+
const normalY = a[pointIndex] - a[pointIndex + 2];
|
|
159
|
+
|
|
160
|
+
let minA: number = undefined;
|
|
161
|
+
let maxA: number = undefined;
|
|
162
|
+
let minB: number = undefined;
|
|
163
|
+
let maxB: number = undefined;
|
|
164
|
+
|
|
165
|
+
for (let i = 0; i < 8; i += 2) {
|
|
166
|
+
const projected = normalX * (a[i] + ax) + normalY * (a[i + 1] + ay);
|
|
167
|
+
|
|
168
|
+
if (minA === undefined || projected < minA) {
|
|
169
|
+
minA = projected;
|
|
170
|
+
}
|
|
171
|
+
if (maxA == undefined || projected > maxA) {
|
|
172
|
+
maxA = projected;
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
for (let i = 0; i < 8; i += 2) {
|
|
177
|
+
const projected = normalX * (b[i] + bx) + normalY * (b[i + 1] + by);
|
|
178
|
+
|
|
179
|
+
if (minB === undefined || projected < minB) {
|
|
180
|
+
minB = projected;
|
|
181
|
+
}
|
|
182
|
+
if (maxB == undefined || projected > maxB) {
|
|
183
|
+
maxB = projected;
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
if (maxA < minB || maxB < minA) {
|
|
188
|
+
return true;
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
return false;
|
|
193
|
+
}
|
|
194
|
+
}
|
package/libs/game/sprite.ts
CHANGED
|
@@ -105,6 +105,7 @@ class Sprite extends sprites.BaseSprite {
|
|
|
105
105
|
_sy: Fx8 // scale
|
|
106
106
|
_width: Fx8 // scaled width
|
|
107
107
|
_height: Fx8 // scaled height
|
|
108
|
+
_rotatedBBox: sprites.RotatedBoundingBox;
|
|
108
109
|
|
|
109
110
|
//% group="Physics" blockSetVariable="mySprite"
|
|
110
111
|
//% blockCombine block="x" callInDebugger
|
|
@@ -200,10 +201,12 @@ class Sprite extends sprites.BaseSprite {
|
|
|
200
201
|
//% group="Physics" blockSetVariable="mySprite"
|
|
201
202
|
//% blockCombine block="sx (scale x)"
|
|
202
203
|
set sx(v: number) {
|
|
204
|
+
const y = this.y;
|
|
203
205
|
const x = this.x;
|
|
204
206
|
this._sx = Fx8(Math.max(0, v));
|
|
205
207
|
this.recalcSize();
|
|
206
|
-
this.
|
|
208
|
+
this.y = y;
|
|
209
|
+
this.x = x;
|
|
207
210
|
}
|
|
208
211
|
//% group="Physics" blockSetVariable="mySprite"
|
|
209
212
|
//% blockCombine block="sy (scale y)" callInDebugger
|
|
@@ -214,10 +217,13 @@ class Sprite extends sprites.BaseSprite {
|
|
|
214
217
|
//% blockCombine block="sy (scale y)"
|
|
215
218
|
set sy(v: number) {
|
|
216
219
|
const y = this.y;
|
|
220
|
+
const x = this.x;
|
|
217
221
|
this._sy = Fx8(Math.max(0, v));
|
|
218
222
|
this.recalcSize();
|
|
219
|
-
this.
|
|
223
|
+
this.y = y;
|
|
224
|
+
this.x = x;
|
|
220
225
|
}
|
|
226
|
+
|
|
221
227
|
//% group="Physics" blockSetVariable="mySprite"
|
|
222
228
|
//% blockCombine block="scale" callInDebugger
|
|
223
229
|
get scale(): number {
|
|
@@ -229,6 +235,36 @@ class Sprite extends sprites.BaseSprite {
|
|
|
229
235
|
this.sx = this.sy = v;
|
|
230
236
|
}
|
|
231
237
|
|
|
238
|
+
//% group="Physics" blockSetVariable="mySprite"
|
|
239
|
+
//% blockCombine block="rotation (radians)" callInDebugger
|
|
240
|
+
get rotation(): number {
|
|
241
|
+
return this._rotatedBBox ? this._rotatedBBox.rotation : 0;
|
|
242
|
+
}
|
|
243
|
+
//% group="Physics" blockSetVariable="mySprite"
|
|
244
|
+
//% blockCombine block="rotation (radians)"
|
|
245
|
+
set rotation(v: number) {
|
|
246
|
+
const x = this.x;
|
|
247
|
+
const y = this.y;
|
|
248
|
+
if (!this._rotatedBBox) {
|
|
249
|
+
this._rotatedBBox = new sprites.RotatedBoundingBox(this, this.width, this.height);
|
|
250
|
+
}
|
|
251
|
+
this._rotatedBBox.setRotation(v);
|
|
252
|
+
this.recalcSize();
|
|
253
|
+
this.x = x;
|
|
254
|
+
this.y = y;
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
//% group="Physics" blockSetVariable="mySprite"
|
|
258
|
+
//% blockCombine block="rotation (degrees)" callInDebugger
|
|
259
|
+
get rotationDegrees(): number {
|
|
260
|
+
return this.rotation * 180 / Math.PI;
|
|
261
|
+
}
|
|
262
|
+
//% group="Physics" blockSetVariable="mySprite"
|
|
263
|
+
//% blockCombine block="rotation (degrees)"
|
|
264
|
+
set rotationDegrees(v: number) {
|
|
265
|
+
this.rotation = v * Math.PI / 180;
|
|
266
|
+
}
|
|
267
|
+
|
|
232
268
|
private _data: any;
|
|
233
269
|
/**
|
|
234
270
|
* Custom data
|
|
@@ -338,7 +374,7 @@ class Sprite extends sprites.BaseSprite {
|
|
|
338
374
|
}
|
|
339
375
|
|
|
340
376
|
calcDimensionalHash() {
|
|
341
|
-
return this._image.revision() + Fx.toIntShifted(this._width, 8) + Fx.toIntShifted(this._height, 16);
|
|
377
|
+
return this._image.revision() + Fx.toIntShifted(this._width, 8) + Fx.toIntShifted(this._height, 16) + this.rotation;
|
|
342
378
|
}
|
|
343
379
|
|
|
344
380
|
resetHitbox() {
|
|
@@ -363,8 +399,15 @@ class Sprite extends sprites.BaseSprite {
|
|
|
363
399
|
}
|
|
364
400
|
|
|
365
401
|
protected recalcSize(): void {
|
|
366
|
-
|
|
367
|
-
|
|
402
|
+
if (this._rotatedBBox) {
|
|
403
|
+
this._rotatedBBox.setDimensions(this._image.width * this.sx, this._image.height * this.sy);
|
|
404
|
+
this._width = Fx8(this._rotatedBBox.width);
|
|
405
|
+
this._height = Fx8(this._rotatedBBox.height);
|
|
406
|
+
}
|
|
407
|
+
else {
|
|
408
|
+
this._width = Fx8(this._image.width * this.sx);
|
|
409
|
+
this._height = Fx8(this._image.height * this.sy);
|
|
410
|
+
}
|
|
368
411
|
this.resetHitbox();
|
|
369
412
|
}
|
|
370
413
|
|
|
@@ -688,7 +731,7 @@ class Sprite extends sprites.BaseSprite {
|
|
|
688
731
|
//% blockId=spriteoverlapswith block="%sprite(mySprite) overlaps with %other=variables_get(otherSprite)"
|
|
689
732
|
//% help=sprites/sprite/overlaps-with
|
|
690
733
|
//% weight=90
|
|
691
|
-
overlapsWith(other: Sprite) {
|
|
734
|
+
overlapsWith(other: Sprite): boolean {
|
|
692
735
|
control.enablePerfCounter("overlapsCPP")
|
|
693
736
|
if (other == this) return false;
|
|
694
737
|
if (this.flags & SPRITE_NO_SPRITE_OVERLAPS)
|
|
@@ -699,7 +742,47 @@ class Sprite extends sprites.BaseSprite {
|
|
|
699
742
|
return other._hitbox.overlapsWith(this._hitbox);
|
|
700
743
|
if (!other._hitbox.overlapsWith(this._hitbox))
|
|
701
744
|
return false;
|
|
702
|
-
if (
|
|
745
|
+
else if (this._rotatedBBox) {
|
|
746
|
+
if (other._rotatedBBox) {
|
|
747
|
+
if (this._rotatedBBox.overlaps(other._rotatedBBox)) {
|
|
748
|
+
return helpers.checkOverlapsTwoScaledRotatedImages(
|
|
749
|
+
other.image,
|
|
750
|
+
this.left - other.left,
|
|
751
|
+
this.top - other.top,
|
|
752
|
+
other.sx,
|
|
753
|
+
other.sy,
|
|
754
|
+
other.rotation,
|
|
755
|
+
this.image,
|
|
756
|
+
this.sx,
|
|
757
|
+
this.sy,
|
|
758
|
+
this.rotation
|
|
759
|
+
);
|
|
760
|
+
}
|
|
761
|
+
else {
|
|
762
|
+
return false;
|
|
763
|
+
}
|
|
764
|
+
}
|
|
765
|
+
else {
|
|
766
|
+
if (this._rotatedBBox.overlapsAABB(other.left, other.top, other.right, other.bottom)) {
|
|
767
|
+
return helpers.checkOverlapsScaledRotatedImage(
|
|
768
|
+
other.image,
|
|
769
|
+
this.left - other.left,
|
|
770
|
+
this.top - other.top,
|
|
771
|
+
this.image,
|
|
772
|
+
this.sx,
|
|
773
|
+
this.sy,
|
|
774
|
+
this.rotation
|
|
775
|
+
);
|
|
776
|
+
}
|
|
777
|
+
else {
|
|
778
|
+
return false;
|
|
779
|
+
}
|
|
780
|
+
}
|
|
781
|
+
}
|
|
782
|
+
else if (other._rotatedBBox) {
|
|
783
|
+
return other.overlapsWith(this);
|
|
784
|
+
}
|
|
785
|
+
else if (!this.isScaled() && !other.isScaled()) {
|
|
703
786
|
return other._image.overlapsWith(
|
|
704
787
|
this._image,
|
|
705
788
|
this.left - other.left,
|
|
@@ -1117,7 +1200,18 @@ class Sprite extends sprites.BaseSprite {
|
|
|
1117
1200
|
}
|
|
1118
1201
|
|
|
1119
1202
|
protected drawSprite(drawLeft: number, drawTop: number) {
|
|
1120
|
-
if (
|
|
1203
|
+
if (this._rotatedBBox) {
|
|
1204
|
+
helpers.imageDrawScaledRotated(
|
|
1205
|
+
screen,
|
|
1206
|
+
drawLeft,
|
|
1207
|
+
drawTop,
|
|
1208
|
+
this._image,
|
|
1209
|
+
this.sx,
|
|
1210
|
+
this.sy,
|
|
1211
|
+
this.rotation
|
|
1212
|
+
);
|
|
1213
|
+
}
|
|
1214
|
+
else if (!this.isScaled())
|
|
1121
1215
|
screen.drawTransparentImage(this._image, drawLeft, drawTop);
|
|
1122
1216
|
else
|
|
1123
1217
|
screen.blit(
|