Skip to content

Commit ff80c7e

Browse files
committed
perf: restart no longer reloads the page
1 parent 5e94f31 commit ff80c7e

File tree

5 files changed

+86
-57
lines changed

5 files changed

+86
-57
lines changed

src/main.ts

Lines changed: 64 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import { Application, Container, type Ticker } from "pixi.js";
22
import { derived, get, writable } from "svelte/store";
33
import { Facing } from "./constants.ts";
44
import { Grid, createCellsFromStageDefinition } from "./grid.ts";
5+
import * as History from "./history.ts";
56
import * as Player from "./player.ts";
67
import type { Context, GameState, UIInfo } from "./public-types.ts";
78
import { bunnyTexture } from "./resources.ts";
@@ -12,18 +13,13 @@ export async function setup(
1213
el: HTMLElement,
1314
stageDefinition: StageDefinition,
1415
bindings: {
15-
onpause: () => void;
16-
onresume: () => void;
17-
ondestroy: () => void;
16+
pause: () => void;
17+
resume: () => void;
18+
destroy: () => void;
19+
reset: () => void;
1820
uiInfo: UIInfo;
1921
},
2022
): Promise<void> {
21-
bindings.onpause = () => {
22-
cx.state.update((prev) => {
23-
prev.paused = true;
24-
return prev;
25-
});
26-
};
2723
const cleanups: (() => void)[] = [];
2824
const unlessPaused = (f: (ticker: Ticker) => void) => (ticker: Ticker) => {
2925
const paused = get(cx.state).paused;
@@ -65,6 +61,7 @@ export async function setup(
6561

6662
const gridMarginY =
6763
(app.screen.height - blockSize * stageDefinition.stage.length) / 2;
64+
// things that don't need to reset on reset()
6865
const config = writable({
6966
gridX,
7067
gridY,
@@ -73,7 +70,12 @@ export async function setup(
7370
initialPlayerX: stageDefinition.initialPlayerX,
7471
initialPlayerY: stageDefinition.initialPlayerY,
7572
});
76-
const state = writable<GameState>({
73+
74+
const initialHistory = {
75+
index: 0,
76+
tree: [],
77+
};
78+
const initialGameState = {
7779
inventory: null,
7880
inventoryIsInfinite: false,
7981
usage: {
@@ -86,7 +88,27 @@ export async function setup(
8688
paused: false,
8789
switches: [],
8890
switchingBlocks: [],
89-
});
91+
};
92+
const initialDynamic = {
93+
focus: null,
94+
player: {
95+
// HACK: these values are immediately overwritten inside Player.init().
96+
sprite: null,
97+
coords: {
98+
x: stageDefinition.initialPlayerX,
99+
y: stageDefinition.initialPlayerY,
100+
},
101+
x: 0,
102+
y: 0,
103+
vx: 0,
104+
vy: 0,
105+
onGround: false,
106+
jumpingBegin: null,
107+
holdingKeys: {},
108+
facing: Facing.right,
109+
},
110+
};
111+
const state = writable<GameState>(structuredClone(initialGameState));
90112
const grid = new Grid(
91113
{
92114
_stage_container: stage,
@@ -97,44 +119,40 @@ export async function setup(
97119
blockSize,
98120
stageDefinition,
99121
);
100-
const history = writable({
101-
index: -1,
102-
tree: [],
103-
});
104-
const uiContext = derived([state, history], ([$state, $history]) => {
105-
return useUI($state, $history);
106-
});
122+
const history = writable(structuredClone(initialHistory));
107123
const cx: Context = {
108124
_stage_container: stage,
109125
grid,
110-
dynamic: {
111-
focus: null,
112-
player: {
113-
// HACK: these values are immediately overwritten inside Player.init().
114-
sprite: null,
115-
coords() {
116-
return { x: this.x, y: this.y };
117-
},
118-
x: 0,
119-
y: 0,
120-
vx: 0,
121-
vy: 0,
122-
onGround: false,
123-
jumpingBegin: null,
124-
holdingKeys: {},
125-
facing: Facing.right,
126-
},
127-
},
126+
dynamic: structuredClone(initialDynamic),
128127
state: state,
129128
history,
130-
uiContext,
131129
config,
132-
elapsed: writable(0),
130+
elapsed: 0, // does this need to be writable? like is anyone listening to this/
131+
};
132+
bindings.reset = () => {
133+
const d = stageDefinition;
134+
cx.history = writable(structuredClone(initialHistory));
135+
// 内部実装ゴリゴリに知ってるリセットなので、直したかったら直して。多分 coords の各プロパティのセッターをいい感じにしてやれば良い
136+
// MEMO: `coords` は抽象化失敗してるので、直すか消すほうが良い
137+
cx.dynamic.player.x = blockSize * d.initialPlayerX;
138+
cx.dynamic.player.y = blockSize * d.initialPlayerY + get(cx.config).marginY;
139+
cx.dynamic.focus = null;
140+
cx.elapsed = 0;
141+
cx.state.update((prev) => ({
142+
...prev,
143+
paused: false,
144+
}));
145+
cx.grid.diffAndUpdateTo(
146+
cx,
147+
createCellsFromStageDefinition(stageDefinition),
148+
);
149+
// 上に同じく。 init を使う?でも init は中で document.addEventListener してるので...
150+
History.record(cx);
133151
};
134152

135153
app.ticker.add(
136154
unlessPaused((ticker) => {
137-
cx.elapsed.update((prev) => prev + ticker.deltaTime);
155+
cx.elapsed += ticker.deltaTime;
138156
}),
139157
);
140158

@@ -157,23 +175,27 @@ export async function setup(
157175
window.removeEventListener("resize", onresize);
158176
});
159177

160-
bindings.ondestroy = () => {
178+
bindings.destroy = () => {
161179
for (const cleanup of cleanups) {
162180
cleanup();
163181
}
164182
};
165-
bindings.onresume = () => {
183+
bindings.resume = () => {
166184
cx.state.update((prev) => {
167185
prev.paused = false;
168186
return prev;
169187
});
170188
};
171-
bindings.onpause = () => {
189+
bindings.pause = () => {
172190
cx.state.update((prev) => {
173191
prev.paused = true;
174192
return prev;
175193
});
176194
};
195+
196+
const uiContext = derived([state, history], ([$state, $history]) => {
197+
return useUI($state, $history);
198+
});
177199
uiContext.subscribe((uiInfo) => {
178200
bindings.uiInfo = uiInfo;
179201
});

src/player.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ export function init(
3939
Ability.init(cx, options?.ability);
4040
return {
4141
sprite,
42-
coords() {
42+
get coords() {
4343
return { x: this.x, y: this.y };
4444
},
4545
get x() {
@@ -65,7 +65,7 @@ export function init(
6565

6666
export function getCoords(cx: Context) {
6767
const { blockSize, marginY } = get(cx.config);
68-
const coords = cx.dynamic.player.coords();
68+
const coords = cx.dynamic.player.coords;
6969
const x = Math.floor(coords.x / blockSize);
7070
const y = Math.round((coords.y - marginY) / blockSize) - 1; // it was not working well so take my patch
7171
return { x, y };
@@ -140,7 +140,7 @@ export function tick(cx: Context, ticker: Ticker) {
140140
if (player.holdingKeys[Inputs.Right]) {
141141
player.vx += consts.moveVX * blockSize;
142142
}
143-
const elapsed = get(cx.elapsed);
143+
const elapsed = cx.elapsed;
144144
if (player.holdingKeys[Inputs.Up]) {
145145
if (player.onGround) {
146146
player.vy = -consts.jumpVY * blockSize;

src/public-types.ts

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -65,7 +65,7 @@ export type Context = {
6565
focus: Coords | null; // current focus coordinates
6666
player: {
6767
holdingKeys: { [key in Inputs]?: boolean };
68-
coords: () => { x: number; y: number };
68+
coords: { x: number; y: number };
6969
sprite: Sprite | null;
7070
x: number;
7171
y: number;
@@ -77,9 +77,7 @@ export type Context = {
7777
};
7878
};
7979
// about time
80-
elapsed: Writable<number>;
81-
// TODO: make it derived from state, because this doesn't need to be separate state
82-
uiContext: Readable<UIInfo>;
80+
elapsed: number;
8381
};
8482

8583
export type UIInfo = {

src/ui-components/Game.svelte

Lines changed: 12 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -13,9 +13,10 @@ const { stageNum, stage }: Props = $props();
1313
let container: HTMLElement | null = $state(null);
1414
1515
const bindings = $state({
16-
onpause: () => {},
17-
onresume: () => {},
18-
ondestroy: () => {},
16+
pause: () => {},
17+
resume: () => {},
18+
destroy: () => {},
19+
reset: () => {},
1920
uiInfo: <UIInfo>{
2021
inventory: null,
2122
inventoryIsInfinite: false,
@@ -35,14 +36,18 @@ $effect(() => {
3536
}
3637
});
3738
38-
onDestroy(() => bindings.ondestroy());
39+
onDestroy(() => bindings.destroy());
3940
</script>
4041

4142
<div bind:this={container} id="container">
4243
<PauseMenu
4344
paused={uiContext.paused}
44-
onpause={() => bindings.onpause()}
45-
onresume={() => bindings.onresume()}
45+
onpause={() => bindings.pause()}
46+
onresume={() => bindings.resume()}
47+
onreset={() => {
48+
// if this isn't working well, we can use window.location.reload(); instead
49+
bindings.reset();
50+
}}
4651
/>
4752
<div
4853
class="uiBackground"
@@ -57,6 +62,7 @@ onDestroy(() => bindings.ondestroy());
5762
<div class="inventory">
5863
{#if uiContext.inventory !== null}
5964
<!-- todo: tint 0xff0000 をする必要があるが、そもそもこの画像は仮なのか本当に赤色にするのか -->
65+
6066
<img
6167
src="/assets/block.png"
6268
alt="inventory"

src/ui-components/PauseMenu.svelte

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,9 @@ type Props = {
33
paused: boolean;
44
onresume: () => void;
55
onpause: () => void;
6+
onreset: () => void;
67
};
7-
const { paused, onresume, onpause }: Props = $props();
8+
let { paused, onresume, onpause, onreset }: Props = $props();
89
let el: HTMLDialogElement;
910
$effect(() => {
1011
if (paused) {
@@ -36,7 +37,9 @@ document.addEventListener("keydown", (ev) => {
3637
<button
3738
style="font-size: 1.5rem;"
3839
class="btn btn-block"
39-
onclick={() => window.location.reload()}
40+
onclick={() => {
41+
onreset();
42+
}}
4043
>
4144
Restart
4245
</button>

0 commit comments

Comments
 (0)