0.1.0-1-alpha.2-fix.2
This commit is contained in:
17
crates/apps/game-reflex-poc-tauri/frontend/index.html
Normal file
17
crates/apps/game-reflex-poc-tauri/frontend/index.html
Normal file
@@ -0,0 +1,17 @@
|
||||
<!doctype html>
|
||||
<html lang="fr">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width,initial-scale=1">
|
||||
<title>Reflex POC Tauri</title>
|
||||
<link rel="stylesheet" href="/style.css">
|
||||
</head>
|
||||
<body>
|
||||
<main id="app">
|
||||
<canvas id="game" aria-label="Reflex POC"></canvas>
|
||||
<output id="score">Score : 0</output>
|
||||
<output id="runtime">Initialisation…</output>
|
||||
</main>
|
||||
<script type="module" src="/ts/main.ts"></script>
|
||||
</body>
|
||||
</html>
|
||||
45
crates/apps/game-reflex-poc-tauri/frontend/style.css
Normal file
45
crates/apps/game-reflex-poc-tauri/frontend/style.css
Normal file
@@ -0,0 +1,45 @@
|
||||
/* file: crates/apps/game-reflex-poc-tauri/frontend/style.css */
|
||||
/* version: 1 */
|
||||
|
||||
html,
|
||||
body {
|
||||
margin: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
overflow: hidden;
|
||||
background: rgb(18 18 24);
|
||||
font-family: sans-serif;
|
||||
}
|
||||
|
||||
#app {
|
||||
position: relative;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
#game {
|
||||
display: block;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
touch-action: none;
|
||||
}
|
||||
|
||||
#score,
|
||||
#runtime {
|
||||
position: absolute;
|
||||
left: 12px;
|
||||
padding: 6px 8px;
|
||||
border-radius: 4px;
|
||||
background: rgb(0 0 0 / 55%);
|
||||
color: white;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
#score {
|
||||
top: 12px;
|
||||
}
|
||||
|
||||
#runtime {
|
||||
bottom: 12px;
|
||||
font-size: 12px;
|
||||
}
|
||||
12
crates/apps/game-reflex-poc-tauri/frontend/ts/bridge.ts
Normal file
12
crates/apps/game-reflex-poc-tauri/frontend/ts/bridge.ts
Normal file
@@ -0,0 +1,12 @@
|
||||
// file: crates/apps/game-reflex-poc-tauri/frontend/ts/bridge.ts
|
||||
// version: 1
|
||||
|
||||
import { invoke } from "@tauri-apps/api/core";
|
||||
|
||||
export async function getRuntimeLabel(): Promise<string> {
|
||||
return await invoke<string>("get_runtime_label");
|
||||
}
|
||||
|
||||
export async function notifyFrontendReady(): Promise<void> {
|
||||
await invoke("frontend_ready");
|
||||
}
|
||||
67
crates/apps/game-reflex-poc-tauri/frontend/ts/game.ts
Normal file
67
crates/apps/game-reflex-poc-tauri/frontend/ts/game.ts
Normal file
@@ -0,0 +1,67 @@
|
||||
// file: crates/apps/game-reflex-poc-tauri/frontend/ts/game.ts
|
||||
// version: 1
|
||||
|
||||
import init, { ReflexWasmGame } from "../wasm/game_reflex_poc_wasm.js";
|
||||
import { tracingDebug, tracingTrace } from "./logging";
|
||||
|
||||
const FRAME_MS = 16;
|
||||
|
||||
function color(red: number, green: number, blue: number): string {
|
||||
return `rgb(${red} ${green} ${blue})`;
|
||||
}
|
||||
|
||||
function resizeCanvas(canvas: HTMLCanvasElement): void {
|
||||
const ratio = window.devicePixelRatio || 1;
|
||||
const width = Math.max(1, Math.floor(canvas.clientWidth * ratio));
|
||||
const height = Math.max(1, Math.floor(canvas.clientHeight * ratio));
|
||||
if (canvas.width !== width || canvas.height !== height) {
|
||||
canvas.width = width;
|
||||
canvas.height = height;
|
||||
}
|
||||
}
|
||||
|
||||
function render(game: ReflexWasmGame, canvas: HTMLCanvasElement, context: CanvasRenderingContext2D, score: HTMLOutputElement): void {
|
||||
resizeCanvas(canvas);
|
||||
context.fillStyle = color(game.background_red(), game.background_green(), game.background_blue());
|
||||
context.fillRect(0, 0, canvas.width, canvas.height);
|
||||
const count = game.rectangle_count();
|
||||
for (let index = 0; index < count; index += 1) {
|
||||
context.fillStyle = color(game.rectangle_red(index), game.rectangle_green(index), game.rectangle_blue(index));
|
||||
context.fillRect(
|
||||
game.rectangle_x(index) * canvas.width,
|
||||
game.rectangle_y(index) * canvas.height,
|
||||
game.rectangle_width(index) * canvas.width,
|
||||
game.rectangle_height(index) * canvas.height,
|
||||
);
|
||||
}
|
||||
score.value = `Score : ${game.score()}`;
|
||||
}
|
||||
|
||||
export async function startGame(canvas: HTMLCanvasElement, score: HTMLOutputElement): Promise<void> {
|
||||
await init();
|
||||
await tracingDebug("Reflex WASM module initialized");
|
||||
const context = canvas.getContext("2d");
|
||||
if (context === null) {
|
||||
throw new Error("2D canvas context is unavailable");
|
||||
}
|
||||
const game = new ReflexWasmGame();
|
||||
canvas.addEventListener("pointerdown", event => {
|
||||
const bounds = canvas.getBoundingClientRect();
|
||||
const x = (event.clientX - bounds.left) / bounds.width;
|
||||
const y = (event.clientY - bounds.top) / bounds.height;
|
||||
game.pointer_down(x, y);
|
||||
render(game, canvas, context, score);
|
||||
void tracingTrace(`pointerdown x=${x.toFixed(4)} y=${y.toFixed(4)} score=${game.score()}`);
|
||||
});
|
||||
let previous = performance.now();
|
||||
function frame(now: number): void {
|
||||
while (now - previous >= FRAME_MS) {
|
||||
game.tick();
|
||||
previous += FRAME_MS;
|
||||
}
|
||||
render(game, canvas, context, score);
|
||||
window.requestAnimationFrame(frame);
|
||||
}
|
||||
render(game, canvas, context, score);
|
||||
window.requestAnimationFrame(frame);
|
||||
}
|
||||
38
crates/apps/game-reflex-poc-tauri/frontend/ts/logging.ts
Normal file
38
crates/apps/game-reflex-poc-tauri/frontend/ts/logging.ts
Normal file
@@ -0,0 +1,38 @@
|
||||
// file: crates/apps/game-reflex-poc-tauri/frontend/ts/logging.ts
|
||||
// version: 1
|
||||
|
||||
import { attachConsole, debug, error, info, trace, warn } from "@fltsci/tauri-plugin-tracing";
|
||||
|
||||
let detachRustConsole: (() => void) | null = null;
|
||||
|
||||
export async function initializeTracing(): Promise<void> {
|
||||
detachRustConsole = await attachConsole();
|
||||
await info("Reflex Tauri frontend tracing initialized", "games::tauri::reflex::frontend");
|
||||
}
|
||||
|
||||
export async function tracingTrace(message: string): Promise<void> {
|
||||
await trace(message, "games::tauri::reflex::frontend");
|
||||
}
|
||||
|
||||
export async function tracingDebug(message: string): Promise<void> {
|
||||
await debug(message, "games::tauri::reflex::frontend");
|
||||
}
|
||||
|
||||
export async function tracingInfo(message: string): Promise<void> {
|
||||
await info(message, "games::tauri::reflex::frontend");
|
||||
}
|
||||
|
||||
export async function tracingWarn(message: string): Promise<void> {
|
||||
await warn(message, "games::tauri::reflex::frontend");
|
||||
}
|
||||
|
||||
export async function tracingError(message: string): Promise<void> {
|
||||
await error(message, "games::tauri::reflex::frontend");
|
||||
}
|
||||
|
||||
export function detachTracingConsole(): void {
|
||||
if (detachRustConsole !== null) {
|
||||
detachRustConsole();
|
||||
detachRustConsole = null;
|
||||
}
|
||||
}
|
||||
25
crates/apps/game-reflex-poc-tauri/frontend/ts/main.ts
Normal file
25
crates/apps/game-reflex-poc-tauri/frontend/ts/main.ts
Normal file
@@ -0,0 +1,25 @@
|
||||
// file: crates/apps/game-reflex-poc-tauri/frontend/ts/main.ts
|
||||
// version: 1
|
||||
|
||||
import { getRuntimeLabel, notifyFrontendReady } from "./bridge";
|
||||
import { startGame } from "./game";
|
||||
import { initializeTracing, tracingError, tracingInfo } from "./logging";
|
||||
|
||||
async function main(): Promise<void> {
|
||||
await initializeTracing();
|
||||
const canvas = document.querySelector<HTMLCanvasElement>("#game");
|
||||
const score = document.querySelector<HTMLOutputElement>("#score");
|
||||
const runtime = document.querySelector<HTMLOutputElement>("#runtime");
|
||||
if (canvas === null || score === null || runtime === null) {
|
||||
throw new Error("Reflex Tauri frontend DOM is incomplete");
|
||||
}
|
||||
runtime.value = await getRuntimeLabel();
|
||||
await startGame(canvas, score);
|
||||
await notifyFrontendReady();
|
||||
await tracingInfo("Reflex Tauri frontend started");
|
||||
}
|
||||
|
||||
void main().catch(async caughtError => {
|
||||
const message = caughtError instanceof Error ? caughtError.stack ?? caughtError.message : String(caughtError);
|
||||
await tracingError(`Reflex Tauri frontend startup failed: ${message}`);
|
||||
});
|
||||
Reference in New Issue
Block a user