@shadowdara/webgameengine 1.2.0 → 1.3.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 +1 -0
- package/dist/build/cli/cli.js +166 -0
- package/dist/build/{tools/devserver → cli}/config.d.ts +1 -1
- package/dist/build/{tools/devserver → cli}/config.js +1 -1
- package/dist/build/cli/new.d.ts +1 -0
- package/dist/build/{tools/devserver → cli}/new.js +43 -7
- package/dist/index.d.ts +4 -3
- package/dist/index.js +2 -4
- package/dist/input.d.ts +2 -0
- package/dist/input.js +7 -0
- package/dist/renderer.d.ts +8 -0
- package/dist/renderer.js +48 -0
- package/dist/texture.d.ts +26 -1
- package/dist/texture.js +123 -5
- package/dist/types/circle.d.ts +13 -0
- package/dist/types/circle.js +30 -0
- package/dist/types/index.d.ts +4 -2
- package/dist/types/index.js +6 -2
- package/dist/types/triangle.d.ts +15 -0
- package/dist/types/triangle.js +38 -0
- package/dist/types/vector2d.d.ts +5 -0
- package/dist/types/vector2d.js +31 -2
- package/dist/types/vector3d.d.ts +6 -0
- package/dist/types/vector3d.js +40 -2
- package/package.json +3 -3
- package/dist/build/tools/devserver/cli.js +0 -143
- package/dist/build/tools/devserver/new.d.ts +0 -1
- /package/dist/build/{tools/devserver → cli}/cli.d.ts +0 -0
package/README.md
CHANGED
|
@@ -54,6 +54,7 @@ startEngine(init, gameLoop);
|
|
|
54
54
|
npx webgameengine # Start Dev Server
|
|
55
55
|
npx webgameengine --release # Production build
|
|
56
56
|
npx webgameengine --new # Create a new project with a simple Snake Clone as Template
|
|
57
|
+
npx webgameengine --new-empty # Create a new empty project
|
|
57
58
|
```
|
|
58
59
|
|
|
59
60
|
### Configuration
|
|
@@ -0,0 +1,166 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { build as esbuild } from "esbuild";
|
|
3
|
+
import { createServer } from "http";
|
|
4
|
+
import { readFile, writeFile, mkdir } from "fs/promises";
|
|
5
|
+
import { watch } from "fs";
|
|
6
|
+
import path from "path";
|
|
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
|
+
import { loadUserConfig } from "./config.js";
|
|
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
|
+
empty: false,
|
|
20
|
+
};
|
|
21
|
+
for (let i = 0; i < args.length; i++) {
|
|
22
|
+
const arg = args[i];
|
|
23
|
+
switch (arg) {
|
|
24
|
+
case "--release":
|
|
25
|
+
case "-r":
|
|
26
|
+
options.release = true;
|
|
27
|
+
break;
|
|
28
|
+
case "--port":
|
|
29
|
+
case "-p":
|
|
30
|
+
options.port = Number(args[i + 1]);
|
|
31
|
+
i++;
|
|
32
|
+
break;
|
|
33
|
+
case "--new":
|
|
34
|
+
case "-n":
|
|
35
|
+
options.newProject = args[i + 1];
|
|
36
|
+
i++;
|
|
37
|
+
break;
|
|
38
|
+
case "--new-empty":
|
|
39
|
+
options.empty = true;
|
|
40
|
+
break;
|
|
41
|
+
case "-h":
|
|
42
|
+
case "--help":
|
|
43
|
+
// TODO
|
|
44
|
+
// make the help Message here
|
|
45
|
+
console.log("Cli Tools for Webgameengine");
|
|
46
|
+
default:
|
|
47
|
+
console.warn(`⚠️ Unknown Argument: ${arg}`);
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
return options;
|
|
51
|
+
}
|
|
52
|
+
// ================= APP FACTORY =================
|
|
53
|
+
function createCLIApp(args, config) {
|
|
54
|
+
const isRelease = args.release;
|
|
55
|
+
const isDev = !isRelease;
|
|
56
|
+
// 👉 State kapseln statt global
|
|
57
|
+
let isBuilding = false;
|
|
58
|
+
let restarting = false;
|
|
59
|
+
let pendingRestart = false;
|
|
60
|
+
const sockets = new Set();
|
|
61
|
+
// ================= BUILD =================
|
|
62
|
+
async function build() {
|
|
63
|
+
if (isBuilding)
|
|
64
|
+
return;
|
|
65
|
+
isBuilding = true;
|
|
66
|
+
flog("🔄 Building new ...");
|
|
67
|
+
await esbuild({
|
|
68
|
+
entryPoints: [`./game/${config.entryname}`],
|
|
69
|
+
outdir: `./${config.outdir}`,
|
|
70
|
+
bundle: true,
|
|
71
|
+
platform: "browser",
|
|
72
|
+
minify: isRelease,
|
|
73
|
+
sourcemap: isDev,
|
|
74
|
+
define: {
|
|
75
|
+
"import.meta.env.DEV": JSON.stringify(isDev),
|
|
76
|
+
},
|
|
77
|
+
});
|
|
78
|
+
const html = GetDefaultHTML(config);
|
|
79
|
+
await writeFile("./dist/index.html", html);
|
|
80
|
+
await copyFolder("./resources", "./dist/resources");
|
|
81
|
+
flog("✅ Build finished!");
|
|
82
|
+
isBuilding = false;
|
|
83
|
+
}
|
|
84
|
+
// ================= SERVER =================
|
|
85
|
+
function startServer() {
|
|
86
|
+
const server = createServer(async (req, res) => {
|
|
87
|
+
const url = req.url || "/";
|
|
88
|
+
const filePath = url === "/" ? "/index.html" : url;
|
|
89
|
+
try {
|
|
90
|
+
const fullPath = path.join(process.cwd(), "dist", filePath);
|
|
91
|
+
const file = await readFile(fullPath);
|
|
92
|
+
res.writeHead(200, {
|
|
93
|
+
"Content-Type": getContentType(filePath),
|
|
94
|
+
});
|
|
95
|
+
res.end(file);
|
|
96
|
+
}
|
|
97
|
+
catch {
|
|
98
|
+
res.writeHead(404);
|
|
99
|
+
res.end("Not Found");
|
|
100
|
+
}
|
|
101
|
+
});
|
|
102
|
+
const wss = new WebSocketServer({ server });
|
|
103
|
+
wss.on("connection", (ws) => {
|
|
104
|
+
sockets.add(ws);
|
|
105
|
+
ws.on("close", () => sockets.delete(ws));
|
|
106
|
+
});
|
|
107
|
+
server.listen(args.port, () => {
|
|
108
|
+
flog(`🚀 Dev Server is running on http://localhost:${args.port}`);
|
|
109
|
+
});
|
|
110
|
+
return server;
|
|
111
|
+
}
|
|
112
|
+
// ================= RELOAD =================
|
|
113
|
+
function reloadClients() {
|
|
114
|
+
flog("🔄 Browser reload...");
|
|
115
|
+
for (const ws of sockets) {
|
|
116
|
+
ws.send("reload");
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
// ================= RESTART =================
|
|
120
|
+
async function restart() {
|
|
121
|
+
if (restarting) {
|
|
122
|
+
pendingRestart = true;
|
|
123
|
+
return;
|
|
124
|
+
}
|
|
125
|
+
restarting = true;
|
|
126
|
+
do {
|
|
127
|
+
pendingRestart = false;
|
|
128
|
+
flog("♻️ Restart...");
|
|
129
|
+
await build();
|
|
130
|
+
reloadClients();
|
|
131
|
+
} while (pendingRestart);
|
|
132
|
+
restarting = false;
|
|
133
|
+
}
|
|
134
|
+
// ================= WATCH =================
|
|
135
|
+
function startWatcher() {
|
|
136
|
+
mkdir("resources", { recursive: true });
|
|
137
|
+
mkdir("game", { recursive: true });
|
|
138
|
+
["resources", "game"].forEach((dir) => {
|
|
139
|
+
watch(dir, { recursive: true }, async () => {
|
|
140
|
+
flog(`📁 Change noticed in ${dir}`);
|
|
141
|
+
await restart();
|
|
142
|
+
});
|
|
143
|
+
});
|
|
144
|
+
flog("👀 Watcher active ...");
|
|
145
|
+
}
|
|
146
|
+
// ================= RUN =================
|
|
147
|
+
async function run() {
|
|
148
|
+
await build();
|
|
149
|
+
if (isDev) {
|
|
150
|
+
startServer();
|
|
151
|
+
startWatcher();
|
|
152
|
+
}
|
|
153
|
+
flog(`Build finished! Mode: ${isRelease ? "Release" : "Dev"}`);
|
|
154
|
+
}
|
|
155
|
+
return { run };
|
|
156
|
+
}
|
|
157
|
+
// ================= MAIN =================
|
|
158
|
+
const args = parseArgs();
|
|
159
|
+
// Create a bew Project
|
|
160
|
+
if (args.newProject) {
|
|
161
|
+
await createProject(args.newProject, args.empty);
|
|
162
|
+
process.exit(0);
|
|
163
|
+
}
|
|
164
|
+
const config = await loadUserConfig();
|
|
165
|
+
const app = createCLIApp(args, config);
|
|
166
|
+
await app.run();
|
|
@@ -13,6 +13,6 @@ export async function loadUserConfig() {
|
|
|
13
13
|
}
|
|
14
14
|
catch (e) {
|
|
15
15
|
console.error(e);
|
|
16
|
-
throw new Error("❌
|
|
16
|
+
throw new Error("❌ Could not find 'webgameengine.config.ts - PATH: " + configPath);
|
|
17
17
|
}
|
|
18
18
|
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function createProject(name: string, empty: boolean): Promise<void>;
|
|
@@ -1,16 +1,48 @@
|
|
|
1
1
|
import path from "path";
|
|
2
2
|
import { writeFile, mkdir } from "fs/promises";
|
|
3
|
-
import { flog } from "
|
|
3
|
+
import { flog } from "../buildhelper.js";
|
|
4
4
|
// ================= NEW PROJECT =================
|
|
5
|
-
export async function createProject(name) {
|
|
5
|
+
export async function createProject(name, empty) {
|
|
6
6
|
const base = path.resolve(name);
|
|
7
7
|
flog(`📦 Erstelle neues Projekt: ${name}`);
|
|
8
8
|
await mkdir(base, { recursive: true });
|
|
9
9
|
await mkdir(path.join(base, "game"), { recursive: true });
|
|
10
10
|
await mkdir(path.join(base, "resources"), { recursive: true });
|
|
11
11
|
await mkdir(path.join(base, "dist"), { recursive: true });
|
|
12
|
-
|
|
13
|
-
|
|
12
|
+
let content = `
|
|
13
|
+
// A empty Project with the Web Framework
|
|
14
|
+
|
|
15
|
+
import { createCanvas, enableFullscreen, setupFullscreenButton } from "@shadowdara/webgameengine";
|
|
16
|
+
import { setupInput, resetInput, getMouse } from "@shadowdara/webgameengine";
|
|
17
|
+
import { startEngine } from "@shadowdara/webgameengine";
|
|
18
|
+
|
|
19
|
+
const { canvas, ctx, applyScaling } = createCanvas({fullscreen: true, scaling: "fit", virtualWidth: window.innerWidth, virtualHeight: window.innerHeight});
|
|
20
|
+
setupInput(canvas);
|
|
21
|
+
|
|
22
|
+
enableFullscreen(canvas);
|
|
23
|
+
setupFullscreenButton(canvas);
|
|
24
|
+
|
|
25
|
+
async function gameStart() {
|
|
26
|
+
// Code which runs at the Game Start
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
function gameLoop(dt: number) {
|
|
30
|
+
// Code which runs every Frame
|
|
31
|
+
|
|
32
|
+
ctx.clearRect(0, 0, canvas.width, canvas.height);
|
|
33
|
+
|
|
34
|
+
const mouse = getMouse();
|
|
35
|
+
|
|
36
|
+
applyScaling();
|
|
37
|
+
|
|
38
|
+
resetInput();
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
// Because start Game is Async
|
|
42
|
+
startEngine(() => { gameStart().then(() => {/* ready */}) }, gameLoop);
|
|
43
|
+
`;
|
|
44
|
+
if (empty) {
|
|
45
|
+
content = `
|
|
14
46
|
// A mini Snake Clone with my Webframework
|
|
15
47
|
|
|
16
48
|
import { createCanvas } from "@shadowdara/webgameengine";
|
|
@@ -32,7 +64,7 @@ let lastMove = 0;
|
|
|
32
64
|
let speed = 0.2; // seconds per cell
|
|
33
65
|
let start = false;
|
|
34
66
|
|
|
35
|
-
function gameStart() {
|
|
67
|
+
async function gameStart() {
|
|
36
68
|
dlog("Snake gestartet");
|
|
37
69
|
}
|
|
38
70
|
|
|
@@ -109,8 +141,12 @@ function gameLoop(dt: number) {
|
|
|
109
141
|
resetInput();
|
|
110
142
|
}
|
|
111
143
|
|
|
112
|
-
|
|
113
|
-
|
|
144
|
+
// Because start Game is Async
|
|
145
|
+
startEngine(() => { gameStart().then(() => {/* ready */}) }, gameLoop);
|
|
146
|
+
`;
|
|
147
|
+
}
|
|
148
|
+
// Basic game entry
|
|
149
|
+
await writeFile(path.join(base, "game", "main.ts"), content);
|
|
114
150
|
// Config
|
|
115
151
|
await writeFile(path.join(base, "webgameengine.config.js"), `
|
|
116
152
|
// Project File for the Game
|
package/dist/index.d.ts
CHANGED
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
export { startEngine } from "./core.js";
|
|
2
|
-
export { renderText, renderBitmapText } from "./renderer.js";
|
|
3
|
-
export { setupInput, isKeyJustPressed, resetInput, getMouse } from "./input.js";
|
|
2
|
+
export { renderText, renderBitmapText, drawRect, drawRectOutline, drawCircle, drawCircleOutline, drawTriangle, drawTriangleOutline, } from "./renderer.js";
|
|
4
3
|
export type { Mouse } from "./input.js";
|
|
4
|
+
export { setupInput, isKeyJustPressed, resetInput, getMouse } from "./input.js";
|
|
5
5
|
export { dlog } from "./logger.js";
|
|
6
6
|
export { saveGame, loadGame, clearSave } from "./save.js";
|
|
7
|
-
export {
|
|
7
|
+
export type { Texture, TextureAtlas, Animation, } from "./texture.js";
|
|
8
|
+
export { loadTextureAsync, getTexture, drawTexture, loadAtlas, drawAtlasFrame, AnimationPlayer, drawAnimation, getFlipFromDirection, } from "./texture.js";
|
|
8
9
|
export { createCanvas, enableFullscreen, setupFullscreenButton } from "./html.js";
|
|
9
10
|
export { Key } from "./keys.js";
|
package/dist/index.js
CHANGED
|
@@ -1,15 +1,13 @@
|
|
|
1
1
|
// Core Engine Exports
|
|
2
2
|
export { startEngine } from "./core.js";
|
|
3
3
|
// Rendering
|
|
4
|
-
export { renderText, renderBitmapText } from "./renderer.js";
|
|
5
|
-
// Input System
|
|
4
|
+
export { renderText, renderBitmapText, drawRect, drawRectOutline, drawCircle, drawCircleOutline, drawTriangle, drawTriangleOutline, } from "./renderer.js";
|
|
6
5
|
export { setupInput, isKeyJustPressed, resetInput, getMouse } from "./input.js";
|
|
7
6
|
// Logging
|
|
8
7
|
export { dlog } from "./logger.js";
|
|
9
8
|
// Save System
|
|
10
9
|
export { saveGame, loadGame, clearSave } from "./save.js";
|
|
11
|
-
|
|
12
|
-
export { drawTexture, getTexture, loadTextureAsync } from "./texture.js";
|
|
10
|
+
export { loadTextureAsync, getTexture, drawTexture, loadAtlas, drawAtlasFrame, AnimationPlayer, drawAnimation, getFlipFromDirection, } from "./texture.js";
|
|
13
11
|
// HTML Generation
|
|
14
12
|
export { createCanvas, enableFullscreen, setupFullscreenButton } from "./html.js";
|
|
15
13
|
// Keys Reference
|
package/dist/input.d.ts
CHANGED
|
@@ -5,6 +5,8 @@ export type Mouse = {
|
|
|
5
5
|
justPressed: boolean;
|
|
6
6
|
justReleased: boolean;
|
|
7
7
|
rightPressed: boolean;
|
|
8
|
+
rightjustPressed: boolean;
|
|
9
|
+
rightjustReleased: boolean;
|
|
8
10
|
wheelDelta: number;
|
|
9
11
|
};
|
|
10
12
|
export declare function setupInput(canvas: HTMLCanvasElement, vWidth?: number, vHeight?: number): void;
|
package/dist/input.js
CHANGED
|
@@ -9,6 +9,8 @@ const mouse = {
|
|
|
9
9
|
// TODO
|
|
10
10
|
// do the same for the right Buttons
|
|
11
11
|
rightPressed: false,
|
|
12
|
+
rightjustPressed: false,
|
|
13
|
+
rightjustReleased: false,
|
|
12
14
|
wheelDelta: 0,
|
|
13
15
|
};
|
|
14
16
|
let canvasRef;
|
|
@@ -45,6 +47,8 @@ export function setupInput(canvas, vWidth = 800, vHeight = 800) {
|
|
|
45
47
|
mouse.pressed = true;
|
|
46
48
|
}
|
|
47
49
|
if (e.button === 2) {
|
|
50
|
+
if (!mouse.rightPressed)
|
|
51
|
+
mouse.rightjustPressed = true;
|
|
48
52
|
mouse.rightPressed = true;
|
|
49
53
|
}
|
|
50
54
|
});
|
|
@@ -55,6 +59,7 @@ export function setupInput(canvas, vWidth = 800, vHeight = 800) {
|
|
|
55
59
|
}
|
|
56
60
|
if (e.button === 2) {
|
|
57
61
|
mouse.rightPressed = false;
|
|
62
|
+
mouse.rightjustReleased = false;
|
|
58
63
|
}
|
|
59
64
|
});
|
|
60
65
|
// 👉 wichtig: verhindert context menu bei right click
|
|
@@ -100,5 +105,7 @@ export function resetInput() {
|
|
|
100
105
|
}
|
|
101
106
|
mouse.justPressed = false;
|
|
102
107
|
mouse.justReleased = false;
|
|
108
|
+
mouse.rightjustPressed = false;
|
|
109
|
+
mouse.rightjustReleased = false;
|
|
103
110
|
mouse.wheelDelta = 0;
|
|
104
111
|
}
|
package/dist/renderer.d.ts
CHANGED
|
@@ -1,5 +1,13 @@
|
|
|
1
1
|
import { type Rect } from "./types/rectangle.js";
|
|
2
|
+
import { type Circle } from "./types/circle.js";
|
|
3
|
+
import { type Triangle } from "./types/triangle.js";
|
|
2
4
|
export declare function renderText(ctx: CanvasRenderingContext2D, text: string, x: number, y: number, color?: string, font?: string): void;
|
|
3
5
|
type CharMap = Record<string, Rect>;
|
|
4
6
|
export declare function renderBitmapText(ctx: CanvasRenderingContext2D, text: string, x: number, y: number, sprite: HTMLImageElement, charMap: CharMap, scale?: number): void;
|
|
7
|
+
export declare function drawRect(ctx: CanvasRenderingContext2D, rect: Rect, color?: string): void;
|
|
8
|
+
export declare function drawRectOutline(ctx: CanvasRenderingContext2D, rect: Rect, color?: string, lineWidth?: number): void;
|
|
9
|
+
export declare function drawCircle(ctx: CanvasRenderingContext2D, circle: Circle, color?: string): void;
|
|
10
|
+
export declare function drawCircleOutline(ctx: CanvasRenderingContext2D, circle: Circle, color?: string, lineWidth?: number): void;
|
|
11
|
+
export declare function drawTriangle(ctx: CanvasRenderingContext2D, triangle: Triangle, color?: string): void;
|
|
12
|
+
export declare function drawTriangleOutline(ctx: CanvasRenderingContext2D, triangle: Triangle, color?: string, lineWidth?: number): void;
|
|
5
13
|
export {};
|
package/dist/renderer.js
CHANGED
|
@@ -15,3 +15,51 @@ export function renderBitmapText(ctx, text, x, y, sprite, charMap, scale = 1) {
|
|
|
15
15
|
offsetX += rect.width * scale;
|
|
16
16
|
}
|
|
17
17
|
}
|
|
18
|
+
// ===== SHAPE DRAWING =====
|
|
19
|
+
// Function to draw a filled Rectangle
|
|
20
|
+
export function drawRect(ctx, rect, color = "white") {
|
|
21
|
+
ctx.fillStyle = color;
|
|
22
|
+
ctx.fillRect(rect.x, rect.y, rect.width, rect.height);
|
|
23
|
+
}
|
|
24
|
+
// Function to draw a Rectangle outline
|
|
25
|
+
export function drawRectOutline(ctx, rect, color = "white", lineWidth = 1) {
|
|
26
|
+
ctx.strokeStyle = color;
|
|
27
|
+
ctx.lineWidth = lineWidth;
|
|
28
|
+
ctx.strokeRect(rect.x, rect.y, rect.width, rect.height);
|
|
29
|
+
}
|
|
30
|
+
// Function to draw a filled Circle
|
|
31
|
+
export function drawCircle(ctx, circle, color = "white") {
|
|
32
|
+
ctx.fillStyle = color;
|
|
33
|
+
ctx.beginPath();
|
|
34
|
+
ctx.arc(circle.x, circle.y, circle.radius, 0, Math.PI * 2);
|
|
35
|
+
ctx.fill();
|
|
36
|
+
}
|
|
37
|
+
// Function to draw a Circle outline
|
|
38
|
+
export function drawCircleOutline(ctx, circle, color = "white", lineWidth = 1) {
|
|
39
|
+
ctx.strokeStyle = color;
|
|
40
|
+
ctx.lineWidth = lineWidth;
|
|
41
|
+
ctx.beginPath();
|
|
42
|
+
ctx.arc(circle.x, circle.y, circle.radius, 0, Math.PI * 2);
|
|
43
|
+
ctx.stroke();
|
|
44
|
+
}
|
|
45
|
+
// Function to draw a filled Triangle
|
|
46
|
+
export function drawTriangle(ctx, triangle, color = "white") {
|
|
47
|
+
ctx.fillStyle = color;
|
|
48
|
+
ctx.beginPath();
|
|
49
|
+
ctx.moveTo(triangle.x1, triangle.y1);
|
|
50
|
+
ctx.lineTo(triangle.x2, triangle.y2);
|
|
51
|
+
ctx.lineTo(triangle.x3, triangle.y3);
|
|
52
|
+
ctx.closePath();
|
|
53
|
+
ctx.fill();
|
|
54
|
+
}
|
|
55
|
+
// Function to draw a Triangle outline
|
|
56
|
+
export function drawTriangleOutline(ctx, triangle, color = "white", lineWidth = 1) {
|
|
57
|
+
ctx.strokeStyle = color;
|
|
58
|
+
ctx.lineWidth = lineWidth;
|
|
59
|
+
ctx.beginPath();
|
|
60
|
+
ctx.moveTo(triangle.x1, triangle.y1);
|
|
61
|
+
ctx.lineTo(triangle.x2, triangle.y2);
|
|
62
|
+
ctx.lineTo(triangle.x3, triangle.y3);
|
|
63
|
+
ctx.closePath();
|
|
64
|
+
ctx.stroke();
|
|
65
|
+
}
|
package/dist/texture.d.ts
CHANGED
|
@@ -1,4 +1,29 @@
|
|
|
1
|
+
import { Rect } from "./types/rectangle.js";
|
|
1
2
|
export type Texture = HTMLImageElement | undefined;
|
|
2
3
|
export declare function loadTextureAsync(src: string): Promise<HTMLImageElement>;
|
|
3
4
|
export declare function getTexture(src: string): Texture;
|
|
4
|
-
export declare function drawTexture(ctx: CanvasRenderingContext2D, texture: Texture, x: number, y: number, width?: number, height?: number): void;
|
|
5
|
+
export declare function drawTexture(ctx: CanvasRenderingContext2D, texture: Texture, x: number, y: number, width?: number, height?: number, rotation?: number, flipX?: boolean, flipY?: boolean): void;
|
|
6
|
+
export type TextureAtlas = {
|
|
7
|
+
image: HTMLImageElement;
|
|
8
|
+
frames: Record<string, Rect>;
|
|
9
|
+
};
|
|
10
|
+
export declare function loadAtlas(imageSrc: string, dataSrc: string): Promise<TextureAtlas>;
|
|
11
|
+
export declare function drawAtlasFrame(ctx: CanvasRenderingContext2D, atlas: TextureAtlas, frameName: string, x: number, y: number, width?: number, height?: number, rotation?: number, flipX?: boolean, flipY?: boolean): void;
|
|
12
|
+
export declare class AnimationPlayer {
|
|
13
|
+
animation: Animation;
|
|
14
|
+
private time;
|
|
15
|
+
private currentFrameIndex;
|
|
16
|
+
private finished;
|
|
17
|
+
constructor(animation: Animation);
|
|
18
|
+
update(deltaTime: number): void;
|
|
19
|
+
getCurrentFrame(): string;
|
|
20
|
+
reset(): void;
|
|
21
|
+
isFinished(): boolean;
|
|
22
|
+
}
|
|
23
|
+
export type Animation = {
|
|
24
|
+
frames: string[];
|
|
25
|
+
fps: number;
|
|
26
|
+
loop?: boolean;
|
|
27
|
+
};
|
|
28
|
+
export declare function drawAnimation(ctx: CanvasRenderingContext2D, atlas: TextureAtlas, player: AnimationPlayer, x: number, y: number, width?: number, height?: number, rotation?: number, flipX?: boolean, flipY?: boolean): void;
|
|
29
|
+
export declare function getFlipFromDirection(dir: number): boolean;
|
package/dist/texture.js
CHANGED
|
@@ -27,20 +27,138 @@ export function loadTextureAsync(src) {
|
|
|
27
27
|
img.src = finalSrc;
|
|
28
28
|
});
|
|
29
29
|
}
|
|
30
|
+
// Function to get the Texture
|
|
30
31
|
export function getTexture(src) {
|
|
31
32
|
return textures[getresourcepath(src)];
|
|
32
33
|
}
|
|
33
|
-
|
|
34
|
+
// Function to draw a texture to the Screen
|
|
35
|
+
export function drawTexture(ctx, texture, x, y, width, height, rotation = 0, flipX = false, flipY = false) {
|
|
34
36
|
if (!texture) {
|
|
35
37
|
dlog("Texture not found");
|
|
36
38
|
ctx.fillStyle = "magenta";
|
|
37
39
|
ctx.fillRect(x, y, width ?? 32, height ?? 32);
|
|
38
40
|
return;
|
|
39
41
|
}
|
|
40
|
-
|
|
41
|
-
|
|
42
|
+
const w = width ?? texture.width;
|
|
43
|
+
const h = height ?? texture.height;
|
|
44
|
+
ctx.save();
|
|
45
|
+
const cx = x + w / 2;
|
|
46
|
+
const cy = y + h / 2;
|
|
47
|
+
ctx.translate(cx, cy);
|
|
48
|
+
ctx.rotate(rotation);
|
|
49
|
+
// 🔹 Flip anwenden
|
|
50
|
+
ctx.scale(flipX ? -1 : 1, flipY ? -1 : 1);
|
|
51
|
+
ctx.drawImage(texture, -w / 2, -h / 2, w, h);
|
|
52
|
+
ctx.restore();
|
|
53
|
+
}
|
|
54
|
+
// Funtion to load an Atlas
|
|
55
|
+
//
|
|
56
|
+
// JSON Format for the LoadAtlas Function
|
|
57
|
+
//
|
|
58
|
+
// {
|
|
59
|
+
// "frames": {
|
|
60
|
+
// "player_idle_0": { "x": 0, "y": 0, "w": 32, "h": 32 },
|
|
61
|
+
// "player_idle_1": { "x": 32, "y": 0, "w": 32, "h": 32 }
|
|
62
|
+
// }
|
|
63
|
+
// }
|
|
64
|
+
//
|
|
65
|
+
export async function loadAtlas(imageSrc, dataSrc) {
|
|
66
|
+
const [image, data] = await Promise.all([
|
|
67
|
+
loadTextureAsync(imageSrc),
|
|
68
|
+
fetch(getresourcepath(dataSrc)).then(r => r.json())
|
|
69
|
+
]);
|
|
70
|
+
return {
|
|
71
|
+
image,
|
|
72
|
+
frames: data.frames
|
|
73
|
+
};
|
|
74
|
+
}
|
|
75
|
+
export function drawAtlasFrame(ctx, atlas, frameName, x, y, width, height, rotation = 0, flipX = false, flipY = false) {
|
|
76
|
+
const frame = atlas.frames[frameName];
|
|
77
|
+
if (!frame) {
|
|
78
|
+
dlog(`Frame not found: ${frameName}`);
|
|
79
|
+
return;
|
|
80
|
+
}
|
|
81
|
+
const w = width ?? frame.width;
|
|
82
|
+
const h = height ?? frame.height;
|
|
83
|
+
ctx.save();
|
|
84
|
+
const cx = x + w / 2;
|
|
85
|
+
const cy = y + h / 2;
|
|
86
|
+
ctx.translate(cx, cy);
|
|
87
|
+
ctx.rotate(rotation);
|
|
88
|
+
// 🔹 Flip
|
|
89
|
+
ctx.scale(flipX ? -1 : 1, flipY ? -1 : 1);
|
|
90
|
+
ctx.drawImage(atlas.image, frame.x, frame.y, frame.width, frame.height, -w / 2, -h / 2, w, h);
|
|
91
|
+
ctx.restore();
|
|
92
|
+
}
|
|
93
|
+
//
|
|
94
|
+
//
|
|
95
|
+
// Animation Player
|
|
96
|
+
//
|
|
97
|
+
//
|
|
98
|
+
//
|
|
99
|
+
// // Define Animation
|
|
100
|
+
// const walkAnimation: Animation = {
|
|
101
|
+
// frames: ["walk_0", "walk_1", "walk_2", "walk_3"],
|
|
102
|
+
// fps: 8,
|
|
103
|
+
// loop: true
|
|
104
|
+
// };
|
|
105
|
+
//
|
|
106
|
+
// const player = new AnimationPlayer(walkAnimation);
|
|
107
|
+
//
|
|
108
|
+
// // Game Loop
|
|
109
|
+
// function update(dt: number) {
|
|
110
|
+
// player.update(dt);
|
|
111
|
+
// }
|
|
112
|
+
//
|
|
113
|
+
// function render(ctx: CanvasRenderingContext2D) {
|
|
114
|
+
// drawAnimation(ctx, atlas, player, 100, 100, 64, 64);
|
|
115
|
+
// }
|
|
116
|
+
//
|
|
117
|
+
//
|
|
118
|
+
// Animation Player Class
|
|
119
|
+
export class AnimationPlayer {
|
|
120
|
+
// Konstruktor Function
|
|
121
|
+
constructor(animation) {
|
|
122
|
+
this.animation = animation;
|
|
123
|
+
this.time = 0;
|
|
124
|
+
this.currentFrameIndex = 0;
|
|
125
|
+
this.finished = false;
|
|
42
126
|
}
|
|
43
|
-
|
|
44
|
-
|
|
127
|
+
update(deltaTime) {
|
|
128
|
+
if (this.finished)
|
|
129
|
+
return;
|
|
130
|
+
this.time += deltaTime;
|
|
131
|
+
const frameDuration = 1 / this.animation.fps;
|
|
132
|
+
while (this.time >= frameDuration) {
|
|
133
|
+
this.time -= frameDuration;
|
|
134
|
+
this.currentFrameIndex++;
|
|
135
|
+
if (this.currentFrameIndex >= this.animation.frames.length) {
|
|
136
|
+
if (this.animation.loop) {
|
|
137
|
+
this.currentFrameIndex = 0;
|
|
138
|
+
}
|
|
139
|
+
else {
|
|
140
|
+
this.currentFrameIndex = this.animation.frames.length - 1;
|
|
141
|
+
this.finished = true;
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
}
|
|
45
145
|
}
|
|
146
|
+
getCurrentFrame() {
|
|
147
|
+
return this.animation.frames[this.currentFrameIndex];
|
|
148
|
+
}
|
|
149
|
+
reset() {
|
|
150
|
+
this.time = 0;
|
|
151
|
+
this.currentFrameIndex = 0;
|
|
152
|
+
this.finished = false;
|
|
153
|
+
}
|
|
154
|
+
isFinished() {
|
|
155
|
+
return this.finished;
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
export function drawAnimation(ctx, atlas, player, x, y, width, height, rotation = 0, flipX = false, flipY = false) {
|
|
159
|
+
const frame = player.getCurrentFrame();
|
|
160
|
+
drawAtlasFrame(ctx, atlas, frame, x, y, width, height, rotation, flipX, flipY);
|
|
161
|
+
}
|
|
162
|
+
export function getFlipFromDirection(dir) {
|
|
163
|
+
return dir < 0; // links = true
|
|
46
164
|
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { Mouse } from "../input.js";
|
|
2
|
+
import { type Vector2d } from "./vector2d.js";
|
|
3
|
+
export type Circle = {
|
|
4
|
+
x: number;
|
|
5
|
+
y: number;
|
|
6
|
+
radius: number;
|
|
7
|
+
};
|
|
8
|
+
export declare function centerCircle(circle: Circle): Vector2d;
|
|
9
|
+
export declare function isPointInCircle(x: number, y: number, circle: Circle): boolean;
|
|
10
|
+
export declare function isMouseInCircle(mouse: Mouse, circle: Circle): boolean;
|
|
11
|
+
export declare function isCircleClicked(mouse: Mouse, circle: Circle): boolean;
|
|
12
|
+
export declare function isCircleColliding(circle1: Circle, circle2: Circle): boolean;
|
|
13
|
+
export declare function getCircleDistance(circle1: Circle, circle2: Circle): number;
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
// Circle Type for Hitboxes
|
|
2
|
+
// Function to get the Center of the Circle as Vector2d
|
|
3
|
+
export function centerCircle(circle) {
|
|
4
|
+
return { x: circle.x, y: circle.y };
|
|
5
|
+
}
|
|
6
|
+
// Check if a Point is in the Circle
|
|
7
|
+
export function isPointInCircle(x, y, circle) {
|
|
8
|
+
const distance = Math.sqrt((x - circle.x) * (x - circle.x) +
|
|
9
|
+
(y - circle.y) * (y - circle.y));
|
|
10
|
+
return distance <= circle.radius;
|
|
11
|
+
}
|
|
12
|
+
// Check if Mouse is in the Circle
|
|
13
|
+
export function isMouseInCircle(mouse, circle) {
|
|
14
|
+
return isPointInCircle(mouse.x, mouse.y, circle);
|
|
15
|
+
}
|
|
16
|
+
// Function to check if a Circle is clicked
|
|
17
|
+
export function isCircleClicked(mouse, circle) {
|
|
18
|
+
return isMouseInCircle(mouse, circle) && mouse.justPressed;
|
|
19
|
+
}
|
|
20
|
+
// Check if two Circles collide
|
|
21
|
+
export function isCircleColliding(circle1, circle2) {
|
|
22
|
+
const distance = Math.sqrt((circle1.x - circle2.x) * (circle1.x - circle2.x) +
|
|
23
|
+
(circle1.y - circle2.y) * (circle1.y - circle2.y));
|
|
24
|
+
return distance <= (circle1.radius + circle2.radius);
|
|
25
|
+
}
|
|
26
|
+
// Check if Circle collides with Rectangle (imported from rectangle.ts would cause circular dependency)
|
|
27
|
+
export function getCircleDistance(circle1, circle2) {
|
|
28
|
+
return Math.sqrt((circle1.x - circle2.x) * (circle1.x - circle2.x) +
|
|
29
|
+
(circle1.y - circle2.y) * (circle1.y - circle2.y));
|
|
30
|
+
}
|
package/dist/types/index.d.ts
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
|
-
export { type Vector2d, normalize2d, clamp2d, lerp2d, map2d } from "./vector2d.js";
|
|
2
|
-
export { type Vector3d, normalize3d, clamp3d, lerp3d, map3d } from "./vector3d.js";
|
|
1
|
+
export { type Vector2d, add2d, subtract2d, length2d, normalize2d, dot2d, distance2d, clamp2d, lerp2d, map2d } from "./vector2d.js";
|
|
2
|
+
export { type Vector3d, add3d, subtract3d, length3d, normalize3d, dot3d, crossprodukt3d, distance3d, clamp3d, lerp3d, map3d } from "./vector3d.js";
|
|
3
3
|
export { type Color, invertcolor, invertHexColor } from "./color.js";
|
|
4
4
|
export { type Rect, centerRectX, centerRectY, centerRect, isPointInRect, isMouseInRect, isRectClicked } from "./rectangle.js";
|
|
5
|
+
export { type Circle, centerCircle, isPointInCircle, isMouseInCircle, isCircleClicked, isCircleColliding, getCircleDistance } from "./circle.js";
|
|
6
|
+
export { type Triangle, centerTriangle, isPointInTriangle, isMouseInTriangle, isTriangleClicked, getTrianglePerimeter, } from "./triangle.js";
|
|
5
7
|
export { clamp, lerp, map } from "./math-utils.js";
|
package/dist/types/index.js
CHANGED
|
@@ -1,11 +1,15 @@
|
|
|
1
1
|
// Types Export
|
|
2
2
|
// Vector 2D
|
|
3
|
-
export { normalize2d, clamp2d, lerp2d, map2d } from "./vector2d.js";
|
|
3
|
+
export { add2d, subtract2d, length2d, normalize2d, dot2d, distance2d, clamp2d, lerp2d, map2d } from "./vector2d.js";
|
|
4
4
|
// Vector 3D
|
|
5
|
-
export { normalize3d, clamp3d, lerp3d, map3d } from "./vector3d.js";
|
|
5
|
+
export { add3d, subtract3d, length3d, normalize3d, dot3d, crossprodukt3d, distance3d, clamp3d, lerp3d, map3d } from "./vector3d.js";
|
|
6
6
|
// Color Type
|
|
7
7
|
export { invertcolor, invertHexColor } from "./color.js";
|
|
8
8
|
// Retangle Type
|
|
9
9
|
export { centerRectX, centerRectY, centerRect, isPointInRect, isMouseInRect, isRectClicked } from "./rectangle.js";
|
|
10
|
+
// Circle Type
|
|
11
|
+
export { centerCircle, isPointInCircle, isMouseInCircle, isCircleClicked, isCircleColliding, getCircleDistance } from "./circle.js";
|
|
12
|
+
// Triangle Type
|
|
13
|
+
export { centerTriangle, isPointInTriangle, isMouseInTriangle, isTriangleClicked, getTrianglePerimeter, } from "./triangle.js";
|
|
10
14
|
// Math Utilities
|
|
11
15
|
export { clamp, lerp, map } from "./math-utils.js";
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { Mouse } from "../input.js";
|
|
2
|
+
import { type Vector2d } from "./vector2d.js";
|
|
3
|
+
export type Triangle = {
|
|
4
|
+
x1: number;
|
|
5
|
+
y1: number;
|
|
6
|
+
x2: number;
|
|
7
|
+
y2: number;
|
|
8
|
+
x3: number;
|
|
9
|
+
y3: number;
|
|
10
|
+
};
|
|
11
|
+
export declare function centerTriangle(triangle: Triangle): Vector2d;
|
|
12
|
+
export declare function isPointInTriangle(x: number, y: number, triangle: Triangle): boolean;
|
|
13
|
+
export declare function isMouseInTriangle(mouse: Mouse, triangle: Triangle): boolean;
|
|
14
|
+
export declare function isTriangleClicked(mouse: Mouse, triangle: Triangle): boolean;
|
|
15
|
+
export declare function getTrianglePerimeter(triangle: Triangle): number;
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
// Triangle Type for Hitboxes
|
|
2
|
+
// Helper function to calculate the area of a triangle (used for point-in-triangle)
|
|
3
|
+
function getTriangleArea(x1, y1, x2, y2, x3, y3) {
|
|
4
|
+
return Math.abs((x1 * (y2 - y3) + x2 * (y3 - y1) + x3 * (y1 - y2)) / 2.0);
|
|
5
|
+
}
|
|
6
|
+
// Function to get the Center of the Triangle
|
|
7
|
+
export function centerTriangle(triangle) {
|
|
8
|
+
return {
|
|
9
|
+
x: (triangle.x1 + triangle.x2 + triangle.x3) / 3,
|
|
10
|
+
y: (triangle.y1 + triangle.y2 + triangle.y3) / 3
|
|
11
|
+
};
|
|
12
|
+
}
|
|
13
|
+
// Check if a Point is in the Triangle using barycentric coordinates
|
|
14
|
+
export function isPointInTriangle(x, y, triangle) {
|
|
15
|
+
const areaTriangle = getTriangleArea(triangle.x1, triangle.y1, triangle.x2, triangle.y2, triangle.x3, triangle.y3);
|
|
16
|
+
const area1 = getTriangleArea(x, y, triangle.x2, triangle.y2, triangle.x3, triangle.y3);
|
|
17
|
+
const area2 = getTriangleArea(triangle.x1, triangle.y1, x, y, triangle.x3, triangle.y3);
|
|
18
|
+
const area3 = getTriangleArea(triangle.x1, triangle.y1, triangle.x2, triangle.y2, x, y);
|
|
19
|
+
return Math.abs(areaTriangle - (area1 + area2 + area3)) < 0.01;
|
|
20
|
+
}
|
|
21
|
+
// Check if Mouse is in the Triangle
|
|
22
|
+
export function isMouseInTriangle(mouse, triangle) {
|
|
23
|
+
return isPointInTriangle(mouse.x, mouse.y, triangle);
|
|
24
|
+
}
|
|
25
|
+
// Function to check if a Triangle is clicked
|
|
26
|
+
export function isTriangleClicked(mouse, triangle) {
|
|
27
|
+
return isMouseInTriangle(mouse, triangle) && mouse.justPressed;
|
|
28
|
+
}
|
|
29
|
+
// Get the Perimeter of the Triangle
|
|
30
|
+
export function getTrianglePerimeter(triangle) {
|
|
31
|
+
const side1 = Math.sqrt((triangle.x2 - triangle.x1) * (triangle.x2 - triangle.x1) +
|
|
32
|
+
(triangle.y2 - triangle.y1) * (triangle.y2 - triangle.y1));
|
|
33
|
+
const side2 = Math.sqrt((triangle.x3 - triangle.x2) * (triangle.x3 - triangle.x2) +
|
|
34
|
+
(triangle.y3 - triangle.y2) * (triangle.y3 - triangle.y2));
|
|
35
|
+
const side3 = Math.sqrt((triangle.x1 - triangle.x3) * (triangle.x1 - triangle.x3) +
|
|
36
|
+
(triangle.y1 - triangle.y3) * (triangle.y1 - triangle.y3));
|
|
37
|
+
return side1 + side2 + side3;
|
|
38
|
+
}
|
package/dist/types/vector2d.d.ts
CHANGED
|
@@ -2,7 +2,12 @@ export type Vector2d = {
|
|
|
2
2
|
x: number;
|
|
3
3
|
y: number;
|
|
4
4
|
};
|
|
5
|
+
export declare function add2d(vector1: Vector2d, vector2: Vector2d): Vector2d;
|
|
6
|
+
export declare function subtract2d(vector1: Vector2d, vector2: Vector2d): Vector2d;
|
|
7
|
+
export declare function length2d(vector: Vector2d): number;
|
|
5
8
|
export declare function normalize2d(vector: Vector2d): Vector2d;
|
|
9
|
+
export declare function dot2d(v1: Vector2d, v2: Vector2d): number;
|
|
10
|
+
export declare function distance2d(v1: Vector2d, v2: Vector2d): number;
|
|
6
11
|
export declare function clamp2d(vector: Vector2d, min: Vector2d, max: Vector2d): Vector2d;
|
|
7
12
|
export declare function lerp2d(start: Vector2d, end: Vector2d, t: Vector2d): Vector2d;
|
|
8
13
|
export declare function map2d(value: Vector2d, inMin: Vector2d, inMax: Vector2d, outMin: Vector2d, outMax: Vector2d): Vector2d;
|
package/dist/types/vector2d.js
CHANGED
|
@@ -1,5 +1,25 @@
|
|
|
1
1
|
// 2 Dimensional Vector Type
|
|
2
2
|
import { clamp, lerp, map } from "./math-utils.js";
|
|
3
|
+
// Function to add 2 Vectors together
|
|
4
|
+
export function add2d(vector1, vector2) {
|
|
5
|
+
return {
|
|
6
|
+
x: vector1.x + vector2.x,
|
|
7
|
+
y: vector1.y + vector2.y,
|
|
8
|
+
};
|
|
9
|
+
}
|
|
10
|
+
// Function to subtract 2 Vectors from each other
|
|
11
|
+
export function subtract2d(vector1, vector2) {
|
|
12
|
+
return {
|
|
13
|
+
x: vector1.x - vector2.x,
|
|
14
|
+
y: vector1.y - vector2.y,
|
|
15
|
+
};
|
|
16
|
+
}
|
|
17
|
+
// Function to get the length from an Vector
|
|
18
|
+
export function length2d(vector) {
|
|
19
|
+
let produkt = vector.x * vector.x + vector.y * vector.y;
|
|
20
|
+
let root = Math.sqrt(produkt);
|
|
21
|
+
return root;
|
|
22
|
+
}
|
|
3
23
|
// Function to normalize a Vector 2d
|
|
4
24
|
export function normalize2d(vector) {
|
|
5
25
|
// Check if the Vector is zero because then you dont need to
|
|
@@ -7,12 +27,21 @@ export function normalize2d(vector) {
|
|
|
7
27
|
if (vector.x == 0 && vector.y == 0) {
|
|
8
28
|
return vector;
|
|
9
29
|
}
|
|
10
|
-
let
|
|
11
|
-
let root = Math.sqrt(produkt);
|
|
30
|
+
let root = length2d(vector);
|
|
12
31
|
vector.x = vector.x / root;
|
|
13
32
|
vector.y = vector.y / root;
|
|
14
33
|
return vector;
|
|
15
34
|
}
|
|
35
|
+
// Function to make scalar produkt from an Vector
|
|
36
|
+
export function dot2d(v1, v2) {
|
|
37
|
+
return (v1.x * v2.x + v1.y * v2.y);
|
|
38
|
+
}
|
|
39
|
+
// crossprodukt (only for 3 Dimensinal Vectors)
|
|
40
|
+
// Calculate the Distance between 2 Vectors
|
|
41
|
+
export function distance2d(v1, v2) {
|
|
42
|
+
let tmp = subtract2d(v1, v2);
|
|
43
|
+
return length2d(tmp);
|
|
44
|
+
}
|
|
16
45
|
// Function to clamp a Vector 2d
|
|
17
46
|
export function clamp2d(vector, min, max) {
|
|
18
47
|
return {
|
package/dist/types/vector3d.d.ts
CHANGED
|
@@ -3,7 +3,13 @@ export type Vector3d = {
|
|
|
3
3
|
y: number;
|
|
4
4
|
z: number;
|
|
5
5
|
};
|
|
6
|
+
export declare function add3d(vector1: Vector3d, vector2: Vector3d): Vector3d;
|
|
7
|
+
export declare function subtract3d(vector1: Vector3d, vector2: Vector3d): Vector3d;
|
|
8
|
+
export declare function length3d(vector: Vector3d): number;
|
|
6
9
|
export declare function normalize3d(vector: Vector3d): Vector3d;
|
|
10
|
+
export declare function dot3d(v1: Vector3d, v2: Vector3d): number;
|
|
11
|
+
export declare function crossprodukt3d(v1: Vector3d, v2: Vector3d): Vector3d;
|
|
12
|
+
export declare function distance3d(v1: Vector3d, v2: Vector3d): number;
|
|
7
13
|
export declare function clamp3d(vector: Vector3d, min: Vector3d, max: Vector3d): Vector3d;
|
|
8
14
|
export declare function lerp3d(start: Vector3d, end: Vector3d, t: Vector3d): Vector3d;
|
|
9
15
|
export declare function map3d(value: Vector3d, inMin: Vector3d, inMax: Vector3d, outMin: Vector3d, outMax: Vector3d): Vector3d;
|
package/dist/types/vector3d.js
CHANGED
|
@@ -1,5 +1,27 @@
|
|
|
1
1
|
// 3d Vector
|
|
2
2
|
import { clamp, lerp, map } from "./math-utils.js";
|
|
3
|
+
// Function to add 2 Vectors together
|
|
4
|
+
export function add3d(vector1, vector2) {
|
|
5
|
+
return {
|
|
6
|
+
x: vector1.x + vector2.x,
|
|
7
|
+
y: vector1.y + vector2.y,
|
|
8
|
+
z: vector1.z + vector2.z,
|
|
9
|
+
};
|
|
10
|
+
}
|
|
11
|
+
// Function to subtract 2 Vectors from each other
|
|
12
|
+
export function subtract3d(vector1, vector2) {
|
|
13
|
+
return {
|
|
14
|
+
x: vector1.x - vector2.x,
|
|
15
|
+
y: vector1.y - vector2.y,
|
|
16
|
+
z: vector1.z - vector2.z,
|
|
17
|
+
};
|
|
18
|
+
}
|
|
19
|
+
// Function to get the length from an Vector
|
|
20
|
+
export function length3d(vector) {
|
|
21
|
+
let produkt = vector.x * vector.x + vector.y * vector.y + vector.z * vector.z;
|
|
22
|
+
let root = Math.sqrt(produkt);
|
|
23
|
+
return root;
|
|
24
|
+
}
|
|
3
25
|
// Function to normalize a Vector 2d
|
|
4
26
|
export function normalize3d(vector) {
|
|
5
27
|
// Check if the Vector is zero because then you dont need to
|
|
@@ -7,13 +29,29 @@ export function normalize3d(vector) {
|
|
|
7
29
|
if (vector.x == 0 && vector.y == 0 && vector.z) {
|
|
8
30
|
return vector;
|
|
9
31
|
}
|
|
10
|
-
let
|
|
11
|
-
let root = Math.sqrt(produkt);
|
|
32
|
+
let root = length3d(vector);
|
|
12
33
|
vector.x = vector.x / root;
|
|
13
34
|
vector.y = vector.y / root;
|
|
14
35
|
vector.z = vector.z / root;
|
|
15
36
|
return vector;
|
|
16
37
|
}
|
|
38
|
+
// Function to make scalar produkt from an Vector
|
|
39
|
+
export function dot3d(v1, v2) {
|
|
40
|
+
return (v1.x * v2.x + v1.y * v2.y + v1.z * v2.z);
|
|
41
|
+
}
|
|
42
|
+
// Function to calculate the cross produkt
|
|
43
|
+
export function crossprodukt3d(v1, v2) {
|
|
44
|
+
return {
|
|
45
|
+
x: (v1.y * v2.z - v1.z * v2.y),
|
|
46
|
+
y: (v1.z * v2.x - v1.x * v2.z),
|
|
47
|
+
z: (v1.x * v2.y - v1.y * v2.x),
|
|
48
|
+
};
|
|
49
|
+
}
|
|
50
|
+
// Calculate the Distance between 2 Vectors
|
|
51
|
+
export function distance3d(v1, v2) {
|
|
52
|
+
let tmp = subtract3d(v1, v2);
|
|
53
|
+
return length3d(tmp);
|
|
54
|
+
}
|
|
17
55
|
// Function to clamp a Vector 3d
|
|
18
56
|
export function clamp3d(vector, min, max) {
|
|
19
57
|
return {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@shadowdara/webgameengine",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.3.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",
|
|
@@ -10,10 +10,10 @@
|
|
|
10
10
|
],
|
|
11
11
|
"scripts": {
|
|
12
12
|
"build": "tsc",
|
|
13
|
-
"pack": "npm run build && npm pack"
|
|
13
|
+
"pack": "node clean.js && npm run build && npm pack"
|
|
14
14
|
},
|
|
15
15
|
"bin": {
|
|
16
|
-
"webgameengine": "dist/build/
|
|
16
|
+
"webgameengine": "dist/build/cli/cli.js"
|
|
17
17
|
},
|
|
18
18
|
"exports": {
|
|
19
19
|
".": "./dist/index.js",
|
|
@@ -1,143 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
import { build as esbuild } from "esbuild";
|
|
3
|
-
import { createServer } from "http";
|
|
4
|
-
import { readFile, writeFile } from "fs/promises";
|
|
5
|
-
import { watch } from "fs";
|
|
6
|
-
import path from "path";
|
|
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
|
-
import { loadUserConfig } from "./config.js";
|
|
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;
|
|
51
|
-
const isDev = !isRelease;
|
|
52
|
-
// ================= CONFIG =================
|
|
53
|
-
const config = await loadUserConfig();
|
|
54
|
-
// ================= BUILD =================
|
|
55
|
-
let isBuilding = false;
|
|
56
|
-
async function build() {
|
|
57
|
-
if (isBuilding)
|
|
58
|
-
return;
|
|
59
|
-
isBuilding = true;
|
|
60
|
-
flog("🔄 Baue neu...");
|
|
61
|
-
await esbuild({
|
|
62
|
-
entryPoints: [`./game/${config.entryname}`],
|
|
63
|
-
outdir: `./${config.outdir}`,
|
|
64
|
-
bundle: true,
|
|
65
|
-
platform: "browser",
|
|
66
|
-
minify: isRelease,
|
|
67
|
-
sourcemap: isDev,
|
|
68
|
-
define: {
|
|
69
|
-
"import.meta.env.DEV": JSON.stringify(isDev),
|
|
70
|
-
},
|
|
71
|
-
});
|
|
72
|
-
const html = GetDefaultHTML(config);
|
|
73
|
-
await writeFile("./dist/index.html", html);
|
|
74
|
-
await copyFolder("./resources", "./dist/resources");
|
|
75
|
-
flog("✅ Build fertig!");
|
|
76
|
-
isBuilding = false;
|
|
77
|
-
}
|
|
78
|
-
// ================= SERVER =================
|
|
79
|
-
let sockets = new Set();
|
|
80
|
-
function startServer() {
|
|
81
|
-
const server = createServer(async (req, res) => {
|
|
82
|
-
const url = req.url || "/";
|
|
83
|
-
const filePath = url === "/" ? "/index.html" : url;
|
|
84
|
-
try {
|
|
85
|
-
const fullPath = path.join(process.cwd(), "dist", filePath);
|
|
86
|
-
const file = await readFile(fullPath);
|
|
87
|
-
res.writeHead(200, {
|
|
88
|
-
"Content-Type": getContentType(filePath),
|
|
89
|
-
});
|
|
90
|
-
res.end(file);
|
|
91
|
-
}
|
|
92
|
-
catch {
|
|
93
|
-
res.writeHead(404);
|
|
94
|
-
res.end("Not Found");
|
|
95
|
-
}
|
|
96
|
-
});
|
|
97
|
-
const wss = new WebSocketServer({ server });
|
|
98
|
-
wss.on("connection", (ws) => {
|
|
99
|
-
sockets.add(ws);
|
|
100
|
-
ws.on("close", () => sockets.delete(ws));
|
|
101
|
-
});
|
|
102
|
-
server.listen(args.port, () => {
|
|
103
|
-
flog(`🚀 Dev Server läuft auf http://localhost:${args.port}`);
|
|
104
|
-
});
|
|
105
|
-
return server;
|
|
106
|
-
}
|
|
107
|
-
// ================= RELOAD =================
|
|
108
|
-
function reloadClients() {
|
|
109
|
-
flog("🔄 Browser reload...");
|
|
110
|
-
for (const ws of sockets) {
|
|
111
|
-
ws.send("reload");
|
|
112
|
-
}
|
|
113
|
-
}
|
|
114
|
-
// ================= WATCH =================
|
|
115
|
-
let restarting = false;
|
|
116
|
-
let pendingRestart = false;
|
|
117
|
-
async function restart() {
|
|
118
|
-
if (restarting) {
|
|
119
|
-
pendingRestart = true;
|
|
120
|
-
return;
|
|
121
|
-
}
|
|
122
|
-
restarting = true;
|
|
123
|
-
do {
|
|
124
|
-
pendingRestart = false;
|
|
125
|
-
flog("♻️ Restart...");
|
|
126
|
-
await build();
|
|
127
|
-
reloadClients();
|
|
128
|
-
} while (pendingRestart);
|
|
129
|
-
restarting = false;
|
|
130
|
-
}
|
|
131
|
-
// ================= START =================
|
|
132
|
-
await build();
|
|
133
|
-
if (isDev) {
|
|
134
|
-
startServer();
|
|
135
|
-
["resources", "game"].forEach((dir) => {
|
|
136
|
-
watch(dir, { recursive: true }, async () => {
|
|
137
|
-
flog(`📁 Änderung erkannt in ${dir}`);
|
|
138
|
-
await restart();
|
|
139
|
-
});
|
|
140
|
-
});
|
|
141
|
-
flog("👀 Watcher aktiv...");
|
|
142
|
-
}
|
|
143
|
-
flog(`Build fertig! Modus: ${isRelease ? "Release" : "Dev"}`);
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export declare function createProject(name: string): Promise<void>;
|
|
File without changes
|