Skip to content

Commit

Permalink
Handle assets loading by renderer
Browse files Browse the repository at this point in the history
  • Loading branch information
delasy committed Oct 19, 2024
1 parent 969ad36 commit cce11a4
Show file tree
Hide file tree
Showing 4 changed files with 90 additions and 130 deletions.
7 changes: 2 additions & 5 deletions app/provider.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@ import { ConvexReactClient } from "convex/react";
import { ThemeProvider } from "next-themes";
import PlausibleProvider from "next-plausible";
import { Toaster } from "@/components/ui/toaster";
import VisualizerProvider from "@/components/VisualizerProvider";

const convex = new ConvexReactClient(process.env.NEXT_PUBLIC_CONVEX_URL!);

Expand All @@ -18,10 +17,8 @@ export function Providers({ children }: { children: React.ReactNode }) {
>
<ThemeProvider attribute="class">
<ConvexAuthNextjsProvider client={convex}>
<VisualizerProvider>
{children}
<Toaster />
</VisualizerProvider>
{children}
<Toaster />
</ConvexAuthNextjsProvider>
</ThemeProvider>
</PlausibleProvider>
Expand Down
11 changes: 2 additions & 9 deletions components/Visualizer.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ import React from "react";
import { Button } from "@/components/ui/button";
import { Renderer } from "@/renderer";
import { ZombieSurvival } from "@/simulators/zombie-survival";
import { useVisualizer } from "@/components/VisualizerProvider";

const AUTO_REPLAY_SPEED = 1_500;
const REPLAY_SPEED = 600;
Expand All @@ -24,7 +23,6 @@ export function Visualizer({
onReset?: () => unknown;
onSimulationEnd?: (isWin: boolean) => unknown;
}) {
const visualizer = useVisualizer();
const simulator = React.useRef<ZombieSurvival>(new ZombieSurvival(map));
const renderer = React.useRef<Renderer | null>(null);
const interval = React.useRef<ReturnType<typeof setTimeout> | null>(null);
Expand All @@ -34,20 +32,15 @@ export function Visualizer({
const [running, setRunning] = React.useState(false);

React.useEffect(() => {
if (
visualizer.ready &&
canvas.current !== null &&
renderer.current === null
) {
if (canvas.current !== null) {
renderer.current = new Renderer(
visualizer.getAssets(),
ZombieSurvival.boardHeight(map),
ZombieSurvival.boardWidth(map),
canvas.current,
Number.parseInt(cellSize, 10),
);
}
}, [canvas, visualizer.ready]);
}, [canvas]);

React.useEffect(() => {
if (autoStart) {
Expand Down
82 changes: 0 additions & 82 deletions components/VisualizerProvider.tsx

This file was deleted.

120 changes: 86 additions & 34 deletions renderer/index.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,78 @@
import { type Entity, EntityType } from "@/simulators/zombie-survival";

export interface RendererAssets {
bg: HTMLImageElement;
box: HTMLImageElement;
player: HTMLImageElement;
rock: HTMLImageElement;
zombie: HTMLImageElement;
loading: boolean;
loaded: boolean;
bg: HTMLImageElement | null;
box: HTMLImageElement | null;
player: HTMLImageElement | null;
rock: HTMLImageElement | null;
zombie: HTMLImageElement | null;
}

const assets: RendererAssets = {
loading: false,
loaded: false,
bg: null,
box: null,
player: null,
rock: null,
zombie: null,
};

async function loadAssets() {
if (assets.loading || assets.loaded) {
return;
}

assets.loading = true;

const [bg, box, player, rock, zombie] = await Promise.all([
loadImage("/map.png"),
loadImage("/entities/block.svg"),
loadImage("/entities/player_alive_1.svg"),
loadImage("/entities/rocks.svg"),
loadImage("/entities/zombie_alive_1.svg"),
]);

assets.loaded = true;
assets.bg = bg;
assets.box = box;
assets.player = player;
assets.rock = rock;
assets.zombie = zombie;
}

async function loadImage(src: string): Promise<HTMLImageElement> {
return await new Promise((resolve) => {
const img = new Image();
img.addEventListener("load", () => resolve(img));
img.src = src;
});
}

function getEntityImage(entity: Entity): HTMLImageElement | null {
switch (entity.getType()) {
case EntityType.Box: {
return assets.box;
}
case EntityType.Player: {
return assets.player;
}
case EntityType.Rock: {
return assets.rock;
}
case EntityType.Zombie: {
return assets.zombie;
}
}
}

function getEntityOffset(entity: Entity): { x: number; y: number } {
return {
x: entity.getType() === EntityType.Zombie ? 16 : 0,
y: 0,
};
}

export class Renderer {
Expand All @@ -17,7 +84,6 @@ export class Renderer {
private ctx: CanvasRenderingContext2D;

public constructor(
assets: RendererAssets,
boardHeight: number,
boardWidth: number,
canvas: HTMLCanvasElement,
Expand All @@ -42,6 +108,7 @@ export class Renderer {
canvas.style.width = `${this.w}px`;

ctx.scale(window.devicePixelRatio, window.devicePixelRatio);
void loadAssets();
}

public render(entities: Entity[]) {
Expand All @@ -56,8 +123,12 @@ export class Renderer {
}

private drawBg() {
if (assets.bg === null) {
return;
}

const canvasRatio = this.w / this.h;
const bgRatio = this.assets.bg.width / this.assets.bg.height;
const bgRatio = assets.bg.width / assets.bg.height;

let drawWidth, drawHeight, offsetX, offsetY;

Expand All @@ -74,14 +145,19 @@ export class Renderer {
}

this.ctx.globalAlpha = 0.5;
this.ctx.drawImage(this.assets.bg, offsetX, offsetY, drawWidth, drawHeight);
this.ctx.drawImage(assets.bg, offsetX, offsetY, drawWidth, drawHeight);
this.ctx.globalAlpha = 1.0;
}

private drawEntity(entity: Entity) {
const entityImage = this.getEntityImage(entity);
const entityImage = getEntityImage(entity);

if (entityImage === null) {
return;
}

const entityPosition = entity.getPosition();
const entityOffset = this.getEntityOffset(entity);
const entityOffset = getEntityOffset(entity);

this.ctx.globalAlpha =
entity.getType() === EntityType.Zombie && entity.getHealth() === 1
Expand All @@ -96,28 +172,4 @@ export class Renderer {
this.cellSize,
);
}

private getEntityImage(entity: Entity): HTMLImageElement {
switch (entity.getType()) {
case EntityType.Box: {
return this.assets.box;
}
case EntityType.Player: {
return this.assets.player;
}
case EntityType.Rock: {
return this.assets.rock;
}
case EntityType.Zombie: {
return this.assets.zombie;
}
}
}

private getEntityOffset(entity: Entity): { x: number; y: number } {
return {
x: entity.getType() === EntityType.Zombie ? 16 : 0,
y: 0,
};
}
}

0 comments on commit cce11a4

Please sign in to comment.