8000 performance tuning · sjefvanleeuwen/shooter@869d07b · GitHub
[go: up one dir, main page]

Skip to content

Commit 869d07b

Browse files
author
Your Correct Name
committed
performance tuning
1 parent 842a403 commit 869d07b

File tree

3 files changed

+136
-82
lines changed

3 files changed

+136
-82
lines changed

js/DebugWindow.js

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
class DebugWindow {
2+
constructor() {
3+
this.visible = false;
4+
this.accumulatedTime = 0;
5+
this.frameCount = 0;
6+
this.fps = 0;
7+
// Pre-set font string to reduce repeated construction
8+
this.fontStyle = '14px monospace';
9+
}
10+
11+
update(delta) {
12+
this.accumulatedTime += delta;
13+
this.frameCount++;
14+
if (this.accumulatedTime >= 1) {
15+
this.fps = this.frameCount;
16+
this.frameCount = 0;
17+
this.accumulatedTime = 0;
18+
}
19+
}
20+
21+
draw(ctx) {
22+
ctx.save();
23+
ctx.font = this.fontStyle;
24+
ctx.fillStyle = 'yellow';
25+
ctx.fillText(`FPS: ${this.fps}`, 10, 20);
26+
ctx.restore();
27+
}
28+
}
29+
30+
export default DebugWindow;

js/game.js

Lines changed: 81 additions & 68 deletions
< 10000 td data-grid-cell-id="diff-cd24f7c6b8fa2f39a95342f4f3a33a775696fb9d5290d25edf22464a22a9a361-213-216-1" data-selected="false" role="gridcell" style="background-color:var(--diffBlob-deletionNum-bgColor, var(--diffBlob-deletion-bgColor-num));text-align:center" tabindex="-1" valign="top" class="focusable-grid-cell diff-line-number position-relative left-side">
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import PatternFormation from './PatternFormation.js'; // Add this import
66
import IntroScreen from './screens/IntroScreen.js';
77
import MusicPlayer from './audio/MusicPlayer.js';
88
import StartupScreen from './screens/StartupScreen.js';
9+
import DebugWindow from './DebugWindow.js';
910

1011
class Game {
1112
constructor() {
@@ -75,6 +76,9 @@ class Game {
7576

7677
// Initialize music player without starting it
7778
this.musicPlayer = new MusicPlayer();
79+
// Create persistent offscreen canvas for player tinting effects
80+
this.offCanvasCache = document.createElement('canvas');
81+
this.debugWindow = new DebugWindow();
7882
}
7983

8084
initGameScreen() {
@@ -146,7 +150,13 @@ class Game {
146150
}
147151

148152
bindEvents() {
149-
window.addEventListener('resize', () => this.setupCanvas());
153+
let resizeTimeout;
154+
window.addEventListener('resize', () => {
155+
clearTimeout(resizeTimeout);
156+
resizeTimeout = setTimeout(() => {
157+
this.setupCanvas();
158+
}, 100);
159+
});
150160
}
151161

152162
reset() {
@@ -157,6 +167,10 @@ class Game {
157167
}
158168

159169
handleInput(e) {
170+
// Toggle debug window when d is pressed
171+
if(e.key === 'd' || e.key === 'D'){
172+
this.debugWindow.visible = !this.debugWindow.visible;
173+
}
160174
// Add null check for current screen
161175
if (this.screens[this.currentScreen] && this.screens[this.currentScreen].handleInput) {
162176
const nextScreen = this.screens[this.currentScreen].handleInput(e.key);
@@ -193,105 +207,98 @@ class Game {
193207
update(timestamp) {
194208
const delta = (timestamp - (this.lastTime || timestamp)) / 1000;
195209
this.lastTime = timestamp;
196-
197-
// Check if startup screen is complete
198-
if (this.currentScreen === 'startup') {
199-
this.screens.startup.update(delta);
200-
if (this.screens.startup.complete) {
201-
this.switchScreen('intro');
202-
}
203-
} else if (this.currentScreen === 'intro') {
204-
this.screens.intro.update(delta);
205-
} else if (this.currentScreen === 'game') {
210+
211+
if (this.currentScreen === 'game') {
212+
// Cache properties to avoid repeated lookups
213+
const player = this.player;
214+
const formation = this.formation;
215+
206216
this.bgScroller.update(delta);
207-
this.player.update(delta);
208-
// Update emitter positions relative to the player's sprite.
209-
const engine1X = this.player.x + this.player.width / 2;
210-
const engine1Y = this.player.y + this.player.height - 25; // was 50
211-
// For engine 2 and 3, adjust: 10px down and 8px inward.
212-
const engine2X = engine1X - 25 + 4; // was 50+8
213-
const engine2Y = engine1Y - 25 + 5; // was 50+10
214-
const engine3X = engine1X + 25 - 4; // was 50-8
215-
const engine3Y = engine1Y - 25 + 5; // was 50+10
217+
player.update(delta);
218+
219+
// Update emitter positions with local variables
220+
const engine1X = player.x + player.width / 2;
221+
const engine1Y = player.y + player.height - 25;
222+
const engine2X = engine1X - 21; // simplified math
223+
const engine2Y = engine1Y - 20;
224+
const engine3X = engine1X + 21;
225+
const engine3Y = engine1Y - 20;
226+
216227
this.particleEngine.setEmitter(engine1X, engine1Y);
217228
this.particleEngine2.setEmitter(engine2X, engine2Y);
218229
this.particleEngine3.setEmitter(engine3X, engine3Y);
219-
// Update particle engines with the new emitter positions.
220230
this.particleEngine.update(delta);
221231
this.particleEngine2.update(delta);
222232
this.particleEngine3.update(delta);
223233

224-
// Update laser firing state
225-
this.laserEngineLeft.setFiring(this.player.isFiring);
226-
this.laserEngineRight.setFiring(this.player.isFiring);
227-
228-
// Update laser emitter positions (from top of sprite, spread apart)
229-
const laserLeftX = this.player.x + this.player.width * 0.3; // 30% from left
230-
const laserRightX = this.player.x + this.player.width * 0.7; // 70% from left
231-
const laserY = this.player.y; // Top of sprite
232-
234+
// Update lasers using cached firing state
235+
const firing = player.isFiring;
236+
this.laserEngineLeft.setFiring(firing);
237+
this.laserEngineRight.setFiring(firing);
238+
const laserLeftX = player.x + player.width * 0.3;
239+
const laserRightX = player.x + player.width * 0.7;
240+
const laserY = player.y;
233241
this.laserEngineLeft.setEmitter(laserLeftX, laserY);
234242
this.laserEngineRight.setEmitter(laserRightX, laserY);
235243

236244
this.laserEngineLeft.update(delta);
237245
this.laserEngineRight.update(delta);
238246

239-
this.formation.update(delta);
240-
241-
// Check player collision with alien lasers with pixel-perfect detection
242-
const alienLasers = this.formation.lasers;
243-
for (const laser of alienLasers) {
244-
// Only check collision if laser is within player bounds
245-
if (laser.x >= this.player.x &&
246-
laser.x <= this.player.x + this.player.width &&
247-
laser.y >= this.player.y &&
248-
laser.y <= this.player.y + this.player.height) {
249-
250-
// Do pixel-perfect collision test
251-
if (this.player.checkPixelCollision(laser.x, laser.y)) {
247+
formation.update(delta);
248+
249+
// Collision detection using cached player bounds
250+
const px = player.x, py = player.y, pw = player.width, ph = player.height;
251+
for (const laser of formation.lasers) {
252+
if (laser.x >= px && laser.x <= px + pw &&
253+
laser.y >= py && laser.y <= py + ph) {
254+
if (player.checkPixelCollision(laser.x, laser.y)) {
252255
this.handlePlayerHit();
253-
laser.life = 0; // Destroy laser
254-
break; // Exit loop after hit
256+
laser.life = 0;
257+
break;
255258
}
256259
}
257260
}
258-
259-
// Check laser collisions with aliens
260-
if (this.laserEngineLeft) {
261-
this.laserEngineLeft.particles.forEach(laser => {
262-
if (this.formation.checkCollision(laser.x, laser.y)) {
263-
laser.life = 0; // Destroy laser on hit
264-
}
265-
});
266-
}
267-
if (this.laserEngineRight) {
268-
this.laserEngineRight.particles.forEach(laser => {
269-
if (this.formation.checkCollision(laser.x, laser.y)) {
270-
laser.life = 0; // Destroy laser on hit
261+
262+
// Check collisions for player lasers
263+
[this.laserEngineLeft, this.laserEngineRight].forEach(engine => {
264+
engine.particles.forEach(laser => {
265+
if (formation.checkCollision(laser.x, laser.y)) {
266+
laser.life = 0;
271267
}
272268
});
273-
}
274-
275-
// Check if all aliens are destroyed
276-
if (this.formation.aliens.length === 0) {
277-
// Create new formation with increased difficulty
269+
});
270+
271+
if (formation.aliens.length === 0) {
278272
this.formation = new PatternFormation(this.ctx, {
279273
virtualWidth: this.virtualWidth,
280274
virtualHeight: this.virtualHeight,
281275
pattern: 'infinity',
282276
bgScroller: this.bgScroller,
283-
difficulty: this.formation.difficulty + 1
277+
difficulty: formation.difficulty + 1
284278
});
285279
}
286-
287-
// Handle invulnerability
280+
288281
if (this.playerInvulnerable) {
289282
this.invulnerabilityTimer += delta;
290283
if (this.invulnerabilityTimer >= this.invulnerabilityTime) {
291284
this.playerInvulnerable = false;
292285
this.invulnerabilityTimer = 0;
293286
}
294287
}
288+
} else {
289+
// ...existing screen update code...
290+
// Check if startup screen is complete
291+
if (this.currentScreen === 'startup') {
292+
this.screens.startup.update(delta);
293+
if (this.screens.startup.complete) {
294+
this.switchScreen('intro');
295+
}
296+
} else if (this.currentScreen === 'intro') {
297+
this.screens.intro.update(delta);
298+
}
299+
}
300+
if(this.debugWindow.visible){
301+
this.debugWindow.update(delta);
295302
}
296303
}
297304

@@ -331,11 +338,12 @@ class Game {
331338
);
332339

333340
// Draw player with radiosity effect applied directly on the player's image
334-
// Create an offscreen canvas to tint the image
335-
const offCanvas = document.createElement('canvas');
341+
// Use offCanvasCache instead of creating a new canvas each frame
342+
const offCanvas = this.offCanvasCache;
336343
offCanvas.width = this.player.img.width;
337344
offCanvas.height = this.player.img.height;
338345
const offCtx = offCanvas.getContext('2d');
346+
offCtx.clearRect(0, 0, offCanvas.width, offCanvas.height);
339347
offCtx.drawImage(this.player.img, 0, 0);
340348
offCtx.globalCompositeOperation = 'source-atop';
341349
offCtx.fillStyle = `rgba(${bgColor.r}, ${bgColor.g}, ${bgColor.b}, 0.33)`;
@@ -367,6 +375,11 @@ class Game {
367375
// Draw HUD if in game screen
368376
this.drawHUD();
369377
}
378+
379+
// Draw debug window if enabled
380+
if(this.debugWindow.visible){
381+
this.debugWindow.draw(this.ctx);
382+
}
370383
}
371384

372385
drawHUD() {

js/imageBackgroundScroller.js

Lines changed: 25 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,15 @@ class ImageBackgroundScroller {
3535
// Track flip states for each position instead of toggling
3636
this.position1Flipped = false;
3737
this.position2Flipped = true; // Start second position flipped for alternating pattern
38+
39+
// Create a reusable temporary canvas for sampling background color.
40+
this.tempCanvas = document.createElement('canvas');
41+
this.tempCanvas.width = 20;
42+
this.tempCanvas.height = 20;
43+
// Cache the 2D context once
44+
this.tempCtx = this.tempCanvas.getContext('2d');
45+
46+
this.lastColorSample = { x: null, y: null, color: null, time: 0 };
3847
}
3948

4049
update(delta) {
@@ -103,40 +112,42 @@ class ImageBackgroundScroller {
103112
}
104113

105114
getColorAt(x, y) {
106-
// Create a small temporary canvas for sampling
107-
const tempCanvas = document.createElement('canvas');
108-
const tempCtx = tempCanvas.getContext('2d');
109-
tempCanvas.width = 20; // Sample area size
110-
tempCanvas.height = 20;
115+
const now = performance.now();
116+
// Increase threshold to 100ms to reduce frequent sampling
117+
if (
118+
this.lastColorSample.x === x &&
119+
this.lastColorSample.y === y &&
120+
now - this.lastColorSample.time < 100
121+
) {
122+
return this.lastColorSample.color;
123+
}
111124

112-
// Draw the currently visible portion of background
125+
const tempCtx = this.tempCtx;
126+
tempCtx.clearRect(0, 0, 20, 20);
113127
const activeImg = this.images[this.currentImageIndex];
114128
const activePos = this.currentImageIndex === 0 ? this.position1 : this.position2;
115129< EE29 div class="diff-text-inner">
116130
tempCtx.drawImage(
117131
activeImg,
118-
x - 10, y - 10 - activePos, // Center the sampling area
119-
20, 20, // Source size
120-
0, 0, // Dest position
121-
20, 20 // Dest size
132+
x - 10, y - 10 - activePos, 20, 20,
133+
0, 0, 20, 20
122134
);
123135

124-
// Get average color
125136
const data = tempCtx.getImageData(0, 0, 20, 20).data;
126137
let r = 0, g = 0, b = 0, count = 0;
127-
128138
for (let i = 0; i < data.length; i += 4) {
129139
r += data[i];
130140
g += data[i + 1];
131141
b += data[i + 2];
132142
count++;
133143
}
134-
135-
return {
144+
const color = {
136145
r: r / count,
137146
g: g / count,
138147
b: b / count
139148
};
149+
this.lastColorSample = { x, y, color, time: now };
150+
return color;
140151
}
141152
}
142153

0 commit comments

Comments
 (0)
0