@@ -20,12 +20,13 @@ import {
2020 CubeTexture
2121} from '@babylonjs/core' ;
2222import '@babylonjs/loaders/glTF' ;
23- import { WaterMaterial } from '@babylonjs/materials'
23+ import { WaterMaterial } from '@babylonjs/materials' ;
2424//@ts -ignore
2525import * as CANNON from 'cannon' ;
2626import { GameStatus , type GameHTMLElementsRefs , type Size3D } from './types' ;
2727import { notRepeatedRandomFreeSpacePositionGenerator } from './utils' ;
2828import { GameStorage , StoredDataType } from './GameStorage' ;
29+ import { GameUiElementsManager } from './UiManager' ;
2930
3031export class Game {
3132 private static readonly SinglePlatformSize : Size3D = Object . freeze ( {
@@ -36,7 +37,7 @@ export class Game {
3637 private static readonly SingleWallSize : Size3D = Object . freeze ( {
3738 height : Game . SinglePlatformSize . width / 3 ,
3839 depth : Game . SinglePlatformSize . width / 8 ,
39- width : ( Game . SinglePlatformSize . width / 3 ) - 0.075
40+ width : Game . SinglePlatformSize . width / 3 - 0.075
4041 } ) ;
4142 private static readonly ZeroVector = new Vector3 ( 0 , 0 , 0 ) ;
4243 private static readonly StartSpeedOfMovingStraight = 6 ;
@@ -57,18 +58,21 @@ export class Game {
5758 private readonly ground : Mesh ;
5859 private readonly camera : Camera ;
5960 private readonly light : PointLight ;
60- private readonly platforms : Mesh [ ] ;
61- private readonly walls : Mesh [ ] ;
62- private readonly ball : Mesh ;
63- private readonly coins : Mesh [ ] = [ ] ;
61+ private platforms : Mesh [ ] = [ ] ;
62+ private walls : Mesh [ ] = [ ] ;
63+ private ball : Mesh = { } as Mesh ;
64+ private coins : Mesh [ ] = [ ] ;
6465 private readonly shadowGenerator : ShadowGenerator ;
6566 private readonly storage = new GameStorage ( ) ;
67+ private readonly uiElementsManager : GameUiElementsManager ;
6668 private coinScore : number = 0 ;
6769 private bestScore : number = 0 ;
6870 private gameStatus = GameStatus . Playing ;
6971
7072 public constructor ( private readonly elementsRefs : GameHTMLElementsRefs ) {
71- this . engine = new Engine ( this . elementsRefs . canvas ) ;
73+ this . uiElementsManager = new GameUiElementsManager ( elementsRefs ) ;
74+
75+ this . engine = new Engine ( elementsRefs . canvas ) ;
7276 this . scene = this . configureScene ( ) ;
7377 this . light = this . configureLight ( ) ;
7478 this . camera = this . configureCamera ( ) ;
@@ -80,22 +84,19 @@ export class Game {
8084
8185 [ this . wallMaterial , this . wallTouchedMaterial ] = this . createWallMaterial ( ) ;
8286 this . platformMaterial = this . createPlatformMaterial ( ) ;
83-
84- this . platforms = this . configurePlatform ( ) ;
85- void this . platforms ;
86- this . ball = this . configureBall ( ) ;
87- this . walls = this . createAllWalls ( ) ;
8887 }
8988
9089 public init ( ) : void {
91- this . shrinkCanvas ( ) ;
92- window . addEventListener ( 'resize' , this . shrinkCanvas . bind ( this ) ) ;
90+ this . onWindowResize ( ) ;
91+ window . addEventListener ( 'resize' , this . onWindowResize . bind ( this ) ) ;
9392
9493 this . initControls ( ) ;
9594
95+ this . createGameObjects ( ) ;
96+
9697 this . coinScore = this . storage . loadScore ( StoredDataType . CurrentScore ) ;
9798 this . bestScore = this . storage . loadScore ( StoredDataType . BestScore ) ;
98- this . updateScoreText ( ) ;
99+ this . uiElementsManager . updateScore ( this . coinScore ) ;
99100
100101 this . gameStatus = GameStatus . Playing ;
101102
@@ -110,6 +111,12 @@ export class Game {
110111 } ) ;
111112 }
112113
114+ private createGameObjects ( ) : void {
115+ this . platforms = this . configurePlatform ( ) ;
116+ this . ball = this . configureBall ( ) ;
117+ this . walls = this . createAllWalls ( ) ;
118+ }
119+
113120 private createCoin ( position : Vector3 ) {
114121 // I see, that deprecated, but Babylon is so strange, that the fastest way to load this
115122 // is just to use deprecated SceneLoader sync import
@@ -144,16 +151,25 @@ export class Game {
144151 }
145152
146153 private initControls ( ) {
154+ for ( const element of this . elementsRefs . restartButtons ) {
155+ ( element as HTMLButtonElement ) . onclick = this . restart . bind ( this ) ;
156+ }
157+
147158 window . addEventListener ( 'keydown' , ( event ) => {
148- if ( this . gameStatus !== GameStatus . Playing ) return ;
159+ if ( this . gameStatus === GameStatus . GameOver && event . key === 'Enter' ) {
160+ this . restart ( ) ;
161+ return ;
162+ }
149163
150- switch ( true ) {
151- case event . key === 'ArrowLeft' || event . key . toLowerCase ( ) === 'a' :
152- this . pushBall ( Game . MoveVectorLeft ) ;
153- break ;
154- case event . key === 'ArrowRight' || event . key . toLocaleLowerCase ( ) === 'd' :
155- this . pushBall ( Game . MoveVectorRight ) ;
156- break ;
164+ if ( this . ball . position . y < 2 ) {
165+ switch ( true ) {
166+ case event . key === 'ArrowLeft' || event . key . toLowerCase ( ) === 'a' :
167+ this . pushBall ( Game . MoveVectorLeft ) ;
168+ break ;
169+ case event . key === 'ArrowRight' || event . key . toLocaleLowerCase ( ) === 'd' :
170+ this . pushBall ( Game . MoveVectorRight ) ;
171+ break ;
172+ }
157173 }
158174 } ) ;
159175
@@ -191,7 +207,6 @@ export class Game {
191207 light . intensity = 0.4 ;
192208 return light ;
193209 }
194-
195210
196211 private configureCamera ( ) : Camera {
197212 const camera = new FreeCamera ( 'camera' , new Vector3 ( - 2 , 5 , - 10 ) , this . scene ) ;
@@ -267,22 +282,25 @@ export class Game {
267282 private configureSky ( ) {
268283 const skyBox = MeshBuilder . CreateBox ( 'skyBox' , { size : 1000 } , this . scene ) ;
269284 const skyBoxMaterial = new StandardMaterial ( 'skyBox' , this . scene ) ;
270- skyBoxMaterial . reflectionTexture = new CubeTexture ( '/assets/environments/TropicalSunnyDay/TropicalSunnyDay' , this . scene ) ;
285+ skyBoxMaterial . reflectionTexture = new CubeTexture (
286+ '/assets/environments/TropicalSunnyDay/TropicalSunnyDay' ,
287+ this . scene
288+ ) ;
271289 skyBoxMaterial . reflectionTexture . coordinatesMode = Texture . SKYBOX_MODE ;
272290 skyBoxMaterial . backFaceCulling = false ;
273291 skyBox . material = skyBoxMaterial ;
274292 return skyBox ;
275293 }
276294
277295 private configureGround ( ) : [ Mesh , Mesh ] {
278- const water = MeshBuilder . CreateGround ( 'water' , { width : 512 , height : 512 } , this . scene ) ;
296+ const water = MeshBuilder . CreateGround ( 'water' , { width : 512 , height : 512 } , this . scene ) ;
279297 water . position = new Vector3 ( 0 , - 5 , 0 ) ;
280298 const waterMaterial = new WaterMaterial ( 'water' , this . scene ) ;
281299 waterMaterial . bumpTexture = new Texture ( '/assets/environments/waterbump.png' , this . scene ) ;
282300 waterMaterial . addToRenderList ( this . sky ) ;
283301 water . material = waterMaterial ;
284302
285- const ground = MeshBuilder . CreateGround ( 'ground' , { width : 512 , height : 512 } , this . scene ) ;
303+ const ground = MeshBuilder . CreateGround ( 'ground' , { width : 512 , height : 512 } , this . scene ) ;
286304 ground . position = new Vector3 ( 0 , - 10 , 0 ) ;
287305 const groundMaterial = new StandardMaterial ( 'ground' , this . scene ) ;
288306 groundMaterial . emissiveTexture = new Texture ( '/assets/environments/ground.jpg' , this . scene ) ;
@@ -291,8 +309,7 @@ export class Game {
291309 waterMaterial . addToRenderList ( this . sky ) ;
292310 waterMaterial . addToRenderList ( ground ) ;
293311
294-
295- ground . physicsImpostor = new PhysicsImpostor ( ground , PhysicsImpostor . BoxImpostor , { mass : 0 } , this . scene ) ;
312+ ground . physicsImpostor = new PhysicsImpostor ( ground , PhysicsImpostor . BoxImpostor , { mass : 0 } , this . scene ) ;
296313
297314 return [ water , ground ] ;
298315 }
@@ -354,7 +371,7 @@ export class Game {
354371
355372 private checkIfGameOver ( ) : void {
356373 if ( this . ball . getAbsolutePosition ( ) . y <= 0 ) {
357- this . gameOver ( ) ;
374+ this . gameOver ( ) ;
358375 }
359376
360377 const check = ( spherePos : Vector3 , box : BoundingBox ) : boolean => {
@@ -404,21 +421,16 @@ export class Game {
404421 this . light . position . z = this . ball . getAbsolutePosition ( ) . z + 10 ;
405422 }
406423
407- private shrinkCanvas ( ) {
408- this . elementsRefs . canvas . width = this . elementsRefs . canvas . clientWidth ;
409- this . elementsRefs . canvas . height = this . elementsRefs . canvas . clientHeight ;
424+ private onWindowResize ( ) {
425+ this . uiElementsManager . shrinkCanvas ( ) ;
410426 this . engine . resize ( ) ;
411427 }
412428
413- private updateScoreText ( ) {
414- this . elementsRefs . scoreText . innerText = String ( this . coinScore ) ;
415- }
416-
417429 private checkCoinEarned ( ) : void {
418430 for ( let i = 0 ; i < this . coins . length ; ++ i ) {
419431 if ( this . ball . intersectsMesh ( this . coins [ i ] , true ) ) {
420432 ++ this . coinScore ;
421- this . updateScoreText ( ) ;
433+ this . uiElementsManager . updateScore ( this . coinScore ) ;
422434 this . scene . removeMesh ( this . coins [ i ] ) ;
423435 this . coins [ i ] . dispose ( ) ;
424436 this . coins . splice ( i , 1 ) ;
@@ -430,7 +442,7 @@ export class Game {
430442
431443 private gameOver ( ) : void {
432444 this . gameStatus = GameStatus . GameOver ;
433- this . elementsRefs . gameOver . screen . style . display = 'flex' ;
445+
434446 const bestScore = this . storage . loadScore ( StoredDataType . BestScore ) ;
435447
436448 if ( this . coinScore > bestScore ) {
@@ -440,7 +452,39 @@ export class Game {
440452
441453 this . storage . saveScore ( StoredDataType . CurrentScore , 0 ) ;
442454
443- this . elementsRefs . gameOver . currentScore . innerText = `CURRENT SCORE: ${ this . coinScore } ` ;
444- this . elementsRefs . gameOver . bestScore . innerText = `BEST SCORE: ${ this . bestScore } ` ;
455+ this . uiElementsManager . hideUiElements ( ) ;
456+ this . uiElementsManager . showGameOverScreen ( this . coinScore , this . bestScore ) ;
457+ }
458+
459+ private resetGameObjects ( ) {
460+ this . platforms . forEach ( ( platform ) => {
461+ this . scene . removeMesh ( platform ) ;
462+ platform . dispose ( ) ;
463+ } ) ;
464+
465+ this . coins . forEach ( ( coin ) => {
466+ this . scene . removeMesh ( coin ) ;
467+ coin . dispose ( ) ;
468+ } ) ;
469+
470+ this . walls . forEach ( ( wall ) => {
471+ this . scene . removeMesh ( wall ) ;
472+ wall . dispose ( ) ;
473+ } ) ;
474+
475+ this . ball . dispose ( ) ;
476+ this . scene . removeMesh ( this . ball ) ;
477+
478+ this . coinScore = 0 ;
479+ this . uiElementsManager . updateScore ( this . coinScore ) ;
480+ this . storage . saveScore ( StoredDataType . CurrentScore , 0 ) ;
481+ }
482+
483+ private restart ( ) {
484+ this . gameStatus = GameStatus . Playing ;
485+ this . resetGameObjects ( ) ;
486+ this . createGameObjects ( ) ;
487+ this . uiElementsManager . hideGameOverScreen ( ) ;
488+ this . uiElementsManager . showUiElements ( ) ;
445489 }
446490}
0 commit comments