Skip to content

Commit a8ffb08

Browse files
refactor: split code into services and files
1 parent 7314caa commit a8ffb08

File tree

9 files changed

+543
-503
lines changed

9 files changed

+543
-503
lines changed

games/running-ball-babylonjs/src/Game.ts

Lines changed: 0 additions & 490 deletions
This file was deleted.
Lines changed: 176 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,176 @@
1+
import { Vector3, type BoundingBox } from '@babylonjs/core';
2+
import '@babylonjs/loaders/glTF';
3+
4+
import { GameStatus, type GameHTMLElementsRefs } from './types';
5+
import { GameStorageService, StoredDataType } from './game-services/storage.service';
6+
import { GameUiElementsService } from './game-services/ui-manager.service.';
7+
import { GameConstants } from './constants';
8+
import { GameSceneService } from './game-services/scene.service';
9+
10+
export class Game {
11+
private readonly movingVectorStraight = new Vector3(0, 0, GameConstants.StartSpeedOfMovingStraight);
12+
13+
private readonly sceneService: GameSceneService;
14+
private readonly storageService: GameStorageService;
15+
private readonly uiElementsService: GameUiElementsService;
16+
17+
private coinScore: number = 0;
18+
private gameStatus = GameStatus.Playing;
19+
20+
public constructor(private readonly elementsRefs: GameHTMLElementsRefs) {
21+
this.sceneService = new GameSceneService(this.elementsRefs.canvas);
22+
this.uiElementsService = new GameUiElementsService(this.elementsRefs);
23+
this.storageService = new GameStorageService();
24+
}
25+
26+
public init(): void {
27+
this.onWindowResize();
28+
window.addEventListener('resize', this.onWindowResize.bind(this));
29+
30+
this.initControls();
31+
32+
this.sceneService.createGameObjects();
33+
34+
this.coinScore = this.storageService.loadScore(StoredDataType.CurrentScore);
35+
this.uiElementsService.updateScore(this.coinScore);
36+
37+
this.gameStatus = GameStatus.Playing;
38+
39+
this.sceneService.setBeforeRenderCallback(this.onBeforeRenderCallback);
40+
this.sceneService.runRenderLoop();
41+
}
42+
43+
private readonly onBeforeRenderCallback = () => {
44+
this.checkIfGameOver();
45+
this.checkCoinEarned();
46+
};
47+
48+
private initControls() {
49+
for (const element of this.elementsRefs.restartButtons) {
50+
(element as HTMLButtonElement).onclick = this.restart.bind(this);
51+
}
52+
53+
window.addEventListener('keydown', (event) => {
54+
if (this.gameStatus === GameStatus.GameOver && event.key === 'Enter') {
55+
this.restart();
56+
return;
57+
}
58+
59+
if (this.sceneService.ball.position.y < 2) {
60+
switch (true) {
61+
case event.key === 'ArrowLeft' || event.key.toLowerCase() === 'a':
62+
this.pushBall(GameConstants.MoveVectorLeft);
63+
break;
64+
case event.key === 'ArrowRight' || event.key.toLocaleLowerCase() === 'd':
65+
this.pushBall(GameConstants.MoveVectorRight);
66+
break;
67+
}
68+
}
69+
});
70+
71+
window.addEventListener('keyup', (event) => {
72+
if (this.gameStatus !== GameStatus.Playing) return;
73+
74+
switch (true) {
75+
case event.key === 'ArrowLeft' || event.key.toLowerCase() === 'a':
76+
this.stopBallMovingAside();
77+
break;
78+
case event.key === 'ArrowRight' || event.key.toLocaleLowerCase() === 'd':
79+
this.stopBallMovingAside();
80+
break;
81+
}
82+
});
83+
}
84+
85+
private checkIfGameOver(): void {
86+
const ball = this.sceneService.ball;
87+
const walls = this.sceneService.walls;
88+
89+
if (ball.getAbsolutePosition().y <= 0) {
90+
this.gameOver();
91+
}
92+
93+
const check = (spherePos: Vector3, box: BoundingBox): boolean => {
94+
const min = box.minimumWorld;
95+
const max = box.maximumWorld;
96+
97+
const closestX = Math.max(min.x, Math.min(spherePos.x, max.x));
98+
const closestY = Math.max(min.y, Math.min(spherePos.y, max.y));
99+
const closestZ = Math.max(min.z, Math.min(spherePos.z, max.z));
100+
101+
const distance = Vector3.Distance(spherePos, new Vector3(closestX, closestY, closestZ));
102+
103+
return distance < GameConstants.BallRadius;
104+
};
105+
106+
for (let i = 0; i < walls.length; ++i) {
107+
if (walls[i] && ball) {
108+
const ballPos = ball.position;
109+
const wallBounds = walls[i].getBoundingInfo().boundingBox;
110+
111+
if (check(ballPos, wallBounds)) {
112+
walls[i].material = this.sceneService.wallTouchedMaterial;
113+
this.gameOver();
114+
break;
115+
}
116+
}
117+
}
118+
}
119+
120+
private pushBall(impuls: Vector3): void {
121+
this.sceneService.ball.physicsImpostor?.applyImpulse(impuls, this.sceneService.ball.getAbsolutePosition());
122+
}
123+
124+
private stopBallMovingAside(): void {
125+
this.movingVectorStraight.z += this.movingVectorStraight.z * 0.01;
126+
this.sceneService.ball.physicsImpostor?.setLinearVelocity(this.movingVectorStraight);
127+
this.sceneService.ball.physicsImpostor?.setAngularVelocity(GameConstants.ZeroVector);
128+
}
129+
130+
private onWindowResize() {
131+
this.uiElementsService.shrinkCanvas();
132+
this.sceneService.resizeEngine();
133+
}
134+
135+
private checkCoinEarned(): void {
136+
const coins = this.sceneService.coins;
137+
138+
for (let i = 0; i < coins.length; ++i) {
139+
if (this.sceneService.ball.intersectsMesh(coins[i], true)) {
140+
++this.coinScore;
141+
this.uiElementsService.updateScore(this.coinScore);
142+
this.sceneService.removeCoinByIndex(i);
143+
this.storageService.saveScore(StoredDataType.CurrentScore, this.coinScore);
144+
break;
145+
}
146+
}
147+
}
148+
149+
private gameOver(): void {
150+
this.gameStatus = GameStatus.GameOver;
151+
152+
const bestScore = this.storageService.loadScore(StoredDataType.BestScore);
153+
154+
if (this.coinScore > bestScore) {
155+
this.storageService.saveScore(StoredDataType.BestScore, this.coinScore);
156+
}
157+
158+
this.storageService.saveScore(StoredDataType.CurrentScore, 0);
159+
160+
this.uiElementsService.hideUiElements();
161+
this.uiElementsService.showGameOverScreen(this.coinScore, this.storageService.loadScore(StoredDataType.BestScore));
162+
}
163+
164+
private restart() {
165+
this.gameStatus = GameStatus.Playing;
166+
167+
this.coinScore = 0;
168+
this.uiElementsService.updateScore(this.coinScore);
169+
this.storageService.saveScore(StoredDataType.CurrentScore, 0);
170+
171+
this.sceneService.resetGameObjects();
172+
this.sceneService.createGameObjects();
173+
this.uiElementsService.hideGameOverScreen();
174+
this.uiElementsService.showUiElements();
175+
}
176+
}
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
import { Vector3 } from '@babylonjs/core';
2+
3+
export const GameConstants = Object.freeze({
4+
SinglePlatformSize: {
5+
height: 0.6,
6+
depth: 6,
7+
width: 8
8+
},
9+
SingleWallSize: {
10+
height: 2.6666, // SinglePlatformSize.width / 3
11+
depth: 1,
12+
width: 2.591666 // SinglePlatformSize.width / 3 - 0.075
13+
},
14+
ZeroVector: new Vector3(0, 0, 0),
15+
StartSpeedOfMovingStraight: 6,
16+
MoveVectorLeft: new Vector3(-2, 0, 0),
17+
MoveVectorRight: new Vector3(2, 0, 0),
18+
BallRadius: 0.75
19+
});

0 commit comments

Comments
 (0)