@shadowdara/webgameengine 1.0.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 +127 -0
- package/dist/build/buildconfig.d.ts +8 -0
- package/dist/build/buildconfig.js +10 -0
- package/dist/build/buildhelper.d.ts +3 -0
- package/dist/build/buildhelper.js +41 -0
- package/dist/build/exporthtml.d.ts +2 -0
- package/dist/build/exporthtml.js +58 -0
- package/dist/build/index.d.ts +2 -0
- package/dist/build/index.js +2 -0
- package/dist/build/tools/devserver/cli.d.ts +2 -0
- package/dist/build/tools/devserver/cli.js +105 -0
- package/dist/build/tools/devserver/config.d.ts +3 -0
- package/dist/build/tools/devserver/config.js +18 -0
- package/dist/core.d.ts +3 -0
- package/dist/core.js +13 -0
- package/dist/html.d.ts +16 -0
- package/dist/html.js +64 -0
- package/dist/index.d.ts +9 -0
- package/dist/index.js +16 -0
- package/dist/input.d.ts +15 -0
- package/dist/input.js +104 -0
- package/dist/keys.d.ts +52 -0
- package/dist/keys.js +55 -0
- package/dist/logger.d.ts +1 -0
- package/dist/logger.js +16 -0
- package/dist/renderer.d.ts +5 -0
- package/dist/renderer.js +17 -0
- package/dist/save.d.ts +5 -0
- package/dist/save.js +27 -0
- package/dist/texture.d.ts +4 -0
- package/dist/texture.js +46 -0
- package/dist/types/color.d.ts +8 -0
- package/dist/types/color.js +18 -0
- package/dist/types/index.d.ts +5 -0
- package/dist/types/index.js +4 -0
- package/dist/types/math-utils.d.ts +3 -0
- package/dist/types/math-utils.js +12 -0
- package/dist/types/rectangle.d.ts +14 -0
- package/dist/types/rectangle.js +31 -0
- package/dist/types/vector2d.d.ts +7 -0
- package/dist/types/vector2d.js +29 -0
- package/dist/types/vector3d.d.ts +8 -0
- package/dist/types/vector3d.js +32 -0
- package/package.json +40 -0
package/README.md
ADDED
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
# WebGameEngine 🎮
|
|
2
|
+
|
|
3
|
+
**(Currently trying to make this into a lib)**
|
|
4
|
+
|
|
5
|
+
**Use `bun tools/build.ts`**
|
|
6
|
+
|
|
7
|
+
A lightweight, TypeScript-first web game engine framework for building 2D games.
|
|
8
|
+
|
|
9
|
+
## Features
|
|
10
|
+
|
|
11
|
+
- 🎯 Simple game loop management
|
|
12
|
+
- 🎨 Rendering system with text and sprite support
|
|
13
|
+
- ⌨️ Input handling (keyboard & mouse)
|
|
14
|
+
- 📦 TypeScript support out of the box
|
|
15
|
+
- 🛠️ Build tools included
|
|
16
|
+
- 📝 Logging utilities
|
|
17
|
+
- 💾 Save/Load system
|
|
18
|
+
|
|
19
|
+
## Installation
|
|
20
|
+
|
|
21
|
+
```bash
|
|
22
|
+
npm install @yourusername/webgameengine
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
Or use directly from source:
|
|
26
|
+
|
|
27
|
+
```bash
|
|
28
|
+
bun install
|
|
29
|
+
bun run dev
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
## Quick Start
|
|
33
|
+
|
|
34
|
+
### Basic Game Loop
|
|
35
|
+
|
|
36
|
+
```typescript
|
|
37
|
+
import { startEngine, setupInput, dlog, renderText } from '@yourusername/webgameengine';
|
|
38
|
+
|
|
39
|
+
const canvas = document.getElementById('gameCanvas') as HTMLCanvasElement;
|
|
40
|
+
const ctx = canvas.getContext('2d')!;
|
|
41
|
+
|
|
42
|
+
setupInput(canvas, 800, 600);
|
|
43
|
+
|
|
44
|
+
function init() {
|
|
45
|
+
dlog('🎮 Game initialized!');
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
function gameLoop(dt: number) {
|
|
49
|
+
// Clear canvas
|
|
50
|
+
ctx.fillStyle = 'black';
|
|
51
|
+
ctx.fillRect(0, 0, canvas.width, canvas.height);
|
|
52
|
+
|
|
53
|
+
// Your game logic here
|
|
54
|
+
renderText(ctx, `FPS: ${(1 / dt).toFixed(0)}`, 10, 20);
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
startEngine(init, gameLoop);
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
## Development & Building
|
|
61
|
+
|
|
62
|
+
### Using Bun (local development)
|
|
63
|
+
|
|
64
|
+
```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
|
+
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
### Configuration
|
|
73
|
+
|
|
74
|
+
Edit `project.ts` to configure your game:
|
|
75
|
+
|
|
76
|
+
```typescript
|
|
77
|
+
import { defineConfig } from './project';
|
|
78
|
+
|
|
79
|
+
export function defineConfig() {
|
|
80
|
+
return {
|
|
81
|
+
entryname: 'main.ts',
|
|
82
|
+
outdir: 'dist',
|
|
83
|
+
// ... other config
|
|
84
|
+
};
|
|
85
|
+
}
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
or
|
|
89
|
+
|
|
90
|
+
```typescript
|
|
91
|
+
// Project File for the Game
|
|
92
|
+
|
|
93
|
+
import { type buildconfig, new_buildconfig } from "./engine/build/buildconfig";
|
|
94
|
+
|
|
95
|
+
export function defineConfig(): buildconfig {
|
|
96
|
+
let config: buildconfig = new_buildconfig();
|
|
97
|
+
return config;
|
|
98
|
+
}
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
## API Reference
|
|
102
|
+
|
|
103
|
+
### Core Engine
|
|
104
|
+
- `startEngine(init, gameLoop)` - Initialize game loop
|
|
105
|
+
|
|
106
|
+
### Rendering
|
|
107
|
+
- `renderText(ctx, text, x, y, color?, font?)` - Render text
|
|
108
|
+
- `renderBitmapText()` - Render bitmap font text
|
|
109
|
+
|
|
110
|
+
### Input System
|
|
111
|
+
- `setupInput(canvas, width?, height?)` - Initialize input
|
|
112
|
+
- `getKeyState(key)` - Check key state
|
|
113
|
+
- Mouse state available via input module
|
|
114
|
+
|
|
115
|
+
### Types
|
|
116
|
+
- `Vector2D` / `Vector3D` - Vector mathematics
|
|
117
|
+
- `Color` - Color management
|
|
118
|
+
- `Rect` - Rectangle collision
|
|
119
|
+
- Math utilities for game logic
|
|
120
|
+
|
|
121
|
+
### Utilities
|
|
122
|
+
- `dlog()` - Development logging
|
|
123
|
+
- `startEngine()` - Manage game loop
|
|
124
|
+
|
|
125
|
+
## License
|
|
126
|
+
|
|
127
|
+
MIT
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
export declare const flog: (...args: any[]) => void;
|
|
2
|
+
export declare function copyFolder(src: string, dest: string): Promise<void>;
|
|
3
|
+
export declare function getContentType(path: string): "application/javascript" | "application/typescript" | "text/html" | "text/css" | "image/png" | "text/plain";
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import { readdirSync, mkdirSync, promises as fsPromises } from "fs";
|
|
2
|
+
import { join } from "path";
|
|
3
|
+
export const flog = (...args) => {
|
|
4
|
+
const now = new Date();
|
|
5
|
+
const time = `[${now.getHours().toString().padStart(2, "0")}:` +
|
|
6
|
+
`${now.getMinutes().toString().padStart(2, "0")}:` +
|
|
7
|
+
`${now.getSeconds().toString().padStart(2, "0")}.` +
|
|
8
|
+
`${now.getMilliseconds().toString().padStart(3, "0")}]`;
|
|
9
|
+
console.log(time, ...args);
|
|
10
|
+
};
|
|
11
|
+
export async function copyFolder(src, dest) {
|
|
12
|
+
// Zielordner erstellen
|
|
13
|
+
mkdirSync(dest, { recursive: true });
|
|
14
|
+
const entries = readdirSync(src, { withFileTypes: true });
|
|
15
|
+
for (const entry of entries) {
|
|
16
|
+
const srcPath = join(src, entry.name);
|
|
17
|
+
const destPath = join(dest, entry.name);
|
|
18
|
+
if (entry.isDirectory()) {
|
|
19
|
+
await copyFolder(srcPath, destPath);
|
|
20
|
+
}
|
|
21
|
+
else if (entry.isFile()) {
|
|
22
|
+
// Datei mit Node.js schreiben
|
|
23
|
+
const data = await fsPromises.readFile(srcPath);
|
|
24
|
+
await fsPromises.writeFile(destPath, data);
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
// === Helper ===
|
|
29
|
+
export function getContentType(path) {
|
|
30
|
+
if (path.endsWith(".js"))
|
|
31
|
+
return "application/javascript";
|
|
32
|
+
if (path.endsWith(".ts"))
|
|
33
|
+
return "application/typescript";
|
|
34
|
+
if (path.endsWith(".html"))
|
|
35
|
+
return "text/html";
|
|
36
|
+
if (path.endsWith(".css"))
|
|
37
|
+
return "text/css";
|
|
38
|
+
if (path.endsWith(".png"))
|
|
39
|
+
return "image/png";
|
|
40
|
+
return "text/plain";
|
|
41
|
+
}
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
// Function to create the Export HTML for the Build
|
|
2
|
+
export function GetDefaultHTML(config) {
|
|
3
|
+
let fullscreenbutton = "";
|
|
4
|
+
let fullscreenBtn = "";
|
|
5
|
+
if (config.show_fullscreen_button) {
|
|
6
|
+
fullscreenbutton = `#fullscreenBtn {
|
|
7
|
+
position: fixed;
|
|
8
|
+
top: 10px;
|
|
9
|
+
right: 10px;
|
|
10
|
+
|
|
11
|
+
padding: 10px 15px;
|
|
12
|
+
font-size: 16px;
|
|
13
|
+
|
|
14
|
+
background: rgba(0, 0, 0, 0.6);
|
|
15
|
+
color: white;
|
|
16
|
+
border: none;
|
|
17
|
+
border-radius: 6px;
|
|
18
|
+
|
|
19
|
+
cursor: pointer;
|
|
20
|
+
z-index: 1000;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
#fullscreenBtn:hover {
|
|
24
|
+
background: rgba(0, 0, 0, 0.8);
|
|
25
|
+
}`;
|
|
26
|
+
fullscreenBtn = `<!-- Button to make it fullscreen -->
|
|
27
|
+
<button id="fullscreenBtn">⛶ Fullscreen</button>`;
|
|
28
|
+
}
|
|
29
|
+
const defaulthtml = `<!-- HTML Web Game made with webgameengine by Shadowdara -->
|
|
30
|
+
<!-- DO NOT REMOVE THIS NOTE ! -->
|
|
31
|
+
<!DOCTYPE html>
|
|
32
|
+
<html>
|
|
33
|
+
<head>
|
|
34
|
+
<meta charset="UTF-8" />
|
|
35
|
+
<title>${config.title}</title>
|
|
36
|
+
<!-- Für Mobile Viewports -->
|
|
37
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=no">
|
|
38
|
+
<style>
|
|
39
|
+
* {
|
|
40
|
+
margin: 0;
|
|
41
|
+
padding: 0;
|
|
42
|
+
box-sizing: border-box;
|
|
43
|
+
}
|
|
44
|
+
body {
|
|
45
|
+
overflow: hidden; /* 🔥 verhindert Scrollbars */
|
|
46
|
+
}
|
|
47
|
+
${fullscreenbutton}
|
|
48
|
+
</style>
|
|
49
|
+
</style>
|
|
50
|
+
</head>
|
|
51
|
+
<body>
|
|
52
|
+
${fullscreenBtn}
|
|
53
|
+
<script type="module" src="${config.entryname}.js"></script>
|
|
54
|
+
</body>
|
|
55
|
+
</html>
|
|
56
|
+
`;
|
|
57
|
+
return defaulthtml;
|
|
58
|
+
}
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
// Shebang
|
|
3
|
+
import { build as esbuild } from "esbuild";
|
|
4
|
+
import { copyFolder, flog, getContentType } from "../../buildhelper.js";
|
|
5
|
+
import { GetDefaultHTML } from "../../exporthtml.js";
|
|
6
|
+
import { createServer } from "http";
|
|
7
|
+
import { readFile, writeFile } from "fs/promises";
|
|
8
|
+
import { watch } from "fs";
|
|
9
|
+
import path from "path";
|
|
10
|
+
import { WebSocketServer } from "ws";
|
|
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");
|
|
16
|
+
const isDev = !isRelease;
|
|
17
|
+
// ================= BUILD =================
|
|
18
|
+
async function build(config) {
|
|
19
|
+
if (isBuilding)
|
|
20
|
+
return;
|
|
21
|
+
isBuilding = true;
|
|
22
|
+
flog("🔄 Baue neu...");
|
|
23
|
+
await esbuild({
|
|
24
|
+
entryPoints: ["./game/" + config.entryname],
|
|
25
|
+
outdir: "./" + config.outdir,
|
|
26
|
+
bundle: true,
|
|
27
|
+
platform: "browser",
|
|
28
|
+
minify: isRelease,
|
|
29
|
+
sourcemap: isDev,
|
|
30
|
+
define: {
|
|
31
|
+
"import.meta.env.DEV": JSON.stringify(isDev),
|
|
32
|
+
},
|
|
33
|
+
});
|
|
34
|
+
const html = GetDefaultHTML(config);
|
|
35
|
+
await writeFile("./dist/index.html", html);
|
|
36
|
+
await copyFolder("./resources", "./dist/resources");
|
|
37
|
+
flog("✅ Build fertig!");
|
|
38
|
+
isBuilding = false;
|
|
39
|
+
}
|
|
40
|
+
// ================= SERVER =================
|
|
41
|
+
let sockets = new Set();
|
|
42
|
+
function startServer() {
|
|
43
|
+
const server = createServer(async (req, res) => {
|
|
44
|
+
let url = req.url || "/";
|
|
45
|
+
let filePath = url === "/" ? "/index.html" : url;
|
|
46
|
+
try {
|
|
47
|
+
const fullPath = path.join(process.cwd(), "dist", filePath);
|
|
48
|
+
const file = await readFile(fullPath);
|
|
49
|
+
res.writeHead(200, {
|
|
50
|
+
"Content-Type": getContentType(filePath),
|
|
51
|
+
});
|
|
52
|
+
res.end(file);
|
|
53
|
+
}
|
|
54
|
+
catch {
|
|
55
|
+
res.writeHead(404);
|
|
56
|
+
res.end("Not Found");
|
|
57
|
+
}
|
|
58
|
+
});
|
|
59
|
+
const wss = new WebSocketServer({ server });
|
|
60
|
+
wss.on("connection", (ws) => {
|
|
61
|
+
sockets.add(ws);
|
|
62
|
+
ws.on("close", () => sockets.delete(ws));
|
|
63
|
+
});
|
|
64
|
+
server.listen(3000, () => {
|
|
65
|
+
flog("🚀 Dev Server läuft auf http://localhost:3000");
|
|
66
|
+
});
|
|
67
|
+
return server;
|
|
68
|
+
}
|
|
69
|
+
// ================= RELOAD =================
|
|
70
|
+
function reloadClients() {
|
|
71
|
+
flog("🔄 Browser reload...");
|
|
72
|
+
for (const ws of sockets) {
|
|
73
|
+
ws.send("reload");
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
// ================= WATCH =================
|
|
77
|
+
let restarting = false;
|
|
78
|
+
let pendingRestart = false;
|
|
79
|
+
async function restart() {
|
|
80
|
+
if (restarting) {
|
|
81
|
+
pendingRestart = true;
|
|
82
|
+
return;
|
|
83
|
+
}
|
|
84
|
+
restarting = true;
|
|
85
|
+
do {
|
|
86
|
+
pendingRestart = false;
|
|
87
|
+
flog("♻️ Restart...");
|
|
88
|
+
await build(config);
|
|
89
|
+
reloadClients();
|
|
90
|
+
} while (pendingRestart);
|
|
91
|
+
restarting = false;
|
|
92
|
+
}
|
|
93
|
+
// ================= START =================
|
|
94
|
+
await build(config);
|
|
95
|
+
if (isDev) {
|
|
96
|
+
startServer();
|
|
97
|
+
["resources", "game"].forEach((dir) => {
|
|
98
|
+
watch(dir, { recursive: true }, async () => {
|
|
99
|
+
flog(`📁 Änderung erkannt in ${dir}`);
|
|
100
|
+
await restart();
|
|
101
|
+
});
|
|
102
|
+
});
|
|
103
|
+
flog("👀 Watcher aktiv...");
|
|
104
|
+
}
|
|
105
|
+
flog(`Build fertig! Modus: ${isRelease ? "Release" : "Dev"}`);
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import "esbuild-register";
|
|
2
|
+
import path from "path";
|
|
3
|
+
import { pathToFileURL } from "url";
|
|
4
|
+
export async function loadUserConfig() {
|
|
5
|
+
const configPath = path.resolve(process.cwd(), "webgameengine.config.ts");
|
|
6
|
+
try {
|
|
7
|
+
const configUrl = pathToFileURL(configPath).href;
|
|
8
|
+
const mod = await import(configUrl);
|
|
9
|
+
const config = typeof mod.default === "function"
|
|
10
|
+
? mod.default()
|
|
11
|
+
: mod.default;
|
|
12
|
+
return config;
|
|
13
|
+
}
|
|
14
|
+
catch (e) {
|
|
15
|
+
console.error(e);
|
|
16
|
+
throw new Error("❌ Konnte webgameengine.config.ts nicht laden\nPATH: " + configPath);
|
|
17
|
+
}
|
|
18
|
+
}
|
package/dist/core.d.ts
ADDED
package/dist/core.js
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
let lastTime = 0;
|
|
2
|
+
let loop;
|
|
3
|
+
export function startEngine(start, gameLoop) {
|
|
4
|
+
loop = gameLoop;
|
|
5
|
+
start();
|
|
6
|
+
requestAnimationFrame(run);
|
|
7
|
+
}
|
|
8
|
+
function run(time) {
|
|
9
|
+
const dt = (time - lastTime) / 1000;
|
|
10
|
+
lastTime = time;
|
|
11
|
+
loop(dt);
|
|
12
|
+
requestAnimationFrame(run);
|
|
13
|
+
}
|
package/dist/html.d.ts
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
export type CanvasConfig = {
|
|
2
|
+
width?: number;
|
|
3
|
+
height?: number;
|
|
4
|
+
fullscreen?: boolean;
|
|
5
|
+
scaling?: "none" | "fit";
|
|
6
|
+
virtualWidth?: number;
|
|
7
|
+
virtualHeight?: number;
|
|
8
|
+
};
|
|
9
|
+
export declare function createCanvas(config?: CanvasConfig): {
|
|
10
|
+
canvas: HTMLCanvasElement;
|
|
11
|
+
ctx: CanvasRenderingContext2D;
|
|
12
|
+
applyScaling: () => void;
|
|
13
|
+
};
|
|
14
|
+
export declare function resizeCanvas(canvas: HTMLCanvasElement): void;
|
|
15
|
+
export declare function enableFullscreen(canvas: HTMLCanvasElement): void;
|
|
16
|
+
export declare function setupFullscreenButton(canvas: HTMLCanvasElement): void;
|
package/dist/html.js
ADDED
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
export function createCanvas(config = {}) {
|
|
2
|
+
const canvas = document.createElement("canvas");
|
|
3
|
+
const ctx = canvas.getContext("2d");
|
|
4
|
+
document.body.appendChild(canvas);
|
|
5
|
+
const virtualWidth = config.virtualWidth ?? 800;
|
|
6
|
+
const virtualHeight = config.virtualHeight ?? 800;
|
|
7
|
+
function resize() {
|
|
8
|
+
if (config.fullscreen) {
|
|
9
|
+
canvas.width = window.innerWidth;
|
|
10
|
+
canvas.height = window.innerHeight;
|
|
11
|
+
}
|
|
12
|
+
else {
|
|
13
|
+
canvas.width = config.width ?? 800;
|
|
14
|
+
canvas.height = config.height ?? 800;
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
window.addEventListener("resize", resize);
|
|
18
|
+
resize();
|
|
19
|
+
function applyScaling() {
|
|
20
|
+
if (config.scaling === "fit") {
|
|
21
|
+
const scale = Math.min(canvas.width / virtualWidth, canvas.height / virtualHeight);
|
|
22
|
+
const offsetX = (canvas.width - virtualWidth * scale) / 2;
|
|
23
|
+
const offsetY = (canvas.height - virtualHeight * scale) / 2;
|
|
24
|
+
ctx.setTransform(scale, 0, 0, scale, offsetX, offsetY);
|
|
25
|
+
}
|
|
26
|
+
else {
|
|
27
|
+
ctx.setTransform(1, 0, 0, 1, 0, 0);
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
return {
|
|
31
|
+
canvas,
|
|
32
|
+
ctx,
|
|
33
|
+
applyScaling,
|
|
34
|
+
};
|
|
35
|
+
}
|
|
36
|
+
export function resizeCanvas(canvas) {
|
|
37
|
+
canvas.width = window.innerWidth;
|
|
38
|
+
canvas.height = window.innerHeight;
|
|
39
|
+
}
|
|
40
|
+
export function enableFullscreen(canvas) {
|
|
41
|
+
window.addEventListener("keydown", (e) => {
|
|
42
|
+
if (e.key === "f") {
|
|
43
|
+
if (!document.fullscreenElement) {
|
|
44
|
+
canvas.requestFullscreen();
|
|
45
|
+
}
|
|
46
|
+
else {
|
|
47
|
+
document.exitFullscreen();
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
});
|
|
51
|
+
}
|
|
52
|
+
export function setupFullscreenButton(canvas) {
|
|
53
|
+
const btn = document.getElementById("fullscreenBtn");
|
|
54
|
+
if (!btn)
|
|
55
|
+
return;
|
|
56
|
+
btn.addEventListener("click", () => {
|
|
57
|
+
if (!document.fullscreenElement) {
|
|
58
|
+
canvas.requestFullscreen();
|
|
59
|
+
}
|
|
60
|
+
else {
|
|
61
|
+
document.exitFullscreen();
|
|
62
|
+
}
|
|
63
|
+
});
|
|
64
|
+
}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
export { startEngine } from "./core.js";
|
|
2
|
+
export { renderText, renderBitmapText } from "./renderer.js";
|
|
3
|
+
export { setupInput, isKeyJustPressed, resetInput, getMouse } from "./input.js";
|
|
4
|
+
export type { Mouse } from "./input.js";
|
|
5
|
+
export { dlog } from "./logger.js";
|
|
6
|
+
export { saveGame, loadGame, clearSave } from "./save.js";
|
|
7
|
+
export { drawTexture, getTexture, type Texture, loadTextureAsync } from "./texture.js";
|
|
8
|
+
export { createCanvas, enableFullscreen, setupFullscreenButton } from "./html.js";
|
|
9
|
+
export { Key } from "./keys.js";
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
// Core Engine Exports
|
|
2
|
+
export { startEngine } from "./core.js";
|
|
3
|
+
// Rendering
|
|
4
|
+
export { renderText, renderBitmapText } from "./renderer.js";
|
|
5
|
+
// Input System
|
|
6
|
+
export { setupInput, isKeyJustPressed, resetInput, getMouse } from "./input.js";
|
|
7
|
+
// Logging
|
|
8
|
+
export { dlog } from "./logger.js";
|
|
9
|
+
// Save System
|
|
10
|
+
export { saveGame, loadGame, clearSave } from "./save.js";
|
|
11
|
+
// Texture Management
|
|
12
|
+
export { drawTexture, getTexture, loadTextureAsync } from "./texture.js";
|
|
13
|
+
// HTML Generation
|
|
14
|
+
export { createCanvas, enableFullscreen, setupFullscreenButton } from "./html.js";
|
|
15
|
+
// Keys Reference
|
|
16
|
+
export { Key } from "./keys.js";
|
package/dist/input.d.ts
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
export type Mouse = {
|
|
2
|
+
x: number;
|
|
3
|
+
y: number;
|
|
4
|
+
pressed: boolean;
|
|
5
|
+
justPressed: boolean;
|
|
6
|
+
justReleased: boolean;
|
|
7
|
+
rightPressed: boolean;
|
|
8
|
+
wheelDelta: number;
|
|
9
|
+
};
|
|
10
|
+
export declare function setupInput(canvas: HTMLCanvasElement, vWidth?: number, vHeight?: number): void;
|
|
11
|
+
export declare function isKeyPressed(code: string): boolean;
|
|
12
|
+
export declare function isKeyJustPressed(code: string): boolean;
|
|
13
|
+
export declare function isKeyJustReleased(code: string): boolean;
|
|
14
|
+
export declare function getMouse(): Readonly<Mouse>;
|
|
15
|
+
export declare function resetInput(): void;
|
package/dist/input.js
ADDED
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
const keys = {};
|
|
2
|
+
const mouse = {
|
|
3
|
+
x: 0,
|
|
4
|
+
y: 0,
|
|
5
|
+
// for Left Buttons
|
|
6
|
+
pressed: false,
|
|
7
|
+
justPressed: false,
|
|
8
|
+
justReleased: false,
|
|
9
|
+
// TODO
|
|
10
|
+
// do the same for the right Buttons
|
|
11
|
+
rightPressed: false,
|
|
12
|
+
wheelDelta: 0,
|
|
13
|
+
};
|
|
14
|
+
let canvasRef;
|
|
15
|
+
// 👉 optional: für scaling (kannst du später aus deiner engine holen)
|
|
16
|
+
let virtualWidth = 800;
|
|
17
|
+
let virtualHeight = 800;
|
|
18
|
+
export function setupInput(canvas, vWidth = 800, vHeight = 800) {
|
|
19
|
+
canvasRef = canvas;
|
|
20
|
+
virtualWidth = vWidth;
|
|
21
|
+
virtualHeight = vHeight;
|
|
22
|
+
// ===== KEYBOARD =====
|
|
23
|
+
window.addEventListener("keydown", (e) => {
|
|
24
|
+
if (!keys[e.code]) {
|
|
25
|
+
keys[e.code] = { pressed: false, justPressed: false, justReleased: false };
|
|
26
|
+
}
|
|
27
|
+
const key = keys[e.code];
|
|
28
|
+
if (!key.pressed)
|
|
29
|
+
key.justPressed = true;
|
|
30
|
+
key.pressed = true;
|
|
31
|
+
});
|
|
32
|
+
window.addEventListener("keyup", (e) => {
|
|
33
|
+
if (!keys[e.code]) {
|
|
34
|
+
keys[e.code] = { pressed: false, justPressed: false, justReleased: false };
|
|
35
|
+
}
|
|
36
|
+
const key = keys[e.code];
|
|
37
|
+
key.pressed = false;
|
|
38
|
+
key.justReleased = true;
|
|
39
|
+
});
|
|
40
|
+
// ===== MOUSE =====
|
|
41
|
+
canvas.addEventListener("mousedown", (e) => {
|
|
42
|
+
if (e.button === 0) {
|
|
43
|
+
if (!mouse.pressed)
|
|
44
|
+
mouse.justPressed = true;
|
|
45
|
+
mouse.pressed = true;
|
|
46
|
+
}
|
|
47
|
+
if (e.button === 2) {
|
|
48
|
+
mouse.rightPressed = true;
|
|
49
|
+
}
|
|
50
|
+
});
|
|
51
|
+
canvas.addEventListener("mouseup", (e) => {
|
|
52
|
+
if (e.button === 0) {
|
|
53
|
+
mouse.pressed = false;
|
|
54
|
+
mouse.justReleased = true;
|
|
55
|
+
}
|
|
56
|
+
if (e.button === 2) {
|
|
57
|
+
mouse.rightPressed = false;
|
|
58
|
+
}
|
|
59
|
+
});
|
|
60
|
+
// 👉 wichtig: verhindert context menu bei right click
|
|
61
|
+
canvas.addEventListener("contextmenu", (e) => e.preventDefault());
|
|
62
|
+
// ===== MOUSE MOVE (mit scaling fix 🔥) =====
|
|
63
|
+
canvas.addEventListener("mousemove", (e) => {
|
|
64
|
+
const rect = canvas.getBoundingClientRect();
|
|
65
|
+
const scale = Math.min(canvas.width / virtualWidth, canvas.height / virtualHeight);
|
|
66
|
+
const offsetX = (canvas.width - virtualWidth * scale) / 2;
|
|
67
|
+
const offsetY = (canvas.height - virtualHeight * scale) / 2;
|
|
68
|
+
mouse.x = (e.clientX - rect.left - offsetX) / scale;
|
|
69
|
+
mouse.y = (e.clientY - rect.top - offsetY) / scale;
|
|
70
|
+
});
|
|
71
|
+
// ===== MOUSE WHEEL =====
|
|
72
|
+
canvas.addEventListener("wheel", (e) => {
|
|
73
|
+
mouse.wheelDelta = e.deltaY;
|
|
74
|
+
});
|
|
75
|
+
// 👉 Fix: wenn Maus Canvas verlässt
|
|
76
|
+
canvas.addEventListener("mouseleave", () => {
|
|
77
|
+
mouse.pressed = false;
|
|
78
|
+
mouse.rightPressed = false;
|
|
79
|
+
});
|
|
80
|
+
}
|
|
81
|
+
// ===== KEY HELPERS =====
|
|
82
|
+
export function isKeyPressed(code) {
|
|
83
|
+
return keys[code]?.pressed || false;
|
|
84
|
+
}
|
|
85
|
+
export function isKeyJustPressed(code) {
|
|
86
|
+
return keys[code]?.justPressed || false;
|
|
87
|
+
}
|
|
88
|
+
export function isKeyJustReleased(code) {
|
|
89
|
+
return keys[code]?.justReleased || false;
|
|
90
|
+
}
|
|
91
|
+
// ===== MOUSE =====
|
|
92
|
+
export function getMouse() {
|
|
93
|
+
return { ...mouse };
|
|
94
|
+
}
|
|
95
|
+
// ===== RESET =====
|
|
96
|
+
export function resetInput() {
|
|
97
|
+
for (const k in keys) {
|
|
98
|
+
keys[k].justPressed = false;
|
|
99
|
+
keys[k].justReleased = false;
|
|
100
|
+
}
|
|
101
|
+
mouse.justPressed = false;
|
|
102
|
+
mouse.justReleased = false;
|
|
103
|
+
mouse.wheelDelta = 0;
|
|
104
|
+
}
|
package/dist/keys.d.ts
ADDED
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
export declare enum Key {
|
|
2
|
+
ArrowUp = "ArrowUp",
|
|
3
|
+
ArrowDown = "ArrowDown",
|
|
4
|
+
ArrowLeft = "ArrowLeft",
|
|
5
|
+
ArrowRight = "ArrowRight",
|
|
6
|
+
Space = "Space",
|
|
7
|
+
Enter = "Enter",
|
|
8
|
+
Escape = "Escape",
|
|
9
|
+
Tab = "Tab",
|
|
10
|
+
ShiftLeft = "ShiftLeft",
|
|
11
|
+
ShiftRight = "ShiftRight",
|
|
12
|
+
ControlLeft = "ControlLeft",
|
|
13
|
+
ControlRight = "ControlRight",
|
|
14
|
+
AltLeft = "AltLeft",
|
|
15
|
+
AltRight = "AltRight",
|
|
16
|
+
KeyA = "KeyA",
|
|
17
|
+
KeyB = "KeyB",
|
|
18
|
+
KeyC = "KeyC",
|
|
19
|
+
KeyD = "KeyD",
|
|
20
|
+
KeyE = "KeyE",
|
|
21
|
+
KeyF = "KeyF",
|
|
22
|
+
KeyG = "KeyG",
|
|
23
|
+
KeyH = "KeyH",
|
|
24
|
+
KeyI = "KeyI",
|
|
25
|
+
KeyJ = "KeyJ",
|
|
26
|
+
KeyK = "KeyK",
|
|
27
|
+
KeyL = "KeyL",
|
|
28
|
+
KeyM = "KeyM",
|
|
29
|
+
KeyN = "KeyN",
|
|
30
|
+
KeyO = "KeyO",
|
|
31
|
+
KeyP = "KeyP",
|
|
32
|
+
KeyQ = "KeyQ",
|
|
33
|
+
KeyR = "KeyR",
|
|
34
|
+
KeyS = "KeyS",
|
|
35
|
+
KeyT = "KeyT",
|
|
36
|
+
KeyU = "KeyU",
|
|
37
|
+
KeyV = "KeyV",
|
|
38
|
+
KeyW = "KeyW",
|
|
39
|
+
KeyX = "KeyX",
|
|
40
|
+
KeyY = "KeyY",
|
|
41
|
+
KeyZ = "KeyZ",
|
|
42
|
+
Digit0 = "Digit0",
|
|
43
|
+
Digit1 = "Digit1",
|
|
44
|
+
Digit2 = "Digit2",
|
|
45
|
+
Digit3 = "Digit3",
|
|
46
|
+
Digit4 = "Digit4",
|
|
47
|
+
Digit5 = "Digit5",
|
|
48
|
+
Digit6 = "Digit6",
|
|
49
|
+
Digit7 = "Digit7",
|
|
50
|
+
Digit8 = "Digit8",
|
|
51
|
+
Digit9 = "Digit9"
|
|
52
|
+
}
|
package/dist/keys.js
ADDED
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
// keys.ts
|
|
2
|
+
// to view the Keys easily with the Autocomplete Feature
|
|
3
|
+
export var Key;
|
|
4
|
+
(function (Key) {
|
|
5
|
+
Key["ArrowUp"] = "ArrowUp";
|
|
6
|
+
Key["ArrowDown"] = "ArrowDown";
|
|
7
|
+
Key["ArrowLeft"] = "ArrowLeft";
|
|
8
|
+
Key["ArrowRight"] = "ArrowRight";
|
|
9
|
+
Key["Space"] = "Space";
|
|
10
|
+
Key["Enter"] = "Enter";
|
|
11
|
+
Key["Escape"] = "Escape";
|
|
12
|
+
Key["Tab"] = "Tab";
|
|
13
|
+
Key["ShiftLeft"] = "ShiftLeft";
|
|
14
|
+
Key["ShiftRight"] = "ShiftRight";
|
|
15
|
+
Key["ControlLeft"] = "ControlLeft";
|
|
16
|
+
Key["ControlRight"] = "ControlRight";
|
|
17
|
+
Key["AltLeft"] = "AltLeft";
|
|
18
|
+
Key["AltRight"] = "AltRight";
|
|
19
|
+
Key["KeyA"] = "KeyA";
|
|
20
|
+
Key["KeyB"] = "KeyB";
|
|
21
|
+
Key["KeyC"] = "KeyC";
|
|
22
|
+
Key["KeyD"] = "KeyD";
|
|
23
|
+
Key["KeyE"] = "KeyE";
|
|
24
|
+
Key["KeyF"] = "KeyF";
|
|
25
|
+
Key["KeyG"] = "KeyG";
|
|
26
|
+
Key["KeyH"] = "KeyH";
|
|
27
|
+
Key["KeyI"] = "KeyI";
|
|
28
|
+
Key["KeyJ"] = "KeyJ";
|
|
29
|
+
Key["KeyK"] = "KeyK";
|
|
30
|
+
Key["KeyL"] = "KeyL";
|
|
31
|
+
Key["KeyM"] = "KeyM";
|
|
32
|
+
Key["KeyN"] = "KeyN";
|
|
33
|
+
Key["KeyO"] = "KeyO";
|
|
34
|
+
Key["KeyP"] = "KeyP";
|
|
35
|
+
Key["KeyQ"] = "KeyQ";
|
|
36
|
+
Key["KeyR"] = "KeyR";
|
|
37
|
+
Key["KeyS"] = "KeyS";
|
|
38
|
+
Key["KeyT"] = "KeyT";
|
|
39
|
+
Key["KeyU"] = "KeyU";
|
|
40
|
+
Key["KeyV"] = "KeyV";
|
|
41
|
+
Key["KeyW"] = "KeyW";
|
|
42
|
+
Key["KeyX"] = "KeyX";
|
|
43
|
+
Key["KeyY"] = "KeyY";
|
|
44
|
+
Key["KeyZ"] = "KeyZ";
|
|
45
|
+
Key["Digit0"] = "Digit0";
|
|
46
|
+
Key["Digit1"] = "Digit1";
|
|
47
|
+
Key["Digit2"] = "Digit2";
|
|
48
|
+
Key["Digit3"] = "Digit3";
|
|
49
|
+
Key["Digit4"] = "Digit4";
|
|
50
|
+
Key["Digit5"] = "Digit5";
|
|
51
|
+
Key["Digit6"] = "Digit6";
|
|
52
|
+
Key["Digit7"] = "Digit7";
|
|
53
|
+
Key["Digit8"] = "Digit8";
|
|
54
|
+
Key["Digit9"] = "Digit9";
|
|
55
|
+
})(Key || (Key = {}));
|
package/dist/logger.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare const dlog: (...args: any[]) => void;
|
package/dist/logger.js
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
// Debug Log Function
|
|
2
|
+
export const dlog =
|
|
3
|
+
// (import.meta.env?.DEV ?? true) // Default true, falls undefined
|
|
4
|
+
// ? (...args: any[]) => {
|
|
5
|
+
// const now = new Date();
|
|
6
|
+
// const time =
|
|
7
|
+
// `[${now.getHours().toString().padStart(2, "0")}:` +
|
|
8
|
+
// `${now.getMinutes().toString().padStart(2, "0")}:` +
|
|
9
|
+
// `${now.getSeconds().toString().padStart(2, "0")}.` +
|
|
10
|
+
// `${now.getMilliseconds().toString().padStart(3, "0")}]`;
|
|
11
|
+
// console.log(time, ...args);
|
|
12
|
+
// }
|
|
13
|
+
// :
|
|
14
|
+
(...args) => { }; // Release: nichts loggen
|
|
15
|
+
// Dlog in Release Mode:
|
|
16
|
+
// export const dlog = () => {};
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import { type Rect } from "./types/rectangle";
|
|
2
|
+
export declare function renderText(ctx: CanvasRenderingContext2D, text: string, x: number, y: number, color?: string, font?: string): void;
|
|
3
|
+
type CharMap = Record<string, Rect>;
|
|
4
|
+
export declare function renderBitmapText(ctx: CanvasRenderingContext2D, text: string, x: number, y: number, sprite: HTMLImageElement, charMap: CharMap, scale?: number): void;
|
|
5
|
+
export {};
|
package/dist/renderer.js
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
// Function to render Text
|
|
2
|
+
export function renderText(ctx, text, x, y, color = "white", font = "20px Arial") {
|
|
3
|
+
ctx.fillStyle = color;
|
|
4
|
+
ctx.font = font;
|
|
5
|
+
ctx.textBaseline = "top";
|
|
6
|
+
ctx.fillText(text, x, y);
|
|
7
|
+
}
|
|
8
|
+
export function renderBitmapText(ctx, text, x, y, sprite, charMap, scale = 1) {
|
|
9
|
+
let offsetX = 0;
|
|
10
|
+
for (const c of text) {
|
|
11
|
+
const rect = charMap[c];
|
|
12
|
+
if (!rect)
|
|
13
|
+
continue;
|
|
14
|
+
ctx.drawImage(sprite, rect.x, rect.y, rect.width, rect.height, x + offsetX, y, rect.width * scale, rect.height * scale);
|
|
15
|
+
offsetX += rect.width * scale;
|
|
16
|
+
}
|
|
17
|
+
}
|
package/dist/save.d.ts
ADDED
package/dist/save.js
ADDED
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
const SAVE_KEY = "my_game_save";
|
|
2
|
+
export function saveGame(data) {
|
|
3
|
+
try {
|
|
4
|
+
const json = JSON.stringify(data);
|
|
5
|
+
localStorage.setItem(SAVE_KEY, json);
|
|
6
|
+
console.log("Game saved!");
|
|
7
|
+
}
|
|
8
|
+
catch (err) {
|
|
9
|
+
console.error("Save failed:", err);
|
|
10
|
+
}
|
|
11
|
+
}
|
|
12
|
+
export function loadGame() {
|
|
13
|
+
try {
|
|
14
|
+
const json = localStorage.getItem(SAVE_KEY);
|
|
15
|
+
if (!json)
|
|
16
|
+
return null;
|
|
17
|
+
return JSON.parse(json);
|
|
18
|
+
}
|
|
19
|
+
catch (err) {
|
|
20
|
+
console.error("Load failed:", err);
|
|
21
|
+
return null;
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
export function clearSave() {
|
|
25
|
+
localStorage.removeItem(SAVE_KEY);
|
|
26
|
+
console.log("Save cleared!");
|
|
27
|
+
}
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
export type Texture = HTMLImageElement | undefined;
|
|
2
|
+
export declare function loadTextureAsync(src: string): Promise<HTMLImageElement>;
|
|
3
|
+
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;
|
package/dist/texture.js
ADDED
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import { dlog } from "./logger";
|
|
2
|
+
const textures = {};
|
|
3
|
+
function getresourcepath(path) {
|
|
4
|
+
return "resources/" + path;
|
|
5
|
+
}
|
|
6
|
+
// Function to load a Texture Async
|
|
7
|
+
export function loadTextureAsync(src) {
|
|
8
|
+
return new Promise((resolve, reject) => {
|
|
9
|
+
// Wenn schon geladen
|
|
10
|
+
const existing = getTexture(src);
|
|
11
|
+
if (existing)
|
|
12
|
+
return resolve(existing);
|
|
13
|
+
const img = new Image();
|
|
14
|
+
// 🔹 Hier Pfad modifizieren, z.B. Prefix hinzufügen
|
|
15
|
+
let finalSrc = getresourcepath(src);
|
|
16
|
+
img.onload = () => {
|
|
17
|
+
// Cache speichern
|
|
18
|
+
textures[finalSrc] = img; // optional: Original-Pfad oder finalSrc?
|
|
19
|
+
resolve(img);
|
|
20
|
+
};
|
|
21
|
+
img.onerror = () => {
|
|
22
|
+
const msg = `❌ Failed to load texture: ${finalSrc}`;
|
|
23
|
+
console.error(msg);
|
|
24
|
+
reject(new Error(msg));
|
|
25
|
+
};
|
|
26
|
+
// 🔹 Bild laden mit finalem Pfad
|
|
27
|
+
img.src = finalSrc;
|
|
28
|
+
});
|
|
29
|
+
}
|
|
30
|
+
export function getTexture(src) {
|
|
31
|
+
return textures[getresourcepath(src)];
|
|
32
|
+
}
|
|
33
|
+
export function drawTexture(ctx, texture, x, y, width, height) {
|
|
34
|
+
if (!texture) {
|
|
35
|
+
dlog("Texture not found");
|
|
36
|
+
ctx.fillStyle = "magenta";
|
|
37
|
+
ctx.fillRect(x, y, width ?? 32, height ?? 32);
|
|
38
|
+
return;
|
|
39
|
+
}
|
|
40
|
+
if (width && height) {
|
|
41
|
+
ctx.drawImage(texture, x, y, width, height);
|
|
42
|
+
}
|
|
43
|
+
else {
|
|
44
|
+
ctx.drawImage(texture, x, y);
|
|
45
|
+
}
|
|
46
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
export function invertcolor(color) {
|
|
2
|
+
return {
|
|
3
|
+
r: 255 - color.r,
|
|
4
|
+
g: 255 - color.g,
|
|
5
|
+
b: 255 - color.b,
|
|
6
|
+
...(color.a !== undefined ? { a: color.a } : {}) // optional alpha behalten
|
|
7
|
+
};
|
|
8
|
+
}
|
|
9
|
+
export function invertHexColor(hex) {
|
|
10
|
+
// Entferne das führende #
|
|
11
|
+
const cleanHex = hex.replace('#', '');
|
|
12
|
+
// Wandeln in R, G, B
|
|
13
|
+
const r = 255 - parseInt(cleanHex.slice(0, 2), 16);
|
|
14
|
+
const g = 255 - parseInt(cleanHex.slice(2, 4), 16);
|
|
15
|
+
const b = 255 - parseInt(cleanHex.slice(4, 6), 16);
|
|
16
|
+
// Zurück in Hex-String
|
|
17
|
+
return '#' + [r, g, b].map(x => x.toString(16).padStart(2, '0')).join('');
|
|
18
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
// Clamp
|
|
2
|
+
export function clamp(input, min, max) {
|
|
3
|
+
return Math.min(Math.max(input, min), max);
|
|
4
|
+
}
|
|
5
|
+
// Lerp
|
|
6
|
+
export function lerp(start, end, t) {
|
|
7
|
+
return (start + (end - start) * t);
|
|
8
|
+
}
|
|
9
|
+
// Map
|
|
10
|
+
export function map(value, inMin, inMax, outMin, outMax) {
|
|
11
|
+
return (outMax - outMin) * ((value - inMin) / (inMax - inMin)) + outMin;
|
|
12
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { Mouse } from "../input";
|
|
2
|
+
import { type Vector2d } from "./vector2d";
|
|
3
|
+
export type Rect = {
|
|
4
|
+
x: number;
|
|
5
|
+
y: number;
|
|
6
|
+
width: number;
|
|
7
|
+
height: number;
|
|
8
|
+
};
|
|
9
|
+
export declare function centerRectX(rect: Rect): number;
|
|
10
|
+
export declare function centerRectY(rect: Rect): number;
|
|
11
|
+
export declare function centerRect(rect: Rect): Vector2d;
|
|
12
|
+
export declare function isPointInRect(x: number, y: number, rect: Rect): boolean;
|
|
13
|
+
export declare function isMouseInRect(mouse: Mouse, rect: Rect): boolean;
|
|
14
|
+
export declare function isRectClicked(mouse: Mouse, rect: Rect): boolean;
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
// Rectangle Type for Hitboxes
|
|
2
|
+
// Function to get the Center of the Width of the Object
|
|
3
|
+
export function centerRectX(rect) {
|
|
4
|
+
return (rect.x + (rect.width / 2));
|
|
5
|
+
}
|
|
6
|
+
// Function to get the Center of the Height of the Object
|
|
7
|
+
export function centerRectY(rect) {
|
|
8
|
+
return (rect.y + (rect.height / 2));
|
|
9
|
+
}
|
|
10
|
+
// Get the Center of a Rectangle
|
|
11
|
+
export function centerRect(rect) {
|
|
12
|
+
let vector = { x: centerRectX(rect), y: centerRectY(rect) };
|
|
13
|
+
return vector;
|
|
14
|
+
}
|
|
15
|
+
// Check if a Point in the Rectangle
|
|
16
|
+
export function isPointInRect(x, y, rect) {
|
|
17
|
+
return (x >= rect.x &&
|
|
18
|
+
x <= rect.x + rect.width &&
|
|
19
|
+
y >= rect.y &&
|
|
20
|
+
y <= rect.y + rect.height);
|
|
21
|
+
}
|
|
22
|
+
export function isMouseInRect(mouse, rect) {
|
|
23
|
+
return (mouse.x >= rect.x &&
|
|
24
|
+
mouse.x <= rect.x + rect.width &&
|
|
25
|
+
mouse.y >= rect.y &&
|
|
26
|
+
mouse.y <= rect.y + rect.height);
|
|
27
|
+
}
|
|
28
|
+
// Function to check if a Rectangle is clicked
|
|
29
|
+
export function isRectClicked(mouse, rect) {
|
|
30
|
+
return isMouseInRect(mouse, rect) && mouse.justPressed;
|
|
31
|
+
}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
export type Vector2d = {
|
|
2
|
+
x: number;
|
|
3
|
+
y: number;
|
|
4
|
+
};
|
|
5
|
+
export declare function normalize2d(vector: Vector2d): Vector2d;
|
|
6
|
+
export declare function clamp2d(vector: Vector2d, min: Vector2d, max: Vector2d): Vector2d;
|
|
7
|
+
export declare function lerp2d(start: Vector2d, end: Vector2d, t: Vector2d): Vector2d;
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
// 2 Dimensional Vector Type
|
|
2
|
+
import { clamp, lerp } from "./math-utils";
|
|
3
|
+
// Function to normalize a Vector 2d
|
|
4
|
+
export function normalize2d(vector) {
|
|
5
|
+
// Check if the Vector is zero because then you dont need to
|
|
6
|
+
// calculate sth
|
|
7
|
+
if (vector.x == 0 && vector.y == 0) {
|
|
8
|
+
return vector;
|
|
9
|
+
}
|
|
10
|
+
let produkt = vector.x * vector.x + vector.y * vector.y;
|
|
11
|
+
let root = Math.sqrt(produkt);
|
|
12
|
+
vector.x = vector.x / root;
|
|
13
|
+
vector.y = vector.y / root;
|
|
14
|
+
return vector;
|
|
15
|
+
}
|
|
16
|
+
// Function to clamp a Vector 2d
|
|
17
|
+
export function clamp2d(vector, min, max) {
|
|
18
|
+
return {
|
|
19
|
+
x: clamp(vector.x, min.x, max.x),
|
|
20
|
+
y: clamp(vector.y, min.y, max.y),
|
|
21
|
+
};
|
|
22
|
+
}
|
|
23
|
+
// Lerp for a 2d Vector
|
|
24
|
+
export function lerp2d(start, end, t) {
|
|
25
|
+
return {
|
|
26
|
+
x: lerp(start.x, end.x, t.x),
|
|
27
|
+
y: lerp(start.y, end.y, t.y),
|
|
28
|
+
};
|
|
29
|
+
}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
export type Vector3d = {
|
|
2
|
+
x: number;
|
|
3
|
+
y: number;
|
|
4
|
+
z: number;
|
|
5
|
+
};
|
|
6
|
+
export declare function normalize3d(vector: Vector3d): Vector3d;
|
|
7
|
+
export declare function clamp3d(vector: Vector3d, min: Vector3d, max: Vector3d): Vector3d;
|
|
8
|
+
export declare function lerp3d(start: Vector3d, end: Vector3d, t: Vector3d): Vector3d;
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
// 3d Vector
|
|
2
|
+
import { clamp, lerp } from "./math-utils";
|
|
3
|
+
// Function to normalize a Vector 2d
|
|
4
|
+
export function normalize3d(vector) {
|
|
5
|
+
// Check if the Vector is zero because then you dont need to
|
|
6
|
+
// calculate sth
|
|
7
|
+
if (vector.x == 0 && vector.y == 0 && vector.z) {
|
|
8
|
+
return vector;
|
|
9
|
+
}
|
|
10
|
+
let produkt = vector.x * vector.x + vector.y * vector.y + vector.z * vector.z;
|
|
11
|
+
let root = Math.sqrt(produkt);
|
|
12
|
+
vector.x = vector.x / root;
|
|
13
|
+
vector.y = vector.y / root;
|
|
14
|
+
vector.z = vector.z / root;
|
|
15
|
+
return vector;
|
|
16
|
+
}
|
|
17
|
+
// Function to clamp a Vector 2d
|
|
18
|
+
export function clamp3d(vector, min, max) {
|
|
19
|
+
return {
|
|
20
|
+
x: clamp(vector.x, min.x, max.x),
|
|
21
|
+
y: clamp(vector.y, min.y, max.y),
|
|
22
|
+
z: clamp(vector.y, min.y, max.y),
|
|
23
|
+
};
|
|
24
|
+
}
|
|
25
|
+
// Lerp for a 2d Vector
|
|
26
|
+
export function lerp3d(start, end, t) {
|
|
27
|
+
return {
|
|
28
|
+
x: lerp(start.x, end.x, t.x),
|
|
29
|
+
y: lerp(start.y, end.y, t.y),
|
|
30
|
+
z: lerp(start.y, end.y, t.y),
|
|
31
|
+
};
|
|
32
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@shadowdara/webgameengine",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "A TypeScript game library to make HTML Games",
|
|
5
|
+
"main": "dist/index.js",
|
|
6
|
+
"types": "dist/index.d.ts",
|
|
7
|
+
"sideEffects": false,
|
|
8
|
+
"files": [
|
|
9
|
+
"dist"
|
|
10
|
+
],
|
|
11
|
+
"scripts": {
|
|
12
|
+
"build": "tsc",
|
|
13
|
+
"pack": "npm run build && npm pack"
|
|
14
|
+
},
|
|
15
|
+
"bin": {
|
|
16
|
+
"webgameengine": "dist/build/tools/devserver/cli.js"
|
|
17
|
+
},
|
|
18
|
+
"exports": {
|
|
19
|
+
".": "./dist/index.js",
|
|
20
|
+
"./build": "./dist/build/index.js",
|
|
21
|
+
"./types": "./dist/types/index.js"
|
|
22
|
+
},
|
|
23
|
+
"keywords": [
|
|
24
|
+
"game",
|
|
25
|
+
"typescript"
|
|
26
|
+
],
|
|
27
|
+
"author": "Shadowdara",
|
|
28
|
+
"license": "MIT",
|
|
29
|
+
"devDependencies": {
|
|
30
|
+
"@types/node": "^25.5.2",
|
|
31
|
+
"@types/ws": "^8.18.1",
|
|
32
|
+
"typescript": "^6.0.2"
|
|
33
|
+
},
|
|
34
|
+
"dependencies": {
|
|
35
|
+
"esbuild": "^0.28.0",
|
|
36
|
+
"esbuild-register": "^3.6.0",
|
|
37
|
+
"mime": "^4.1.0",
|
|
38
|
+
"ws": "^8.20.0"
|
|
39
|
+
}
|
|
40
|
+
}
|