Compare commits

..

2 Commits

Author SHA1 Message Date
github-actions[bot]
e7d95dfdaf chore: release packages (#443)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2026-01-06 10:34:07 +08:00
YHH
bffe90b6a1 feat(math): add blueprint nodes for math library (#442)
* feat(math): add blueprint nodes for math library

- Add Vector2 blueprint nodes (Make, Break, arithmetic, Length, Normalize, Dot, Cross, Distance, Lerp, Rotate, FromAngle)
- Add Fixed32 blueprint nodes (conversions, arithmetic, math functions, comparison)
- Add FixedVector2 blueprint nodes (Make, Break, arithmetic, vector operations)
- Add Color blueprint nodes (Make, Break, conversions, color manipulation, constants)
- Add documentation with visual examples for all math blueprint nodes
- Update sidebar navigation to include math module

* fix(ci): adjust build order - blueprint before math

math package now depends on blueprint, so blueprint must be built first
2026-01-06 10:32:02 +08:00
28 changed files with 3442 additions and 7 deletions

View File

@@ -63,9 +63,9 @@ jobs:
- name: Build framework packages
run: |
pnpm --filter @esengine/ecs-framework build
pnpm --filter @esengine/blueprint build
pnpm --filter @esengine/ecs-framework-math build
pnpm --filter @esengine/behavior-tree build
pnpm --filter @esengine/blueprint build
pnpm --filter @esengine/fsm build
pnpm --filter @esengine/timer build
pnpm --filter @esengine/spatial build

View File

@@ -45,9 +45,9 @@ jobs:
run: |
# Only build packages managed by Changesets (not in ignore list)
pnpm --filter "@esengine/ecs-framework" build
pnpm --filter "@esengine/blueprint" build
pnpm --filter "@esengine/ecs-framework-math" build
pnpm --filter "@esengine/behavior-tree" build
pnpm --filter "@esengine/blueprint" build
pnpm --filter "@esengine/fsm" build
pnpm --filter "@esengine/timer" build
pnpm --filter "@esengine/spatial" build

View File

@@ -247,6 +247,14 @@ export default defineConfig({
{ label: '实际示例', slug: 'modules/blueprint/examples', translations: { en: 'Examples' } },
],
},
{
label: '数学库',
translations: { en: 'Math' },
items: [
{ label: '概述', slug: 'modules/math', translations: { en: 'Overview' } },
{ label: '蓝图节点', slug: 'modules/math/blueprint-nodes', translations: { en: 'Blueprint Nodes' } },
],
},
{
label: '程序生成',
translations: { en: 'Procgen' },

View File

@@ -472,6 +472,12 @@ Control blueprint execution flow:
| `Less` | A < B | Boolean |
| `Less Or Equal` | A <= B | Boolean |
### Extended Math Nodes
> **Vector2, Fixed32, FixedVector2, Color** and other advanced math nodes are provided by the `@esengine/ecs-framework-math` module.
>
> See: [Math Blueprint Nodes](/en/modules/math/blueprint-nodes)
### Example: Clamp Value
<div class="bp-graph" style="" data-connections='[{"from":"en-rand-result","to":"en-clamp-value","type":"float"}]'>
@@ -604,6 +610,7 @@ Blueprint-defined variables automatically generate Get and Set nodes:
## Related Documentation
- [Math Blueprint Nodes](/en/modules/math/blueprint-nodes) - Vector2, Fixed32, Color and other math nodes
- [Blueprint Editor Guide](/en/modules/blueprint/editor-guide) - Learn how to use the editor
- [Custom Nodes](/en/modules/blueprint/custom-nodes) - Create custom nodes
- [Blueprint VM](/en/modules/blueprint/vm) - Runtime API

View File

@@ -0,0 +1,489 @@
---
title: "Math Blueprint Nodes"
description: "Blueprint nodes provided by the Math module - Vector2, Fixed32, FixedVector2, Color"
---
This document describes the blueprint nodes provided by the `@esengine/ecs-framework-math` module.
> **Note**: These nodes require the math module to be installed.
<script src="/js/blueprint-graph.js"></script>
## Pin Type Legend
<div class="bp-legend">
<div class="bp-legend-item"><svg width="12" height="12"><circle cx="6" cy="6" r="4" fill="transparent" stroke="#7ecd32" stroke-width="2"/></svg> Float</div>
<div class="bp-legend-item"><svg width="12" height="12"><circle cx="6" cy="6" r="4" fill="transparent" stroke="#2196F3" stroke-width="2"/></svg> Vector2</div>
<div class="bp-legend-item"><svg width="12" height="12"><circle cx="6" cy="6" r="4" fill="transparent" stroke="#9C27B0" stroke-width="2"/></svg> Fixed32</div>
<div class="bp-legend-item"><svg width="12" height="12"><circle cx="6" cy="6" r="4" fill="transparent" stroke="#673AB7" stroke-width="2"/></svg> FixedVector2</div>
<div class="bp-legend-item"><svg width="12" height="12"><circle cx="6" cy="6" r="4" fill="transparent" stroke="#FF9800" stroke-width="2"/></svg> Color</div>
</div>
---
## Vector2 Nodes
2D vector operations for position, velocity, and direction calculations.
### Node List
| Node | Description | Inputs | Output |
|------|-------------|--------|--------|
| `Make Vector2` | Create Vector2 from X, Y | X, Y | Vector2 |
| `Break Vector2` | Decompose Vector2 to X, Y | Vector | X, Y |
| `Vector2 +` | Vector addition | A, B | Vector2 |
| `Vector2 -` | Vector subtraction | A, B | Vector2 |
| `Vector2 *` | Vector scaling | Vector, Scalar | Vector2 |
| `Vector2 Length` | Get vector length | Vector | Float |
| `Vector2 Normalize` | Normalize to unit vector | Vector | Vector2 |
| `Vector2 Dot` | Dot product | A, B | Float |
| `Vector2 Cross` | 2D cross product | A, B | Float |
| `Vector2 Distance` | Distance between two points | A, B | Float |
| `Vector2 Lerp` | Linear interpolation | A, B, T | Vector2 |
| `Vector2 Rotate` | Rotate by angle (radians) | Vector, Angle | Vector2 |
| `Vector2 From Angle` | Create unit vector from angle | Angle | Vector2 |
### Example: Calculate Movement Direction
Direction vector from start to end point:
<div class="bp-graph" data-connections='[{"from":"v2-start","to":"v2-sub-a","type":"vector2"},{"from":"v2-end","to":"v2-sub-b","type":"vector2"},{"from":"v2-sub-result","to":"v2-norm-in","type":"vector2"}]'>
<svg class="bp-connections"></svg>
<div class="bp-node" style="position: absolute; left: 20px; top: 10px; width: 130px;">
<div class="bp-node-header math" style="background: #2196F3;">Make Vector2</div>
<div class="bp-node-body">
<div class="bp-pin-row input">
<span class="bp-pin"><svg width="12" height="12"><circle cx="6" cy="6" r="4" fill="none" stroke="#7ecd32" stroke-width="2"/></svg></span>
<span class="bp-pin-label">X</span>
<span class="bp-pin-value">0</span>
</div>
<div class="bp-pin-row input">
<span class="bp-pin"><svg width="12" height="12"><circle cx="6" cy="6" r="4" fill="none" stroke="#7ecd32" stroke-width="2"/></svg></span>
<span class="bp-pin-label">Y</span>
<span class="bp-pin-value">0</span>
</div>
<div class="bp-pin-row output">
<span class="bp-pin" data-pin="v2-start"><svg width="12" height="12"><circle cx="6" cy="6" r="4" fill="#2196F3"/></svg></span>
<span class="bp-pin-label">Vector</span>
</div>
</div>
</div>
<div class="bp-node" style="position: absolute; left: 20px; top: 180px; width: 130px;">
<div class="bp-node-header math" style="background: #2196F3;">Make Vector2</div>
<div class="bp-node-body">
<div class="bp-pin-row input">
<span class="bp-pin"><svg width="12" height="12"><circle cx="6" cy="6" r="4" fill="none" stroke="#7ecd32" stroke-width="2"/></svg></span>
<span class="bp-pin-label">X</span>
<span class="bp-pin-value">100</span>
</div>
<div class="bp-pin-row input">
<span class="bp-pin"><svg width="12" height="12"><circle cx="6" cy="6" r="4" fill="none" stroke="#7ecd32" stroke-width="2"/></svg></span>
<span class="bp-pin-label">Y</span>
<span class="bp-pin-value">50</span>
</div>
<div class="bp-pin-row output">
<span class="bp-pin" data-pin="v2-end"><svg width="12" height="12"><circle cx="6" cy="6" r="4" fill="#2196F3"/></svg></span>
<span class="bp-pin-label">Vector</span>
</div>
</div>
</div>
<div class="bp-node" style="position: absolute; left: 220px; top: 90px; width: 120px;">
<div class="bp-node-header math" style="background: #2196F3;">Vector2 -</div>
<div class="bp-node-body">
<div class="bp-pin-row input">
<span class="bp-pin" data-pin="v2-sub-b"><svg width="12" height="12"><circle cx="6" cy="6" r="4" fill="#2196F3"/></svg></span>
<span class="bp-pin-label">A</span>
</div>
<div class="bp-pin-row input">
<span class="bp-pin" data-pin="v2-sub-a"><svg width="12" height="12"><circle cx="6" cy="6" r="4" fill="#2196F3"/></svg></span>
<span class="bp-pin-label">B</span>
</div>
<div class="bp-pin-row output">
<span class="bp-pin" data-pin="v2-sub-result"><svg width="12" height="12"><circle cx="6" cy="6" r="4" fill="#2196F3"/></svg></span>
<span class="bp-pin-label">Result</span>
</div>
</div>
</div>
<div class="bp-node" style="position: absolute; left: 400px; top: 55px; width: 140px;">
<div class="bp-node-header math" style="background: #2196F3;">Vector2 Normalize</div>
<div class="bp-node-body">
<div class="bp-pin-row input">
<span class="bp-pin" data-pin="v2-norm-in"><svg width="12" height="12"><circle cx="6" cy="6" r="4" fill="#2196F3"/></svg></span>
<span class="bp-pin-label">Vector</span>
</div>
<div class="bp-pin-row output">
<span class="bp-pin"><svg width="12" height="12"><circle cx="6" cy="6" r="4" fill="#2196F3"/></svg></span>
<span class="bp-pin-label">Result</span>
</div>
</div>
</div>
</div>
### Example: Circular Motion
Calculate circular position using angle and radius:
<div class="bp-graph" data-connections='[{"from":"v2-angle-out","to":"v2-scale-vec","type":"vector2"},{"from":"v2-scale-result","to":"v2-add-b","type":"vector2"}]'>
<svg class="bp-connections"></svg>
<div class="bp-node" style="position: absolute; left: 20px; top: 40px; width: 150px;">
<div class="bp-node-header math" style="background: #2196F3;">Vector2 From Angle</div>
<div class="bp-node-body">
<div class="bp-pin-row input">
<span class="bp-pin"><svg width="12" height="12"><circle cx="6" cy="6" r="4" fill="none" stroke="#7ecd32" stroke-width="2"/></svg></span>
<span class="bp-pin-label">Angle</span>
<span class="bp-pin-value">1.57</span>
</div>
<div class="bp-pin-row output">
<span class="bp-pin" data-pin="v2-angle-out"><svg width="12" height="12"><circle cx="6" cy="6" r="4" fill="#2196F3"/></svg></span>
<span class="bp-pin-label">Vector</span>
</div>
</div>
</div>
<div class="bp-node" style="position: absolute; left: 230px; top: 40px; width: 120px;">
<div class="bp-node-header math" style="background: #2196F3;">Vector2 *</div>
<div class="bp-node-body">
<div class="bp-pin-row input">
<span class="bp-pin" data-pin="v2-scale-vec"><svg width="12" height="12"><circle cx="6" cy="6" r="4" fill="#2196F3"/></svg></span>
<span class="bp-pin-label">Vector</span>
</div>
<div class="bp-pin-row input">
<span class="bp-pin"><svg width="12" height="12"><circle cx="6" cy="6" r="4" fill="none" stroke="#7ecd32" stroke-width="2"/></svg></span>
<span class="bp-pin-label">Scalar</span>
<span class="bp-pin-value">50</span>
</div>
<div class="bp-pin-row output">
<span class="bp-pin" data-pin="v2-scale-result"><svg width="12" height="12"><circle cx="6" cy="6" r="4" fill="#2196F3"/></svg></span>
<span class="bp-pin-label">Result</span>
</div>
</div>
</div>
<div class="bp-node" style="position: absolute; left: 420px; top: 40px; width: 120px;">
<div class="bp-node-header math" style="background: #2196F3;">Vector2 +</div>
<div class="bp-node-body">
<div class="bp-pin-row input">
<span class="bp-pin"><svg width="12" height="12"><circle cx="6" cy="6" r="4" fill="none" stroke="#2196F3" stroke-width="2"/></svg></span>
<span class="bp-pin-label">A (Center)</span>
</div>
<div class="bp-pin-row input">
<span class="bp-pin" data-pin="v2-add-b"><svg width="12" height="12"><circle cx="6" cy="6" r="4" fill="#2196F3"/></svg></span>
<span class="bp-pin-label">B</span>
</div>
<div class="bp-pin-row output">
<span class="bp-pin"><svg width="12" height="12"><circle cx="6" cy="6" r="4" fill="#2196F3"/></svg></span>
<span class="bp-pin-label">Position</span>
</div>
</div>
</div>
</div>
---
## Fixed32 Nodes (Fixed-Point Numbers)
Q16.16 fixed-point number operations for lockstep networking games, ensuring cross-platform calculation consistency.
### Node List
| Node | Description | Inputs | Output |
|------|-------------|--------|--------|
| `Fixed32 From Float` | Create from float | Float | Fixed32 |
| `Fixed32 From Int` | Create from integer | Int | Fixed32 |
| `Fixed32 To Float` | Convert to float | Fixed32 | Float |
| `Fixed32 To Int` | Convert to integer | Fixed32 | Int |
| `Fixed32 +` | Addition | A, B | Fixed32 |
| `Fixed32 -` | Subtraction | A, B | Fixed32 |
| `Fixed32 *` | Multiplication | A, B | Fixed32 |
| `Fixed32 /` | Division | A, B | Fixed32 |
| `Fixed32 Abs` | Absolute value | Value | Fixed32 |
| `Fixed32 Sqrt` | Square root | Value | Fixed32 |
| `Fixed32 Floor` | Floor | Value | Fixed32 |
| `Fixed32 Ceil` | Ceiling | Value | Fixed32 |
| `Fixed32 Round` | Round | Value | Fixed32 |
| `Fixed32 Sign` | Sign (-1, 0, 1) | Value | Fixed32 |
| `Fixed32 Min` | Minimum | A, B | Fixed32 |
| `Fixed32 Max` | Maximum | A, B | Fixed32 |
| `Fixed32 Clamp` | Clamp to range | Value, Min, Max | Fixed32 |
| `Fixed32 Lerp` | Linear interpolation | A, B, T | Fixed32 |
### Example: Lockstep Movement Speed Calculation
<div class="bp-graph" data-connections='[{"from":"f32-speed","to":"f32-mul-a","type":"fixed32"},{"from":"f32-dt","to":"f32-mul-b","type":"fixed32"},{"from":"f32-mul-result","to":"f32-tofloat","type":"fixed32"}]'>
<svg class="bp-connections"></svg>
<div class="bp-node" style="position: absolute; left: 20px; top: 10px; width: 150px;">
<div class="bp-node-header math" style="background: #9C27B0;">Fixed32 From Float</div>
<div class="bp-node-body">
<div class="bp-pin-row input">
<span class="bp-pin"><svg width="12" height="12"><circle cx="6" cy="6" r="4" fill="none" stroke="#7ecd32" stroke-width="2"/></svg></span>
<span class="bp-pin-label">Value</span>
<span class="bp-pin-value">5.0</span>
</div>
<div class="bp-pin-row output">
<span class="bp-pin" data-pin="f32-speed"><svg width="12" height="12"><circle cx="6" cy="6" r="4" fill="#9C27B0"/></svg></span>
<span class="bp-pin-label">Speed</span>
</div>
</div>
</div>
<div class="bp-node" style="position: absolute; left: 20px; top: 160px; width: 150px;">
<div class="bp-node-header math" style="background: #9C27B0;">Fixed32 From Float</div>
<div class="bp-node-body">
<div class="bp-pin-row input">
<span class="bp-pin"><svg width="12" height="12"><circle cx="6" cy="6" r="4" fill="none" stroke="#7ecd32" stroke-width="2"/></svg></span>
<span class="bp-pin-label">Value</span>
<span class="bp-pin-value">0.016</span>
</div>
<div class="bp-pin-row output">
<span class="bp-pin" data-pin="f32-dt"><svg width="12" height="12"><circle cx="6" cy="6" r="4" fill="#9C27B0"/></svg></span>
<span class="bp-pin-label">DeltaTime</span>
</div>
</div>
</div>
<div class="bp-node" style="position: absolute; left: 240px; top: 75px; width: 120px;">
<div class="bp-node-header math" style="background: #9C27B0;">Fixed32 *</div>
<div class="bp-node-body">
<div class="bp-pin-row input">
<span class="bp-pin" data-pin="f32-mul-a"><svg width="12" height="12"><circle cx="6" cy="6" r="4" fill="#9C27B0"/></svg></span>
<span class="bp-pin-label">A</span>
</div>
<div class="bp-pin-row input">
<span class="bp-pin" data-pin="f32-mul-b"><svg width="12" height="12"><circle cx="6" cy="6" r="4" fill="#9C27B0"/></svg></span>
<span class="bp-pin-label">B</span>
</div>
<div class="bp-pin-row output">
<span class="bp-pin" data-pin="f32-mul-result"><svg width="12" height="12"><circle cx="6" cy="6" r="4" fill="#9C27B0"/></svg></span>
<span class="bp-pin-label">Result</span>
</div>
</div>
</div>
<div class="bp-node" style="position: absolute; left: 430px; top: 75px; width: 150px;">
<div class="bp-node-header math" style="background: #9C27B0;">Fixed32 To Float</div>
<div class="bp-node-body">
<div class="bp-pin-row input">
<span class="bp-pin" data-pin="f32-tofloat"><svg width="12" height="12"><circle cx="6" cy="6" r="4" fill="#9C27B0"/></svg></span>
<span class="bp-pin-label">Fixed</span>
</div>
<div class="bp-pin-row output">
<span class="bp-pin"><svg width="12" height="12"><circle cx="6" cy="6" r="4" fill="#7ecd32"/></svg></span>
<span class="bp-pin-label">Float</span>
</div>
</div>
</div>
</div>
---
## FixedVector2 Nodes (Fixed-Point Vectors)
Fixed-point vector operations for deterministic physics calculations, suitable for lockstep networking.
### Node List
| Node | Description | Inputs | Output |
|------|-------------|--------|--------|
| `Make FixedVector2` | Create from X, Y floats | X, Y | FixedVector2 |
| `Break FixedVector2` | Decompose to X, Y floats | Vector | X, Y |
| `FixedVector2 +` | Vector addition | A, B | FixedVector2 |
| `FixedVector2 -` | Vector subtraction | A, B | FixedVector2 |
| `FixedVector2 *` | Scale by Fixed32 | Vector, Scalar | FixedVector2 |
| `FixedVector2 Negate` | Negate vector | Vector | FixedVector2 |
| `FixedVector2 Length` | Get length | Vector | Fixed32 |
| `FixedVector2 Normalize` | Normalize | Vector | FixedVector2 |
| `FixedVector2 Dot` | Dot product | A, B | Fixed32 |
| `FixedVector2 Cross` | 2D cross product | A, B | Fixed32 |
| `FixedVector2 Distance` | Distance between points | A, B | Fixed32 |
| `FixedVector2 Lerp` | Linear interpolation | A, B, T | FixedVector2 |
### Example: Deterministic Position Update
<div class="bp-graph" data-connections='[{"from":"fv2-pos","to":"fv2-add-a","type":"fixedvector2"},{"from":"fv2-vel","to":"fv2-add-b","type":"fixedvector2"}]'>
<svg class="bp-connections"></svg>
<div class="bp-node" style="position: absolute; left: 20px; top: 10px; width: 150px;">
<div class="bp-node-header math" style="background: #673AB7;">Make FixedVector2</div>
<div class="bp-node-body">
<div class="bp-pin-row input">
<span class="bp-pin"><svg width="12" height="12"><circle cx="6" cy="6" r="4" fill="none" stroke="#7ecd32" stroke-width="2"/></svg></span>
<span class="bp-pin-label">X</span>
<span class="bp-pin-value">10</span>
</div>
<div class="bp-pin-row input">
<span class="bp-pin"><svg width="12" height="12"><circle cx="6" cy="6" r="4" fill="none" stroke="#7ecd32" stroke-width="2"/></svg></span>
<span class="bp-pin-label">Y</span>
<span class="bp-pin-value">20</span>
</div>
<div class="bp-pin-row output">
<span class="bp-pin" data-pin="fv2-pos"><svg width="12" height="12"><circle cx="6" cy="6" r="4" fill="#673AB7"/></svg></span>
<span class="bp-pin-label">Position</span>
</div>
</div>
</div>
<div class="bp-node" style="position: absolute; left: 20px; top: 180px; width: 150px;">
<div class="bp-node-header math" style="background: #673AB7;">Make FixedVector2</div>
<div class="bp-node-body">
<div class="bp-pin-row input">
<span class="bp-pin"><svg width="12" height="12"><circle cx="6" cy="6" r="4" fill="none" stroke="#7ecd32" stroke-width="2"/></svg></span>
<span class="bp-pin-label">X</span>
<span class="bp-pin-value">1</span>
</div>
<div class="bp-pin-row input">
<span class="bp-pin"><svg width="12" height="12"><circle cx="6" cy="6" r="4" fill="none" stroke="#7ecd32" stroke-width="2"/></svg></span>
<span class="bp-pin-label">Y</span>
<span class="bp-pin-value">0</span>
</div>
<div class="bp-pin-row output">
<span class="bp-pin" data-pin="fv2-vel"><svg width="12" height="12"><circle cx="6" cy="6" r="4" fill="#673AB7"/></svg></span>
<span class="bp-pin-label">Velocity</span>
</div>
</div>
</div>
<div class="bp-node" style="position: absolute; left: 250px; top: 90px; width: 140px;">
<div class="bp-node-header math" style="background: #673AB7;">FixedVector2 +</div>
<div class="bp-node-body">
<div class="bp-pin-row input">
<span class="bp-pin" data-pin="fv2-add-a"><svg width="12" height="12"><circle cx="6" cy="6" r="4" fill="#673AB7"/></svg></span>
<span class="bp-pin-label">A</span>
</div>
<div class="bp-pin-row input">
<span class="bp-pin" data-pin="fv2-add-b"><svg width="12" height="12"><circle cx="6" cy="6" r="4" fill="#673AB7"/></svg></span>
<span class="bp-pin-label">B</span>
</div>
<div class="bp-pin-row output">
<span class="bp-pin"><svg width="12" height="12"><circle cx="6" cy="6" r="4" fill="#673AB7"/></svg></span>
<span class="bp-pin-label">New Position</span>
</div>
</div>
</div>
</div>
---
## Color Nodes
Color creation and manipulation nodes.
### Node List
| Node | Description | Inputs | Output |
|------|-------------|--------|--------|
| `Make Color` | Create from RGBA | R, G, B, A | Color |
| `Break Color` | Decompose to RGBA | Color | R, G, B, A |
| `Color From Hex` | Create from hex string | Hex | Color |
| `Color To Hex` | Convert to hex string | Color | String |
| `Color From HSL` | Create from HSL | H, S, L | Color |
| `Color To HSL` | Convert to HSL | Color | H, S, L |
| `Color Lerp` | Color interpolation | A, B, T | Color |
| `Color Lighten` | Lighten | Color, Amount | Color |
| `Color Darken` | Darken | Color, Amount | Color |
| `Color Saturate` | Increase saturation | Color, Amount | Color |
| `Color Desaturate` | Decrease saturation | Color, Amount | Color |
| `Color Invert` | Invert | Color | Color |
| `Color Grayscale` | Convert to grayscale | Color | Color |
| `Color Luminance` | Get luminance | Color | Float |
### Color Constants
| Node | Value |
|------|-------|
| `Color White` | (1, 1, 1, 1) |
| `Color Black` | (0, 0, 0, 1) |
| `Color Red` | (1, 0, 0, 1) |
| `Color Green` | (0, 1, 0, 1) |
| `Color Blue` | (0, 0, 1, 1) |
| `Color Transparent` | (0, 0, 0, 0) |
### Example: Color Transition Animation
<div class="bp-graph" data-connections='[{"from":"color-a","to":"color-lerp-a","type":"color"},{"from":"color-b","to":"color-lerp-b","type":"color"}]'>
<svg class="bp-connections"></svg>
<div class="bp-node" style="position: absolute; left: 20px; top: 10px; width: 120px;">
<div class="bp-node-header math" style="background: #FF9800;">Color Red</div>
<div class="bp-node-body">
<div class="bp-pin-row output">
<span class="bp-pin" data-pin="color-a"><svg width="12" height="12"><circle cx="6" cy="6" r="4" fill="#FF9800"/></svg></span>
<span class="bp-pin-label">Color</span>
</div>
</div>
</div>
<div class="bp-node" style="position: absolute; left: 20px; top: 130px; width: 120px;">
<div class="bp-node-header math" style="background: #FF9800;">Color Blue</div>
<div class="bp-node-body">
<div class="bp-pin-row output">
<span class="bp-pin" data-pin="color-b"><svg width="12" height="12"><circle cx="6" cy="6" r="4" fill="#FF9800"/></svg></span>
<span class="bp-pin-label">Color</span>
</div>
</div>
</div>
<div class="bp-node" style="position: absolute; left: 220px; top: 50px; width: 130px;">
<div class="bp-node-header math" style="background: #FF9800;">Color Lerp</div>
<div class="bp-node-body">
<div class="bp-pin-row input">
<span class="bp-pin" data-pin="color-lerp-a"><svg width="12" height="12"><circle cx="6" cy="6" r="4" fill="#FF9800"/></svg></span>
<span class="bp-pin-label">A</span>
</div>
<div class="bp-pin-row input">
<span class="bp-pin" data-pin="color-lerp-b"><svg width="12" height="12"><circle cx="6" cy="6" r="4" fill="#FF9800"/></svg></span>
<span class="bp-pin-label">B</span>
</div>
<div class="bp-pin-row input">
<span class="bp-pin"><svg width="12" height="12"><circle cx="6" cy="6" r="4" fill="none" stroke="#7ecd32" stroke-width="2"/></svg></span>
<span class="bp-pin-label">T</span>
<span class="bp-pin-value">0.5</span>
</div>
<div class="bp-pin-row output">
<span class="bp-pin"><svg width="12" height="12"><circle cx="6" cy="6" r="4" fill="#FF9800"/></svg></span>
<span class="bp-pin-label">Result</span>
</div>
</div>
</div>
</div>
### Example: Create Color from Hex
<div class="bp-graph" data-connections='[{"from":"hex-color","to":"break-color","type":"color"}]'>
<svg class="bp-connections"></svg>
<div class="bp-node" style="position: absolute; left: 20px; top: 30px; width: 150px;">
<div class="bp-node-header math" style="background: #FF9800;">Color From Hex</div>
<div class="bp-node-body">
<div class="bp-pin-row input">
<span class="bp-pin"><svg width="12" height="12"><circle cx="6" cy="6" r="4" fill="none" stroke="#e060e0" stroke-width="2"/></svg></span>
<span class="bp-pin-label">Hex</span>
<span class="bp-pin-value">"#FF5722"</span>
</div>
<div class="bp-pin-row output">
<span class="bp-pin" data-pin="hex-color"><svg width="12" height="12"><circle cx="6" cy="6" r="4" fill="#FF9800"/></svg></span>
<span class="bp-pin-label">Color</span>
</div>
</div>
</div>
<div class="bp-node" style="position: absolute; left: 250px; top: 20px; width: 130px;">
<div class="bp-node-header math" style="background: #FF9800;">Break Color</div>
<div class="bp-node-body">
<div class="bp-pin-row input">
<span class="bp-pin" data-pin="break-color"><svg width="12" height="12"><circle cx="6" cy="6" r="4" fill="#FF9800"/></svg></span>
<span class="bp-pin-label">Color</span>
</div>
<div class="bp-pin-row output">
<span class="bp-pin"><svg width="12" height="12"><circle cx="6" cy="6" r="4" fill="#7ecd32"/></svg></span>
<span class="bp-pin-label">R</span>
</div>
<div class="bp-pin-row output">
<span class="bp-pin"><svg width="12" height="12"><circle cx="6" cy="6" r="4" fill="#7ecd32"/></svg></span>
<span class="bp-pin-label">G</span>
</div>
<div class="bp-pin-row output">
<span class="bp-pin"><svg width="12" height="12"><circle cx="6" cy="6" r="4" fill="#7ecd32"/></svg></span>
<span class="bp-pin-label">B</span>
</div>
<div class="bp-pin-row output">
<span class="bp-pin"><svg width="12" height="12"><circle cx="6" cy="6" r="4" fill="#7ecd32"/></svg></span>
<span class="bp-pin-label">A</span>
</div>
</div>
</div>
</div>
---
## Related Documentation
- [Blueprint Node Reference](/en/modules/blueprint/nodes) - Core blueprint nodes
- [Blueprint Editor Guide](/en/modules/blueprint/editor-guide) - Editor usage
- [Custom Nodes](/en/modules/blueprint/custom-nodes) - Create custom nodes

View File

@@ -0,0 +1,79 @@
---
title: "Math Library"
description: "ESEngine Math Library - Vector2, Fixed32, FixedVector2, Color and other math types"
---
The `@esengine/ecs-framework-math` module provides common math types and operations for game development.
## Core Types
| Type | Description |
|------|-------------|
| `Vector2` | 2D floating-point vector for position, velocity, direction |
| `Fixed32` | Q16.16 fixed-point number for deterministic lockstep calculations |
| `FixedVector2` | 2D fixed-point vector for deterministic physics |
| `Color` | RGBA color |
## Features
### Vector2
- Addition, subtraction, scaling
- Dot product, cross product
- Length, normalization
- Distance, interpolation
- Rotation, angle conversion
### Fixed32 Fixed-Point Numbers
Designed for lockstep networking games, ensuring cross-platform calculation consistency:
- Basic operations: add, subtract, multiply, divide
- Math functions: absolute value, square root, rounding
- Comparison, clamping, interpolation
- Constants: 0, 1, 0.5, PI, 2*PI
### Color
- RGB/RGBA creation and decomposition
- Hex string conversion
- HSL color space conversion
- Color operations: lighten, darken, saturation adjustment
- Color blending and interpolation
## Blueprint Support
The math library provides rich blueprint nodes, see:
- [Math Blueprint Nodes](/en/modules/math/blueprint-nodes)
## Installation
```bash
pnpm add @esengine/ecs-framework-math
```
## Basic Usage
```typescript
import { Vector2, Fixed32, FixedVector2, Color } from '@esengine/ecs-framework-math';
// Vector2
const pos = new Vector2(10, 20);
const dir = pos.normalized();
// Fixed32 (lockstep)
const speed = Fixed32.from(5.0);
const dt = Fixed32.from(0.016);
const distance = speed.mul(dt);
// FixedVector2
const fixedPos = FixedVector2.from(10, 20);
const fixedVel = FixedVector2.from(1, 0);
const newPos = fixedPos.add(fixedVel);
// Color
const red = Color.RED;
const blue = Color.BLUE;
const purple = Color.lerp(red, blue, 0.5);
```

View File

@@ -470,6 +470,12 @@ description: "蓝图内置 ECS 操作节点完整参考"
| `Less` | A < B | Boolean |
| `Less Or Equal` | A <= B | Boolean |
### 扩展数学节点
> **Vector2、Fixed32、FixedVector2、Color** 等高级数学节点由 `@esengine/ecs-framework-math` 模块提供。
>
> 详见:[数学库蓝图节点](/modules/math/blueprint-nodes)
### 示例:钳制数值
<div class="bp-graph" style="" data-connections='[{"from":"rand-result","to":"clamp-value","type":"float"}]'>
@@ -535,6 +541,7 @@ description: "蓝图内置 ECS 操作节点完整参考"
## 相关文档
- [数学库蓝图节点](/modules/math/blueprint-nodes) - Vector2、Fixed32、Color 等数学节点
- [蓝图编辑器指南](/modules/blueprint/editor-guide) - 学习如何使用编辑器
- [自定义节点](/modules/blueprint/custom-nodes) - 创建自定义节点
- [蓝图虚拟机](/modules/blueprint/vm) - 运行时 API

View File

@@ -0,0 +1,489 @@
---
title: "数学库蓝图节点"
description: "Math 模块提供的蓝图节点 - Vector2、Fixed32、FixedVector2、Color"
---
本文档介绍 `@esengine/ecs-framework-math` 模块提供的蓝图节点。
> **注意**:这些节点需要安装 math 模块才能使用。
<script src="/js/blueprint-graph.js"></script>
## 引脚类型说明
<div class="bp-legend">
<div class="bp-legend-item"><svg width="12" height="12"><circle cx="6" cy="6" r="4" fill="transparent" stroke="#7ecd32" stroke-width="2"/></svg> 浮点数 (Float)</div>
<div class="bp-legend-item"><svg width="12" height="12"><circle cx="6" cy="6" r="4" fill="transparent" stroke="#2196F3" stroke-width="2"/></svg> Vector2</div>
<div class="bp-legend-item"><svg width="12" height="12"><circle cx="6" cy="6" r="4" fill="transparent" stroke="#9C27B0" stroke-width="2"/></svg> Fixed32</div>
<div class="bp-legend-item"><svg width="12" height="12"><circle cx="6" cy="6" r="4" fill="transparent" stroke="#673AB7" stroke-width="2"/></svg> FixedVector2</div>
<div class="bp-legend-item"><svg width="12" height="12"><circle cx="6" cy="6" r="4" fill="transparent" stroke="#FF9800" stroke-width="2"/></svg> Color</div>
</div>
---
## Vector2 节点
2D 向量操作,用于位置、速度、方向计算。
### 节点列表
| 节点 | 说明 | 输入 | 输出 |
|------|------|------|------|
| `Make Vector2` | 从 X, Y 创建 Vector2 | X, Y | Vector2 |
| `Break Vector2` | 分解 Vector2 为 X, Y | Vector | X, Y |
| `Vector2 +` | 向量加法 | A, B | Vector2 |
| `Vector2 -` | 向量减法 | A, B | Vector2 |
| `Vector2 *` | 向量缩放 | Vector, Scalar | Vector2 |
| `Vector2 Length` | 获取向量长度 | Vector | Float |
| `Vector2 Normalize` | 归一化为单位向量 | Vector | Vector2 |
| `Vector2 Dot` | 点积 | A, B | Float |
| `Vector2 Cross` | 2D 叉积 | A, B | Float |
| `Vector2 Distance` | 两点距离 | A, B | Float |
| `Vector2 Lerp` | 线性插值 | A, B, T | Vector2 |
| `Vector2 Rotate` | 旋转(弧度) | Vector, Angle | Vector2 |
| `Vector2 From Angle` | 从角度创建单位向量 | Angle | Vector2 |
### 示例:计算移动方向
从起点到终点的方向向量:
<div class="bp-graph" data-connections='[{"from":"v2-start","to":"v2-sub-a","type":"vector2"},{"from":"v2-end","to":"v2-sub-b","type":"vector2"},{"from":"v2-sub-result","to":"v2-norm-in","type":"vector2"}]'>
<svg class="bp-connections"></svg>
<div class="bp-node" style="position: absolute; left: 20px; top: 10px; width: 130px;">
<div class="bp-node-header math" style="background: #2196F3;">Make Vector2</div>
<div class="bp-node-body">
<div class="bp-pin-row input">
<span class="bp-pin"><svg width="12" height="12"><circle cx="6" cy="6" r="4" fill="none" stroke="#7ecd32" stroke-width="2"/></svg></span>
<span class="bp-pin-label">X</span>
<span class="bp-pin-value">0</span>
</div>
<div class="bp-pin-row input">
<span class="bp-pin"><svg width="12" height="12"><circle cx="6" cy="6" r="4" fill="none" stroke="#7ecd32" stroke-width="2"/></svg></span>
<span class="bp-pin-label">Y</span>
<span class="bp-pin-value">0</span>
</div>
<div class="bp-pin-row output">
<span class="bp-pin" data-pin="v2-start"><svg width="12" height="12"><circle cx="6" cy="6" r="4" fill="#2196F3"/></svg></span>
<span class="bp-pin-label">Vector</span>
</div>
</div>
</div>
<div class="bp-node" style="position: absolute; left: 20px; top: 180px; width: 130px;">
<div class="bp-node-header math" style="background: #2196F3;">Make Vector2</div>
<div class="bp-node-body">
<div class="bp-pin-row input">
<span class="bp-pin"><svg width="12" height="12"><circle cx="6" cy="6" r="4" fill="none" stroke="#7ecd32" stroke-width="2"/></svg></span>
<span class="bp-pin-label">X</span>
<span class="bp-pin-value">100</span>
</div>
<div class="bp-pin-row input">
<span class="bp-pin"><svg width="12" height="12"><circle cx="6" cy="6" r="4" fill="none" stroke="#7ecd32" stroke-width="2"/></svg></span>
<span class="bp-pin-label">Y</span>
<span class="bp-pin-value">50</span>
</div>
<div class="bp-pin-row output">
<span class="bp-pin" data-pin="v2-end"><svg width="12" height="12"><circle cx="6" cy="6" r="4" fill="#2196F3"/></svg></span>
<span class="bp-pin-label">Vector</span>
</div>
</div>
</div>
<div class="bp-node" style="position: absolute; left: 220px; top: 90px; width: 120px;">
<div class="bp-node-header math" style="background: #2196F3;">Vector2 -</div>
<div class="bp-node-body">
<div class="bp-pin-row input">
<span class="bp-pin" data-pin="v2-sub-b"><svg width="12" height="12"><circle cx="6" cy="6" r="4" fill="#2196F3"/></svg></span>
<span class="bp-pin-label">A</span>
</div>
<div class="bp-pin-row input">
<span class="bp-pin" data-pin="v2-sub-a"><svg width="12" height="12"><circle cx="6" cy="6" r="4" fill="#2196F3"/></svg></span>
<span class="bp-pin-label">B</span>
</div>
<div class="bp-pin-row output">
<span class="bp-pin" data-pin="v2-sub-result"><svg width="12" height="12"><circle cx="6" cy="6" r="4" fill="#2196F3"/></svg></span>
<span class="bp-pin-label">Result</span>
</div>
</div>
</div>
<div class="bp-node" style="position: absolute; left: 400px; top: 55px; width: 140px;">
<div class="bp-node-header math" style="background: #2196F3;">Vector2 Normalize</div>
<div class="bp-node-body">
<div class="bp-pin-row input">
<span class="bp-pin" data-pin="v2-norm-in"><svg width="12" height="12"><circle cx="6" cy="6" r="4" fill="#2196F3"/></svg></span>
<span class="bp-pin-label">Vector</span>
</div>
<div class="bp-pin-row output">
<span class="bp-pin"><svg width="12" height="12"><circle cx="6" cy="6" r="4" fill="#2196F3"/></svg></span>
<span class="bp-pin-label">Result</span>
</div>
</div>
</div>
</div>
### 示例:圆周运动
使用角度和半径计算圆周位置:
<div class="bp-graph" data-connections='[{"from":"v2-angle-out","to":"v2-scale-vec","type":"vector2"},{"from":"v2-scale-result","to":"v2-add-b","type":"vector2"}]'>
<svg class="bp-connections"></svg>
<div class="bp-node" style="position: absolute; left: 20px; top: 40px; width: 150px;">
<div class="bp-node-header math" style="background: #2196F3;">Vector2 From Angle</div>
<div class="bp-node-body">
<div class="bp-pin-row input">
<span class="bp-pin"><svg width="12" height="12"><circle cx="6" cy="6" r="4" fill="none" stroke="#7ecd32" stroke-width="2"/></svg></span>
<span class="bp-pin-label">Angle</span>
<span class="bp-pin-value">1.57</span>
</div>
<div class="bp-pin-row output">
<span class="bp-pin" data-pin="v2-angle-out"><svg width="12" height="12"><circle cx="6" cy="6" r="4" fill="#2196F3"/></svg></span>
<span class="bp-pin-label">Vector</span>
</div>
</div>
</div>
<div class="bp-node" style="position: absolute; left: 230px; top: 40px; width: 120px;">
<div class="bp-node-header math" style="background: #2196F3;">Vector2 *</div>
<div class="bp-node-body">
<div class="bp-pin-row input">
<span class="bp-pin" data-pin="v2-scale-vec"><svg width="12" height="12"><circle cx="6" cy="6" r="4" fill="#2196F3"/></svg></span>
<span class="bp-pin-label">Vector</span>
</div>
<div class="bp-pin-row input">
<span class="bp-pin"><svg width="12" height="12"><circle cx="6" cy="6" r="4" fill="none" stroke="#7ecd32" stroke-width="2"/></svg></span>
<span class="bp-pin-label">Scalar</span>
<span class="bp-pin-value">50</span>
</div>
<div class="bp-pin-row output">
<span class="bp-pin" data-pin="v2-scale-result"><svg width="12" height="12"><circle cx="6" cy="6" r="4" fill="#2196F3"/></svg></span>
<span class="bp-pin-label">Result</span>
</div>
</div>
</div>
<div class="bp-node" style="position: absolute; left: 420px; top: 40px; width: 120px;">
<div class="bp-node-header math" style="background: #2196F3;">Vector2 +</div>
<div class="bp-node-body">
<div class="bp-pin-row input">
<span class="bp-pin"><svg width="12" height="12"><circle cx="6" cy="6" r="4" fill="none" stroke="#2196F3" stroke-width="2"/></svg></span>
<span class="bp-pin-label">A (Center)</span>
</div>
<div class="bp-pin-row input">
<span class="bp-pin" data-pin="v2-add-b"><svg width="12" height="12"><circle cx="6" cy="6" r="4" fill="#2196F3"/></svg></span>
<span class="bp-pin-label">B</span>
</div>
<div class="bp-pin-row output">
<span class="bp-pin"><svg width="12" height="12"><circle cx="6" cy="6" r="4" fill="#2196F3"/></svg></span>
<span class="bp-pin-label">Position</span>
</div>
</div>
</div>
</div>
---
## Fixed32 定点数节点
Q16.16 定点数运算,适用于帧同步网络游戏,保证跨平台计算一致性。
### 节点列表
| 节点 | 说明 | 输入 | 输出 |
|------|------|------|------|
| `Fixed32 From Float` | 从浮点数创建 | Float | Fixed32 |
| `Fixed32 From Int` | 从整数创建 | Int | Fixed32 |
| `Fixed32 To Float` | 转换为浮点数 | Fixed32 | Float |
| `Fixed32 To Int` | 转换为整数 | Fixed32 | Int |
| `Fixed32 +` | 加法 | A, B | Fixed32 |
| `Fixed32 -` | 减法 | A, B | Fixed32 |
| `Fixed32 *` | 乘法 | A, B | Fixed32 |
| `Fixed32 /` | 除法 | A, B | Fixed32 |
| `Fixed32 Abs` | 绝对值 | Value | Fixed32 |
| `Fixed32 Sqrt` | 平方根 | Value | Fixed32 |
| `Fixed32 Floor` | 向下取整 | Value | Fixed32 |
| `Fixed32 Ceil` | 向上取整 | Value | Fixed32 |
| `Fixed32 Round` | 四舍五入 | Value | Fixed32 |
| `Fixed32 Sign` | 符号 (-1, 0, 1) | Value | Fixed32 |
| `Fixed32 Min` | 最小值 | A, B | Fixed32 |
| `Fixed32 Max` | 最大值 | A, B | Fixed32 |
| `Fixed32 Clamp` | 钳制范围 | Value, Min, Max | Fixed32 |
| `Fixed32 Lerp` | 线性插值 | A, B, T | Fixed32 |
### 示例:帧同步移动速度计算
<div class="bp-graph" data-connections='[{"from":"f32-speed","to":"f32-mul-a","type":"fixed32"},{"from":"f32-dt","to":"f32-mul-b","type":"fixed32"},{"from":"f32-mul-result","to":"f32-tofloat","type":"fixed32"}]'>
<svg class="bp-connections"></svg>
<div class="bp-node" style="position: absolute; left: 20px; top: 10px; width: 150px;">
<div class="bp-node-header math" style="background: #9C27B0;">Fixed32 From Float</div>
<div class="bp-node-body">
<div class="bp-pin-row input">
<span class="bp-pin"><svg width="12" height="12"><circle cx="6" cy="6" r="4" fill="none" stroke="#7ecd32" stroke-width="2"/></svg></span>
<span class="bp-pin-label">Value</span>
<span class="bp-pin-value">5.0</span>
</div>
<div class="bp-pin-row output">
<span class="bp-pin" data-pin="f32-speed"><svg width="12" height="12"><circle cx="6" cy="6" r="4" fill="#9C27B0"/></svg></span>
<span class="bp-pin-label">Speed</span>
</div>
</div>
</div>
<div class="bp-node" style="position: absolute; left: 20px; top: 160px; width: 150px;">
<div class="bp-node-header math" style="background: #9C27B0;">Fixed32 From Float</div>
<div class="bp-node-body">
<div class="bp-pin-row input">
<span class="bp-pin"><svg width="12" height="12"><circle cx="6" cy="6" r="4" fill="none" stroke="#7ecd32" stroke-width="2"/></svg></span>
<span class="bp-pin-label">Value</span>
<span class="bp-pin-value">0.016</span>
</div>
<div class="bp-pin-row output">
<span class="bp-pin" data-pin="f32-dt"><svg width="12" height="12"><circle cx="6" cy="6" r="4" fill="#9C27B0"/></svg></span>
<span class="bp-pin-label">DeltaTime</span>
</div>
</div>
</div>
<div class="bp-node" style="position: absolute; left: 240px; top: 75px; width: 120px;">
<div class="bp-node-header math" style="background: #9C27B0;">Fixed32 *</div>
<div class="bp-node-body">
<div class="bp-pin-row input">
<span class="bp-pin" data-pin="f32-mul-a"><svg width="12" height="12"><circle cx="6" cy="6" r="4" fill="#9C27B0"/></svg></span>
<span class="bp-pin-label">A</span>
</div>
<div class="bp-pin-row input">
<span class="bp-pin" data-pin="f32-mul-b"><svg width="12" height="12"><circle cx="6" cy="6" r="4" fill="#9C27B0"/></svg></span>
<span class="bp-pin-label">B</span>
</div>
<div class="bp-pin-row output">
<span class="bp-pin" data-pin="f32-mul-result"><svg width="12" height="12"><circle cx="6" cy="6" r="4" fill="#9C27B0"/></svg></span>
<span class="bp-pin-label">Result</span>
</div>
</div>
</div>
<div class="bp-node" style="position: absolute; left: 430px; top: 75px; width: 150px;">
<div class="bp-node-header math" style="background: #9C27B0;">Fixed32 To Float</div>
<div class="bp-node-body">
<div class="bp-pin-row input">
<span class="bp-pin" data-pin="f32-tofloat"><svg width="12" height="12"><circle cx="6" cy="6" r="4" fill="#9C27B0"/></svg></span>
<span class="bp-pin-label">Fixed</span>
</div>
<div class="bp-pin-row output">
<span class="bp-pin"><svg width="12" height="12"><circle cx="6" cy="6" r="4" fill="#7ecd32"/></svg></span>
<span class="bp-pin-label">Float</span>
</div>
</div>
</div>
</div>
---
## FixedVector2 定点向量节点
定点向量运算,用于确定性物理计算,适用于帧同步。
### 节点列表
| 节点 | 说明 | 输入 | 输出 |
|------|------|------|------|
| `Make FixedVector2` | 从 X, Y 浮点数创建 | X, Y | FixedVector2 |
| `Break FixedVector2` | 分解为 X, Y 浮点数 | Vector | X, Y |
| `FixedVector2 +` | 向量加法 | A, B | FixedVector2 |
| `FixedVector2 -` | 向量减法 | A, B | FixedVector2 |
| `FixedVector2 *` | 按 Fixed32 缩放 | Vector, Scalar | FixedVector2 |
| `FixedVector2 Negate` | 取反 | Vector | FixedVector2 |
| `FixedVector2 Length` | 获取长度 | Vector | Fixed32 |
| `FixedVector2 Normalize` | 归一化 | Vector | FixedVector2 |
| `FixedVector2 Dot` | 点积 | A, B | Fixed32 |
| `FixedVector2 Cross` | 2D 叉积 | A, B | Fixed32 |
| `FixedVector2 Distance` | 两点距离 | A, B | Fixed32 |
| `FixedVector2 Lerp` | 线性插值 | A, B, T | FixedVector2 |
### 示例:确定性位置更新
<div class="bp-graph" data-connections='[{"from":"fv2-pos","to":"fv2-add-a","type":"fixedvector2"},{"from":"fv2-vel","to":"fv2-add-b","type":"fixedvector2"}]'>
<svg class="bp-connections"></svg>
<div class="bp-node" style="position: absolute; left: 20px; top: 10px; width: 150px;">
<div class="bp-node-header math" style="background: #673AB7;">Make FixedVector2</div>
<div class="bp-node-body">
<div class="bp-pin-row input">
<span class="bp-pin"><svg width="12" height="12"><circle cx="6" cy="6" r="4" fill="none" stroke="#7ecd32" stroke-width="2"/></svg></span>
<span class="bp-pin-label">X</span>
<span class="bp-pin-value">10</span>
</div>
<div class="bp-pin-row input">
<span class="bp-pin"><svg width="12" height="12"><circle cx="6" cy="6" r="4" fill="none" stroke="#7ecd32" stroke-width="2"/></svg></span>
<span class="bp-pin-label">Y</span>
<span class="bp-pin-value">20</span>
</div>
<div class="bp-pin-row output">
<span class="bp-pin" data-pin="fv2-pos"><svg width="12" height="12"><circle cx="6" cy="6" r="4" fill="#673AB7"/></svg></span>
<span class="bp-pin-label">Position</span>
</div>
</div>
</div>
<div class="bp-node" style="position: absolute; left: 20px; top: 180px; width: 150px;">
<div class="bp-node-header math" style="background: #673AB7;">Make FixedVector2</div>
<div class="bp-node-body">
<div class="bp-pin-row input">
<span class="bp-pin"><svg width="12" height="12"><circle cx="6" cy="6" r="4" fill="none" stroke="#7ecd32" stroke-width="2"/></svg></span>
<span class="bp-pin-label">X</span>
<span class="bp-pin-value">1</span>
</div>
<div class="bp-pin-row input">
<span class="bp-pin"><svg width="12" height="12"><circle cx="6" cy="6" r="4" fill="none" stroke="#7ecd32" stroke-width="2"/></svg></span>
<span class="bp-pin-label">Y</span>
<span class="bp-pin-value">0</span>
</div>
<div class="bp-pin-row output">
<span class="bp-pin" data-pin="fv2-vel"><svg width="12" height="12"><circle cx="6" cy="6" r="4" fill="#673AB7"/></svg></span>
<span class="bp-pin-label">Velocity</span>
</div>
</div>
</div>
<div class="bp-node" style="position: absolute; left: 250px; top: 90px; width: 140px;">
<div class="bp-node-header math" style="background: #673AB7;">FixedVector2 +</div>
<div class="bp-node-body">
<div class="bp-pin-row input">
<span class="bp-pin" data-pin="fv2-add-a"><svg width="12" height="12"><circle cx="6" cy="6" r="4" fill="#673AB7"/></svg></span>
<span class="bp-pin-label">A</span>
</div>
<div class="bp-pin-row input">
<span class="bp-pin" data-pin="fv2-add-b"><svg width="12" height="12"><circle cx="6" cy="6" r="4" fill="#673AB7"/></svg></span>
<span class="bp-pin-label">B</span>
</div>
<div class="bp-pin-row output">
<span class="bp-pin"><svg width="12" height="12"><circle cx="6" cy="6" r="4" fill="#673AB7"/></svg></span>
<span class="bp-pin-label">New Position</span>
</div>
</div>
</div>
</div>
---
## Color 颜色节点
颜色创建与操作节点。
### 节点列表
| 节点 | 说明 | 输入 | 输出 |
|------|------|------|------|
| `Make Color` | 从 RGBA 创建 | R, G, B, A | Color |
| `Break Color` | 分解为 RGBA | Color | R, G, B, A |
| `Color From Hex` | 从十六进制字符串创建 | Hex | Color |
| `Color To Hex` | 转换为十六进制字符串 | Color | String |
| `Color From HSL` | 从 HSL 创建 | H, S, L | Color |
| `Color To HSL` | 转换为 HSL | Color | H, S, L |
| `Color Lerp` | 颜色插值 | A, B, T | Color |
| `Color Lighten` | 提亮 | Color, Amount | Color |
| `Color Darken` | 变暗 | Color, Amount | Color |
| `Color Saturate` | 增加饱和度 | Color, Amount | Color |
| `Color Desaturate` | 降低饱和度 | Color, Amount | Color |
| `Color Invert` | 反色 | Color | Color |
| `Color Grayscale` | 灰度化 | Color | Color |
| `Color Luminance` | 获取亮度 | Color | Float |
### 颜色常量
| 节点 | 值 |
|------|------|
| `Color White` | (1, 1, 1, 1) |
| `Color Black` | (0, 0, 0, 1) |
| `Color Red` | (1, 0, 0, 1) |
| `Color Green` | (0, 1, 0, 1) |
| `Color Blue` | (0, 0, 1, 1) |
| `Color Transparent` | (0, 0, 0, 0) |
### 示例:颜色过渡动画
<div class="bp-graph" data-connections='[{"from":"color-a","to":"color-lerp-a","type":"color"},{"from":"color-b","to":"color-lerp-b","type":"color"}]'>
<svg class="bp-connections"></svg>
<div class="bp-node" style="position: absolute; left: 20px; top: 10px; width: 120px;">
<div class="bp-node-header math" style="background: #FF9800;">Color Red</div>
<div class="bp-node-body">
<div class="bp-pin-row output">
<span class="bp-pin" data-pin="color-a"><svg width="12" height="12"><circle cx="6" cy="6" r="4" fill="#FF9800"/></svg></span>
<span class="bp-pin-label">Color</span>
</div>
</div>
</div>
<div class="bp-node" style="position: absolute; left: 20px; top: 130px; width: 120px;">
<div class="bp-node-header math" style="background: #FF9800;">Color Blue</div>
<div class="bp-node-body">
<div class="bp-pin-row output">
<span class="bp-pin" data-pin="color-b"><svg width="12" height="12"><circle cx="6" cy="6" r="4" fill="#FF9800"/></svg></span>
<span class="bp-pin-label">Color</span>
</div>
</div>
</div>
<div class="bp-node" style="position: absolute; left: 220px; top: 50px; width: 130px;">
<div class="bp-node-header math" style="background: #FF9800;">Color Lerp</div>
<div class="bp-node-body">
<div class="bp-pin-row input">
<span class="bp-pin" data-pin="color-lerp-a"><svg width="12" height="12"><circle cx="6" cy="6" r="4" fill="#FF9800"/></svg></span>
<span class="bp-pin-label">A</span>
</div>
<div class="bp-pin-row input">
<span class="bp-pin" data-pin="color-lerp-b"><svg width="12" height="12"><circle cx="6" cy="6" r="4" fill="#FF9800"/></svg></span>
<span class="bp-pin-label">B</span>
</div>
<div class="bp-pin-row input">
<span class="bp-pin"><svg width="12" height="12"><circle cx="6" cy="6" r="4" fill="none" stroke="#7ecd32" stroke-width="2"/></svg></span>
<span class="bp-pin-label">T</span>
<span class="bp-pin-value">0.5</span>
</div>
<div class="bp-pin-row output">
<span class="bp-pin"><svg width="12" height="12"><circle cx="6" cy="6" r="4" fill="#FF9800"/></svg></span>
<span class="bp-pin-label">Result</span>
</div>
</div>
</div>
</div>
### 示例:从 Hex 创建颜色
<div class="bp-graph" data-connections='[{"from":"hex-color","to":"break-color","type":"color"}]'>
<svg class="bp-connections"></svg>
<div class="bp-node" style="position: absolute; left: 20px; top: 30px; width: 150px;">
<div class="bp-node-header math" style="background: #FF9800;">Color From Hex</div>
<div class="bp-node-body">
<div class="bp-pin-row input">
<span class="bp-pin"><svg width="12" height="12"><circle cx="6" cy="6" r="4" fill="none" stroke="#e060e0" stroke-width="2"/></svg></span>
<span class="bp-pin-label">Hex</span>
<span class="bp-pin-value">"#FF5722"</span>
</div>
<div class="bp-pin-row output">
<span class="bp-pin" data-pin="hex-color"><svg width="12" height="12"><circle cx="6" cy="6" r="4" fill="#FF9800"/></svg></span>
<span class="bp-pin-label">Color</span>
</div>
</div>
</div>
<div class="bp-node" style="position: absolute; left: 250px; top: 20px; width: 130px;">
<div class="bp-node-header math" style="background: #FF9800;">Break Color</div>
<div class="bp-node-body">
<div class="bp-pin-row input">
<span class="bp-pin" data-pin="break-color"><svg width="12" height="12"><circle cx="6" cy="6" r="4" fill="#FF9800"/></svg></span>
<span class="bp-pin-label">Color</span>
</div>
<div class="bp-pin-row output">
<span class="bp-pin"><svg width="12" height="12"><circle cx="6" cy="6" r="4" fill="#7ecd32"/></svg></span>
<span class="bp-pin-label">R</span>
</div>
<div class="bp-pin-row output">
<span class="bp-pin"><svg width="12" height="12"><circle cx="6" cy="6" r="4" fill="#7ecd32"/></svg></span>
<span class="bp-pin-label">G</span>
</div>
<div class="bp-pin-row output">
<span class="bp-pin"><svg width="12" height="12"><circle cx="6" cy="6" r="4" fill="#7ecd32"/></svg></span>
<span class="bp-pin-label">B</span>
</div>
<div class="bp-pin-row output">
<span class="bp-pin"><svg width="12" height="12"><circle cx="6" cy="6" r="4" fill="#7ecd32"/></svg></span>
<span class="bp-pin-label">A</span>
</div>
</div>
</div>
</div>
---
## 相关文档
- [蓝图节点参考](/modules/blueprint/nodes) - 核心蓝图节点
- [蓝图编辑器指南](/modules/blueprint/editor-guide) - 编辑器使用方法
- [自定义节点](/modules/blueprint/custom-nodes) - 创建自定义节点

View File

@@ -0,0 +1,79 @@
---
title: "数学库"
description: "ESEngine 数学库 - Vector2, Fixed32, FixedVector2, Color 等数学类型"
---
`@esengine/ecs-framework-math` 模块提供游戏开发常用的数学类型和运算。
## 核心类型
| 类型 | 说明 |
|------|------|
| `Vector2` | 2D 浮点向量,用于位置、速度、方向 |
| `Fixed32` | Q16.16 定点数,用于帧同步确定性计算 |
| `FixedVector2` | 2D 定点向量,用于确定性物理 |
| `Color` | RGBA 颜色 |
## 功能特性
### Vector2
- 加法、减法、缩放
- 点积、叉积
- 长度、归一化
- 距离、插值
- 旋转、角度转换
### Fixed32 定点数
专为帧同步网络游戏设计,保证跨平台计算一致性:
- 基本运算:加、减、乘、除
- 数学函数:绝对值、平方根、取整
- 比较、钳制、插值
- 常量0、1、0.5、π、2π
### Color 颜色
- RGB/RGBA 创建与分解
- Hex 十六进制转换
- HSL 色彩空间转换
- 颜色操作:提亮、变暗、饱和度调整
- 颜色混合与插值
## 蓝图支持
数学库提供了丰富的蓝图节点,详见:
- [数学库蓝图节点](/modules/math/blueprint-nodes)
## 安装
```bash
pnpm add @esengine/ecs-framework-math
```
## 基本用法
```typescript
import { Vector2, Fixed32, FixedVector2, Color } from '@esengine/ecs-framework-math';
// Vector2
const pos = new Vector2(10, 20);
const dir = pos.normalized();
// Fixed32 (帧同步)
const speed = Fixed32.from(5.0);
const dt = Fixed32.from(0.016);
const distance = speed.mul(dt);
// FixedVector2
const fixedPos = FixedVector2.from(10, 20);
const fixedVel = FixedVector2.from(1, 0);
const newPos = fixedPos.add(fixedVel);
// Color
const red = Color.RED;
const blue = Color.BLUE;
const purple = Color.lerp(red, blue, 0.5);
```

View File

@@ -296,6 +296,10 @@ nav.sidebar-content ul li a[aria-current="page"] {
.bp-conn.component { stroke: #7030c0; }
.bp-conn.array { stroke: #7030c0; }
.bp-conn.any { stroke: #707070; }
.bp-conn.vector2 { stroke: #2196F3; }
.bp-conn.fixed32 { stroke: #9C27B0; }
.bp-conn.fixedvector2 { stroke: #673AB7; }
.bp-conn.color { stroke: #FF9800; }
/* ==================== Node Container ==================== */
.bp-node {

View File

@@ -561,3 +561,325 @@ export class SignExecutor implements INodeExecutor {
return { outputs: { result: Math.sign(value) } };
}
}
// ============================================================================
// Wrap Node (循环限制节点)
// ============================================================================
export const WrapTemplate: BlueprintNodeTemplate = {
type: 'Wrap',
title: 'Wrap',
category: 'math',
color: '#4CAF50',
description: 'Wraps value to stay within min and max range (将值循环限制在范围内)',
keywords: ['wrap', 'loop', 'cycle', 'range', 'math'],
isPure: true,
inputs: [
{ name: 'value', type: 'float', displayName: 'Value', defaultValue: 0 },
{ name: 'min', type: 'float', displayName: 'Min', defaultValue: 0 },
{ name: 'max', type: 'float', displayName: 'Max', defaultValue: 1 }
],
outputs: [
{ name: 'result', type: 'float', displayName: 'Result' }
]
};
@RegisterNode(WrapTemplate)
export class WrapExecutor implements INodeExecutor {
execute(node: BlueprintNode, context: ExecutionContext): ExecutionResult {
const value = Number(context.evaluateInput(node.id, 'value', 0));
const min = Number(context.evaluateInput(node.id, 'min', 0));
const max = Number(context.evaluateInput(node.id, 'max', 1));
const range = max - min;
if (range <= 0) return { outputs: { result: min } };
const wrapped = ((value - min) % range + range) % range + min;
return { outputs: { result: wrapped } };
}
}
// ============================================================================
// Sin Node (正弦节点)
// ============================================================================
export const SinTemplate: BlueprintNodeTemplate = {
type: 'Sin',
title: 'Sin',
category: 'math',
color: '#4CAF50',
description: 'Returns the sine of angle in radians (返回弧度角的正弦值)',
keywords: ['sin', 'sine', 'trig', 'math'],
isPure: true,
inputs: [
{ name: 'radians', type: 'float', displayName: 'Radians', defaultValue: 0 }
],
outputs: [
{ name: 'result', type: 'float', displayName: 'Result' }
]
};
@RegisterNode(SinTemplate)
export class SinExecutor implements INodeExecutor {
execute(node: BlueprintNode, context: ExecutionContext): ExecutionResult {
const radians = Number(context.evaluateInput(node.id, 'radians', 0));
return { outputs: { result: Math.sin(radians) } };
}
}
// ============================================================================
// Cos Node (余弦节点)
// ============================================================================
export const CosTemplate: BlueprintNodeTemplate = {
type: 'Cos',
title: 'Cos',
category: 'math',
color: '#4CAF50',
description: 'Returns the cosine of angle in radians (返回弧度角的余弦值)',
keywords: ['cos', 'cosine', 'trig', 'math'],
isPure: true,
inputs: [
{ name: 'radians', type: 'float', displayName: 'Radians', defaultValue: 0 }
],
outputs: [
{ name: 'result', type: 'float', displayName: 'Result' }
]
};
@RegisterNode(CosTemplate)
export class CosExecutor implements INodeExecutor {
execute(node: BlueprintNode, context: ExecutionContext): ExecutionResult {
const radians = Number(context.evaluateInput(node.id, 'radians', 0));
return { outputs: { result: Math.cos(radians) } };
}
}
// ============================================================================
// Tan Node (正切节点)
// ============================================================================
export const TanTemplate: BlueprintNodeTemplate = {
type: 'Tan',
title: 'Tan',
category: 'math',
color: '#4CAF50',
description: 'Returns the tangent of angle in radians (返回弧度角的正切值)',
keywords: ['tan', 'tangent', 'trig', 'math'],
isPure: true,
inputs: [
{ name: 'radians', type: 'float', displayName: 'Radians', defaultValue: 0 }
],
outputs: [
{ name: 'result', type: 'float', displayName: 'Result' }
]
};
@RegisterNode(TanTemplate)
export class TanExecutor implements INodeExecutor {
execute(node: BlueprintNode, context: ExecutionContext): ExecutionResult {
const radians = Number(context.evaluateInput(node.id, 'radians', 0));
return { outputs: { result: Math.tan(radians) } };
}
}
// ============================================================================
// Asin Node (反正弦节点)
// ============================================================================
export const AsinTemplate: BlueprintNodeTemplate = {
type: 'Asin',
title: 'Asin',
category: 'math',
color: '#4CAF50',
description: 'Returns the arc sine in radians (返回反正弦值,单位为弧度)',
keywords: ['asin', 'arc', 'sine', 'inverse', 'trig', 'math'],
isPure: true,
inputs: [
{ name: 'value', type: 'float', displayName: 'Value', defaultValue: 0 }
],
outputs: [
{ name: 'result', type: 'float', displayName: 'Radians' }
]
};
@RegisterNode(AsinTemplate)
export class AsinExecutor implements INodeExecutor {
execute(node: BlueprintNode, context: ExecutionContext): ExecutionResult {
const value = Number(context.evaluateInput(node.id, 'value', 0));
return { outputs: { result: Math.asin(Math.max(-1, Math.min(1, value))) } };
}
}
// ============================================================================
// Acos Node (反余弦节点)
// ============================================================================
export const AcosTemplate: BlueprintNodeTemplate = {
type: 'Acos',
title: 'Acos',
category: 'math',
color: '#4CAF50',
description: 'Returns the arc cosine in radians (返回反余弦值,单位为弧度)',
keywords: ['acos', 'arc', 'cosine', 'inverse', 'trig', 'math'],
isPure: true,
inputs: [
{ name: 'value', type: 'float', displayName: 'Value', defaultValue: 0 }
],
outputs: [
{ name: 'result', type: 'float', displayName: 'Radians' }
]
};
@RegisterNode(AcosTemplate)
export class AcosExecutor implements INodeExecutor {
execute(node: BlueprintNode, context: ExecutionContext): ExecutionResult {
const value = Number(context.evaluateInput(node.id, 'value', 0));
return { outputs: { result: Math.acos(Math.max(-1, Math.min(1, value))) } };
}
}
// ============================================================================
// Atan Node (反正切节点)
// ============================================================================
export const AtanTemplate: BlueprintNodeTemplate = {
type: 'Atan',
title: 'Atan',
category: 'math',
color: '#4CAF50',
description: 'Returns the arc tangent in radians (返回反正切值,单位为弧度)',
keywords: ['atan', 'arc', 'tangent', 'inverse', 'trig', 'math'],
isPure: true,
inputs: [
{ name: 'value', type: 'float', displayName: 'Value', defaultValue: 0 }
],
outputs: [
{ name: 'result', type: 'float', displayName: 'Radians' }
]
};
@RegisterNode(AtanTemplate)
export class AtanExecutor implements INodeExecutor {
execute(node: BlueprintNode, context: ExecutionContext): ExecutionResult {
const value = Number(context.evaluateInput(node.id, 'value', 0));
return { outputs: { result: Math.atan(value) } };
}
}
// ============================================================================
// Atan2 Node (两参数反正切节点)
// ============================================================================
export const Atan2Template: BlueprintNodeTemplate = {
type: 'Atan2',
title: 'Atan2',
category: 'math',
color: '#4CAF50',
description: 'Returns the angle in radians between the positive X axis and the point (x, y) (返回点(x,y)与正X轴之间的弧度角)',
keywords: ['atan2', 'angle', 'direction', 'trig', 'math'],
isPure: true,
inputs: [
{ name: 'y', type: 'float', displayName: 'Y', defaultValue: 0 },
{ name: 'x', type: 'float', displayName: 'X', defaultValue: 1 }
],
outputs: [
{ name: 'result', type: 'float', displayName: 'Radians' }
]
};
@RegisterNode(Atan2Template)
export class Atan2Executor implements INodeExecutor {
execute(node: BlueprintNode, context: ExecutionContext): ExecutionResult {
const y = Number(context.evaluateInput(node.id, 'y', 0));
const x = Number(context.evaluateInput(node.id, 'x', 1));
return { outputs: { result: Math.atan2(y, x) } };
}
}
// ============================================================================
// Degrees to Radians Node (角度转弧度节点)
// ============================================================================
export const DegToRadTemplate: BlueprintNodeTemplate = {
type: 'DegToRad',
title: 'Degrees to Radians',
category: 'math',
color: '#4CAF50',
description: 'Converts degrees to radians (将角度转换为弧度)',
keywords: ['degrees', 'radians', 'convert', 'angle', 'math'],
isPure: true,
inputs: [
{ name: 'degrees', type: 'float', displayName: 'Degrees', defaultValue: 0 }
],
outputs: [
{ name: 'result', type: 'float', displayName: 'Radians' }
]
};
@RegisterNode(DegToRadTemplate)
export class DegToRadExecutor implements INodeExecutor {
execute(node: BlueprintNode, context: ExecutionContext): ExecutionResult {
const degrees = Number(context.evaluateInput(node.id, 'degrees', 0));
return { outputs: { result: degrees * (Math.PI / 180) } };
}
}
// ============================================================================
// Radians to Degrees Node (弧度转角度节点)
// ============================================================================
export const RadToDegTemplate: BlueprintNodeTemplate = {
type: 'RadToDeg',
title: 'Radians to Degrees',
category: 'math',
color: '#4CAF50',
description: 'Converts radians to degrees (将弧度转换为角度)',
keywords: ['radians', 'degrees', 'convert', 'angle', 'math'],
isPure: true,
inputs: [
{ name: 'radians', type: 'float', displayName: 'Radians', defaultValue: 0 }
],
outputs: [
{ name: 'result', type: 'float', displayName: 'Degrees' }
]
};
@RegisterNode(RadToDegTemplate)
export class RadToDegExecutor implements INodeExecutor {
execute(node: BlueprintNode, context: ExecutionContext): ExecutionResult {
const radians = Number(context.evaluateInput(node.id, 'radians', 0));
return { outputs: { result: radians * (180 / Math.PI) } };
}
}
// ============================================================================
// Inverse Lerp Node (反向线性插值节点)
// ============================================================================
export const InverseLerpTemplate: BlueprintNodeTemplate = {
type: 'InverseLerp',
title: 'Inverse Lerp',
category: 'math',
color: '#4CAF50',
description: 'Returns the percentage of Value between A and B (返回值在 A 和 B 之间的百分比位置)',
keywords: ['inverse', 'lerp', 'percentage', 'ratio', 'math'],
isPure: true,
inputs: [
{ name: 'a', type: 'float', displayName: 'A', defaultValue: 0 },
{ name: 'b', type: 'float', displayName: 'B', defaultValue: 1 },
{ name: 'value', type: 'float', displayName: 'Value', defaultValue: 0.5 }
],
outputs: [
{ name: 'result', type: 'float', displayName: 'Alpha (0-1)' }
]
};
@RegisterNode(InverseLerpTemplate)
export class InverseLerpExecutor implements INodeExecutor {
execute(node: BlueprintNode, context: ExecutionContext): ExecutionResult {
const a = Number(context.evaluateInput(node.id, 'a', 0));
const b = Number(context.evaluateInput(node.id, 'b', 1));
const value = Number(context.evaluateInput(node.id, 'value', 0.5));
if (b === a) return { outputs: { result: 0 } };
return { outputs: { result: (value - a) / (b - a) } };
}
}

View File

@@ -1,5 +1,16 @@
# @esengine/ecs-framework-math
## 2.9.0
### Minor Changes
- [#442](https://github.com/esengine/esengine/pull/442) [`bffe90b`](https://github.com/esengine/esengine/commit/bffe90b6a17563cc90709faf339b229dc3abd22d) Thanks [@esengine](https://github.com/esengine)! - feat(math): add blueprint nodes for math library
- Add Vector2 blueprint nodes (Make, Break, Add, Sub, Mul, Length, Normalize, Dot, Cross, Distance, Lerp, Rotate, FromAngle)
- Add Fixed32 blueprint nodes (FromFloat, FromInt, ToFloat, ToInt, arithmetic operations, Abs, Sqrt, Floor, Ceil, Round, Sign, Min, Max, Clamp, Lerp)
- Add FixedVector2 blueprint nodes (Make, Break, Add, Sub, Mul, Negate, Length, Normalize, Dot, Cross, Distance, Lerp)
- Add Color blueprint nodes (Make, Break, FromHex, ToHex, FromHSL, ToHSL, Lerp, Lighten, Darken, Saturate, Desaturate, Invert, Grayscale, Luminance, constants)
- Add documentation for math blueprint nodes (Chinese and English)
## 2.8.0
### Minor Changes

View File

@@ -1,6 +1,6 @@
{
"name": "@esengine/ecs-framework-math",
"version": "2.8.0",
"version": "2.9.0",
"description": "ECS框架2D数学库 - 提供向量、矩阵、几何形状和碰撞检测功能",
"main": "bin/index.js",
"types": "bin/index.d.ts",
@@ -40,6 +40,9 @@
},
"author": "yhh",
"license": "MIT",
"dependencies": {
"@esengine/blueprint": "workspace:*"
},
"devDependencies": {
"@rollup/plugin-commonjs": "^28.0.3",
"@rollup/plugin-node-resolve": "^16.0.1",

View File

@@ -33,3 +33,6 @@ export * from './Collision';
// 动画和插值
export * from './Animation';
// 蓝图节点
export * from './nodes';

View File

@@ -0,0 +1,463 @@
/**
* @zh 颜色蓝图节点
* @en Color Blueprint Nodes
*/
import type { BlueprintNodeTemplate, BlueprintNode, INodeExecutor, ExecutionResult } from '@esengine/blueprint';
import { Color } from '../Color';
interface ColorContext {
evaluateInput(nodeId: string, pinName: string, defaultValue?: unknown): unknown;
}
// Make Color from RGBA
export const MakeColorTemplate: BlueprintNodeTemplate = {
type: 'MakeColor',
title: 'Make Color',
category: 'math',
description: 'Creates a Color from RGBA',
keywords: ['make', 'create', 'color', 'rgba'],
menuPath: ['Math', 'Color', 'Make Color'],
isPure: true,
inputs: [
{ name: 'r', displayName: 'R (0-255)', type: 'int', defaultValue: 255 },
{ name: 'g', displayName: 'G (0-255)', type: 'int', defaultValue: 255 },
{ name: 'b', displayName: 'B (0-255)', type: 'int', defaultValue: 255 },
{ name: 'a', displayName: 'A (0-1)', type: 'float', defaultValue: 1 }
],
outputs: [
{ name: 'color', displayName: 'Color', type: 'color' }
],
color: '#E91E63'
};
export class MakeColorExecutor implements INodeExecutor {
execute(node: BlueprintNode, context: unknown): ExecutionResult {
const ctx = context as ColorContext;
const r = Number(ctx.evaluateInput(node.id, 'r', 255));
const g = Number(ctx.evaluateInput(node.id, 'g', 255));
const b = Number(ctx.evaluateInput(node.id, 'b', 255));
const a = Number(ctx.evaluateInput(node.id, 'a', 1));
return { outputs: { color: new Color(r, g, b, a) } };
}
}
// Break Color
export const BreakColorTemplate: BlueprintNodeTemplate = {
type: 'BreakColor',
title: 'Break Color',
category: 'math',
description: 'Breaks a Color into RGBA',
keywords: ['break', 'split', 'color', 'rgba'],
menuPath: ['Math', 'Color', 'Break Color'],
isPure: true,
inputs: [
{ name: 'color', displayName: 'Color', type: 'color' }
],
outputs: [
{ name: 'r', displayName: 'R', type: 'int' },
{ name: 'g', displayName: 'G', type: 'int' },
{ name: 'b', displayName: 'B', type: 'int' },
{ name: 'a', displayName: 'A', type: 'float' }
],
color: '#E91E63'
};
export class BreakColorExecutor implements INodeExecutor {
execute(node: BlueprintNode, context: unknown): ExecutionResult {
const ctx = context as ColorContext;
const color = ctx.evaluateInput(node.id, 'color', Color.WHITE) as Color;
const c = color ?? Color.WHITE;
return { outputs: { r: c.r, g: c.g, b: c.b, a: c.a } };
}
}
// Color from Hex
export const ColorFromHexTemplate: BlueprintNodeTemplate = {
type: 'ColorFromHex',
title: 'Color From Hex',
category: 'math',
description: 'Creates a Color from hex string',
keywords: ['color', 'hex', 'from', 'create'],
menuPath: ['Math', 'Color', 'From Hex'],
isPure: true,
inputs: [
{ name: 'hex', displayName: 'Hex', type: 'string', defaultValue: '#FFFFFF' },
{ name: 'alpha', displayName: 'Alpha', type: 'float', defaultValue: 1 }
],
outputs: [
{ name: 'color', displayName: 'Color', type: 'color' }
],
color: '#E91E63'
};
export class ColorFromHexExecutor implements INodeExecutor {
execute(node: BlueprintNode, context: unknown): ExecutionResult {
const ctx = context as ColorContext;
const hex = String(ctx.evaluateInput(node.id, 'hex', '#FFFFFF'));
const alpha = Number(ctx.evaluateInput(node.id, 'alpha', 1));
return { outputs: { color: Color.fromHex(hex, alpha) } };
}
}
// Color to Hex
export const ColorToHexTemplate: BlueprintNodeTemplate = {
type: 'ColorToHex',
title: 'Color To Hex',
category: 'math',
description: 'Converts a Color to hex string',
keywords: ['color', 'hex', 'to', 'convert'],
menuPath: ['Math', 'Color', 'To Hex'],
isPure: true,
inputs: [
{ name: 'color', displayName: 'Color', type: 'color' }
],
outputs: [
{ name: 'hex', displayName: 'Hex', type: 'string' }
],
color: '#E91E63'
};
export class ColorToHexExecutor implements INodeExecutor {
execute(node: BlueprintNode, context: unknown): ExecutionResult {
const ctx = context as ColorContext;
const color = ctx.evaluateInput(node.id, 'color', Color.WHITE) as Color;
return { outputs: { hex: (color ?? Color.WHITE).toHex() } };
}
}
// Color from HSL
export const ColorFromHSLTemplate: BlueprintNodeTemplate = {
type: 'ColorFromHSL',
title: 'Color From HSL',
category: 'math',
description: 'Creates a Color from HSL values',
keywords: ['color', 'hsl', 'hue', 'saturation', 'lightness'],
menuPath: ['Math', 'Color', 'From HSL'],
isPure: true,
inputs: [
{ name: 'h', displayName: 'H (0-360)', type: 'float', defaultValue: 0 },
{ name: 's', displayName: 'S (0-1)', type: 'float', defaultValue: 1 },
{ name: 'l', displayName: 'L (0-1)', type: 'float', defaultValue: 0.5 },
{ name: 'a', displayName: 'A (0-1)', type: 'float', defaultValue: 1 }
],
outputs: [
{ name: 'color', displayName: 'Color', type: 'color' }
],
color: '#E91E63'
};
export class ColorFromHSLExecutor implements INodeExecutor {
execute(node: BlueprintNode, context: unknown): ExecutionResult {
const ctx = context as ColorContext;
const h = Number(ctx.evaluateInput(node.id, 'h', 0));
const s = Number(ctx.evaluateInput(node.id, 's', 1));
const l = Number(ctx.evaluateInput(node.id, 'l', 0.5));
const a = Number(ctx.evaluateInput(node.id, 'a', 1));
return { outputs: { color: Color.fromHSL(h, s, l, a) } };
}
}
// Color to HSL
export const ColorToHSLTemplate: BlueprintNodeTemplate = {
type: 'ColorToHSL',
title: 'Color To HSL',
category: 'math',
description: 'Converts a Color to HSL values',
keywords: ['color', 'hsl', 'convert'],
menuPath: ['Math', 'Color', 'To HSL'],
isPure: true,
inputs: [
{ name: 'color', displayName: 'Color', type: 'color' }
],
outputs: [
{ name: 'h', displayName: 'H', type: 'float' },
{ name: 's', displayName: 'S', type: 'float' },
{ name: 'l', displayName: 'L', type: 'float' }
],
color: '#E91E63'
};
export class ColorToHSLExecutor implements INodeExecutor {
execute(node: BlueprintNode, context: unknown): ExecutionResult {
const ctx = context as ColorContext;
const color = ctx.evaluateInput(node.id, 'color', Color.WHITE) as Color;
const hsl = (color ?? Color.WHITE).toHSL();
return { outputs: { h: hsl.h, s: hsl.s, l: hsl.l } };
}
}
// Color Lerp
export const ColorLerpTemplate: BlueprintNodeTemplate = {
type: 'ColorLerp',
title: 'Color Lerp',
category: 'math',
description: 'Linear interpolation between two colors',
keywords: ['color', 'lerp', 'interpolate', 'blend'],
menuPath: ['Math', 'Color', 'Lerp'],
isPure: true,
inputs: [
{ name: 'from', displayName: 'From', type: 'color' },
{ name: 'to', displayName: 'To', type: 'color' },
{ name: 't', displayName: 'T', type: 'float', defaultValue: 0.5 }
],
outputs: [
{ name: 'result', displayName: 'Result', type: 'color' }
],
color: '#E91E63'
};
export class ColorLerpExecutor implements INodeExecutor {
execute(node: BlueprintNode, context: unknown): ExecutionResult {
const ctx = context as ColorContext;
const from = ctx.evaluateInput(node.id, 'from', Color.BLACK) as Color;
const to = ctx.evaluateInput(node.id, 'to', Color.WHITE) as Color;
const t = Number(ctx.evaluateInput(node.id, 't', 0.5));
return { outputs: { result: Color.lerp(from ?? Color.BLACK, to ?? Color.WHITE, t) } };
}
}
// Color Lighten
export const ColorLightenTemplate: BlueprintNodeTemplate = {
type: 'ColorLighten',
title: 'Color Lighten',
category: 'math',
description: 'Lightens a color',
keywords: ['color', 'lighten', 'bright'],
menuPath: ['Math', 'Color', 'Lighten'],
isPure: true,
inputs: [
{ name: 'color', displayName: 'Color', type: 'color' },
{ name: 'amount', displayName: 'Amount', type: 'float', defaultValue: 0.1 }
],
outputs: [
{ name: 'result', displayName: 'Result', type: 'color' }
],
color: '#E91E63'
};
export class ColorLightenExecutor implements INodeExecutor {
execute(node: BlueprintNode, context: unknown): ExecutionResult {
const ctx = context as ColorContext;
const color = ctx.evaluateInput(node.id, 'color', Color.GRAY) as Color;
const amount = Number(ctx.evaluateInput(node.id, 'amount', 0.1));
return { outputs: { result: Color.lighten(color ?? Color.GRAY, amount) } };
}
}
// Color Darken
export const ColorDarkenTemplate: BlueprintNodeTemplate = {
type: 'ColorDarken',
title: 'Color Darken',
category: 'math',
description: 'Darkens a color',
keywords: ['color', 'darken', 'dark'],
menuPath: ['Math', 'Color', 'Darken'],
isPure: true,
inputs: [
{ name: 'color', displayName: 'Color', type: 'color' },
{ name: 'amount', displayName: 'Amount', type: 'float', defaultValue: 0.1 }
],
outputs: [
{ name: 'result', displayName: 'Result', type: 'color' }
],
color: '#E91E63'
};
export class ColorDarkenExecutor implements INodeExecutor {
execute(node: BlueprintNode, context: unknown): ExecutionResult {
const ctx = context as ColorContext;
const color = ctx.evaluateInput(node.id, 'color', Color.GRAY) as Color;
const amount = Number(ctx.evaluateInput(node.id, 'amount', 0.1));
return { outputs: { result: Color.darken(color ?? Color.GRAY, amount) } };
}
}
// Color Saturate
export const ColorSaturateTemplate: BlueprintNodeTemplate = {
type: 'ColorSaturate',
title: 'Color Saturate',
category: 'math',
description: 'Increases color saturation',
keywords: ['color', 'saturate', 'saturation'],
menuPath: ['Math', 'Color', 'Saturate'],
isPure: true,
inputs: [
{ name: 'color', displayName: 'Color', type: 'color' },
{ name: 'amount', displayName: 'Amount', type: 'float', defaultValue: 0.1 }
],
outputs: [
{ name: 'result', displayName: 'Result', type: 'color' }
],
color: '#E91E63'
};
export class ColorSaturateExecutor implements INodeExecutor {
execute(node: BlueprintNode, context: unknown): ExecutionResult {
const ctx = context as ColorContext;
const color = ctx.evaluateInput(node.id, 'color', Color.GRAY) as Color;
const amount = Number(ctx.evaluateInput(node.id, 'amount', 0.1));
return { outputs: { result: Color.saturate(color ?? Color.GRAY, amount) } };
}
}
// Color Desaturate
export const ColorDesaturateTemplate: BlueprintNodeTemplate = {
type: 'ColorDesaturate',
title: 'Color Desaturate',
category: 'math',
description: 'Decreases color saturation',
keywords: ['color', 'desaturate', 'saturation'],
menuPath: ['Math', 'Color', 'Desaturate'],
isPure: true,
inputs: [
{ name: 'color', displayName: 'Color', type: 'color' },
{ name: 'amount', displayName: 'Amount', type: 'float', defaultValue: 0.1 }
],
outputs: [
{ name: 'result', displayName: 'Result', type: 'color' }
],
color: '#E91E63'
};
export class ColorDesaturateExecutor implements INodeExecutor {
execute(node: BlueprintNode, context: unknown): ExecutionResult {
const ctx = context as ColorContext;
const color = ctx.evaluateInput(node.id, 'color', Color.GRAY) as Color;
const amount = Number(ctx.evaluateInput(node.id, 'amount', 0.1));
return { outputs: { result: Color.desaturate(color ?? Color.GRAY, amount) } };
}
}
// Color Invert
export const ColorInvertTemplate: BlueprintNodeTemplate = {
type: 'ColorInvert',
title: 'Color Invert',
category: 'math',
description: 'Inverts a color',
keywords: ['color', 'invert', 'inverse'],
menuPath: ['Math', 'Color', 'Invert'],
isPure: true,
inputs: [
{ name: 'color', displayName: 'Color', type: 'color' }
],
outputs: [
{ name: 'result', displayName: 'Result', type: 'color' }
],
color: '#E91E63'
};
export class ColorInvertExecutor implements INodeExecutor {
execute(node: BlueprintNode, context: unknown): ExecutionResult {
const ctx = context as ColorContext;
const color = ctx.evaluateInput(node.id, 'color', Color.WHITE) as Color;
return { outputs: { result: Color.invert(color ?? Color.WHITE) } };
}
}
// Color Grayscale
export const ColorGrayscaleTemplate: BlueprintNodeTemplate = {
type: 'ColorGrayscale',
title: 'Color Grayscale',
category: 'math',
description: 'Converts color to grayscale',
keywords: ['color', 'grayscale', 'gray', 'grey'],
menuPath: ['Math', 'Color', 'Grayscale'],
isPure: true,
inputs: [
{ name: 'color', displayName: 'Color', type: 'color' }
],
outputs: [
{ name: 'result', displayName: 'Result', type: 'color' }
],
color: '#E91E63'
};
export class ColorGrayscaleExecutor implements INodeExecutor {
execute(node: BlueprintNode, context: unknown): ExecutionResult {
const ctx = context as ColorContext;
const color = ctx.evaluateInput(node.id, 'color', Color.WHITE) as Color;
return { outputs: { result: Color.grayscale(color ?? Color.WHITE) } };
}
}
// Color Luminance
export const ColorLuminanceTemplate: BlueprintNodeTemplate = {
type: 'ColorLuminance',
title: 'Color Luminance',
category: 'math',
description: 'Gets perceived brightness (0-1)',
keywords: ['color', 'luminance', 'brightness'],
menuPath: ['Math', 'Color', 'Luminance'],
isPure: true,
inputs: [
{ name: 'color', displayName: 'Color', type: 'color' }
],
outputs: [
{ name: 'luminance', displayName: 'Luminance', type: 'float' }
],
color: '#E91E63'
};
export class ColorLuminanceExecutor implements INodeExecutor {
execute(node: BlueprintNode, context: unknown): ExecutionResult {
const ctx = context as ColorContext;
const color = ctx.evaluateInput(node.id, 'color', Color.WHITE) as Color;
return { outputs: { luminance: Color.luminance(color ?? Color.WHITE) } };
}
}
// Color Constants
export const ColorConstantsTemplate: BlueprintNodeTemplate = {
type: 'ColorConstants',
title: 'Color Constants',
category: 'math',
description: 'Common color constants',
keywords: ['color', 'constant', 'red', 'green', 'blue', 'white', 'black'],
menuPath: ['Math', 'Color', 'Constants'],
isPure: true,
inputs: [],
outputs: [
{ name: 'white', displayName: 'White', type: 'color' },
{ name: 'black', displayName: 'Black', type: 'color' },
{ name: 'red', displayName: 'Red', type: 'color' },
{ name: 'green', displayName: 'Green', type: 'color' },
{ name: 'blue', displayName: 'Blue', type: 'color' },
{ name: 'transparent', displayName: 'Transparent', type: 'color' }
],
color: '#E91E63'
};
export class ColorConstantsExecutor implements INodeExecutor {
execute(): ExecutionResult {
return {
outputs: {
white: Color.WHITE,
black: Color.BLACK,
red: Color.RED,
green: Color.GREEN,
blue: Color.BLUE,
transparent: Color.TRANSPARENT
}
};
}
}
// Node definitions collection
export const ColorNodeDefinitions = [
{ template: MakeColorTemplate, executor: new MakeColorExecutor() },
{ template: BreakColorTemplate, executor: new BreakColorExecutor() },
{ template: ColorFromHexTemplate, executor: new ColorFromHexExecutor() },
{ template: ColorToHexTemplate, executor: new ColorToHexExecutor() },
{ template: ColorFromHSLTemplate, executor: new ColorFromHSLExecutor() },
{ template: ColorToHSLTemplate, executor: new ColorToHSLExecutor() },
{ template: ColorLerpTemplate, executor: new ColorLerpExecutor() },
{ template: ColorLightenTemplate, executor: new ColorLightenExecutor() },
{ template: ColorDarkenTemplate, executor: new ColorDarkenExecutor() },
{ template: ColorSaturateTemplate, executor: new ColorSaturateExecutor() },
{ template: ColorDesaturateTemplate, executor: new ColorDesaturateExecutor() },
{ template: ColorInvertTemplate, executor: new ColorInvertExecutor() },
{ template: ColorGrayscaleTemplate, executor: new ColorGrayscaleExecutor() },
{ template: ColorLuminanceTemplate, executor: new ColorLuminanceExecutor() },
{ template: ColorConstantsTemplate, executor: new ColorConstantsExecutor() }
];

View File

@@ -0,0 +1,662 @@
/**
* @zh Fixed32 定点数蓝图节点
* @en Fixed32 Blueprint Nodes
*/
import type { BlueprintNodeTemplate, BlueprintNode, INodeExecutor, ExecutionResult } from '@esengine/blueprint';
import { Fixed32 } from '../Fixed32';
interface FixedContext {
evaluateInput(nodeId: string, pinName: string, defaultValue?: unknown): unknown;
}
// Make Fixed32 from float
export const Fixed32FromTemplate: BlueprintNodeTemplate = {
type: 'Fixed32From',
title: 'Fixed32 From Float',
category: 'math',
description: 'Creates Fixed32 from float',
keywords: ['fixed', 'fixed32', 'from', 'create', 'deterministic'],
menuPath: ['Math', 'Fixed', 'From Float'],
isPure: true,
inputs: [
{ name: 'value', displayName: 'Value', type: 'float', defaultValue: 0 }
],
outputs: [
{ name: 'fixed', displayName: 'Fixed32', type: 'object' }
],
color: '#9C27B0'
};
export class Fixed32FromExecutor implements INodeExecutor {
execute(node: BlueprintNode, context: unknown): ExecutionResult {
const ctx = context as FixedContext;
const value = Number(ctx.evaluateInput(node.id, 'value', 0));
return { outputs: { fixed: Fixed32.from(value) } };
}
}
// Make Fixed32 from int
export const Fixed32FromIntTemplate: BlueprintNodeTemplate = {
type: 'Fixed32FromInt',
title: 'Fixed32 From Int',
category: 'math',
description: 'Creates Fixed32 from integer (no precision loss)',
keywords: ['fixed', 'fixed32', 'from', 'int', 'integer'],
menuPath: ['Math', 'Fixed', 'From Int'],
isPure: true,
inputs: [
{ name: 'value', displayName: 'Value', type: 'int', defaultValue: 0 }
],
outputs: [
{ name: 'fixed', displayName: 'Fixed32', type: 'object' }
],
color: '#9C27B0'
};
export class Fixed32FromIntExecutor implements INodeExecutor {
execute(node: BlueprintNode, context: unknown): ExecutionResult {
const ctx = context as FixedContext;
const value = Math.floor(Number(ctx.evaluateInput(node.id, 'value', 0)));
return { outputs: { fixed: Fixed32.fromInt(value) } };
}
}
// Fixed32 to float
export const Fixed32ToFloatTemplate: BlueprintNodeTemplate = {
type: 'Fixed32ToFloat',
title: 'Fixed32 To Float',
category: 'math',
description: 'Converts Fixed32 to float',
keywords: ['fixed', 'fixed32', 'to', 'float', 'convert'],
menuPath: ['Math', 'Fixed', 'To Float'],
isPure: true,
inputs: [
{ name: 'fixed', displayName: 'Fixed32', type: 'object' }
],
outputs: [
{ name: 'value', displayName: 'Value', type: 'float' }
],
color: '#9C27B0'
};
export class Fixed32ToFloatExecutor implements INodeExecutor {
execute(node: BlueprintNode, context: unknown): ExecutionResult {
const ctx = context as FixedContext;
const fixed = ctx.evaluateInput(node.id, 'fixed', Fixed32.ZERO) as Fixed32;
return { outputs: { value: fixed?.toNumber() ?? 0 } };
}
}
// Fixed32 to int
export const Fixed32ToIntTemplate: BlueprintNodeTemplate = {
type: 'Fixed32ToInt',
title: 'Fixed32 To Int',
category: 'math',
description: 'Converts Fixed32 to integer (floor)',
keywords: ['fixed', 'fixed32', 'to', 'int', 'integer'],
menuPath: ['Math', 'Fixed', 'To Int'],
isPure: true,
inputs: [
{ name: 'fixed', displayName: 'Fixed32', type: 'object' }
],
outputs: [
{ name: 'value', displayName: 'Value', type: 'int' }
],
color: '#9C27B0'
};
export class Fixed32ToIntExecutor implements INodeExecutor {
execute(node: BlueprintNode, context: unknown): ExecutionResult {
const ctx = context as FixedContext;
const fixed = ctx.evaluateInput(node.id, 'fixed', Fixed32.ZERO) as Fixed32;
return { outputs: { value: fixed?.toInt() ?? 0 } };
}
}
// Fixed32 Add
export const Fixed32AddTemplate: BlueprintNodeTemplate = {
type: 'Fixed32Add',
title: 'Fixed32 +',
category: 'math',
description: 'Adds two Fixed32 values',
keywords: ['fixed', 'add', 'plus', '+'],
menuPath: ['Math', 'Fixed', 'Add'],
isPure: true,
inputs: [
{ name: 'a', displayName: 'A', type: 'object' },
{ name: 'b', displayName: 'B', type: 'object' }
],
outputs: [
{ name: 'result', displayName: 'Result', type: 'object' }
],
color: '#9C27B0'
};
export class Fixed32AddExecutor implements INodeExecutor {
execute(node: BlueprintNode, context: unknown): ExecutionResult {
const ctx = context as FixedContext;
const a = ctx.evaluateInput(node.id, 'a', Fixed32.ZERO) as Fixed32;
const b = ctx.evaluateInput(node.id, 'b', Fixed32.ZERO) as Fixed32;
return { outputs: { result: (a ?? Fixed32.ZERO).add(b ?? Fixed32.ZERO) } };
}
}
// Fixed32 Subtract
export const Fixed32SubtractTemplate: BlueprintNodeTemplate = {
type: 'Fixed32Subtract',
title: 'Fixed32 -',
category: 'math',
description: 'Subtracts B from A',
keywords: ['fixed', 'subtract', 'minus', '-'],
menuPath: ['Math', 'Fixed', 'Subtract'],
isPure: true,
inputs: [
{ name: 'a', displayName: 'A', type: 'object' },
{ name: 'b', displayName: 'B', type: 'object' }
],
outputs: [
{ name: 'result', displayName: 'Result', type: 'object' }
],
color: '#9C27B0'
};
export class Fixed32SubtractExecutor implements INodeExecutor {
execute(node: BlueprintNode, context: unknown): ExecutionResult {
const ctx = context as FixedContext;
const a = ctx.evaluateInput(node.id, 'a', Fixed32.ZERO) as Fixed32;
const b = ctx.evaluateInput(node.id, 'b', Fixed32.ZERO) as Fixed32;
return { outputs: { result: (a ?? Fixed32.ZERO).sub(b ?? Fixed32.ZERO) } };
}
}
// Fixed32 Multiply
export const Fixed32MultiplyTemplate: BlueprintNodeTemplate = {
type: 'Fixed32Multiply',
title: 'Fixed32 *',
category: 'math',
description: 'Multiplies two Fixed32 values',
keywords: ['fixed', 'multiply', 'times', '*'],
menuPath: ['Math', 'Fixed', 'Multiply'],
isPure: true,
inputs: [
{ name: 'a', displayName: 'A', type: 'object' },
{ name: 'b', displayName: 'B', type: 'object' }
],
outputs: [
{ name: 'result', displayName: 'Result', type: 'object' }
],
color: '#9C27B0'
};
export class Fixed32MultiplyExecutor implements INodeExecutor {
execute(node: BlueprintNode, context: unknown): ExecutionResult {
const ctx = context as FixedContext;
const a = ctx.evaluateInput(node.id, 'a', Fixed32.ONE) as Fixed32;
const b = ctx.evaluateInput(node.id, 'b', Fixed32.ONE) as Fixed32;
return { outputs: { result: (a ?? Fixed32.ONE).mul(b ?? Fixed32.ONE) } };
}
}
// Fixed32 Divide
export const Fixed32DivideTemplate: BlueprintNodeTemplate = {
type: 'Fixed32Divide',
title: 'Fixed32 /',
category: 'math',
description: 'Divides A by B',
keywords: ['fixed', 'divide', '/'],
menuPath: ['Math', 'Fixed', 'Divide'],
isPure: true,
inputs: [
{ name: 'a', displayName: 'A', type: 'object' },
{ name: 'b', displayName: 'B', type: 'object' }
],
outputs: [
{ name: 'result', displayName: 'Result', type: 'object' }
],
color: '#9C27B0'
};
export class Fixed32DivideExecutor implements INodeExecutor {
execute(node: BlueprintNode, context: unknown): ExecutionResult {
const ctx = context as FixedContext;
const a = ctx.evaluateInput(node.id, 'a', Fixed32.ZERO) as Fixed32;
const b = ctx.evaluateInput(node.id, 'b', Fixed32.ONE) as Fixed32;
const divisor = b ?? Fixed32.ONE;
if (divisor.isZero()) {
return { outputs: { result: Fixed32.ZERO } };
}
return { outputs: { result: (a ?? Fixed32.ZERO).div(divisor) } };
}
}
// Fixed32 Negate
export const Fixed32NegateTemplate: BlueprintNodeTemplate = {
type: 'Fixed32Negate',
title: 'Fixed32 Negate',
category: 'math',
description: 'Negates a Fixed32 value',
keywords: ['fixed', 'negate', '-'],
menuPath: ['Math', 'Fixed', 'Negate'],
isPure: true,
inputs: [
{ name: 'value', displayName: 'Value', type: 'object' }
],
outputs: [
{ name: 'result', displayName: 'Result', type: 'object' }
],
color: '#9C27B0'
};
export class Fixed32NegateExecutor implements INodeExecutor {
execute(node: BlueprintNode, context: unknown): ExecutionResult {
const ctx = context as FixedContext;
const value = ctx.evaluateInput(node.id, 'value', Fixed32.ZERO) as Fixed32;
return { outputs: { result: (value ?? Fixed32.ZERO).neg() } };
}
}
// Fixed32 Abs
export const Fixed32AbsTemplate: BlueprintNodeTemplate = {
type: 'Fixed32Abs',
title: 'Fixed32 Abs',
category: 'math',
description: 'Absolute value of Fixed32',
keywords: ['fixed', 'abs', 'absolute'],
menuPath: ['Math', 'Fixed', 'Abs'],
isPure: true,
inputs: [
{ name: 'value', displayName: 'Value', type: 'object' }
],
outputs: [
{ name: 'result', displayName: 'Result', type: 'object' }
],
color: '#9C27B0'
};
export class Fixed32AbsExecutor implements INodeExecutor {
execute(node: BlueprintNode, context: unknown): ExecutionResult {
const ctx = context as FixedContext;
const value = ctx.evaluateInput(node.id, 'value', Fixed32.ZERO) as Fixed32;
return { outputs: { result: (value ?? Fixed32.ZERO).abs() } };
}
}
// Fixed32 Sqrt
export const Fixed32SqrtTemplate: BlueprintNodeTemplate = {
type: 'Fixed32Sqrt',
title: 'Fixed32 Sqrt',
category: 'math',
description: 'Square root (deterministic)',
keywords: ['fixed', 'sqrt', 'square', 'root'],
menuPath: ['Math', 'Fixed', 'Sqrt'],
isPure: true,
inputs: [
{ name: 'value', displayName: 'Value', type: 'object' }
],
outputs: [
{ name: 'result', displayName: 'Result', type: 'object' }
],
color: '#9C27B0'
};
export class Fixed32SqrtExecutor implements INodeExecutor {
execute(node: BlueprintNode, context: unknown): ExecutionResult {
const ctx = context as FixedContext;
const value = ctx.evaluateInput(node.id, 'value', Fixed32.ZERO) as Fixed32;
return { outputs: { result: Fixed32.sqrt(value ?? Fixed32.ZERO) } };
}
}
// Fixed32 Floor
export const Fixed32FloorTemplate: BlueprintNodeTemplate = {
type: 'Fixed32Floor',
title: 'Fixed32 Floor',
category: 'math',
description: 'Floor of Fixed32',
keywords: ['fixed', 'floor', 'round', 'down'],
menuPath: ['Math', 'Fixed', 'Floor'],
isPure: true,
inputs: [
{ name: 'value', displayName: 'Value', type: 'object' }
],
outputs: [
{ name: 'result', displayName: 'Result', type: 'object' }
],
color: '#9C27B0'
};
export class Fixed32FloorExecutor implements INodeExecutor {
execute(node: BlueprintNode, context: unknown): ExecutionResult {
const ctx = context as FixedContext;
const value = ctx.evaluateInput(node.id, 'value', Fixed32.ZERO) as Fixed32;
return { outputs: { result: Fixed32.floor(value ?? Fixed32.ZERO) } };
}
}
// Fixed32 Ceil
export const Fixed32CeilTemplate: BlueprintNodeTemplate = {
type: 'Fixed32Ceil',
title: 'Fixed32 Ceil',
category: 'math',
description: 'Ceiling of Fixed32',
keywords: ['fixed', 'ceil', 'ceiling', 'round', 'up'],
menuPath: ['Math', 'Fixed', 'Ceil'],
isPure: true,
inputs: [
{ name: 'value', displayName: 'Value', type: 'object' }
],
outputs: [
{ name: 'result', displayName: 'Result', type: 'object' }
],
color: '#9C27B0'
};
export class Fixed32CeilExecutor implements INodeExecutor {
execute(node: BlueprintNode, context: unknown): ExecutionResult {
const ctx = context as FixedContext;
const value = ctx.evaluateInput(node.id, 'value', Fixed32.ZERO) as Fixed32;
return { outputs: { result: Fixed32.ceil(value ?? Fixed32.ZERO) } };
}
}
// Fixed32 Round
export const Fixed32RoundTemplate: BlueprintNodeTemplate = {
type: 'Fixed32Round',
title: 'Fixed32 Round',
category: 'math',
description: 'Rounds Fixed32 to nearest integer',
keywords: ['fixed', 'round'],
menuPath: ['Math', 'Fixed', 'Round'],
isPure: true,
inputs: [
{ name: 'value', displayName: 'Value', type: 'object' }
],
outputs: [
{ name: 'result', displayName: 'Result', type: 'object' }
],
color: '#9C27B0'
};
export class Fixed32RoundExecutor implements INodeExecutor {
execute(node: BlueprintNode, context: unknown): ExecutionResult {
const ctx = context as FixedContext;
const value = ctx.evaluateInput(node.id, 'value', Fixed32.ZERO) as Fixed32;
return { outputs: { result: Fixed32.round(value ?? Fixed32.ZERO) } };
}
}
// Fixed32 Sign
export const Fixed32SignTemplate: BlueprintNodeTemplate = {
type: 'Fixed32Sign',
title: 'Fixed32 Sign',
category: 'math',
description: 'Sign of Fixed32 (-1, 0, or 1)',
keywords: ['fixed', 'sign'],
menuPath: ['Math', 'Fixed', 'Sign'],
isPure: true,
inputs: [
{ name: 'value', displayName: 'Value', type: 'object' }
],
outputs: [
{ name: 'result', displayName: 'Result', type: 'object' }
],
color: '#9C27B0'
};
export class Fixed32SignExecutor implements INodeExecutor {
execute(node: BlueprintNode, context: unknown): ExecutionResult {
const ctx = context as FixedContext;
const value = ctx.evaluateInput(node.id, 'value', Fixed32.ZERO) as Fixed32;
return { outputs: { result: Fixed32.sign(value ?? Fixed32.ZERO) } };
}
}
// Fixed32 Min
export const Fixed32MinTemplate: BlueprintNodeTemplate = {
type: 'Fixed32Min',
title: 'Fixed32 Min',
category: 'math',
description: 'Minimum of two Fixed32 values',
keywords: ['fixed', 'min', 'minimum'],
menuPath: ['Math', 'Fixed', 'Min'],
isPure: true,
inputs: [
{ name: 'a', displayName: 'A', type: 'object' },
{ name: 'b', displayName: 'B', type: 'object' }
],
outputs: [
{ name: 'result', displayName: 'Result', type: 'object' }
],
color: '#9C27B0'
};
export class Fixed32MinExecutor implements INodeExecutor {
execute(node: BlueprintNode, context: unknown): ExecutionResult {
const ctx = context as FixedContext;
const a = ctx.evaluateInput(node.id, 'a', Fixed32.ZERO) as Fixed32;
const b = ctx.evaluateInput(node.id, 'b', Fixed32.ZERO) as Fixed32;
return { outputs: { result: Fixed32.min(a ?? Fixed32.ZERO, b ?? Fixed32.ZERO) } };
}
}
// Fixed32 Max
export const Fixed32MaxTemplate: BlueprintNodeTemplate = {
type: 'Fixed32Max',
title: 'Fixed32 Max',
category: 'math',
description: 'Maximum of two Fixed32 values',
keywords: ['fixed', 'max', 'maximum'],
menuPath: ['Math', 'Fixed', 'Max'],
isPure: true,
inputs: [
{ name: 'a', displayName: 'A', type: 'object' },
{ name: 'b', displayName: 'B', type: 'object' }
],
outputs: [
{ name: 'result', displayName: 'Result', type: 'object' }
],
color: '#9C27B0'
};
export class Fixed32MaxExecutor implements INodeExecutor {
execute(node: BlueprintNode, context: unknown): ExecutionResult {
const ctx = context as FixedContext;
const a = ctx.evaluateInput(node.id, 'a', Fixed32.ZERO) as Fixed32;
const b = ctx.evaluateInput(node.id, 'b', Fixed32.ZERO) as Fixed32;
return { outputs: { result: Fixed32.max(a ?? Fixed32.ZERO, b ?? Fixed32.ZERO) } };
}
}
// Fixed32 Clamp
export const Fixed32ClampTemplate: BlueprintNodeTemplate = {
type: 'Fixed32Clamp',
title: 'Fixed32 Clamp',
category: 'math',
description: 'Clamps Fixed32 to range',
keywords: ['fixed', 'clamp', 'limit', 'range'],
menuPath: ['Math', 'Fixed', 'Clamp'],
isPure: true,
inputs: [
{ name: 'value', displayName: 'Value', type: 'object' },
{ name: 'min', displayName: 'Min', type: 'object' },
{ name: 'max', displayName: 'Max', type: 'object' }
],
outputs: [
{ name: 'result', displayName: 'Result', type: 'object' }
],
color: '#9C27B0'
};
export class Fixed32ClampExecutor implements INodeExecutor {
execute(node: BlueprintNode, context: unknown): ExecutionResult {
const ctx = context as FixedContext;
const value = ctx.evaluateInput(node.id, 'value', Fixed32.ZERO) as Fixed32;
const min = ctx.evaluateInput(node.id, 'min', Fixed32.ZERO) as Fixed32;
const max = ctx.evaluateInput(node.id, 'max', Fixed32.ONE) as Fixed32;
return { outputs: { result: Fixed32.clamp(value ?? Fixed32.ZERO, min ?? Fixed32.ZERO, max ?? Fixed32.ONE) } };
}
}
// Fixed32 Lerp
export const Fixed32LerpTemplate: BlueprintNodeTemplate = {
type: 'Fixed32Lerp',
title: 'Fixed32 Lerp',
category: 'math',
description: 'Linear interpolation between A and B',
keywords: ['fixed', 'lerp', 'interpolate', 'blend'],
menuPath: ['Math', 'Fixed', 'Lerp'],
isPure: true,
inputs: [
{ name: 'a', displayName: 'A', type: 'object' },
{ name: 'b', displayName: 'B', type: 'object' },
{ name: 't', displayName: 'T', type: 'object' }
],
outputs: [
{ name: 'result', displayName: 'Result', type: 'object' }
],
color: '#9C27B0'
};
export class Fixed32LerpExecutor implements INodeExecutor {
execute(node: BlueprintNode, context: unknown): ExecutionResult {
const ctx = context as FixedContext;
const a = ctx.evaluateInput(node.id, 'a', Fixed32.ZERO) as Fixed32;
const b = ctx.evaluateInput(node.id, 'b', Fixed32.ONE) as Fixed32;
const t = ctx.evaluateInput(node.id, 't', Fixed32.HALF) as Fixed32;
return { outputs: { result: Fixed32.lerp(a ?? Fixed32.ZERO, b ?? Fixed32.ONE, t ?? Fixed32.HALF) } };
}
}
// Fixed32 Compare
export const Fixed32CompareTemplate: BlueprintNodeTemplate = {
type: 'Fixed32Compare',
title: 'Fixed32 Compare',
category: 'math',
description: 'Compares two Fixed32 values',
keywords: ['fixed', 'compare', 'equal', 'less', 'greater'],
menuPath: ['Math', 'Fixed', 'Compare'],
isPure: true,
inputs: [
{ name: 'a', displayName: 'A', type: 'object' },
{ name: 'b', displayName: 'B', type: 'object' }
],
outputs: [
{ name: 'equal', displayName: 'A == B', type: 'bool' },
{ name: 'less', displayName: 'A < B', type: 'bool' },
{ name: 'greater', displayName: 'A > B', type: 'bool' }
],
color: '#9C27B0'
};
export class Fixed32CompareExecutor implements INodeExecutor {
execute(node: BlueprintNode, context: unknown): ExecutionResult {
const ctx = context as FixedContext;
const a = ctx.evaluateInput(node.id, 'a', Fixed32.ZERO) as Fixed32;
const b = ctx.evaluateInput(node.id, 'b', Fixed32.ZERO) as Fixed32;
const aVal = a ?? Fixed32.ZERO;
const bVal = b ?? Fixed32.ZERO;
return {
outputs: {
equal: aVal.eq(bVal),
less: aVal.lt(bVal),
greater: aVal.gt(bVal)
}
};
}
}
// Fixed32 IsZero
export const Fixed32IsZeroTemplate: BlueprintNodeTemplate = {
type: 'Fixed32IsZero',
title: 'Fixed32 Is Zero',
category: 'math',
description: 'Checks if Fixed32 is zero, positive, or negative',
keywords: ['fixed', 'zero', 'check'],
menuPath: ['Math', 'Fixed', 'Is Zero'],
isPure: true,
inputs: [
{ name: 'value', displayName: 'Value', type: 'object' }
],
outputs: [
{ name: 'isZero', displayName: 'Is Zero', type: 'bool' },
{ name: 'isPositive', displayName: 'Is Positive', type: 'bool' },
{ name: 'isNegative', displayName: 'Is Negative', type: 'bool' }
],
color: '#9C27B0'
};
export class Fixed32IsZeroExecutor implements INodeExecutor {
execute(node: BlueprintNode, context: unknown): ExecutionResult {
const ctx = context as FixedContext;
const value = ctx.evaluateInput(node.id, 'value', Fixed32.ZERO) as Fixed32;
const val = value ?? Fixed32.ZERO;
return {
outputs: {
isZero: val.isZero(),
isPositive: val.isPositive(),
isNegative: val.isNegative()
}
};
}
}
// Fixed32 Constants
export const Fixed32ConstantsTemplate: BlueprintNodeTemplate = {
type: 'Fixed32Constants',
title: 'Fixed32 Constants',
category: 'math',
description: 'Common Fixed32 constants',
keywords: ['fixed', 'constant', 'pi', 'zero', 'one'],
menuPath: ['Math', 'Fixed', 'Constants'],
isPure: true,
inputs: [],
outputs: [
{ name: 'zero', displayName: '0', type: 'object' },
{ name: 'one', displayName: '1', type: 'object' },
{ name: 'half', displayName: '0.5', type: 'object' },
{ name: 'pi', displayName: 'PI', type: 'object' },
{ name: 'twoPi', displayName: '2PI', type: 'object' }
],
color: '#9C27B0'
};
export class Fixed32ConstantsExecutor implements INodeExecutor {
execute(): ExecutionResult {
return {
outputs: {
zero: Fixed32.ZERO,
one: Fixed32.ONE,
half: Fixed32.HALF,
pi: Fixed32.PI,
twoPi: Fixed32.TWO_PI
}
};
}
}
// Node definitions collection
export const FixedNodeDefinitions = [
{ template: Fixed32FromTemplate, executor: new Fixed32FromExecutor() },
{ template: Fixed32FromIntTemplate, executor: new Fixed32FromIntExecutor() },
{ template: Fixed32ToFloatTemplate, executor: new Fixed32ToFloatExecutor() },
{ template: Fixed32ToIntTemplate, executor: new Fixed32ToIntExecutor() },
{ template: Fixed32AddTemplate, executor: new Fixed32AddExecutor() },
{ template: Fixed32SubtractTemplate, executor: new Fixed32SubtractExecutor() },
{ template: Fixed32MultiplyTemplate, executor: new Fixed32MultiplyExecutor() },
{ template: Fixed32DivideTemplate, executor: new Fixed32DivideExecutor() },
{ template: Fixed32NegateTemplate, executor: new Fixed32NegateExecutor() },
{ template: Fixed32AbsTemplate, executor: new Fixed32AbsExecutor() },
{ template: Fixed32SqrtTemplate, executor: new Fixed32SqrtExecutor() },
{ template: Fixed32FloorTemplate, executor: new Fixed32FloorExecutor() },
{ template: Fixed32CeilTemplate, executor: new Fixed32CeilExecutor() },
{ template: Fixed32RoundTemplate, executor: new Fixed32RoundExecutor() },
{ template: Fixed32SignTemplate, executor: new Fixed32SignExecutor() },
{ template: Fixed32MinTemplate, executor: new Fixed32MinExecutor() },
{ template: Fixed32MaxTemplate, executor: new Fixed32MaxExecutor() },
{ template: Fixed32ClampTemplate, executor: new Fixed32ClampExecutor() },
{ template: Fixed32LerpTemplate, executor: new Fixed32LerpExecutor() },
{ template: Fixed32CompareTemplate, executor: new Fixed32CompareExecutor() },
{ template: Fixed32IsZeroTemplate, executor: new Fixed32IsZeroExecutor() },
{ template: Fixed32ConstantsTemplate, executor: new Fixed32ConstantsExecutor() }
];

View File

@@ -0,0 +1,360 @@
/**
* @zh FixedVector2 定点向量蓝图节点
* @en FixedVector2 Blueprint Nodes
*/
import type { BlueprintNodeTemplate, BlueprintNode, INodeExecutor, ExecutionResult } from '@esengine/blueprint';
import { FixedVector2 } from '../FixedVector2';
import { Fixed32 } from '../Fixed32';
interface FixedVectorContext {
evaluateInput(nodeId: string, pinName: string, defaultValue?: unknown): unknown;
}
// Make FixedVector2
export const MakeFixedVector2Template: BlueprintNodeTemplate = {
type: 'MakeFixedVector2',
title: 'Make FixedVector2',
category: 'math',
description: 'Creates FixedVector2 from floats',
keywords: ['make', 'create', 'fixed', 'vector', 'deterministic'],
menuPath: ['Math', 'Fixed Vector', 'Make FixedVector2'],
isPure: true,
inputs: [
{ name: 'x', displayName: 'X', type: 'float', defaultValue: 0 },
{ name: 'y', displayName: 'Y', type: 'float', defaultValue: 0 }
],
outputs: [
{ name: 'vector', displayName: 'Vector', type: 'object' }
],
color: '#673AB7'
};
export class MakeFixedVector2Executor implements INodeExecutor {
execute(node: BlueprintNode, context: unknown): ExecutionResult {
const ctx = context as FixedVectorContext;
const x = Number(ctx.evaluateInput(node.id, 'x', 0));
const y = Number(ctx.evaluateInput(node.id, 'y', 0));
return { outputs: { vector: FixedVector2.from(x, y) } };
}
}
// Break FixedVector2
export const BreakFixedVector2Template: BlueprintNodeTemplate = {
type: 'BreakFixedVector2',
title: 'Break FixedVector2',
category: 'math',
description: 'Breaks FixedVector2 into X and Y floats',
keywords: ['break', 'split', 'fixed', 'vector'],
menuPath: ['Math', 'Fixed Vector', 'Break FixedVector2'],
isPure: true,
inputs: [
{ name: 'vector', displayName: 'Vector', type: 'object' }
],
outputs: [
{ name: 'x', displayName: 'X', type: 'float' },
{ name: 'y', displayName: 'Y', type: 'float' }
],
color: '#673AB7'
};
export class BreakFixedVector2Executor implements INodeExecutor {
execute(node: BlueprintNode, context: unknown): ExecutionResult {
const ctx = context as FixedVectorContext;
const vector = ctx.evaluateInput(node.id, 'vector', FixedVector2.ZERO) as FixedVector2;
const v = vector ?? FixedVector2.ZERO;
return { outputs: { x: v.x.toNumber(), y: v.y.toNumber() } };
}
}
// FixedVector2 Add
export const FixedVector2AddTemplate: BlueprintNodeTemplate = {
type: 'FixedVector2Add',
title: 'FixedVector2 +',
category: 'math',
description: 'Adds two fixed vectors',
keywords: ['fixed', 'vector', 'add', '+'],
menuPath: ['Math', 'Fixed Vector', 'Add'],
isPure: true,
inputs: [
{ name: 'a', displayName: 'A', type: 'object' },
{ name: 'b', displayName: 'B', type: 'object' }
],
outputs: [
{ name: 'result', displayName: 'Result', type: 'object' }
],
color: '#673AB7'
};
export class FixedVector2AddExecutor implements INodeExecutor {
execute(node: BlueprintNode, context: unknown): ExecutionResult {
const ctx = context as FixedVectorContext;
const a = ctx.evaluateInput(node.id, 'a', FixedVector2.ZERO) as FixedVector2;
const b = ctx.evaluateInput(node.id, 'b', FixedVector2.ZERO) as FixedVector2;
return { outputs: { result: (a ?? FixedVector2.ZERO).add(b ?? FixedVector2.ZERO) } };
}
}
// FixedVector2 Subtract
export const FixedVector2SubtractTemplate: BlueprintNodeTemplate = {
type: 'FixedVector2Subtract',
title: 'FixedVector2 -',
category: 'math',
description: 'Subtracts B from A',
keywords: ['fixed', 'vector', 'subtract', '-'],
menuPath: ['Math', 'Fixed Vector', 'Subtract'],
isPure: true,
inputs: [
{ name: 'a', displayName: 'A', type: 'object' },
{ name: 'b', displayName: 'B', type: 'object' }
],
outputs: [
{ name: 'result', displayName: 'Result', type: 'object' }
],
color: '#673AB7'
};
export class FixedVector2SubtractExecutor implements INodeExecutor {
execute(node: BlueprintNode, context: unknown): ExecutionResult {
const ctx = context as FixedVectorContext;
const a = ctx.evaluateInput(node.id, 'a', FixedVector2.ZERO) as FixedVector2;
const b = ctx.evaluateInput(node.id, 'b', FixedVector2.ZERO) as FixedVector2;
return { outputs: { result: (a ?? FixedVector2.ZERO).sub(b ?? FixedVector2.ZERO) } };
}
}
// FixedVector2 Scale
export const FixedVector2ScaleTemplate: BlueprintNodeTemplate = {
type: 'FixedVector2Scale',
title: 'FixedVector2 *',
category: 'math',
description: 'Scales vector by Fixed32 scalar',
keywords: ['fixed', 'vector', 'scale', '*'],
menuPath: ['Math', 'Fixed Vector', 'Scale'],
isPure: true,
inputs: [
{ name: 'vector', displayName: 'Vector', type: 'object' },
{ name: 'scalar', displayName: 'Scalar', type: 'object' }
],
outputs: [
{ name: 'result', displayName: 'Result', type: 'object' }
],
color: '#673AB7'
};
export class FixedVector2ScaleExecutor implements INodeExecutor {
execute(node: BlueprintNode, context: unknown): ExecutionResult {
const ctx = context as FixedVectorContext;
const vector = ctx.evaluateInput(node.id, 'vector', FixedVector2.ZERO) as FixedVector2;
const scalar = ctx.evaluateInput(node.id, 'scalar', Fixed32.ONE) as Fixed32;
return { outputs: { result: (vector ?? FixedVector2.ZERO).mul(scalar ?? Fixed32.ONE) } };
}
}
// FixedVector2 Negate
export const FixedVector2NegateTemplate: BlueprintNodeTemplate = {
type: 'FixedVector2Negate',
title: 'FixedVector2 Negate',
category: 'math',
description: 'Negates a fixed vector',
keywords: ['fixed', 'vector', 'negate', '-'],
menuPath: ['Math', 'Fixed Vector', 'Negate'],
isPure: true,
inputs: [
{ name: 'vector', displayName: 'Vector', type: 'object' }
],
outputs: [
{ name: 'result', displayName: 'Result', type: 'object' }
],
color: '#673AB7'
};
export class FixedVector2NegateExecutor implements INodeExecutor {
execute(node: BlueprintNode, context: unknown): ExecutionResult {
const ctx = context as FixedVectorContext;
const vector = ctx.evaluateInput(node.id, 'vector', FixedVector2.ZERO) as FixedVector2;
return { outputs: { result: (vector ?? FixedVector2.ZERO).neg() } };
}
}
// FixedVector2 Length
export const FixedVector2LengthTemplate: BlueprintNodeTemplate = {
type: 'FixedVector2Length',
title: 'FixedVector2 Length',
category: 'math',
description: 'Gets the length of a fixed vector',
keywords: ['fixed', 'vector', 'length', 'magnitude'],
menuPath: ['Math', 'Fixed Vector', 'Length'],
isPure: true,
inputs: [
{ name: 'vector', displayName: 'Vector', type: 'object' }
],
outputs: [
{ name: 'length', displayName: 'Length', type: 'object' }
],
color: '#673AB7'
};
export class FixedVector2LengthExecutor implements INodeExecutor {
execute(node: BlueprintNode, context: unknown): ExecutionResult {
const ctx = context as FixedVectorContext;
const vector = ctx.evaluateInput(node.id, 'vector', FixedVector2.ZERO) as FixedVector2;
return { outputs: { length: (vector ?? FixedVector2.ZERO).length() } };
}
}
// FixedVector2 Normalize
export const FixedVector2NormalizeTemplate: BlueprintNodeTemplate = {
type: 'FixedVector2Normalize',
title: 'FixedVector2 Normalize',
category: 'math',
description: 'Normalizes a fixed vector',
keywords: ['fixed', 'vector', 'normalize', 'unit'],
menuPath: ['Math', 'Fixed Vector', 'Normalize'],
isPure: true,
inputs: [
{ name: 'vector', displayName: 'Vector', type: 'object' }
],
outputs: [
{ name: 'result', displayName: 'Result', type: 'object' }
],
color: '#673AB7'
};
export class FixedVector2NormalizeExecutor implements INodeExecutor {
execute(node: BlueprintNode, context: unknown): ExecutionResult {
const ctx = context as FixedVectorContext;
const vector = ctx.evaluateInput(node.id, 'vector', FixedVector2.ZERO) as FixedVector2;
return { outputs: { result: (vector ?? FixedVector2.ZERO).normalize() } };
}
}
// FixedVector2 Dot
export const FixedVector2DotTemplate: BlueprintNodeTemplate = {
type: 'FixedVector2Dot',
title: 'FixedVector2 Dot',
category: 'math',
description: 'Calculates dot product',
keywords: ['fixed', 'vector', 'dot', 'product'],
menuPath: ['Math', 'Fixed Vector', 'Dot Product'],
isPure: true,
inputs: [
{ name: 'a', displayName: 'A', type: 'object' },
{ name: 'b', displayName: 'B', type: 'object' }
],
outputs: [
{ name: 'result', displayName: 'Result', type: 'object' }
],
color: '#673AB7'
};
export class FixedVector2DotExecutor implements INodeExecutor {
execute(node: BlueprintNode, context: unknown): ExecutionResult {
const ctx = context as FixedVectorContext;
const a = ctx.evaluateInput(node.id, 'a', FixedVector2.ZERO) as FixedVector2;
const b = ctx.evaluateInput(node.id, 'b', FixedVector2.ZERO) as FixedVector2;
return { outputs: { result: FixedVector2.dot(a ?? FixedVector2.ZERO, b ?? FixedVector2.ZERO) } };
}
}
// FixedVector2 Cross
export const FixedVector2CrossTemplate: BlueprintNodeTemplate = {
type: 'FixedVector2Cross',
title: 'FixedVector2 Cross',
category: 'math',
description: '2D cross product (returns Fixed32)',
keywords: ['fixed', 'vector', 'cross', 'product'],
menuPath: ['Math', 'Fixed Vector', 'Cross Product'],
isPure: true,
inputs: [
{ name: 'a', displayName: 'A', type: 'object' },
{ name: 'b', displayName: 'B', type: 'object' }
],
outputs: [
{ name: 'result', displayName: 'Result', type: 'object' }
],
color: '#673AB7'
};
export class FixedVector2CrossExecutor implements INodeExecutor {
execute(node: BlueprintNode, context: unknown): ExecutionResult {
const ctx = context as FixedVectorContext;
const a = ctx.evaluateInput(node.id, 'a', FixedVector2.ZERO) as FixedVector2;
const b = ctx.evaluateInput(node.id, 'b', FixedVector2.ZERO) as FixedVector2;
return { outputs: { result: FixedVector2.cross(a ?? FixedVector2.ZERO, b ?? FixedVector2.ZERO) } };
}
}
// FixedVector2 Distance
export const FixedVector2DistanceTemplate: BlueprintNodeTemplate = {
type: 'FixedVector2Distance',
title: 'FixedVector2 Distance',
category: 'math',
description: 'Distance between two points',
keywords: ['fixed', 'vector', 'distance'],
menuPath: ['Math', 'Fixed Vector', 'Distance'],
isPure: true,
inputs: [
{ name: 'a', displayName: 'A', type: 'object' },
{ name: 'b', displayName: 'B', type: 'object' }
],
outputs: [
{ name: 'distance', displayName: 'Distance', type: 'object' }
],
color: '#673AB7'
};
export class FixedVector2DistanceExecutor implements INodeExecutor {
execute(node: BlueprintNode, context: unknown): ExecutionResult {
const ctx = context as FixedVectorContext;
const a = ctx.evaluateInput(node.id, 'a', FixedVector2.ZERO) as FixedVector2;
const b = ctx.evaluateInput(node.id, 'b', FixedVector2.ZERO) as FixedVector2;
return { outputs: { distance: FixedVector2.distance(a ?? FixedVector2.ZERO, b ?? FixedVector2.ZERO) } };
}
}
// FixedVector2 Lerp
export const FixedVector2LerpTemplate: BlueprintNodeTemplate = {
type: 'FixedVector2Lerp',
title: 'FixedVector2 Lerp',
category: 'math',
description: 'Linear interpolation between two vectors',
keywords: ['fixed', 'vector', 'lerp', 'interpolate'],
menuPath: ['Math', 'Fixed Vector', 'Lerp'],
isPure: true,
inputs: [
{ name: 'a', displayName: 'A', type: 'object' },
{ name: 'b', displayName: 'B', type: 'object' },
{ name: 't', displayName: 'T', type: 'object' }
],
outputs: [
{ name: 'result', displayName: 'Result', type: 'object' }
],
color: '#673AB7'
};
export class FixedVector2LerpExecutor implements INodeExecutor {
execute(node: BlueprintNode, context: unknown): ExecutionResult {
const ctx = context as FixedVectorContext;
const a = ctx.evaluateInput(node.id, 'a', FixedVector2.ZERO) as FixedVector2;
const b = ctx.evaluateInput(node.id, 'b', FixedVector2.ZERO) as FixedVector2;
const t = ctx.evaluateInput(node.id, 't', Fixed32.HALF) as Fixed32;
return { outputs: { result: FixedVector2.lerp(a ?? FixedVector2.ZERO, b ?? FixedVector2.ZERO, t ?? Fixed32.HALF) } };
}
}
// Node definitions collection
export const FixedVectorNodeDefinitions = [
{ template: MakeFixedVector2Template, executor: new MakeFixedVector2Executor() },
{ template: BreakFixedVector2Template, executor: new BreakFixedVector2Executor() },
{ template: FixedVector2AddTemplate, executor: new FixedVector2AddExecutor() },
{ template: FixedVector2SubtractTemplate, executor: new FixedVector2SubtractExecutor() },
{ template: FixedVector2ScaleTemplate, executor: new FixedVector2ScaleExecutor() },
{ template: FixedVector2NegateTemplate, executor: new FixedVector2NegateExecutor() },
{ template: FixedVector2LengthTemplate, executor: new FixedVector2LengthExecutor() },
{ template: FixedVector2NormalizeTemplate, executor: new FixedVector2NormalizeExecutor() },
{ template: FixedVector2DotTemplate, executor: new FixedVector2DotExecutor() },
{ template: FixedVector2CrossTemplate, executor: new FixedVector2CrossExecutor() },
{ template: FixedVector2DistanceTemplate, executor: new FixedVector2DistanceExecutor() },
{ template: FixedVector2LerpTemplate, executor: new FixedVector2LerpExecutor() }
];

View File

@@ -0,0 +1,387 @@
/**
* @zh Vector2 蓝图节点
* @en Vector2 Blueprint Nodes
*/
import type { BlueprintNodeTemplate, BlueprintNode, INodeExecutor, ExecutionResult } from '@esengine/blueprint';
import { Vector2 } from '../Vector2';
interface VectorContext {
evaluateInput(nodeId: string, pinName: string, defaultValue?: unknown): unknown;
}
// Make Vector2
export const MakeVector2Template: BlueprintNodeTemplate = {
type: 'MakeVector2',
title: 'Make Vector2',
category: 'math',
description: 'Creates a Vector2 from X and Y',
keywords: ['make', 'create', 'vector', 'vector2'],
menuPath: ['Math', 'Vector', 'Make Vector2'],
isPure: true,
inputs: [
{ name: 'x', displayName: 'X', type: 'float', defaultValue: 0 },
{ name: 'y', displayName: 'Y', type: 'float', defaultValue: 0 }
],
outputs: [
{ name: 'vector', displayName: 'Vector', type: 'vector2' }
],
color: '#2196F3'
};
export class MakeVector2Executor implements INodeExecutor {
execute(node: BlueprintNode, context: unknown): ExecutionResult {
const ctx = context as VectorContext;
const x = Number(ctx.evaluateInput(node.id, 'x', 0));
const y = Number(ctx.evaluateInput(node.id, 'y', 0));
return { outputs: { vector: new Vector2(x, y) } };
}
}
// Break Vector2
export const BreakVector2Template: BlueprintNodeTemplate = {
type: 'BreakVector2',
title: 'Break Vector2',
category: 'math',
description: 'Breaks a Vector2 into X and Y',
keywords: ['break', 'split', 'vector', 'vector2'],
menuPath: ['Math', 'Vector', 'Break Vector2'],
isPure: true,
inputs: [
{ name: 'vector', displayName: 'Vector', type: 'vector2' }
],
outputs: [
{ name: 'x', displayName: 'X', type: 'float' },
{ name: 'y', displayName: 'Y', type: 'float' }
],
color: '#2196F3'
};
export class BreakVector2Executor implements INodeExecutor {
execute(node: BlueprintNode, context: unknown): ExecutionResult {
const ctx = context as VectorContext;
const vector = ctx.evaluateInput(node.id, 'vector', Vector2.ZERO) as Vector2;
return { outputs: { x: vector?.x ?? 0, y: vector?.y ?? 0 } };
}
}
// Vector2 Add
export const Vector2AddTemplate: BlueprintNodeTemplate = {
type: 'Vector2Add',
title: 'Vector2 +',
category: 'math',
description: 'Adds two vectors',
keywords: ['add', 'plus', 'vector'],
menuPath: ['Math', 'Vector', 'Add'],
isPure: true,
inputs: [
{ name: 'a', displayName: 'A', type: 'vector2' },
{ name: 'b', displayName: 'B', type: 'vector2' }
],
outputs: [
{ name: 'result', displayName: 'Result', type: 'vector2' }
],
color: '#2196F3'
};
export class Vector2AddExecutor implements INodeExecutor {
execute(node: BlueprintNode, context: unknown): ExecutionResult {
const ctx = context as VectorContext;
const a = ctx.evaluateInput(node.id, 'a', Vector2.ZERO) as Vector2;
const b = ctx.evaluateInput(node.id, 'b', Vector2.ZERO) as Vector2;
return { outputs: { result: Vector2.add(a ?? Vector2.ZERO, b ?? Vector2.ZERO) } };
}
}
// Vector2 Subtract
export const Vector2SubtractTemplate: BlueprintNodeTemplate = {
type: 'Vector2Subtract',
title: 'Vector2 -',
category: 'math',
description: 'Subtracts B from A',
keywords: ['subtract', 'minus', 'vector'],
menuPath: ['Math', 'Vector', 'Subtract'],
isPure: true,
inputs: [
{ name: 'a', displayName: 'A', type: 'vector2' },
{ name: 'b', displayName: 'B', type: 'vector2' }
],
outputs: [
{ name: 'result', displayName: 'Result', type: 'vector2' }
],
color: '#2196F3'
};
export class Vector2SubtractExecutor implements INodeExecutor {
execute(node: BlueprintNode, context: unknown): ExecutionResult {
const ctx = context as VectorContext;
const a = ctx.evaluateInput(node.id, 'a', Vector2.ZERO) as Vector2;
const b = ctx.evaluateInput(node.id, 'b', Vector2.ZERO) as Vector2;
return { outputs: { result: Vector2.subtract(a ?? Vector2.ZERO, b ?? Vector2.ZERO) } };
}
}
// Vector2 Scale
export const Vector2ScaleTemplate: BlueprintNodeTemplate = {
type: 'Vector2Scale',
title: 'Vector2 *',
category: 'math',
description: 'Scales a vector by a scalar',
keywords: ['scale', 'multiply', 'vector'],
menuPath: ['Math', 'Vector', 'Scale'],
isPure: true,
inputs: [
{ name: 'vector', displayName: 'Vector', type: 'vector2' },
{ name: 'scalar', displayName: 'Scalar', type: 'float', defaultValue: 1 }
],
outputs: [
{ name: 'result', displayName: 'Result', type: 'vector2' }
],
color: '#2196F3'
};
export class Vector2ScaleExecutor implements INodeExecutor {
execute(node: BlueprintNode, context: unknown): ExecutionResult {
const ctx = context as VectorContext;
const vector = ctx.evaluateInput(node.id, 'vector', Vector2.ZERO) as Vector2;
const scalar = Number(ctx.evaluateInput(node.id, 'scalar', 1));
return { outputs: { result: Vector2.multiply(vector ?? Vector2.ZERO, scalar) } };
}
}
// Vector2 Length
export const Vector2LengthTemplate: BlueprintNodeTemplate = {
type: 'Vector2Length',
title: 'Vector2 Length',
category: 'math',
description: 'Gets the length of a vector',
keywords: ['length', 'magnitude', 'vector'],
menuPath: ['Math', 'Vector', 'Length'],
isPure: true,
inputs: [
{ name: 'vector', displayName: 'Vector', type: 'vector2' }
],
outputs: [
{ name: 'length', displayName: 'Length', type: 'float' }
],
color: '#2196F3'
};
export class Vector2LengthExecutor implements INodeExecutor {
execute(node: BlueprintNode, context: unknown): ExecutionResult {
const ctx = context as VectorContext;
const vector = ctx.evaluateInput(node.id, 'vector', Vector2.ZERO) as Vector2;
return { outputs: { length: (vector ?? Vector2.ZERO).length } };
}
}
// Vector2 Normalize
export const Vector2NormalizeTemplate: BlueprintNodeTemplate = {
type: 'Vector2Normalize',
title: 'Vector2 Normalize',
category: 'math',
description: 'Normalizes a vector to unit length',
keywords: ['normalize', 'unit', 'vector'],
menuPath: ['Math', 'Vector', 'Normalize'],
isPure: true,
inputs: [
{ name: 'vector', displayName: 'Vector', type: 'vector2' }
],
outputs: [
{ name: 'result', displayName: 'Result', type: 'vector2' }
],
color: '#2196F3'
};
export class Vector2NormalizeExecutor implements INodeExecutor {
execute(node: BlueprintNode, context: unknown): ExecutionResult {
const ctx = context as VectorContext;
const vector = ctx.evaluateInput(node.id, 'vector', Vector2.ZERO) as Vector2;
return { outputs: { result: (vector ?? Vector2.ZERO).normalized() } };
}
}
// Vector2 Dot
export const Vector2DotTemplate: BlueprintNodeTemplate = {
type: 'Vector2Dot',
title: 'Vector2 Dot',
category: 'math',
description: 'Calculates dot product',
keywords: ['dot', 'product', 'vector'],
menuPath: ['Math', 'Vector', 'Dot Product'],
isPure: true,
inputs: [
{ name: 'a', displayName: 'A', type: 'vector2' },
{ name: 'b', displayName: 'B', type: 'vector2' }
],
outputs: [
{ name: 'result', displayName: 'Result', type: 'float' }
],
color: '#2196F3'
};
export class Vector2DotExecutor implements INodeExecutor {
execute(node: BlueprintNode, context: unknown): ExecutionResult {
const ctx = context as VectorContext;
const a = ctx.evaluateInput(node.id, 'a', Vector2.ZERO) as Vector2;
const b = ctx.evaluateInput(node.id, 'b', Vector2.ZERO) as Vector2;
return { outputs: { result: Vector2.dot(a ?? Vector2.ZERO, b ?? Vector2.ZERO) } };
}
}
// Vector2 Cross
export const Vector2CrossTemplate: BlueprintNodeTemplate = {
type: 'Vector2Cross',
title: 'Vector2 Cross',
category: 'math',
description: '2D cross product (returns scalar)',
keywords: ['cross', 'product', 'vector'],
menuPath: ['Math', 'Vector', 'Cross Product'],
isPure: true,
inputs: [
{ name: 'a', displayName: 'A', type: 'vector2' },
{ name: 'b', displayName: 'B', type: 'vector2' }
],
outputs: [
{ name: 'result', displayName: 'Result', type: 'float' }
],
color: '#2196F3'
};
export class Vector2CrossExecutor implements INodeExecutor {
execute(node: BlueprintNode, context: unknown): ExecutionResult {
const ctx = context as VectorContext;
const a = ctx.evaluateInput(node.id, 'a', Vector2.ZERO) as Vector2;
const b = ctx.evaluateInput(node.id, 'b', Vector2.ZERO) as Vector2;
return { outputs: { result: Vector2.cross(a ?? Vector2.ZERO, b ?? Vector2.ZERO) } };
}
}
// Vector2 Distance
export const Vector2DistanceTemplate: BlueprintNodeTemplate = {
type: 'Vector2Distance',
title: 'Vector2 Distance',
category: 'math',
description: 'Distance between two points',
keywords: ['distance', 'length', 'vector'],
menuPath: ['Math', 'Vector', 'Distance'],
isPure: true,
inputs: [
{ name: 'a', displayName: 'A', type: 'vector2' },
{ name: 'b', displayName: 'B', type: 'vector2' }
],
outputs: [
{ name: 'distance', displayName: 'Distance', type: 'float' }
],
color: '#2196F3'
};
export class Vector2DistanceExecutor implements INodeExecutor {
execute(node: BlueprintNode, context: unknown): ExecutionResult {
const ctx = context as VectorContext;
const a = ctx.evaluateInput(node.id, 'a', Vector2.ZERO) as Vector2;
const b = ctx.evaluateInput(node.id, 'b', Vector2.ZERO) as Vector2;
return { outputs: { distance: Vector2.distance(a ?? Vector2.ZERO, b ?? Vector2.ZERO) } };
}
}
// Vector2 Lerp
export const Vector2LerpTemplate: BlueprintNodeTemplate = {
type: 'Vector2Lerp',
title: 'Vector2 Lerp',
category: 'math',
description: 'Linear interpolation between two vectors',
keywords: ['lerp', 'interpolate', 'vector'],
menuPath: ['Math', 'Vector', 'Lerp'],
isPure: true,
inputs: [
{ name: 'a', displayName: 'A', type: 'vector2' },
{ name: 'b', displayName: 'B', type: 'vector2' },
{ name: 't', displayName: 'T', type: 'float', defaultValue: 0.5 }
],
outputs: [
{ name: 'result', displayName: 'Result', type: 'vector2' }
],
color: '#2196F3'
};
export class Vector2LerpExecutor implements INodeExecutor {
execute(node: BlueprintNode, context: unknown): ExecutionResult {
const ctx = context as VectorContext;
const a = ctx.evaluateInput(node.id, 'a', Vector2.ZERO) as Vector2;
const b = ctx.evaluateInput(node.id, 'b', Vector2.ZERO) as Vector2;
const t = Number(ctx.evaluateInput(node.id, 't', 0.5));
return { outputs: { result: Vector2.lerp(a ?? Vector2.ZERO, b ?? Vector2.ZERO, t) } };
}
}
// Vector2 Rotate
export const Vector2RotateTemplate: BlueprintNodeTemplate = {
type: 'Vector2Rotate',
title: 'Vector2 Rotate',
category: 'math',
description: 'Rotates a vector by angle (radians)',
keywords: ['rotate', 'turn', 'vector'],
menuPath: ['Math', 'Vector', 'Rotate'],
isPure: true,
inputs: [
{ name: 'vector', displayName: 'Vector', type: 'vector2' },
{ name: 'angle', displayName: 'Angle (rad)', type: 'float', defaultValue: 0 }
],
outputs: [
{ name: 'result', displayName: 'Result', type: 'vector2' }
],
color: '#2196F3'
};
export class Vector2RotateExecutor implements INodeExecutor {
execute(node: BlueprintNode, context: unknown): ExecutionResult {
const ctx = context as VectorContext;
const vector = ctx.evaluateInput(node.id, 'vector', Vector2.ZERO) as Vector2;
const angle = Number(ctx.evaluateInput(node.id, 'angle', 0));
return { outputs: { result: (vector ?? Vector2.ZERO).rotated(angle) } };
}
}
// Vector2 From Angle
export const Vector2FromAngleTemplate: BlueprintNodeTemplate = {
type: 'Vector2FromAngle',
title: 'Vector2 From Angle',
category: 'math',
description: 'Creates unit vector from angle (radians)',
keywords: ['from', 'angle', 'direction', 'vector'],
menuPath: ['Math', 'Vector', 'From Angle'],
isPure: true,
inputs: [
{ name: 'angle', displayName: 'Angle (rad)', type: 'float', defaultValue: 0 }
],
outputs: [
{ name: 'vector', displayName: 'Vector', type: 'vector2' }
],
color: '#2196F3'
};
export class Vector2FromAngleExecutor implements INodeExecutor {
execute(node: BlueprintNode, context: unknown): ExecutionResult {
const ctx = context as VectorContext;
const angle = Number(ctx.evaluateInput(node.id, 'angle', 0));
return { outputs: { vector: Vector2.fromAngle(angle) } };
}
}
// Node definitions collection
export const VectorNodeDefinitions = [
{ template: MakeVector2Template, executor: new MakeVector2Executor() },
{ template: BreakVector2Template, executor: new BreakVector2Executor() },
{ template: Vector2AddTemplate, executor: new Vector2AddExecutor() },
{ template: Vector2SubtractTemplate, executor: new Vector2SubtractExecutor() },
{ template: Vector2ScaleTemplate, executor: new Vector2ScaleExecutor() },
{ template: Vector2LengthTemplate, executor: new Vector2LengthExecutor() },
{ template: Vector2NormalizeTemplate, executor: new Vector2NormalizeExecutor() },
{ template: Vector2DotTemplate, executor: new Vector2DotExecutor() },
{ template: Vector2CrossTemplate, executor: new Vector2CrossExecutor() },
{ template: Vector2DistanceTemplate, executor: new Vector2DistanceExecutor() },
{ template: Vector2LerpTemplate, executor: new Vector2LerpExecutor() },
{ template: Vector2RotateTemplate, executor: new Vector2RotateExecutor() },
{ template: Vector2FromAngleTemplate, executor: new Vector2FromAngleExecutor() }
];

View File

@@ -0,0 +1,29 @@
/**
* @zh 数学库蓝图节点
* @en Math Library Blueprint Nodes
*
* @zh 导出所有数学相关的蓝图节点
* @en Exports all math-related blueprint nodes
*/
export * from './VectorNodes';
export * from './FixedNodes';
export * from './FixedVectorNodes';
export * from './ColorNodes';
// Re-export node definition collections
import { VectorNodeDefinitions } from './VectorNodes';
import { FixedNodeDefinitions } from './FixedNodes';
import { FixedVectorNodeDefinitions } from './FixedVectorNodes';
import { ColorNodeDefinitions } from './ColorNodes';
/**
* @zh 所有数学库蓝图节点定义
* @en All math library blueprint node definitions
*/
export const MathNodeDefinitions = [
...VectorNodeDefinitions,
...FixedNodeDefinitions,
...FixedVectorNodeDefinitions,
...ColorNodeDefinitions
];

View File

@@ -1,5 +1,12 @@
# @esengine/network
## 11.0.0
### Patch Changes
- Updated dependencies [[`bffe90b`](https://github.com/esengine/esengine/commit/bffe90b6a17563cc90709faf339b229dc3abd22d)]:
- @esengine/ecs-framework-math@2.9.0
## 10.0.0
### Minor Changes

View File

@@ -1,6 +1,6 @@
{
"name": "@esengine/network",
"version": "10.0.0",
"version": "11.0.0",
"description": "Network synchronization for multiplayer games",
"esengine": {
"plugin": true,

View File

@@ -1,5 +1,12 @@
# @esengine/pathfinding
## 10.0.0
### Patch Changes
- Updated dependencies [[`bffe90b`](https://github.com/esengine/esengine/commit/bffe90b6a17563cc90709faf339b229dc3abd22d)]:
- @esengine/ecs-framework-math@2.9.0
## 9.0.0
### Patch Changes

View File

@@ -1,6 +1,6 @@
{
"name": "@esengine/pathfinding",
"version": "9.0.0",
"version": "10.0.0",
"description": "寻路系统 | Pathfinding System - A*, Grid, NavMesh",
"type": "module",
"main": "./dist/index.js",

View File

@@ -1,5 +1,12 @@
# @esengine/spatial
## 10.0.0
### Patch Changes
- Updated dependencies [[`bffe90b`](https://github.com/esengine/esengine/commit/bffe90b6a17563cc90709faf339b229dc3abd22d)]:
- @esengine/ecs-framework-math@2.9.0
## 9.0.0
### Patch Changes

View File

@@ -1,6 +1,6 @@
{
"name": "@esengine/spatial",
"version": "9.0.0",
"version": "10.0.0",
"description": "Spatial query and indexing system for ECS Framework / ECS 框架的空间查询和索引系统",
"type": "module",
"main": "./dist/index.js",

View File

@@ -1,5 +1,13 @@
# @esengine/demos
## 1.0.16
### Patch Changes
- Updated dependencies []:
- @esengine/pathfinding@10.0.0
- @esengine/spatial@10.0.0
## 1.0.15
### Patch Changes

View File

@@ -1,6 +1,6 @@
{
"name": "@esengine/demos",
"version": "1.0.15",
"version": "1.0.16",
"private": true,
"description": "Demo tests for ESEngine modules documentation",
"type": "module",

4
pnpm-lock.yaml generated
View File

@@ -1575,6 +1575,10 @@ importers:
version: 5.9.3
packages/framework/math:
dependencies:
'@esengine/blueprint':
specifier: workspace:*
version: link:../blueprint
devDependencies:
'@rollup/plugin-commonjs':
specifier: ^28.0.3