Sidequest

Clean Up

Before we move onto the pipe manager, we're going to delete any code we added related to testing our pipes.

We're going to need to delete several lines of code from main.ts:

1
import spriteSheetUrl from "#/assets/image/spritesheet.png";
2
import { BoxCollider } from "#/components/box-collider";
3
import { CircleCollider } from "#/components/circle-collider";
4
import { SpriteAnimation } from "#/components/sprite-animation";
5
import { SpriteAnimationDetails } from "#/components/sprite-animation-details";
6
import { SpriteData } from "#/components/sprite-data";
7
import { Vector2d } from "#/components/vector2d";
8
import { config } from "#/config";
9
import { Bird } from "#/entities/bird";
10
import { Ground } from "#/entities/ground";
-
import { Pipe } from "#/entities/pipe";
12
import { Game, GameState } from "#/game";
13
import { loadImage } from "#/lib/asset-loader";
14
import { circleRectangleIntersects } from "#/lib/collision";
15
import { spriteMap } from "#/sprite-map";
16
17
const spriteSheet = await loadImage(spriteSheetUrl);
18
const game = new Game();
19
20
const canvas = document.querySelector("canvas");
21
if (canvas == null) {
22
throw new Error("Could not find canvas element");
23
}
24
25
canvas.width = config.gameWidth;
26
canvas.height = config.gameHeight;
27
28
const context = canvas.getContext("2d");
29
30
if (context == null) {
31
throw new Error("Could not obtain 2d context");
32
}
33
34
context.imageSmoothingEnabled = false;
35
36
// Add event listener to trigger bird flap
37
canvas.addEventListener("click", () => {
38
switch (game.state) {
39
case GameState.Title: {
40
game.state = GameState.Playing;
41
bird.flap();
42
43
break;
44
}
45
46
case GameState.Playing: {
47
bird.flap();
48
49
break;
50
}
51
52
case GameState.GameOver: {
53
game.reset();
54
bird.reset();
55
ground.start();
56
57
break;
58
}
59
}
60
});
61
62
window.addEventListener("keypress", (event) => {
63
if (event.code === "KeyD") {
64
game.debug = !game.debug;
65
}
66
});
67
68
const ground = new Ground({
69
boxCollider: new BoxCollider(
70
0,
71
0,
72
spriteMap.ground.width,
73
spriteMap.ground.height
74
),
75
game,
76
position: new Vector2d(0, config.gameHeight - spriteMap.ground.height),
77
spriteData: spriteMap.ground,
78
spriteSheet: spriteSheet,
79
scrollSpeed: 120,
80
});
81
82
const bird = new Bird({
83
game,
84
spriteSheet: spriteSheet,
85
position: new Vector2d(config.gameWidth / 4, config.gameHeight / 2),
86
spriteData: new SpriteData(
87
spriteMap.bird.idle.sourceX,
88
spriteMap.bird.idle.sourceY,
89
spriteMap.bird.idle.width,
90
spriteMap.bird.idle.height
91
),
92
flapAnimation: new SpriteAnimation(
93
new SpriteAnimationDetails(
94
spriteMap.bird.animations.flap.sourceX,
95
spriteMap.bird.animations.flap.sourceY,
96
spriteMap.bird.animations.flap.width,
97
spriteMap.bird.animations.flap.height,
98
spriteMap.bird.animations.flap.frameWidth,
99
spriteMap.bird.animations.flap.frameHeight
100
),
101
0.3
102
),
103
circlCollider: new CircleCollider(0, 0, 12),
104
});
105
-
const pipeTop = new Pipe({
-
game,
-
position: new Vector2d(
-
config.gameWidth / 2 - spriteMap.pipes.green.slice.width / 2,
-
config.gameHeight / 2 - 50
-
),
-
rimSpriteData: spriteMap.pipes.green.bottom,
-
sliceSpriteData: spriteMap.pipes.green.slice,
-
spriteSheet,
-
type: "top",
-
});
-
-
const pipeBottom = new Pipe({
-
game,
-
position: new Vector2d(
-
config.gameWidth / 2 - spriteMap.pipes.green.slice.width / 2,
-
config.gameHeight / 2 + 50
-
),
-
rimSpriteData: spriteMap.pipes.green.top,
-
sliceSpriteData: spriteMap.pipes.green.slice,
-
spriteSheet,
-
type: "bottom",
-
});
129
130
let last = performance.now();
131
132
/**
133
* The game loop.
134
*/
135
const frame = (hrt: DOMHighResTimeStamp) => {
136
const dt = Math.min(1000, hrt - last) / 1000;
137
138
context.clearRect(0, 0, canvas.width, canvas.height);
139
140
ground.update(dt);
141
bird.update(dt);
142
143
const didBirdHitGround = circleRectangleIntersects(
144
bird.position.x + bird.circleCollider.offsetX,
145
bird.position.y + bird.circleCollider.offsetY,
146
bird.circleCollider.radius,
147
ground.position.x + ground.boxCollider.offsetX,
148
ground.position.y + ground.boxCollider.offsetY,
149
ground.boxCollider.width,
150
ground.boxCollider.height
151
);
152
153
if (didBirdHitGround) {
154
bird.die();
155
ground.stop();
156
game.state = GameState.GameOver;
157
}
158
159
// Draw the background
160
context.drawImage(
161
spriteSheet,
162
spriteMap.background.sourceX,
163
spriteMap.background.sourceY,
164
spriteMap.background.width,
165
spriteMap.background.height,
166
0,
167
0,
168
spriteMap.background.width,
169
spriteMap.background.height
170
);
171
172
bird.draw(context);
-
pipeTop.draw(context);
-
pipeBottom.draw(context);
175
ground.draw(context);
176
-
const x = config.gameWidth / 2 - spriteMap.pipes.green.slice.width / 2;
-
const y = config.gameHeight / 2;
-
const buffer = 50;
-
const lineLength = 65;
-
-
// Where the bottom of the top pipe will be
-
context.fillStyle = "red";
-
context.fillRect(x, y - buffer, lineLength, 1);
-
// Center of the canvas (our spawn point)
-
context.fillStyle = "purple";
-
context.fillRect(x, y, lineLength, 1);
-
// Where the top of the bottom pipe will be
-
context.fillStyle = "red";
-
context.fillRect(x, y + buffer, lineLength, 1);
191
192
last = hrt;
193
194
requestAnimationFrame(frame);
195
};
196
197
// Start the game loop.
198
requestAnimationFrame(frame);

Now that we're in a nice clean state again, let's move onto the pipe manager!