Skip to content

Commit 5605b31

Browse files
committed
[feat] Loading menus now appear on startup and on game load.
1 parent 0b84982 commit 5605b31

File tree

6 files changed

+56
-23
lines changed

6 files changed

+56
-23
lines changed

src/frontend/components/atoms/loading.tsx

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ export function Loading({
88
className,
99
loadingDone,
1010
}: {
11-
progress: number;
11+
progress?: number;
1212
className?: string;
1313
loadingDone: () => void;
1414
}) {
@@ -27,6 +27,11 @@ export function Loading({
2727
if (!videoRef.current) {
2828
return;
2929
}
30+
if (progress === undefined) {
31+
videoRef.current.playbackRate = 2;
32+
videoRef.current.play();
33+
return;
34+
}
3035
const expectedProgress = VIDEO_TIME_S * progress;
3136
const currentTime = videoRef.current.currentTime;
3237
if (expectedProgress > currentTime) {
@@ -38,14 +43,16 @@ export function Loading({
3843
}
3944
}, [videoRef, progress]);
4045

46+
// Always play muted, because it prevents browsers blocking the animation.
4147
return (
4248
<video
49+
muted
4350
style={{ maxWidth: "500px", width: "15vw" }}
4451
className={className}
4552
ref={videoRef}
4653
src={video}
4754
controls={false}
4855
preload="auto"
49-
></video>
56+
/>
5057
);
5158
}

src/frontend/components/ingame-view.tsx

Lines changed: 26 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,8 @@ import styles from "./ingame-view.module.css";
33
import { Game } from "../../game";
44
import { AmmoCount, GameReactChannel } from "../../interop/gamechannel";
55
import { WeaponSelector } from "./gameui/weapon-select";
6-
import { IRunningGameInstance } from "../../logic/gameinstance";
6+
import { IRunningGameInstance, LocalGameInstance } from "../../logic/gameinstance";
7+
import { LoadingPage } from "./loading-page";
78

89
export function IngameView({
910
scenario,
@@ -16,21 +17,35 @@ export function IngameView({
1617
gameReactChannel: GameReactChannel;
1718
gameInstance: IRunningGameInstance;
1819
}) {
20+
const [hasLoaded, setLoaded] = useState<boolean>(false);
1921
const [fatalError, setFatalError] = useState<Error>();
2022
const [game, setGame] = useState<Game>();
2123
const ref = useRef<HTMLDivElement>(null);
2224
const [weaponMenu, setWeaponMenu] = useState<AmmoCount | null>(null);
2325
useEffect(() => {
24-
Game.create(window, scenario, gameReactChannel, gameInstance, level)
25-
.then((game) => {
26-
(window as unknown as { wormgine: Game }).wormgine = game;
27-
game.loadResources().then(() => {
28-
setGame(game);
29-
});
30-
})
31-
.catch((ex) => {
32-
setFatalError(ex);
26+
27+
async function init() {
28+
if (gameInstance instanceof LocalGameInstance) {
29+
// XXX: Only so the game feels more responsive by capturing this inside the loading phase.
30+
await gameInstance.startGame();
31+
console.log("Game started");
32+
}
33+
const game = await Game.create(window, scenario, gameReactChannel, gameInstance, level);
34+
setGame(game);
35+
game.ready$.subscribe(r => {
36+
if (r) {
37+
console.log("Game loaded");
38+
setLoaded(true);
39+
}
3340
});
41+
// Bind the game to the window such that we can debug it.
42+
(globalThis as unknown as { wormgine: Game }).wormgine = game;
43+
await game.loadResources();
44+
}
45+
46+
void init().catch((ex) => {
47+
setFatalError(ex);
48+
});
3449
}, []);
3550

3651
useEffect(() => {
@@ -42,7 +57,6 @@ export function IngameView({
4257
return;
4358
}
4459

45-
// Bind the game to the window such that we can debug it.
4660
ref.current.appendChild(game.canvas);
4761
game.run().catch((ex) => {
4862
setFatalError(ex);
@@ -85,6 +99,7 @@ export function IngameView({
8599

86100
return (
87101
<>
102+
<LoadingPage visible={!hasLoaded} force />
88103
<div id="overlay" className={styles.overlay}>
89104
<WeaponSelector
90105
weapons={weaponMenu}

src/frontend/components/loading-page.tsx

Lines changed: 13 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6,12 +6,17 @@ import { useAnimate } from "framer-motion";
66
export function LoadingPage({
77
progress,
88
visible,
9+
force,
910
}: {
1011
visible: boolean;
11-
progress: number;
12+
progress?: number;
13+
/**
14+
* Force hiding the loading bar when visible is false */
15+
force?: boolean;
1216
}) {
1317
const [scope, animate] = useAnimate();
1418
const [isLoadingDone, setLoadingDone] = useState(false);
19+
const [shouldOverlay, setShouldOverlay] = useState(true);
1520

1621
useEffect(() => {
1722
if (!scope.current) {
@@ -24,17 +29,21 @@ export function LoadingPage({
2429
{ opacity: 1 },
2530
{ delay: 0, duration: 0.25, ease: "easeIn" },
2631
);
27-
} else if (isLoadingDone) {
32+
} else if (isLoadingDone || force) {
2833
await animate(
2934
scope.current,
3035
{ opacity: 0 },
3136
{ delay: 0.5, duration: 0.5, ease: "easeIn" },
3237
);
33-
console.log("safetoremove");
38+
setShouldOverlay(false);
3439
}
3540
}
3641
void runAnim();
37-
}, [visible, isLoadingDone, scope.current]);
42+
}, [visible, force, isLoadingDone, scope.current]);
43+
44+
if (!shouldOverlay) {
45+
return null;
46+
}
3847

3948
return (
4049
<>

src/frontend/components/menu.tsx

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -94,7 +94,7 @@ function mainMenu(
9494
<span onMouseOver={videoHover} onMouseOut={videoHoverOut}>
9595
Skirmish
9696
</span>
97-
<video src={settingsAnim} loop />
97+
<video muted src={settingsAnim} loop />
9898
</button>
9999
</li>
100100
<li>
@@ -107,7 +107,7 @@ function mainMenu(
107107
<span onMouseOver={videoHover} onMouseOut={videoHoverOut}>
108108
Team Editor
109109
</span>
110-
<video src={settingsAnim} loop />
110+
<video muted src={settingsAnim} loop />
111111
</button>
112112
</li>
113113
<li>
@@ -120,7 +120,7 @@ function mainMenu(
120120
<span onMouseOver={videoHover} onMouseOut={videoHoverOut}>
121121
Online Play
122122
</span>
123-
<video src={settingsAnim} loop />
123+
<video muted src={settingsAnim} loop />
124124
</button>
125125
</li>
126126
<li>
@@ -133,7 +133,7 @@ function mainMenu(
133133
<span onMouseOver={videoHover} onMouseOut={videoHoverOut}>
134134
Settings
135135
</span>
136-
<video src={settingsAnim} loop />
136+
<video muted src={settingsAnim} loop />
137137
</button>
138138
</li>
139139
</ul>

src/frontend/components/menus/lobby.tsx

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -308,7 +308,6 @@ function LocalLobby({ onOpenIngame, exitToMenu }: Omit<Props, "client">) {
308308
<ActiveLobby
309309
gameInstance={gameInstance}
310310
onOpenIngame={() => {
311-
gameInstance.startGame();
312311
onOpenIngame(gameInstance);
313312
}}
314313
exitToMenu={exitToMenu}

src/game.ts

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ import Logger from "./log";
1414
import { CriticalGameError } from "./errors";
1515
import { getGameSettings } from "./settings";
1616
import { NetGameWorld } from "./net/netGameWorld";
17-
import { debounceTime, fromEvent, map, merge, Observable, of } from "rxjs";
17+
import { BehaviorSubject, debounceTime, fromEvent, map, merge, Observable, of } from "rxjs";
1818
import { IRunningGameInstance } from "./logic/gameinstance";
1919
import { RunningNetGameInstance } from "./net/netgameinstance";
2020

@@ -29,6 +29,8 @@ export class Game {
2929
public readonly world: GameWorld;
3030
public readonly rapierGfx: Graphics;
3131
public readonly screenSize$: Observable<{ width: number; height: number }>;
32+
private readonly ready = new BehaviorSubject(false);
33+
public readonly ready$ = this.ready.asObservable() ;
3234

3335
public get pixiRoot() {
3436
return this.viewport;
@@ -124,6 +126,7 @@ export class Game {
124126
undefined,
125127
);
126128
this.pixiApp.stage.addChildAt(this.rapierGfx, 0);
129+
this.ready.next(true);
127130

128131
// Run physics engine at 90fps.
129132
const tickEveryMs = 1000 / 90;

0 commit comments

Comments
 (0)