bloody-engine 1.0.9 → 1.0.11
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 +32 -0
- package/dist/node/index.js +62 -11
- package/dist/node/simulation/entity.d.ts +49 -2
- package/dist/node/simulation/entity.d.ts.map +1 -1
- package/package.json +2 -1
package/README.md
CHANGED
|
@@ -22,6 +22,38 @@ A WebGL-based 2.5D graphics engine for isometric rendering on Node.js, written i
|
|
|
22
22
|
npm install bloody-engine
|
|
23
23
|
```
|
|
24
24
|
|
|
25
|
+
## Understanding Coordinate Systems
|
|
26
|
+
|
|
27
|
+
**⚠️ IMPORTANT**: Before building your game, understand the coordinate systems to avoid inverted controls!
|
|
28
|
+
|
|
29
|
+
Bloody Engine uses different coordinate systems for different purposes. Mixing these up is the #1 cause of inverted controls.
|
|
30
|
+
|
|
31
|
+
### Quick Summary
|
|
32
|
+
|
|
33
|
+
| System | Used For | Y-Axis | Example |
|
|
34
|
+
|--------|----------|--------|---------|
|
|
35
|
+
| **Grid Space** | Game logic, entity positions | Y-UP (↓ Y = North/Up) | `entity.move(0, -1, 0)` moves up on screen |
|
|
36
|
+
| **Screen Space** | Rendering, camera, mouse | Y-DOWN (↓ Y = Down) | `camera.y += 10` moves camera down |
|
|
37
|
+
|
|
38
|
+
**Golden Rule**: Use grid space for game logic, transform to screen space only for rendering.
|
|
39
|
+
|
|
40
|
+
### Common Mistake
|
|
41
|
+
|
|
42
|
+
❌ **Wrong**: `camera.y += 1` for "up" movement (moves down on screen!)
|
|
43
|
+
✅ **Right**: Use direction deltas: `entity.move(0, -1, 0)` for North
|
|
44
|
+
|
|
45
|
+
### WASD Controls
|
|
46
|
+
|
|
47
|
+
| Key | Direction | Delta | Screen Effect |
|
|
48
|
+
|-----|-----------|-------|---------------|
|
|
49
|
+
| **W** / ↑ | North | `{dx: 0, dy: -1}` | ✅ Up |
|
|
50
|
+
| **S** / ↓ | South | `{dx: 0, dy: 1}` | ✅ Down |
|
|
51
|
+
| **A** / ← | West | `{dx: -1, dy: 0}` | ✅ Left |
|
|
52
|
+
| **D** / → | East | `{dx: 1, dy: 0}` | ✅ Right |
|
|
53
|
+
|
|
54
|
+
📖 **Full Guide**: [docs/COORDINATE_SYSTEMS.md](docs/COORDINATE_SYSTEMS.md)
|
|
55
|
+
🚀 **Interactive Demo**: Run `npm run demo:coordinates` after building
|
|
56
|
+
|
|
25
57
|
## API Overview
|
|
26
58
|
|
|
27
59
|
### Core Graphics
|
package/dist/node/index.js
CHANGED
|
@@ -3963,6 +3963,39 @@ class Entity {
|
|
|
3963
3963
|
get previousState() {
|
|
3964
3964
|
return this._previousState;
|
|
3965
3965
|
}
|
|
3966
|
+
/**
|
|
3967
|
+
* Get position as floats (for smooth movement and rendering)
|
|
3968
|
+
* Returns a copy to prevent mutation
|
|
3969
|
+
*/
|
|
3970
|
+
getPosition() {
|
|
3971
|
+
return {
|
|
3972
|
+
x: this._state.gridPos.xgrid,
|
|
3973
|
+
y: this._state.gridPos.ygrid,
|
|
3974
|
+
z: this._state.gridPos.zheight
|
|
3975
|
+
};
|
|
3976
|
+
}
|
|
3977
|
+
/**
|
|
3978
|
+
* Get grid position as integers (for logic that needs discrete cells)
|
|
3979
|
+
* Uses Math.floor for consistent cell mapping
|
|
3980
|
+
*/
|
|
3981
|
+
getGridPos() {
|
|
3982
|
+
return {
|
|
3983
|
+
x: Math.floor(this._state.gridPos.xgrid),
|
|
3984
|
+
y: Math.floor(this._state.gridPos.ygrid),
|
|
3985
|
+
z: Math.floor(this._state.gridPos.zheight)
|
|
3986
|
+
};
|
|
3987
|
+
}
|
|
3988
|
+
/**
|
|
3989
|
+
* Get rounded grid position (nearest integer)
|
|
3990
|
+
* Uses Math.round for picking/clicking operations
|
|
3991
|
+
*/
|
|
3992
|
+
getRoundedGridPos() {
|
|
3993
|
+
return {
|
|
3994
|
+
x: Math.round(this._state.gridPos.xgrid),
|
|
3995
|
+
y: Math.round(this._state.gridPos.ygrid),
|
|
3996
|
+
z: Math.round(this._state.gridPos.zheight)
|
|
3997
|
+
};
|
|
3998
|
+
}
|
|
3966
3999
|
/**
|
|
3967
4000
|
* Store current state as previous state before updating
|
|
3968
4001
|
*/
|
|
@@ -3975,10 +4008,18 @@ class Entity {
|
|
|
3975
4008
|
}
|
|
3976
4009
|
/**
|
|
3977
4010
|
* Set grid position directly (instant movement)
|
|
4011
|
+
* Accepts floats for sub-grid positioning
|
|
3978
4012
|
*/
|
|
3979
4013
|
setGridPos(x, y, z = 0) {
|
|
3980
4014
|
this._state.gridPos = { xgrid: x, ygrid: y, zheight: z };
|
|
3981
4015
|
}
|
|
4016
|
+
/**
|
|
4017
|
+
* Set position using integers (for discrete grid movement)
|
|
4018
|
+
* Convenience method for logic that works with integer coordinates
|
|
4019
|
+
*/
|
|
4020
|
+
setGridPosInt(x, y, z = 0) {
|
|
4021
|
+
this._state.gridPos = { xgrid: x, ygrid: y, zheight: z };
|
|
4022
|
+
}
|
|
3982
4023
|
/**
|
|
3983
4024
|
* Set velocity for continuous movement
|
|
3984
4025
|
*/
|
|
@@ -3987,13 +4028,26 @@ class Entity {
|
|
|
3987
4028
|
this._state.isMoving = x !== 0 || y !== 0 || z !== 0;
|
|
3988
4029
|
}
|
|
3989
4030
|
/**
|
|
3990
|
-
* Move by
|
|
4031
|
+
* Move by relative amount
|
|
4032
|
+
* Accepts floats for smooth, sub-grid movement
|
|
4033
|
+
* @param dx Movement in X (can be fractional)
|
|
4034
|
+
* @param dy Movement in Y (can be fractional)
|
|
4035
|
+
* @param dz Movement in Z (can be fractional)
|
|
3991
4036
|
*/
|
|
3992
4037
|
move(dx, dy, dz = 0) {
|
|
3993
4038
|
this._state.gridPos.xgrid += dx;
|
|
3994
4039
|
this._state.gridPos.ygrid += dy;
|
|
3995
4040
|
this._state.gridPos.zheight += dz;
|
|
3996
4041
|
}
|
|
4042
|
+
/**
|
|
4043
|
+
* Move by integer grid cells (for discrete movement)
|
|
4044
|
+
* Convenience method for logic that needs whole-cell movement
|
|
4045
|
+
*/
|
|
4046
|
+
moveGridCells(dx, dy, dz = 0) {
|
|
4047
|
+
this._state.gridPos.xgrid += Math.floor(dx);
|
|
4048
|
+
this._state.gridPos.ygrid += Math.floor(dy);
|
|
4049
|
+
this._state.gridPos.zheight += Math.floor(dz);
|
|
4050
|
+
}
|
|
3997
4051
|
/**
|
|
3998
4052
|
* Set rotation angle (in radians)
|
|
3999
4053
|
*/
|
|
@@ -4008,7 +4062,8 @@ class Entity {
|
|
|
4008
4062
|
}
|
|
4009
4063
|
/**
|
|
4010
4064
|
* Update entity based on velocity and delta time
|
|
4011
|
-
*
|
|
4065
|
+
* Now properly tracks fractional movement for smooth animation
|
|
4066
|
+
* Returns true if position changed (by any amount)
|
|
4012
4067
|
*/
|
|
4013
4068
|
updateVelocity(dt) {
|
|
4014
4069
|
if (!this._state.isMoving) {
|
|
@@ -4017,15 +4072,10 @@ class Entity {
|
|
|
4017
4072
|
const dx = this._state.velocity.x * this._state.speed * dt;
|
|
4018
4073
|
const dy = this._state.velocity.y * this._state.speed * dt;
|
|
4019
4074
|
const dz = this._state.velocity.z * this._state.speed * dt;
|
|
4020
|
-
|
|
4021
|
-
|
|
4022
|
-
|
|
4023
|
-
|
|
4024
|
-
Math.sign(dz)
|
|
4025
|
-
);
|
|
4026
|
-
return true;
|
|
4027
|
-
}
|
|
4028
|
-
return false;
|
|
4075
|
+
this._state.gridPos.xgrid += dx;
|
|
4076
|
+
this._state.gridPos.ygrid += dy;
|
|
4077
|
+
this._state.gridPos.zheight += dz;
|
|
4078
|
+
return dx !== 0 || dy !== 0 || dz !== 0;
|
|
4029
4079
|
}
|
|
4030
4080
|
/**
|
|
4031
4081
|
* Clone this entity (for state snapshots)
|
|
@@ -4041,6 +4091,7 @@ class Entity {
|
|
|
4041
4091
|
}
|
|
4042
4092
|
/**
|
|
4043
4093
|
* Serialize entity state for transmission/saving
|
|
4094
|
+
* Preserves floating-point positions for smooth movement
|
|
4044
4095
|
*/
|
|
4045
4096
|
serialize() {
|
|
4046
4097
|
return JSON.stringify({
|
|
@@ -2,6 +2,9 @@ import { GridCoord } from '../rendering/projection';
|
|
|
2
2
|
/**
|
|
3
3
|
* Entity state at a specific point in time
|
|
4
4
|
* Used for state interpolation and deterministic replay
|
|
5
|
+
*
|
|
6
|
+
* Position is stored as floats for smooth, continuous movement.
|
|
7
|
+
* Use getGridPos() to get integer grid coordinates when needed.
|
|
5
8
|
*/
|
|
6
9
|
export interface EntityState {
|
|
7
10
|
gridPos: GridCoord;
|
|
@@ -32,22 +35,64 @@ export declare class Entity {
|
|
|
32
35
|
* Get previous entity state (for interpolation)
|
|
33
36
|
*/
|
|
34
37
|
get previousState(): Readonly<EntityState>;
|
|
38
|
+
/**
|
|
39
|
+
* Get position as floats (for smooth movement and rendering)
|
|
40
|
+
* Returns a copy to prevent mutation
|
|
41
|
+
*/
|
|
42
|
+
getPosition(): {
|
|
43
|
+
x: number;
|
|
44
|
+
y: number;
|
|
45
|
+
z: number;
|
|
46
|
+
};
|
|
47
|
+
/**
|
|
48
|
+
* Get grid position as integers (for logic that needs discrete cells)
|
|
49
|
+
* Uses Math.floor for consistent cell mapping
|
|
50
|
+
*/
|
|
51
|
+
getGridPos(): {
|
|
52
|
+
x: number;
|
|
53
|
+
y: number;
|
|
54
|
+
z: number;
|
|
55
|
+
};
|
|
56
|
+
/**
|
|
57
|
+
* Get rounded grid position (nearest integer)
|
|
58
|
+
* Uses Math.round for picking/clicking operations
|
|
59
|
+
*/
|
|
60
|
+
getRoundedGridPos(): {
|
|
61
|
+
x: number;
|
|
62
|
+
y: number;
|
|
63
|
+
z: number;
|
|
64
|
+
};
|
|
35
65
|
/**
|
|
36
66
|
* Store current state as previous state before updating
|
|
37
67
|
*/
|
|
38
68
|
saveState(): void;
|
|
39
69
|
/**
|
|
40
70
|
* Set grid position directly (instant movement)
|
|
71
|
+
* Accepts floats for sub-grid positioning
|
|
41
72
|
*/
|
|
42
73
|
setGridPos(x: number, y: number, z?: number): void;
|
|
74
|
+
/**
|
|
75
|
+
* Set position using integers (for discrete grid movement)
|
|
76
|
+
* Convenience method for logic that works with integer coordinates
|
|
77
|
+
*/
|
|
78
|
+
setGridPosInt(x: number, y: number, z?: number): void;
|
|
43
79
|
/**
|
|
44
80
|
* Set velocity for continuous movement
|
|
45
81
|
*/
|
|
46
82
|
setVelocity(x: number, y: number, z?: number): void;
|
|
47
83
|
/**
|
|
48
|
-
* Move by
|
|
84
|
+
* Move by relative amount
|
|
85
|
+
* Accepts floats for smooth, sub-grid movement
|
|
86
|
+
* @param dx Movement in X (can be fractional)
|
|
87
|
+
* @param dy Movement in Y (can be fractional)
|
|
88
|
+
* @param dz Movement in Z (can be fractional)
|
|
49
89
|
*/
|
|
50
90
|
move(dx: number, dy: number, dz?: number): void;
|
|
91
|
+
/**
|
|
92
|
+
* Move by integer grid cells (for discrete movement)
|
|
93
|
+
* Convenience method for logic that needs whole-cell movement
|
|
94
|
+
*/
|
|
95
|
+
moveGridCells(dx: number, dy: number, dz?: number): void;
|
|
51
96
|
/**
|
|
52
97
|
* Set rotation angle (in radians)
|
|
53
98
|
*/
|
|
@@ -58,7 +103,8 @@ export declare class Entity {
|
|
|
58
103
|
setSpeed(speed: number): void;
|
|
59
104
|
/**
|
|
60
105
|
* Update entity based on velocity and delta time
|
|
61
|
-
*
|
|
106
|
+
* Now properly tracks fractional movement for smooth animation
|
|
107
|
+
* Returns true if position changed (by any amount)
|
|
62
108
|
*/
|
|
63
109
|
updateVelocity(dt: number): boolean;
|
|
64
110
|
/**
|
|
@@ -67,6 +113,7 @@ export declare class Entity {
|
|
|
67
113
|
clone(): Entity;
|
|
68
114
|
/**
|
|
69
115
|
* Serialize entity state for transmission/saving
|
|
116
|
+
* Preserves floating-point positions for smooth movement
|
|
70
117
|
*/
|
|
71
118
|
serialize(): string;
|
|
72
119
|
/**
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"entity.d.ts","sourceRoot":"","sources":["../../../src/simulation/entity.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,yBAAyB,CAAC;AAEzD
|
|
1
|
+
{"version":3,"file":"entity.d.ts","sourceRoot":"","sources":["../../../src/simulation/entity.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,yBAAyB,CAAC;AAEzD;;;;;;GAMG;AACH,MAAM,WAAW,WAAW;IAC1B,OAAO,EAAE,SAAS,CAAC;IACnB,QAAQ,EAAE;QAAE,CAAC,EAAE,MAAM,CAAC;QAAC,CAAC,EAAE,MAAM,CAAC;QAAC,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC;IAC9C,QAAQ,EAAE,MAAM,CAAC;IACjB,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,EAAE,OAAO,CAAC;CACnB;AAED;;;GAGG;AACH,qBAAa,MAAM;IACjB,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAC;IACpB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,OAAO,CAAC,MAAM,CAAc;IAC5B,OAAO,CAAC,cAAc,CAAc;gBAExB,EAAE,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,YAAY,GAAE,OAAO,CAAC,WAAW,CAAM;IAkB7E;;OAEG;IACH,IAAI,KAAK,IAAI,QAAQ,CAAC,WAAW,CAAC,CAEjC;IAED;;OAEG;IACH,IAAI,aAAa,IAAI,QAAQ,CAAC,WAAW,CAAC,CAEzC;IAED;;;OAGG;IACH,WAAW,IAAI;QAAE,CAAC,EAAE,MAAM,CAAC;QAAC,CAAC,EAAE,MAAM,CAAC;QAAC,CAAC,EAAE,MAAM,CAAA;KAAE;IAQlD;;;OAGG;IACH,UAAU,IAAI;QAAE,CAAC,EAAE,MAAM,CAAC;QAAC,CAAC,EAAE,MAAM,CAAC;QAAC,CAAC,EAAE,MAAM,CAAA;KAAE;IAQjD;;;OAGG;IACH,iBAAiB,IAAI;QAAE,CAAC,EAAE,MAAM,CAAC;QAAC,CAAC,EAAE,MAAM,CAAC;QAAC,CAAC,EAAE,MAAM,CAAA;KAAE;IAQxD;;OAEG;IACH,SAAS,IAAI,IAAI;IAQjB;;;OAGG;IACH,UAAU,CAAC,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,EAAE,CAAC,GAAE,MAAU,GAAG,IAAI;IAIrD;;;OAGG;IACH,aAAa,CAAC,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,EAAE,CAAC,GAAE,MAAU,GAAG,IAAI;IAIxD;;OAEG;IACH,WAAW,CAAC,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,EAAE,CAAC,GAAE,MAAU,GAAG,IAAI;IAKtD;;;;;;OAMG;IACH,IAAI,CAAC,EAAE,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,EAAE,EAAE,GAAE,MAAU,GAAG,IAAI;IAMlD;;;OAGG;IACH,aAAa,CAAC,EAAE,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,EAAE,EAAE,GAAE,MAAU,GAAG,IAAI;IAM3D;;OAEG;IACH,WAAW,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI;IAIhC;;OAEG;IACH,QAAQ,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI;IAI7B;;;;OAIG;IACH,cAAc,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO;IAkBnC;;OAEG;IACH,KAAK,IAAI,MAAM;IAUf;;;OAGG;IACH,SAAS,IAAI,MAAM;IAQnB;;OAEG;IACH,MAAM,CAAC,WAAW,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM;IAMxC;;;OAGG;IACG,eAAe,IAAI,OAAO,CAAC,UAAU,CAAC;IAM5C;;;;OAIG;WACU,iBAAiB,CAAC,IAAI,EAAE,UAAU,GAAG,OAAO,CAAC,MAAM,CAAC;IAMjE;;;;OAIG;IACH,YAAY,CAAC,KAAK,EAAE,WAAW,GAAG,IAAI;IAQtC;;;OAGG;IACH,YAAY,IAAI,WAAW;CAO5B"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "bloody-engine",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.11",
|
|
4
4
|
"description": "A WebGL-based 2.5D graphics engine for isometric rendering",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|
|
@@ -42,6 +42,7 @@
|
|
|
42
42
|
"build:node": "vite build --config vite.config.node.ts",
|
|
43
43
|
"build:demo": "vite build --config vite.config.demo.ts",
|
|
44
44
|
"demo": "npm run build:demo && node dist/demo/index.js",
|
|
45
|
+
"demo:coordinates": "npm run build && node dist/node/examples/coordinate-systems-demo.js",
|
|
45
46
|
"test:visual": "node test-visual.js",
|
|
46
47
|
"test:determinism": "node test-determinism.js",
|
|
47
48
|
"test": "vitest run",
|