Sidequest

Entity Colliders

Add Circle Collider to the Bird

Now we need to update the Bird class to setup a circle collider:

+
import { CircleCollider } from "#/components/circle-collider";
2
import { SpriteAnimation } from "#/components/sprite-animation";
3
import { SpriteData } from "#/components/sprite-data";
4
import { Vector2d } from "#/components/vector2d";
5
6
type BirdOptions = {
7
spriteSheet: HTMLImageElement;
8
spriteData: SpriteData;
9
position: Vector2d;
10
flapAnimation: SpriteAnimation;
+
circlCollider: CircleCollider;
12
};
13
14
export class Bird {
15
state = BirdState.Idle;
16
spriteSheet: HTMLImageElement;
17
spriteData: SpriteData;
18
position: Vector2d;
19
flapAnimation: SpriteAnimation;
20
velocity = new Vector2d();
+
circleCollider: CircleCollider;
22
23
// ...
24
25
constructor(options: BirdOptions) {
26
this.spriteSheet = options.spriteSheet;
27
this.spriteData = options.spriteData;
28
this.position = options.position;
29
this.flapAnimation = options.flapAnimation;
+
this.circleCollider = options.circlCollider;
31
}
32
33
// ...
34
}

Then update main.ts:

1
import spriteSheetUrl from "#/assets/image/spritesheet.png";
+
import { CircleCollider } from "#/components/circle-collider";
3
import { SpriteAnimation } from "#/components/sprite-animation";
4
5
// ...
6
7
const bird = new Bird({
8
spriteSheet: spriteSheet,
9
position: new Vector2d(config.gameWidth / 4, config.gameHeight / 2),
10
spriteData: new SpriteData(
11
spriteMap.bird.idle.sourceX,
12
spriteMap.bird.idle.sourceY,
13
spriteMap.bird.idle.width,
14
spriteMap.bird.idle.height
15
),
16
flapAnimation: new SpriteAnimation(
17
new SpriteAnimationDetails(
18
spriteMap.bird.animations.flap.sourceX,
19
spriteMap.bird.animations.flap.sourceY,
20
spriteMap.bird.animations.flap.width,
21
spriteMap.bird.animations.flap.height,
22
spriteMap.bird.animations.flap.frameWidth,
23
spriteMap.bird.animations.flap.frameHeight
24
),
25
0.3
26
),
+
circlCollider: new CircleCollider(0, 0, 12),
28
});
29
30
// ...

Add Box Collider to the Ground

Let's update the Ground constructor to take a box collider, then update the instance.

+
import { BoxCollider } from "#/components/box-collider";
2
import { SpriteData } from "#/components/sprite-data";
3
import { Vector2d } from "#/components/vector2d";
4
5
type GroundOptions = {
+
boxCollider: BoxCollider;
7
position: Vector2d;
8
spriteData: SpriteData;
9
spriteSheet: HTMLImageElement;
10
11
/**
12
* This is in pixels **per frame**.
13
*/
14
scrollSpeed: number;
15
};
16
17
export class Ground {
+
boxCollider: BoxCollider;
19
position: Vector2d;
20
spriteData: SpriteData;
21
spriteSheet: HTMLImageElement;
22
scrollSpeed: number;
23
24
// Track the current scroll position separately from the position.
25
public scrollPositionX = 0;
26
27
constructor(options: GroundOptions) {
+
this.boxCollider = options.boxCollider;
29
this.position = options.position;
30
this.spriteData = options.spriteData;
31
this.spriteSheet = options.spriteSheet;
32
this.scrollSpeed = options.scrollSpeed;
33
}
34
35
// ...
36
}
1
import spriteSheetUrl from "#/assets/image/spritesheet.png";
+
import { BoxCollider } from "#/components/box-collider";
3
import { CircleCollider } from "#/components/circle-collider";
4
5
// ...
6
7
const ground = new Ground({
+
boxCollider: new BoxCollider(
+
0,
+
0,
+
spriteMap.ground.width,
+
spriteMap.ground.height
+
),
14
position: new Vector2d(0, config.gameHeight - spriteMap.ground.height),
15
spriteData: spriteMap.ground,
16
spriteSheet: spriteSheet,
17
scrollSpeed: 120,
18
});

Next, let's add support for toggling the rendering of the colliders when we press the d key. It would be nice to get a little visual feedback on whether or not our colliders look correct.