├── .gitignore
├── README.md
├── dist
├── .DS_Store
├── app.js
├── app.js.map
├── index.html
└── public
│ ├── .DS_Store
│ ├── btn-default.png
│ ├── colyseus.png
│ ├── ground.jpg
│ └── textures
│ ├── skybox_nx.jpg
│ ├── skybox_ny.jpg
│ ├── skybox_nz.jpg
│ ├── skybox_px.jpg
│ ├── skybox_py.jpg
│ └── skybox_pz.jpg
├── index.html
├── package-lock.json
├── package.json
├── public
├── btn-default.png
├── colyseus.png
├── ground.jpg
└── textures
│ ├── skybox_nx.jpg
│ ├── skybox_ny.jpg
│ ├── skybox_nz.jpg
│ ├── skybox_px.jpg
│ ├── skybox_py.jpg
│ └── skybox_pz.jpg
├── src
├── app.ts
├── game.ts
├── menu.ts
└── utils.ts
├── tsconfig.json
└── webpack.config.js
/.gitignore:
--------------------------------------------------------------------------------
1 | lib-cov
2 | *.seed
3 | *.log
4 | *.csv
5 | *.dat
6 | *.out
7 | *.pid
8 | *.gz
9 | *.swp
10 |
11 | pids
12 | logs
13 | results
14 | tmp
15 |
16 | # Build
17 | public/css/main.css
18 | dist/
19 |
20 | # Coverage reports
21 | coverage
22 |
23 | # API keys and secrets
24 | .env
25 |
26 | # Dependency directory
27 | node_modules
28 | bower_components
29 |
30 | # Editors
31 | .idea
32 | *.iml
33 |
34 | # OS metadata
35 | .DS_Store
36 | Thumbs.db
37 |
38 | # Ignore built ts files
39 | dist/**/*
40 |
41 | # ignore yarn.lock
42 | yarn.lock
43 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # (Client-side) BabylonJS: Real-time Multiplayer with Colyseus
2 |
3 | This is the client code for a step-by-step tutorial on how to use BabylonJS + Colyseus together.
4 |
5 | - [See step-by-step Tutorial](https://doc.babylonjs.com/guidedLearning/multiplayer/Colyseus)
6 | - [See server-side Project](https://github.com/colyseus/tutorial-babylonjs-server)
7 | - [See Colyseus documentation](https://docs.colyseus.io/)
8 |
9 | ## How to run
10 |
11 | - Download and install [Node.js LTS](https://nodejs.org/en/download/)
12 | - Clone or download this repository.
13 | - Run `npm install`
14 | - Run `npm start`
15 |
16 | The client should be accessible at [`http://localhost:8080`](http://localhost:8080).
17 |
18 | ## License
19 |
20 | MIT
--------------------------------------------------------------------------------
/dist/.DS_Store:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/colyseus/tutorial-babylonjs-client/bd1b9f1635acf92458adf9bd3ccf0b7b044f7a2e/dist/.DS_Store
--------------------------------------------------------------------------------
/dist/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Colyseus + Babylon.js Demo
6 |
7 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
--------------------------------------------------------------------------------
/dist/public/.DS_Store:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/colyseus/tutorial-babylonjs-client/bd1b9f1635acf92458adf9bd3ccf0b7b044f7a2e/dist/public/.DS_Store
--------------------------------------------------------------------------------
/dist/public/btn-default.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/colyseus/tutorial-babylonjs-client/bd1b9f1635acf92458adf9bd3ccf0b7b044f7a2e/dist/public/btn-default.png
--------------------------------------------------------------------------------
/dist/public/colyseus.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/colyseus/tutorial-babylonjs-client/bd1b9f1635acf92458adf9bd3ccf0b7b044f7a2e/dist/public/colyseus.png
--------------------------------------------------------------------------------
/dist/public/ground.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/colyseus/tutorial-babylonjs-client/bd1b9f1635acf92458adf9bd3ccf0b7b044f7a2e/dist/public/ground.jpg
--------------------------------------------------------------------------------
/dist/public/textures/skybox_nx.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/colyseus/tutorial-babylonjs-client/bd1b9f1635acf92458adf9bd3ccf0b7b044f7a2e/dist/public/textures/skybox_nx.jpg
--------------------------------------------------------------------------------
/dist/public/textures/skybox_ny.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/colyseus/tutorial-babylonjs-client/bd1b9f1635acf92458adf9bd3ccf0b7b044f7a2e/dist/public/textures/skybox_ny.jpg
--------------------------------------------------------------------------------
/dist/public/textures/skybox_nz.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/colyseus/tutorial-babylonjs-client/bd1b9f1635acf92458adf9bd3ccf0b7b044f7a2e/dist/public/textures/skybox_nz.jpg
--------------------------------------------------------------------------------
/dist/public/textures/skybox_px.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/colyseus/tutorial-babylonjs-client/bd1b9f1635acf92458adf9bd3ccf0b7b044f7a2e/dist/public/textures/skybox_px.jpg
--------------------------------------------------------------------------------
/dist/public/textures/skybox_py.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/colyseus/tutorial-babylonjs-client/bd1b9f1635acf92458adf9bd3ccf0b7b044f7a2e/dist/public/textures/skybox_py.jpg
--------------------------------------------------------------------------------
/dist/public/textures/skybox_pz.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/colyseus/tutorial-babylonjs-client/bd1b9f1635acf92458adf9bd3ccf0b7b044f7a2e/dist/public/textures/skybox_pz.jpg
--------------------------------------------------------------------------------
/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Colyseus + Babylon.js Demo
6 |
7 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "babylon-demo",
3 | "version": "0.1.0",
4 | "description": "Babylon.js Colyseus demo",
5 | "scripts": {
6 | "start": "webpack serve",
7 | "build": "webpack",
8 | "test": "echo \"Error: no test specified\" && exit 1"
9 | },
10 | "author": "",
11 | "license": "ISC",
12 | "devDependencies": {
13 | "@babylonjs/core": "preview",
14 | "@babylonjs/inspector": "preview",
15 | "@types/node": "^17.0.12",
16 | "ts-loader": "^9.2.6",
17 | "typescript": "^4.5.4",
18 | "webpack": "^5.66.0",
19 | "webpack-cli": "^4.9.1",
20 | "webpack-dev-server": "^4.7.4"
21 | },
22 | "dependencies": {
23 | "babylonjs": "preview",
24 | "babylonjs-gui": "preview",
25 | "babylonjs-loaders": "preview",
26 | "babylonjs-materials": "preview",
27 | "babylonjs-procedural-textures": "preview",
28 | "colyseus.js": "^0.15.0-preview.2",
29 | "copy-webpack-plugin": "^10.2.4",
30 | "dotenv-webpack": "^7.0.3"
31 | }
32 | }
33 |
--------------------------------------------------------------------------------
/public/btn-default.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/colyseus/tutorial-babylonjs-client/bd1b9f1635acf92458adf9bd3ccf0b7b044f7a2e/public/btn-default.png
--------------------------------------------------------------------------------
/public/colyseus.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/colyseus/tutorial-babylonjs-client/bd1b9f1635acf92458adf9bd3ccf0b7b044f7a2e/public/colyseus.png
--------------------------------------------------------------------------------
/public/ground.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/colyseus/tutorial-babylonjs-client/bd1b9f1635acf92458adf9bd3ccf0b7b044f7a2e/public/ground.jpg
--------------------------------------------------------------------------------
/public/textures/skybox_nx.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/colyseus/tutorial-babylonjs-client/bd1b9f1635acf92458adf9bd3ccf0b7b044f7a2e/public/textures/skybox_nx.jpg
--------------------------------------------------------------------------------
/public/textures/skybox_ny.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/colyseus/tutorial-babylonjs-client/bd1b9f1635acf92458adf9bd3ccf0b7b044f7a2e/public/textures/skybox_ny.jpg
--------------------------------------------------------------------------------
/public/textures/skybox_nz.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/colyseus/tutorial-babylonjs-client/bd1b9f1635acf92458adf9bd3ccf0b7b044f7a2e/public/textures/skybox_nz.jpg
--------------------------------------------------------------------------------
/public/textures/skybox_px.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/colyseus/tutorial-babylonjs-client/bd1b9f1635acf92458adf9bd3ccf0b7b044f7a2e/public/textures/skybox_px.jpg
--------------------------------------------------------------------------------
/public/textures/skybox_py.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/colyseus/tutorial-babylonjs-client/bd1b9f1635acf92458adf9bd3ccf0b7b044f7a2e/public/textures/skybox_py.jpg
--------------------------------------------------------------------------------
/public/textures/skybox_pz.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/colyseus/tutorial-babylonjs-client/bd1b9f1635acf92458adf9bd3ccf0b7b044f7a2e/public/textures/skybox_pz.jpg
--------------------------------------------------------------------------------
/src/app.ts:
--------------------------------------------------------------------------------
1 | import Menu from './menu'
2 |
3 | window.addEventListener('DOMContentLoaded', () => {
4 | // Create the game using the 'renderCanvas'.
5 | let menu = new Menu('renderCanvas');
6 |
7 | // Create the scene.
8 | menu.createMenu();
9 | });
10 |
--------------------------------------------------------------------------------
/src/game.ts:
--------------------------------------------------------------------------------
1 | import * as BABYLON from 'babylonjs';
2 | import * as GUI from 'babylonjs-gui';
3 | import { Room } from "colyseus.js";
4 |
5 | import Menu from "./menu";
6 | import { createSkyBox } from "./utils";
7 |
8 | const GROUND_SIZE = 500;
9 |
10 | export default class Game {
11 | private canvas: HTMLCanvasElement;
12 | private engine: BABYLON.Engine;
13 | private scene: BABYLON.Scene;
14 | private camera: BABYLON.ArcRotateCamera;
15 | private light: BABYLON.Light;
16 |
17 | private room: Room;
18 | private playerEntities: { [playerId: string]: BABYLON.Mesh } = {};
19 | private playerNextPosition: { [playerId: string]: BABYLON.Vector3 } = {};
20 |
21 | constructor(canvas: HTMLCanvasElement, engine: BABYLON.Engine, room: Room) {
22 | this.canvas = canvas;
23 | this.engine = engine;
24 | this.room = room;
25 | }
26 |
27 | initPlayers(): void {
28 | this.room.state.players.onAdd((player, sessionId) => {
29 | const isCurrentPlayer = (sessionId === this.room.sessionId);
30 |
31 | const sphere = BABYLON.MeshBuilder.CreateSphere(`player-${sessionId}`, {
32 | segments: 8,
33 | diameter: 40
34 | }, this.scene);
35 |
36 | // Set player mesh properties
37 | const sphereMaterial = new BABYLON.StandardMaterial(`playerMat-${sessionId}`, this.scene);
38 | sphereMaterial.emissiveColor = (isCurrentPlayer) ? BABYLON.Color3.FromHexString("#ff9900") : BABYLON.Color3.Gray();
39 | sphere.material = sphereMaterial;
40 |
41 | // Set player spawning position
42 | sphere.position.set(player.x, player.y, player.z);
43 |
44 | this.playerEntities[sessionId] = sphere;
45 | this.playerNextPosition[sessionId] = sphere.position.clone();
46 |
47 | // update local target position
48 | player.onChange(() => {
49 | this.playerNextPosition[sessionId].set(player.x, player.y, player.z);
50 | });
51 | });
52 |
53 | this.room.state.players.onRemove((player, playerId) => {
54 | this.playerEntities[playerId].dispose();
55 | delete this.playerEntities[playerId];
56 | delete this.playerNextPosition[playerId];
57 | });
58 |
59 | this.room.onLeave(code => {
60 | this.gotoMenu();
61 | })
62 | }
63 |
64 | createGround(): void {
65 | // Create ground plane
66 | const plane = BABYLON.MeshBuilder.CreatePlane("plane", { size: GROUND_SIZE }, this.scene);
67 | plane.position.y = -15;
68 | plane.rotation.x = Math.PI / 2;
69 |
70 | let floorPlane = new BABYLON.StandardMaterial('floorTexturePlane', this.scene);
71 | floorPlane.diffuseTexture = new BABYLON.Texture('./public/ground.jpg', this.scene);
72 | floorPlane.backFaceCulling = false; // Always show the front and the back of an element
73 |
74 | let materialPlane = new BABYLON.MultiMaterial('materialPlane', this.scene);
75 | materialPlane.subMaterials.push(floorPlane);
76 |
77 | plane.material = materialPlane;
78 | }
79 |
80 | displayGameControls() {
81 | const advancedTexture = GUI.AdvancedDynamicTexture.CreateFullscreenUI("textUI");
82 |
83 | const playerInfo = new GUI.TextBlock("playerInfo");
84 | playerInfo.text = `Room name: ${this.room.name} Player: ${this.room.sessionId}`.toUpperCase();
85 | playerInfo.color = "#eaeaea";
86 | playerInfo.fontFamily = "Roboto";
87 | playerInfo.fontSize = 20;
88 | playerInfo.textHorizontalAlignment = GUI.Control.HORIZONTAL_ALIGNMENT_LEFT;
89 | playerInfo.textVerticalAlignment = GUI.Control.VERTICAL_ALIGNMENT_TOP;
90 | playerInfo.paddingTop = "10px";
91 | playerInfo.paddingLeft = "10px";
92 | playerInfo.outlineColor = "#000000";
93 | advancedTexture.addControl(playerInfo);
94 |
95 | const instructions = new GUI.TextBlock("instructions");
96 | instructions.text = "CLICK ANYWHERE ON THE GROUND!";
97 | instructions.color = "#fff000"
98 | instructions.fontFamily = "Roboto";
99 | instructions.fontSize = 24;
100 | instructions.textHorizontalAlignment = GUI.Control.HORIZONTAL_ALIGNMENT_CENTER;
101 | instructions.textVerticalAlignment = GUI.Control.VERTICAL_ALIGNMENT_BOTTOM;
102 | instructions.paddingBottom = "10px";
103 | advancedTexture.addControl(instructions);
104 |
105 | // back to menu button
106 | const button = GUI.Button.CreateImageWithCenterTextButton("back", "<- BACK", "./public/btn-default.png");
107 | button.width = "100px";
108 | button.height = "50px";
109 | button.fontFamily = "Roboto";
110 | button.thickness = 0;
111 | button.color = "#f8f8f8";
112 | button.horizontalAlignment = GUI.Control.HORIZONTAL_ALIGNMENT_RIGHT;
113 | button.verticalAlignment = GUI.Control.VERTICAL_ALIGNMENT_TOP;
114 | button.paddingTop = "10px";
115 | button.paddingRight = "10px";
116 | button.onPointerClickObservable.add(async () => {
117 | await this.room.leave(true);
118 | });
119 | advancedTexture.addControl(button);
120 | }
121 |
122 | bootstrap(): void {
123 | this.scene = new BABYLON.Scene(this.engine);
124 | this.light = new BABYLON.HemisphericLight("pointLight", new BABYLON.Vector3(), this.scene);
125 | this.camera = new BABYLON.ArcRotateCamera("camera", Math.PI / 2, 1.0, 550, BABYLON.Vector3.Zero(), this.scene);
126 | this.camera.setTarget(BABYLON.Vector3.Zero());
127 |
128 | createSkyBox(this.scene);
129 | this.createGround();
130 | this.displayGameControls();
131 | this.initPlayers();
132 |
133 | this.scene.onPointerDown = (event, pointer) => {
134 | if (event.button == 0) {
135 | const targetPosition = pointer.pickedPoint.clone();
136 |
137 | // Position adjustments for the current play ground.
138 | targetPosition.y = -1;
139 | if (targetPosition.x > 245) targetPosition.x = 245;
140 | else if (targetPosition.x < -245) targetPosition.x = -245;
141 | if (targetPosition.z > 245) targetPosition.z = 245;
142 | else if (targetPosition.z < -245) targetPosition.z = -245;
143 |
144 | this.playerNextPosition[this.room.sessionId] = targetPosition;
145 |
146 | // Send position update to the server
147 | this.room.send("updatePosition", {
148 | x: targetPosition.x,
149 | y: targetPosition.y,
150 | z: targetPosition.z,
151 | });
152 | }
153 | };
154 |
155 | this.doRender();
156 | }
157 |
158 | private gotoMenu() {
159 | this.scene.dispose();
160 | const menu = new Menu('renderCanvas');
161 | menu.createMenu();
162 | }
163 |
164 | private doRender(): void {
165 | // constantly lerp players
166 | this.scene.registerBeforeRender(() => {
167 | for (let sessionId in this.playerEntities) {
168 | const entity = this.playerEntities[sessionId];
169 | const targetPosition = this.playerNextPosition[sessionId];
170 | entity.position = BABYLON.Vector3.Lerp(entity.position, targetPosition, 0.05);
171 | }
172 | });
173 |
174 | // Run the render loop.
175 | this.engine.runRenderLoop(() => {
176 | this.scene.render();
177 | });
178 |
179 | // The canvas/window resize event handler.
180 | window.addEventListener('resize', () => {
181 | this.engine.resize();
182 | });
183 | }
184 | }
185 |
--------------------------------------------------------------------------------
/src/menu.ts:
--------------------------------------------------------------------------------
1 | import * as BABYLON from 'babylonjs';
2 | import * as GUI from 'babylonjs-gui';
3 | import { Client } from "colyseus.js";
4 |
5 | import Game from './game';
6 | import { createSkyBox } from "./utils";
7 |
8 | const ROOM_NAME = "my_room";
9 | const ENDPOINT = "ws://localhost:2567";
10 | // const ENDPOINT = "wss://tutorial-babylonjs-server.glitch.me";
11 |
12 | export default class Menu {
13 | private _canvas: HTMLCanvasElement;
14 | private _engine: BABYLON.Engine;
15 | private _scene: BABYLON.Scene;
16 | private _camera: BABYLON.ArcRotateCamera;
17 | private _advancedTexture: GUI.AdvancedDynamicTexture;
18 |
19 | private _colyseus: Client = new Client(ENDPOINT);
20 |
21 | private _errorMessage: GUI.TextBlock = new GUI.TextBlock("errorText");
22 |
23 | constructor(canvasElement: string) {
24 | // Create canvas and engine.
25 | this._canvas = document.getElementById(canvasElement) as HTMLCanvasElement;
26 | this._engine = new BABYLON.Engine(this._canvas, true);
27 | }
28 |
29 | createMenu(): void {
30 | this._scene = new BABYLON.Scene(this._engine);
31 | this._camera = new BABYLON.ArcRotateCamera("camera", Math.PI / 2, 1.0, 110, BABYLON.Vector3.Zero(), this._scene);
32 | this._camera.useAutoRotationBehavior = true;
33 | this._camera.setTarget(BABYLON.Vector3.Zero());
34 | this._advancedTexture = GUI.AdvancedDynamicTexture.CreateFullscreenUI("UI");
35 |
36 | createSkyBox(this._scene);
37 |
38 | // Colyseus logo
39 | const controlBox = new GUI.Rectangle("controlBox");
40 | controlBox.horizontalAlignment = GUI.Control.HORIZONTAL_ALIGNMENT_CENTER;
41 | controlBox.verticalAlignment = GUI.Control.VERTICAL_ALIGNMENT_TOP;
42 | controlBox.height = "100%";
43 | controlBox.width = "40%";
44 | controlBox.thickness = 0;
45 |
46 | const logo = new GUI.Image("ColyseusLogo", "./public/colyseus.png");
47 | logo.horizontalAlignment = GUI.Control.HORIZONTAL_ALIGNMENT_CENTER;
48 | logo.verticalAlignment = GUI.Control.VERTICAL_ALIGNMENT_TOP;
49 | logo.height = "40%";
50 | logo.paddingTop = "10px";
51 | logo.stretch = GUI.Image.STRETCH_UNIFORM;
52 | controlBox.addControl(logo);
53 |
54 | // Button positioning
55 | const stackPanel = new GUI.StackPanel();
56 | stackPanel.isVertical = true;
57 | stackPanel.height = "50%";
58 | stackPanel.horizontalAlignment = GUI.Control.HORIZONTAL_ALIGNMENT_CENTER;
59 | stackPanel.verticalAlignment = GUI.Control.VERTICAL_ALIGNMENT_BOTTOM;
60 |
61 | const createGameButton = this.createMenuButton("createGame", "CREATE GAME");
62 | createGameButton.onPointerClickObservable.add(async () => {
63 | this.swapControls(false);
64 | await this.createGame("create");
65 | });
66 | stackPanel.addControl(createGameButton);
67 |
68 | const joinGameButton = this.createMenuButton("joinGame", "JOIN GAME");
69 | joinGameButton.onPointerClickObservable.add(async () => {
70 | this.swapControls(false);
71 | await this.createGame("join");
72 | });
73 | stackPanel.addControl(joinGameButton);
74 |
75 | const createOrJoinButton = this.createMenuButton("createOrJoinGame", "CREATE OR JOIN");
76 | createOrJoinButton.onPointerClickObservable.add(async () => {
77 | this.swapControls(false);
78 | await this.createGame("joinOrCreate");
79 | });
80 | stackPanel.addControl(createOrJoinButton);
81 |
82 | controlBox.addControl(stackPanel);
83 |
84 | this._advancedTexture.addControl(controlBox);
85 |
86 | this.initLoadingMessageBox();
87 | this.initErrorMessageBox();
88 | this.swapLoadingMessageBox(false);
89 | this.swapErrorMessageBox(false);
90 |
91 | this.doRender();
92 | }
93 |
94 | private createMenuButton(name: string, text: string): GUI.Button {
95 | const button = GUI.Button.CreateImageWithCenterTextButton(name, text, "./public/btn-default.png");
96 | button.width = "45%";
97 | button.height = "55px";
98 | button.fontFamily = "Roboto";
99 | button.fontSize = "6%";
100 | button.thickness = 0;
101 | button.paddingTop = "10px"
102 | button.color = "#c0c0c0";
103 | return button;
104 | }
105 |
106 | private swapControls(isEnabled: boolean) {
107 | for (let btn of this._advancedTexture.getControlsByType("Button")) {
108 | btn.isEnabled = isEnabled;
109 | }
110 | }
111 |
112 | private async createGame(method: string): Promise {
113 | let game: Game;
114 | try {
115 | switch (method) {
116 | case "create":
117 | this.swapLoadingMessageBox(true);
118 | game = new Game(this._canvas, this._engine, await this._colyseus.create(ROOM_NAME));
119 | break;
120 | case "join":
121 | this.swapLoadingMessageBox(true);
122 | game = new Game(this._canvas, this._engine, await this._colyseus.join(ROOM_NAME));
123 | break;
124 | default:
125 | this.swapLoadingMessageBox(true);
126 | game = new Game(this._canvas, this._engine, await this._colyseus.joinOrCreate(ROOM_NAME));
127 | }
128 | this._scene.dispose();
129 | game.bootstrap();
130 | } catch (error) {
131 | this._errorMessage.text = error.message;
132 | this.swapErrorMessageBox(true);
133 | }
134 | }
135 |
136 | private doRender(): void {
137 | // Run the render loop.
138 | this._engine.runRenderLoop(() => {
139 | this._scene.render();
140 | });
141 |
142 | // The canvas/window resize event handler.
143 | window.addEventListener('resize', () => {
144 | this._engine.resize();
145 | });
146 | }
147 |
148 | private initLoadingMessageBox() {
149 | const loadingMessage = new GUI.Rectangle("messageBox");
150 | loadingMessage.thickness = 0;
151 | loadingMessage.background = "#131313";
152 |
153 | const loadingText = new GUI.TextBlock("loadingText");
154 | loadingText.text = "LOADING..."
155 | loadingText.fontFamily = "Roboto";
156 | loadingText.color = "#fad836";
157 | loadingText.fontSize = "30px";
158 | loadingMessage.addControl(loadingText);
159 |
160 | this._advancedTexture.addControl(loadingMessage);
161 | }
162 |
163 | private initErrorMessageBox() {
164 | const errorMessageBox = new GUI.Rectangle("errorMessageBox");
165 | errorMessageBox.thickness = 0;
166 | errorMessageBox.background = "#131313";
167 |
168 | this._errorMessage.fontFamily = "Roboto";
169 | this._errorMessage.color = "#ff1616";
170 | this._errorMessage.fontSize = "20px";
171 | this._errorMessage.textWrapping = true;
172 | errorMessageBox.addControl(this._errorMessage);
173 |
174 | const button = GUI.Button.CreateImageWithCenterTextButton("tryAgainButton", "<- TRY AGAIN", "./public/btn-default.png");
175 | button.width = "200px";
176 | button.height = "60px";
177 | button.fontFamily = "Roboto";
178 | button.thickness = 0;
179 | button.color = "#c0c0c0";
180 | button.verticalAlignment = GUI.Control.VERTICAL_ALIGNMENT_BOTTOM;
181 | button.paddingBottom = "20px";
182 | button.onPointerClickObservable.add(() => {
183 | this.swapControls(true);
184 | this.swapLoadingMessageBox(false);
185 | this.swapErrorMessageBox(false);
186 | });
187 | errorMessageBox.addControl(button);
188 |
189 | this._advancedTexture.addControl(errorMessageBox);
190 | }
191 |
192 | private swapLoadingMessageBox(isEnabled: boolean) {
193 | const messageBox = this._advancedTexture.getControlByName("messageBox");
194 | messageBox.isEnabled = isEnabled;
195 | messageBox.alpha = isEnabled ? 0.75 : 0;
196 | }
197 |
198 | private swapErrorMessageBox(isEnabled: boolean) {
199 | this.swapLoadingMessageBox(false);
200 |
201 | const messageBox = this._advancedTexture.getControlByName("errorMessageBox");
202 | this._advancedTexture.getControlByName("tryAgainButton").isEnabled = true;
203 | messageBox.isEnabled = isEnabled;
204 | messageBox.alpha = isEnabled ? 0.75 : 0;
205 | }
206 | }
207 |
--------------------------------------------------------------------------------
/src/utils.ts:
--------------------------------------------------------------------------------
1 | import * as BABYLON from "babylonjs";
2 |
3 | export const createSkyBox = (scene: BABYLON.Scene) => {
4 | const skybox = BABYLON.MeshBuilder.CreateBox("skyBox", {size: 1000.0}, scene);
5 | const skyboxMaterial = new BABYLON.StandardMaterial("skyBox", scene);
6 | skyboxMaterial.backFaceCulling = false;
7 | skyboxMaterial.reflectionTexture = new BABYLON.CubeTexture("./public/textures/skybox", scene);
8 | skyboxMaterial.reflectionTexture.coordinatesMode = BABYLON.Texture.SKYBOX_MODE;
9 | skyboxMaterial.diffuseColor = new BABYLON.Color3(0, 0, 0);
10 | skyboxMaterial.specularColor = new BABYLON.Color3(0, 0, 0);
11 | skybox.material = skyboxMaterial;
12 | }
13 |
--------------------------------------------------------------------------------
/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compileOnSave": true,
3 | "compilerOptions": {
4 | "target": "es6",
5 | "module": "commonjs",
6 | "sourceMap": true,
7 | "outDir": "./dist",
8 | "types": [
9 | "babylonjs",
10 | "babylonjs-gui",
11 | "babylonjs-materials",
12 | "node"
13 | ]
14 | },
15 | "include": [
16 | "src/**/*"
17 | ],
18 | "exclude": [
19 | "node_modules",
20 | "**/*.spec.js"
21 | ]
22 | }
23 |
--------------------------------------------------------------------------------
/webpack.config.js:
--------------------------------------------------------------------------------
1 | // webpack.config.js
2 | const path = require('path')
3 | const CopyWebpackPlugin = require('copy-webpack-plugin');
4 |
5 | module.exports = {
6 | mode: "development",
7 | entry: {
8 | app: "./src/app.ts"
9 | },
10 | output: {
11 | path: path.resolve(__dirname, 'dist'),
12 | filename: '[name].js'
13 | },
14 | resolve: {
15 | extensions: ['.ts', 'tsx', '.js']
16 | },
17 | devtool: 'source-map',
18 | plugins: [
19 | new CopyWebpackPlugin({
20 | patterns: [
21 | { from: 'public', to: 'public' },
22 | { from: 'index.html'}
23 | ]
24 | })
25 | ],
26 | module: {
27 | rules: [{
28 | test: /\.tsx?$/,
29 | loader: 'ts-loader',
30 | exclude: /node_modules/
31 | }]
32 | }
33 | }
34 |
--------------------------------------------------------------------------------