@shadowdara/webgameengine 1.0.0 → 1.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -1,9 +1,5 @@
1
1
  # WebGameEngine 🎮
2
2
 
3
- **(Currently trying to make this into a lib)**
4
-
5
- **Use `bun tools/build.ts`**
6
-
7
3
  A lightweight, TypeScript-first web game engine framework for building 2D games.
8
4
 
9
5
  ## Features
@@ -19,14 +15,7 @@ A lightweight, TypeScript-first web game engine framework for building 2D games.
19
15
  ## Installation
20
16
 
21
17
  ```bash
22
- npm install @yourusername/webgameengine
23
- ```
24
-
25
- Or use directly from source:
26
-
27
- ```bash
28
- bun install
29
- bun run dev
18
+ npm install @shadowdara/webgameengine
30
19
  ```
31
20
 
32
21
  ## Quick Start
@@ -34,7 +23,7 @@ bun run dev
34
23
  ### Basic Game Loop
35
24
 
36
25
  ```typescript
37
- import { startEngine, setupInput, dlog, renderText } from '@yourusername/webgameengine';
26
+ import { startEngine, setupInput, dlog, renderText } from '@shadowdara/webgameengine';
38
27
 
39
28
  const canvas = document.getElementById('gameCanvas') as HTMLCanvasElement;
40
29
  const ctx = canvas.getContext('2d')!;
@@ -62,19 +51,17 @@ startEngine(init, gameLoop);
62
51
  ### Using Bun (local development)
63
52
 
64
53
  ```bash
65
- bun tools/build.ts # Build main game
66
- bun tools/build.ts --entry main2 # Build specific entry
67
- bun tools/build.ts --release # Production build
68
-
69
-
54
+ npx webgameengine # Start Dev Server
55
+ npx webgameengine --release # Production build
56
+ npx webgameengine --new # Create a new project with a simple Snake Clone as Template
70
57
  ```
71
58
 
72
59
  ### Configuration
73
60
 
74
- Edit `project.ts` to configure your game:
61
+ Edit `webgameengine.config.ts` to configure your game:
75
62
 
76
63
  ```typescript
77
- import { defineConfig } from './project';
64
+ import { defineConfig } from '@shadowdara/webgameengine/build';
78
65
 
79
66
  export function defineConfig() {
80
67
  return {
@@ -90,7 +77,7 @@ or
90
77
  ```typescript
91
78
  // Project File for the Game
92
79
 
93
- import { type buildconfig, new_buildconfig } from "./engine/build/buildconfig";
80
+ import { type buildconfig, new_buildconfig } from "@shadowdara/webgameengine/build";
94
81
 
95
82
  export function defineConfig(): buildconfig {
96
83
  let config: buildconfig = new_buildconfig();
@@ -1,28 +1,66 @@
1
1
  #!/usr/bin/env node
2
- // Shebang
3
2
  import { build as esbuild } from "esbuild";
4
- import { copyFolder, flog, getContentType } from "../../buildhelper.js";
5
- import { GetDefaultHTML } from "../../exporthtml.js";
6
3
  import { createServer } from "http";
7
4
  import { readFile, writeFile } from "fs/promises";
8
5
  import { watch } from "fs";
9
6
  import path from "path";
10
7
  import { WebSocketServer } from "ws";
8
+ import { createProject } from "./new.js";
9
+ import { copyFolder, flog, getContentType } from "../../buildhelper.js";
10
+ import { GetDefaultHTML } from "../../exporthtml.js";
11
11
  import { loadUserConfig } from "./config.js";
12
- const config = await loadUserConfig();
13
- // console.log("CONFIG:", config);
14
- let isBuilding = false;
15
- const isRelease = process.argv.includes("--release");
12
+ // ================= ARG PARSING =================
13
+ function parseArgs() {
14
+ const args = process.argv.slice(2);
15
+ const options = {
16
+ release: false,
17
+ port: 3000,
18
+ newProject: null,
19
+ };
20
+ for (let i = 0; i < args.length; i++) {
21
+ const arg = args[i];
22
+ switch (arg) {
23
+ case "--release":
24
+ case "-r":
25
+ options.release = true;
26
+ break;
27
+ case "--port":
28
+ case "-p":
29
+ options.port = Number(args[i + 1]);
30
+ i++;
31
+ break;
32
+ case "--new":
33
+ case "-n":
34
+ options.newProject = args[i + 1];
35
+ i++;
36
+ break;
37
+ default:
38
+ console.warn(`⚠️ Unbekanntes Argument: ${arg}`);
39
+ }
40
+ }
41
+ return options;
42
+ }
43
+ const args = parseArgs();
44
+ // Falls --new → sofort ausführen und beenden
45
+ if (args.newProject) {
46
+ await createProject(args.newProject);
47
+ // and make exit afterwards
48
+ }
49
+ // ================= FLAGS =================
50
+ const isRelease = args.release;
16
51
  const isDev = !isRelease;
52
+ // ================= CONFIG =================
53
+ const config = await loadUserConfig();
17
54
  // ================= BUILD =================
18
- async function build(config) {
55
+ let isBuilding = false;
56
+ async function build() {
19
57
  if (isBuilding)
20
58
  return;
21
59
  isBuilding = true;
22
60
  flog("🔄 Baue neu...");
23
61
  await esbuild({
24
- entryPoints: ["./game/" + config.entryname],
25
- outdir: "./" + config.outdir,
62
+ entryPoints: [`./game/${config.entryname}`],
63
+ outdir: `./${config.outdir}`,
26
64
  bundle: true,
27
65
  platform: "browser",
28
66
  minify: isRelease,
@@ -41,8 +79,8 @@ async function build(config) {
41
79
  let sockets = new Set();
42
80
  function startServer() {
43
81
  const server = createServer(async (req, res) => {
44
- let url = req.url || "/";
45
- let filePath = url === "/" ? "/index.html" : url;
82
+ const url = req.url || "/";
83
+ const filePath = url === "/" ? "/index.html" : url;
46
84
  try {
47
85
  const fullPath = path.join(process.cwd(), "dist", filePath);
48
86
  const file = await readFile(fullPath);
@@ -61,8 +99,8 @@ function startServer() {
61
99
  sockets.add(ws);
62
100
  ws.on("close", () => sockets.delete(ws));
63
101
  });
64
- server.listen(3000, () => {
65
- flog("🚀 Dev Server läuft auf http://localhost:3000");
102
+ server.listen(args.port, () => {
103
+ flog(`🚀 Dev Server läuft auf http://localhost:${args.port}`);
66
104
  });
67
105
  return server;
68
106
  }
@@ -85,13 +123,13 @@ async function restart() {
85
123
  do {
86
124
  pendingRestart = false;
87
125
  flog("♻️ Restart...");
88
- await build(config);
126
+ await build();
89
127
  reloadClients();
90
128
  } while (pendingRestart);
91
129
  restarting = false;
92
130
  }
93
131
  // ================= START =================
94
- await build(config);
132
+ await build();
95
133
  if (isDev) {
96
134
  startServer();
97
135
  ["resources", "game"].forEach((dir) => {
@@ -0,0 +1 @@
1
+ export declare function createProject(name: string): Promise<void>;
@@ -0,0 +1,156 @@
1
+ import path from "path";
2
+ import { writeFile, mkdir } from "fs/promises";
3
+ import { flog } from "../../buildhelper.js";
4
+ // ================= NEW PROJECT =================
5
+ export async function createProject(name) {
6
+ const base = path.resolve(name);
7
+ flog(`📦 Erstelle neues Projekt: ${name}`);
8
+ await mkdir(base, { recursive: true });
9
+ await mkdir(path.join(base, "game"), { recursive: true });
10
+ await mkdir(path.join(base, "resources"), { recursive: true });
11
+ await mkdir(path.join(base, "dist"), { recursive: true });
12
+ // Basic game entry
13
+ await writeFile(path.join(base, "game", "main.ts"), `
14
+ // A mini Snake Clone with my Webframework
15
+
16
+ import { createCanvas } from "@shadowdara/webgameengine";
17
+ import { setupInput, isKeyJustPressed, resetInput } from "@shadowdara/webgameengine";
18
+ import { startEngine } from "@shadowdara/webgameengine";
19
+ import { renderText } from "@shadowdara/webgameengine";
20
+ import { Vector2d } from "@shadowdara/webgameengine/types";
21
+ import { dlog } from "@shadowdara/webgameengine";
22
+ import { Key } from "@shadowdara/webgameengine";
23
+
24
+ const { canvas, ctx } = createCanvas({fullscreen: true, scaling: "fit", virtualWidth: window.innerWidth, virtualHeight: window.innerHeight});
25
+ setupInput(canvas);
26
+
27
+ let snake: Vector2d[] = [{ x: 10, y: 10 }];
28
+ let dir: Vector2d = { x: 1, y: 0 };
29
+ let food: Vector2d = { x: 15, y: 10 };
30
+ let gridSize = 20;
31
+ let lastMove = 0;
32
+ let speed = 0.2; // seconds per cell
33
+ let start = false;
34
+
35
+ function gameStart() {
36
+ dlog("Snake gestartet");
37
+ }
38
+
39
+ function gameLoop(dt: number) {
40
+ if (isKeyJustPressed(Key.Escape)) {
41
+ start = !start
42
+ }
43
+
44
+ if (!start) {
45
+ ctx.fillStyle = "white";
46
+ ctx.fillRect(0, 0, canvas.width, canvas.height);
47
+
48
+ renderText(ctx, "Snake", 10, 10, "black", "24px Arial");
49
+ renderText(ctx, "Press ESC to start or Pause the Game!", 10, 50, "black", "24px Arial");
50
+
51
+ // return;
52
+ } else {
53
+
54
+ // Input
55
+ if (isKeyJustPressed(Key.ArrowUp) && dir.y === 0) dir = { x: 0, y: -1 };
56
+ if (isKeyJustPressed(Key.ArrowDown) && dir.y === 0) dir = { x: 0, y: 1 };
57
+ if (isKeyJustPressed(Key.ArrowLeft) && dir.x === 0) dir = { x: -1, y: 0 };
58
+ if (isKeyJustPressed(Key.ArrowRight) && dir.x === 0) dir = { x: 1, y: 0 };
59
+
60
+ lastMove += dt;
61
+
62
+ if (lastMove >= speed) {
63
+ lastMove = 0;
64
+ const head = { x: snake[0].x + dir.x, y: snake[0].y + dir.y };
65
+
66
+ // Kollision mit Walls
67
+ if (head.x < 0 || head.y < 0 || head.x >= canvas.width / gridSize || head.y >= canvas.height / gridSize) {
68
+ snake = [{ x: 10, y: 10 }];
69
+ dir = { x: 1, y: 0 };
70
+ dlog("Game Over");
71
+ return;
72
+ }
73
+
74
+ // Kollision mit sich selbst
75
+ if (snake.some(s => s.x === head.x && s.y === head.y)) {
76
+ snake = [{ x: 10, y: 10 }];
77
+ dir = { x: 1, y: 0 };
78
+ dlog(\`Game Over! Highscore: \${snake.length - 1}\`);
79
+ return;
80
+ }
81
+
82
+ snake.unshift(head);
83
+
84
+ // Food Check
85
+ if (head.x === food.x && head.y === food.y) {
86
+ food = { x: Math.floor(Math.random() * (canvas.width / gridSize)), y: Math.floor(Math.random() * (canvas.height / gridSize)) };
87
+ } else {
88
+ snake.pop();
89
+ }
90
+ }
91
+
92
+ // Zeichnen
93
+ ctx.clearRect(0, 0, canvas.width, canvas.height);
94
+
95
+ ctx.fillStyle = "black";
96
+ ctx.fillRect(0, 0, canvas.width, canvas.height);
97
+
98
+ ctx.fillStyle = "green";
99
+ snake.forEach(s => ctx.fillRect(s.x * gridSize, s.y * gridSize, gridSize, gridSize));
100
+
101
+ ctx.fillStyle = "red";
102
+ ctx.fillRect(food.x * gridSize, food.y * gridSize, gridSize, gridSize);
103
+
104
+ renderText(ctx, "Score: " + (snake.length - 1), 10, 10, "yellow", "24px Arial");
105
+
106
+ }
107
+
108
+ // Reset Input
109
+ resetInput();
110
+ }
111
+
112
+ startEngine(gameStart, gameLoop);
113
+ `);
114
+ // Config
115
+ await writeFile(path.join(base, "webgameengine.config.js"), `
116
+ // Project File for the Game
117
+
118
+ import type { buildconfig } from "@shadowdara/webgameengine/build";
119
+ import { new_buildconfig } from "@shadowdara/webgameengine/build";
120
+
121
+ export default function defineConfig(): buildconfig {
122
+ let config: buildconfig = new_buildconfig();
123
+ return config;
124
+ }
125
+ `);
126
+ // package.json
127
+ // await writeFile(
128
+ // path.join(base, "package.json"),
129
+ // JSON.stringify(
130
+ // {
131
+ // name,
132
+ // version: "1.0.0",
133
+ // type: "module",
134
+ // scripts: {
135
+ // dev: "mycli",
136
+ // build: "mycli --release",
137
+ // },
138
+ // },
139
+ // null,
140
+ // 2
141
+ // )
142
+ // );
143
+ flog("✅ Projekt created!");
144
+ // flog(`👉 cd ${name}`);
145
+ // flog(`👉 npm install`);
146
+ // flog(`👉 npm run dev`);
147
+ console.log(`Add to your package.json file:
148
+ "scripts": {
149
+ "dev": "npx webgameengine",
150
+ "build": "npx webgameengine --release"
151
+ },
152
+
153
+ Run "npm run dev" to start the Dev Server and play a little Snake Clone
154
+ `);
155
+ process.exit(0);
156
+ }
@@ -1,4 +1,4 @@
1
- import { type Rect } from "./types/rectangle";
1
+ import { type Rect } from "./types/rectangle.js";
2
2
  export declare function renderText(ctx: CanvasRenderingContext2D, text: string, x: number, y: number, color?: string, font?: string): void;
3
3
  type CharMap = Record<string, Rect>;
4
4
  export declare function renderBitmapText(ctx: CanvasRenderingContext2D, text: string, x: number, y: number, sprite: HTMLImageElement, charMap: CharMap, scale?: number): void;
package/dist/texture.js CHANGED
@@ -1,4 +1,4 @@
1
- import { dlog } from "./logger";
1
+ import { dlog } from "./logger.js";
2
2
  const textures = {};
3
3
  function getresourcepath(path) {
4
4
  return "resources/" + path;
@@ -1,5 +1,5 @@
1
- export type { Vector2d } from "./vector2d.js";
2
- export type { Vector3d } from "./vector3d.js";
3
- export type { Color } from "./color.js";
4
- export type { Rect } from "./rectangle.js";
5
- export { isRectClicked } from "./rectangle.js";
1
+ export { type Vector2d, normalize2d, clamp2d, lerp2d, map2d } from "./vector2d.js";
2
+ export { type Vector3d, normalize3d, clamp3d, lerp3d, map3d } from "./vector3d.js";
3
+ export { type Color, invertcolor, invertHexColor } from "./color.js";
4
+ export { type Rect, centerRectX, centerRectY, centerRect, isPointInRect, isMouseInRect, isRectClicked } from "./rectangle.js";
5
+ export { clamp, lerp, map } from "./math-utils.js";
@@ -1,4 +1,11 @@
1
1
  // Types Export
2
- export { isRectClicked } from "./rectangle.js";
2
+ // Vector 2D
3
+ export { normalize2d, clamp2d, lerp2d, map2d } from "./vector2d.js";
4
+ // Vector 3D
5
+ export { normalize3d, clamp3d, lerp3d, map3d } from "./vector3d.js";
6
+ // Color Type
7
+ export { invertcolor, invertHexColor } from "./color.js";
8
+ // Retangle Type
9
+ export { centerRectX, centerRectY, centerRect, isPointInRect, isMouseInRect, isRectClicked } from "./rectangle.js";
3
10
  // Math Utilities
4
- // export * from "./math-utils.js";
11
+ export { clamp, lerp, map } from "./math-utils.js";
@@ -1,5 +1,5 @@
1
- import { Mouse } from "../input";
2
- import { type Vector2d } from "./vector2d";
1
+ import { Mouse } from "../input.js";
2
+ import { type Vector2d } from "./vector2d.js";
3
3
  export type Rect = {
4
4
  x: number;
5
5
  y: number;
@@ -5,3 +5,4 @@ export type Vector2d = {
5
5
  export declare function normalize2d(vector: Vector2d): Vector2d;
6
6
  export declare function clamp2d(vector: Vector2d, min: Vector2d, max: Vector2d): Vector2d;
7
7
  export declare function lerp2d(start: Vector2d, end: Vector2d, t: Vector2d): Vector2d;
8
+ export declare function map2d(value: Vector2d, inMin: Vector2d, inMax: Vector2d, outMin: Vector2d, outMax: Vector2d): Vector2d;
@@ -1,5 +1,5 @@
1
1
  // 2 Dimensional Vector Type
2
- import { clamp, lerp } from "./math-utils";
2
+ import { clamp, lerp, map } from "./math-utils.js";
3
3
  // Function to normalize a Vector 2d
4
4
  export function normalize2d(vector) {
5
5
  // Check if the Vector is zero because then you dont need to
@@ -27,3 +27,10 @@ export function lerp2d(start, end, t) {
27
27
  y: lerp(start.y, end.y, t.y),
28
28
  };
29
29
  }
30
+ // Map Function for a 2d Vector
31
+ export function map2d(value, inMin, inMax, outMin, outMax) {
32
+ return {
33
+ x: map(value.x, inMin.x, inMax.x, outMin.x, outMax.x),
34
+ y: map(value.y, inMin.y, inMax.y, outMin.y, outMax.y),
35
+ };
36
+ }
@@ -6,3 +6,4 @@ export type Vector3d = {
6
6
  export declare function normalize3d(vector: Vector3d): Vector3d;
7
7
  export declare function clamp3d(vector: Vector3d, min: Vector3d, max: Vector3d): Vector3d;
8
8
  export declare function lerp3d(start: Vector3d, end: Vector3d, t: Vector3d): Vector3d;
9
+ export declare function map3d(value: Vector3d, inMin: Vector3d, inMax: Vector3d, outMin: Vector3d, outMax: Vector3d): Vector3d;
@@ -1,5 +1,5 @@
1
1
  // 3d Vector
2
- import { clamp, lerp } from "./math-utils";
2
+ import { clamp, lerp, map } from "./math-utils.js";
3
3
  // Function to normalize a Vector 2d
4
4
  export function normalize3d(vector) {
5
5
  // Check if the Vector is zero because then you dont need to
@@ -14,7 +14,7 @@ export function normalize3d(vector) {
14
14
  vector.z = vector.z / root;
15
15
  return vector;
16
16
  }
17
- // Function to clamp a Vector 2d
17
+ // Function to clamp a Vector 3d
18
18
  export function clamp3d(vector, min, max) {
19
19
  return {
20
20
  x: clamp(vector.x, min.x, max.x),
@@ -22,7 +22,7 @@ export function clamp3d(vector, min, max) {
22
22
  z: clamp(vector.y, min.y, max.y),
23
23
  };
24
24
  }
25
- // Lerp for a 2d Vector
25
+ // Lerp for a 3d Vector
26
26
  export function lerp3d(start, end, t) {
27
27
  return {
28
28
  x: lerp(start.x, end.x, t.x),
@@ -30,3 +30,11 @@ export function lerp3d(start, end, t) {
30
30
  z: lerp(start.y, end.y, t.y),
31
31
  };
32
32
  }
33
+ // Map Function for a 3d Vector
34
+ export function map3d(value, inMin, inMax, outMin, outMax) {
35
+ return {
36
+ x: map(value.x, inMin.x, inMax.x, outMin.x, outMax.x),
37
+ y: map(value.y, inMin.y, inMax.y, outMin.y, outMax.y),
38
+ z: map(value.z, inMin.z, inMax.z, outMin.z, outMax.z),
39
+ };
40
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@shadowdara/webgameengine",
3
- "version": "1.0.0",
3
+ "version": "1.2.0",
4
4
  "description": "A TypeScript game library to make HTML Games",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",