toaimin-indie-engine 1.0.1 → 1.0.4

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.
Files changed (3) hide show
  1. package/README.md +121 -37
  2. package/cli/bin.cjs +75 -19
  3. package/package.json +1 -1
package/README.md CHANGED
@@ -1,55 +1,139 @@
1
- # FrameworkGame
1
+ # toaimin-indie-engine 🚀
2
2
 
3
- A production-grade, modular 2D game framework in TypeScript for Web/HTML5 tailored for indie game developers.
3
+ A production-grade, highly scalable, and developer-friendly **2D Game Engine** built purely in TypeScript. Designed specifically for Indie Developers who want maximum performance with minimum boilerplate.
4
4
 
5
- ## Features
5
+ ![NPM Version](https://img.shields.io/npm/v/toaimin-indie-engine)
6
+ ![License](https://img.shields.io/npm/l/toaimin-indie-engine)
6
7
 
7
- - **Zero Heavy Runtime Dependencies**: Pure TypeScript, minimal footprint, maximum performance.
8
- - **Fixed-Timestep Simulation**: 60Hz deterministic game loop with render interpolation (`alpha`) and spiral-of-death protection.
9
- - **Robust Math & Affine Transforms**:
10
- - `Vector2`: High-performance 2D vector with mutable in-place chaining, immutable helpers, and zero-division guards.
11
- - `Matrix2D`: 3x3 Canvas-compatible affine transformation matrix with determinant, inversion, point/direction transform.
12
- - `Transform2D`: Hierarchical scene-graph transform with parent-child propagation and lazy dirty-flag caching.
13
- - `Rect` & `Circle`: 2D spatial primitives with comprehensive intersection and containment algorithms.
14
- - `MathUtils`: Frame-rate independent exponential damping (`damp`), angle wrapping, clamp, lerp, and RNG helpers.
15
- - `Color`: High-precision RGBA color with Hex, RGB, RGBA, HSL parsing and string formatting.
16
- - **Core Engine Lifecycle**:
17
- - `GameLoop`: Accumulator-based loop separating `update(dt)`, `fixedUpdate(fixedDt)`, and `render(alpha)`.
18
- - `Time`: Scaled and unscaled delta time tracking with rolling average FPS counter.
19
- - `EventEmitter`: Type-safe pub/sub event bus with snapshot iteration and unbind helpers.
8
+ ## 🌟 Key Features
20
9
 
21
- ## Installation & Build
10
+ * **Fluent API (Builder Pattern):** Create complex game entities without classes. Chain `.withSprite().withPhysics()` seamlessly.
11
+ * **CLI Tool (`indie-engine`):** Generate a complete game project with `init`, run with `dev`, and package with `build` in one click.
12
+ * **Tiled Map Integration (v1.0.1):** Load `.json` tilemaps exported from the popular *Tiled Editor*. Automatically generates static physics colliders for your map layers!
13
+ * **Real-time Multiplayer (v1.0.2):** Built-in Native WebSocket client and `NetworkIdentity` component. Sync player positions globally with zero extra libraries.
14
+ * **Tweening & Easing (v1.0.3):** Butter-smooth animations for UI and sprites. 12+ Easing curves included (Bounce, Elastic, Quad, etc).
15
+ * **Robust Physics 2D:** Rigid bodies, Box/Circle colliders, and contact solvers built-in.
16
+ * **Zero Heavy Dependencies:** Extremely lightweight and fast.
17
+
18
+ ---
19
+
20
+ ## 📦 Quick Start (CLI)
21
+
22
+ No need to configure Webpack or Vite. Just use our global CLI!
22
23
 
23
24
  ```bash
24
- # Build library (ESM, CJS, and TypeScript typings)
25
- npm run build
25
+ # 1. Create a new game project
26
+ npx toaimin-indie-engine init my-awesome-game
26
27
 
27
- # Run unit test suite
28
- npm run test
28
+ # 2. Go to your folder
29
+ cd my-awesome-game
29
30
 
30
- # Run interactive demo
31
+ # 3. Start the dev server
31
32
  npm run dev
32
33
 
33
- # Build demo for production
34
- npm run build:demo
34
+ # 4. Build for Android/iOS (Auto-installs Capacitor & SDK wrappers)
35
+ npm run build:android
36
+ npm run build:ios
37
+
38
+ # 5. Build for Desktop PC/Mac (Auto-installs Electron)
39
+ npm run build:desktop
40
+ ```
41
+
42
+ ---
43
+
44
+ ## 🎮 Writing Code the "Indie" Way
45
+
46
+ Forget messy boilerplate. Here is how you create a moving player:
47
+
48
+ ```typescript
49
+ import { createIndieGame, createEntity, Scene } from 'toaimin-indie-engine';
50
+
51
+ class MainScene extends Scene {
52
+ override onStart() {
53
+ // Spawn a player with Physics and Sprite!
54
+ createEntity('Player')
55
+ .withPosition(400, 300)
56
+ .withSprite('assets/ninja.png')
57
+ .withPhysics({ type: 'dynamic', mass: 10 })
58
+ .withBoxCollider(64, 64)
59
+ .onUpdate((dt, entity) => {
60
+ // Your logic here (e.g. Move right)
61
+ entity.transform.position.x += 100 * dt;
62
+ })
63
+ .build()
64
+ .setParent(this.root);
65
+ }
66
+ }
67
+
68
+ // Bootstrap the game!
69
+ createIndieGame()
70
+ .setSize(800, 600)
71
+ .setBackground('#222')
72
+ .addScene('Main', MainScene)
73
+ .start();
74
+ ```
75
+
76
+ ---
77
+
78
+ ## 🗺️ Tiled Map Support (Auto Collisions)
79
+
80
+ Draw your map in *Tiled*, export as JSON, and load it into your game. If you name your collision layer `"Collisions"`, the engine will automatically generate invisible walls for you!
81
+
82
+ ```typescript
83
+ import { createTilemap } from 'toaimin-indie-engine';
84
+
85
+ const map = createTilemap('assets/level1.json', 'Collisions');
86
+ map.setParent(this.root);
35
87
  ```
36
88
 
37
- ## Architecture
89
+ ---
90
+
91
+ ## 🌐 Real-time Multiplayer Plugin
92
+
93
+ Make your game online in literally 2 steps:
38
94
 
95
+ ```typescript
96
+ import { MultiplayerPlugin, createEntity } from 'toaimin-indie-engine';
97
+
98
+ // 1. Enable Plugin
99
+ createIndieGame()
100
+ .use(new MultiplayerPlugin({ url: 'wss://your-server.com' }))
101
+ .start();
102
+
103
+ // 2. Add NetworkIdentity to sync position globally
104
+ createEntity('OnlinePlayer')
105
+ .withPosition(100, 100)
106
+ .withNetworkIdentity('player_123', true) // true = Local Player
107
+ .build();
39
108
  ```
40
- src/
41
- ├── index.ts # Framework public entry point
42
- ├── core/ # GameLoop, Time, EventEmitter
43
- ├── math/ # Vector2, Matrix2D, Transform2D, Rect, Circle, MathUtils, Color
44
- ├── rendering/ # Canvas2D renderer, camera, sprites, particles (M2)
45
- ├── physics/ # Rigid bodies, colliders, contact solver (M3)
46
- ├── input/ # Keyboard, pointer, touch, virtual joystick (M2)
47
- ├── audio/ # Web Audio bus, voice pool, synth (M4)
48
- ├── scene/ # Scene lifecycle, modal stack, transitions (M4)
49
- ├── ui/ # Retained-mode UI hierarchy, widgets (M4)
50
- └── entity/ # Entity-component model (M5)
109
+
110
+ ---
111
+
112
+ ## 🎬 Tweening Animations
113
+
114
+ Animate UI buttons, coins, or anything smoothly!
115
+
116
+ ```typescript
117
+ import { tween } from 'toaimin-indie-engine';
118
+
119
+ // Make a coin bounce up
120
+ tween(coin.transform.position)
121
+ .to({ y: coin.transform.position.y - 50 }, 1000)
122
+ .easing('BounceOut')
123
+ .start();
51
124
  ```
52
125
 
126
+ ---
127
+
128
+ ## 🏛️ Architecture
129
+
130
+ If you prefer the hardcore OOP way, the engine fully supports ECS (Entity Component System):
131
+
132
+ * `Game`: Core Orchestrator
133
+ * `Entity`: The Node/GameObject
134
+ * `Component`: Behavior logic (e.g., `ScriptComponent`, `SpriteComponent`)
135
+ * `System`: PhysicsWorld2D, SceneManager, InputManager
136
+
53
137
  ## License
54
138
 
55
- MIT
139
+ MIT © Toaimin
package/cli/bin.cjs CHANGED
@@ -1,17 +1,22 @@
1
1
  #!/usr/bin/env node
2
2
  const fs = require('fs');
3
3
  const path = require('path');
4
- const { spawn } = require('child_process');
4
+ const { spawn, execSync } = require('child_process');
5
5
 
6
6
  const args = process.argv.slice(2);
7
7
  const command = args[0];
8
8
 
9
9
  if (!command) {
10
10
  console.log('Usage: indie-engine <command>');
11
- console.log('Commands: init, dev, build');
11
+ console.log('Commands: init, dev, build, build:android, build:ios, build:desktop');
12
12
  process.exit(1);
13
13
  }
14
14
 
15
+ const runCommand = (cmd, cwd = process.cwd()) => {
16
+ console.log(`> ${cmd}`);
17
+ execSync(cmd, { stdio: 'inherit', cwd });
18
+ };
19
+
15
20
  if (command === 'init') {
16
21
  const projectName = args[1] || 'my-game';
17
22
  const targetDir = path.resolve(process.cwd(), projectName);
@@ -31,19 +36,19 @@ if (command === 'init') {
31
36
  private: true,
32
37
  scripts: {
33
38
  dev: "indie-engine dev",
34
- build: "indie-engine build"
39
+ build: "indie-engine build",
40
+ "build:android": "indie-engine build:android",
41
+ "build:ios": "indie-engine build:ios",
42
+ "build:desktop": "indie-engine build:desktop"
35
43
  },
36
44
  dependencies: {
37
- "framework-game": "latest"
45
+ "toaimin-indie-engine": "latest"
38
46
  }
39
47
  };
40
48
 
41
- fs.writeFileSync(
42
- path.join(targetDir, 'package.json'),
43
- JSON.stringify(pkgJson, null, 2)
44
- );
49
+ fs.writeFileSync(path.join(targetDir, 'package.json'), JSON.stringify(pkgJson, null, 2));
45
50
 
46
- const mainTs = `import { createIndieGame, createEntity, Scene } from 'framework-game';
51
+ const mainTs = `import { createIndieGame, createEntity, Scene } from 'toaimin-indie-engine';
47
52
 
48
53
  class MainScene extends Scene {
49
54
  override onStart() {
@@ -51,9 +56,6 @@ class MainScene extends Scene {
51
56
  .withPosition(400, 300)
52
57
  .withSprite({ texture: 'assets/player.png', width: 64, height: 64 })
53
58
  .withPhysics({ type: 'dynamic', mass: 10 })
54
- .onUpdate((dt, entity) => {
55
- // Player logic here
56
- })
57
59
  .build()
58
60
  .setParent(this.root);
59
61
  }
@@ -73,10 +75,7 @@ createIndieGame()
73
75
  <head>
74
76
  <meta charset="UTF-8">
75
77
  <title>${projectName}</title>
76
- <style>
77
- body { margin: 0; background: #000; display: flex; justify-content: center; align-items: center; height: 100vh; }
78
- canvas { display: block; }
79
- </style>
78
+ <style>body { margin: 0; background: #000; display: flex; justify-content: center; align-items: center; height: 100vh; }</style>
80
79
  </head>
81
80
  <body>
82
81
  <script type="module" src="/src/main.ts"></script>
@@ -86,13 +85,70 @@ createIndieGame()
86
85
  fs.writeFileSync(path.join(targetDir, 'index.html'), html);
87
86
 
88
87
  console.log(`Created project ${projectName}`);
89
- console.log(`cd ${projectName}`);
90
- console.log(`npm install`);
91
- console.log(`npm run dev`);
88
+ console.log(`cd ${projectName} && npm install`);
92
89
  } else if (command === 'dev' || command === 'build') {
93
90
  const viteRunnerPath = path.join(__dirname, 'vite-runner.cjs');
94
91
  const child = spawn('node', [viteRunnerPath, command], { stdio: 'inherit' });
95
92
  child.on('exit', (code) => process.exit(code));
93
+ } else if (command === 'build:android' || command === 'build:ios') {
94
+ const platform = command.split(':')[1];
95
+ console.log(`🚀 Building for ${platform.toUpperCase()}...`);
96
+
97
+ // 1. Build web first
98
+ const viteRunnerPath = path.join(__dirname, 'vite-runner.cjs');
99
+ runCommand(`node "${viteRunnerPath}" build`);
100
+
101
+ // 2. Init capacitor if missing
102
+ if (!fs.existsSync(path.join(process.cwd(), 'capacitor.config.json'))) {
103
+ console.log('Installing Capacitor CLI...');
104
+ runCommand('npm install @capacitor/cli @capacitor/core');
105
+ runCommand(`npx cap init mygame com.indie.game --web-dir dist`);
106
+ }
107
+
108
+ // 3. Add platform if missing
109
+ if (!fs.existsSync(path.join(process.cwd(), platform))) {
110
+ console.log(`Installing Capacitor ${platform}...`);
111
+ runCommand(`npm install @capacitor/${platform}`);
112
+ runCommand(`npx cap add ${platform}`);
113
+ }
114
+
115
+ // 4. Sync and open
116
+ runCommand(`npx cap sync ${platform}`);
117
+ console.log(`✅ Ready! Run 'npx cap open ${platform}' to build the binary.`);
118
+ } else if (command === 'build:desktop') {
119
+ console.log(`🚀 Building for DESKTOP (Electron)...`);
120
+
121
+ // 1. Build web
122
+ const viteRunnerPath = path.join(__dirname, 'vite-runner.cjs');
123
+ runCommand(`node "${viteRunnerPath}" build`);
124
+
125
+ // 2. Setup Electron
126
+ if (!fs.existsSync(path.join(process.cwd(), 'electron'))) {
127
+ fs.mkdirSync(path.join(process.cwd(), 'electron'));
128
+ const mainJs = `
129
+ const { app, BrowserWindow } = require('electron');
130
+ const path = require('path');
131
+ function createWindow() {
132
+ const win = new BrowserWindow({ width: 800, height: 600 });
133
+ win.loadFile(path.join(__dirname, '../dist/index.html'));
134
+ }
135
+ app.whenReady().then(createWindow);
136
+ `;
137
+ fs.writeFileSync(path.join(process.cwd(), 'electron', 'main.js'), mainJs);
138
+
139
+ // Modify package.json to point to main
140
+ const pkgPath = path.join(process.cwd(), 'package.json');
141
+ const pkg = JSON.parse(fs.readFileSync(pkgPath, 'utf8'));
142
+ pkg.main = "electron/main.js";
143
+ fs.writeFileSync(pkgPath, JSON.stringify(pkg, null, 2));
144
+
145
+ console.log('Installing Electron dependencies...');
146
+ runCommand('npm install electron electron-builder --save-dev');
147
+ }
148
+
149
+ console.log(`✅ Desktop structure ready!`);
150
+ console.log(`Test: npx electron .`);
151
+ console.log(`Build: npx electron-builder --mac --win --linux`);
96
152
  } else {
97
153
  console.error(`Unknown command: ${command}`);
98
154
  process.exit(1);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "toaimin-indie-engine",
3
- "version": "1.0.1",
3
+ "version": "1.0.4",
4
4
  "description": "Production-grade 2D Game Framework in TypeScript for Web/HTML5 designed for indie game developers",
5
5
  "type": "module",
6
6
  "bin": {