@@ -6,6 +6,7 @@ import PatternFormation from './PatternFormation.js'; // Add this import
66import IntroScreen from './screens/IntroScreen.js' ;
77import MusicPlayer from './audio/MusicPlayer.js' ;
88import StartupScreen from './screens/StartupScreen.js' ;
9+ import DebugWindow from './DebugWindow.js' ;
910
1011class 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 ( ) {
0 commit comments