初次提交
This commit is contained in:
@@ -0,0 +1,41 @@
|
||||
// BulletManagerCs — 子弹 SoA 热路径 C# 内核(S0 骨架)
|
||||
// S0 范围:帧时间监控 + AsSpan() 沟通基线验证
|
||||
// 完整积分热路径在 S1 展开(见 development_plan.md S1)
|
||||
// ADR-L1 规厙1:内层循环禁止 Call() | 规厙2:热数据通过 AsSpan() 零拷贝
|
||||
using Godot;
|
||||
using System;
|
||||
|
||||
public partial class BulletManagerCs : Node
|
||||
{
|
||||
private GodotObject _gdBulletManager;
|
||||
|
||||
// S0 性能计时(P-S0-07 帧预算回填)
|
||||
private double _accumMs;
|
||||
private int _frames;
|
||||
|
||||
public override void _Ready()
|
||||
{
|
||||
_gdBulletManager = Engine.GetSingleton("BulletManager");
|
||||
// S0 骨架不接管积分,保持 _cs_node = null,让 GDScript fallback 运行
|
||||
GD.Print("[BulletManagerCs] S0 骨架就绪,GDScript fallback 活跞");
|
||||
}
|
||||
|
||||
public override void _PhysicsProcess(double delta)
|
||||
{
|
||||
if (_gdBulletManager is null) return;
|
||||
var t0 = Time.GetTicksUsec();
|
||||
|
||||
// S0:仅记录帧时间(积分由 GDScript fallback 负责)
|
||||
var activeCount = _gdBulletManager.Get("_active_count").AsInt32();
|
||||
_ = activeCount;
|
||||
|
||||
_accumMs += (Time.GetTicksUsec() - t0) / 1000.0;
|
||||
_frames++;
|
||||
if (_frames >= 60)
|
||||
{
|
||||
GD.Print($"[BulletManagerCs] 帧时间均倦: {_accumMs / _frames:F3} ms " +
|
||||
$"(active={activeCount} TODO S1: 启用完整热路径)");
|
||||
_accumMs = 0; _frames = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
uid://d305dubg5veqd
|
||||
@@ -0,0 +1,36 @@
|
||||
// EnemyManagerCs — 敌人 SoA 热路径 C# 内核(S0 骨架)
|
||||
// S0 范围:帧时间监控,完整 Boid 积分在 S1 展开
|
||||
using Godot;
|
||||
using System;
|
||||
|
||||
public partial class EnemyManagerCs : Node
|
||||
{
|
||||
private GodotObject _gdEnemyManager;
|
||||
private double _accumMs;
|
||||
private int _frames;
|
||||
|
||||
public override void _Ready()
|
||||
{
|
||||
_gdEnemyManager = Engine.GetSingleton("EnemyManager");
|
||||
// S0 骨架不接管积分,保持 _cs_node = null,让 GDScript fallback 运行
|
||||
GD.Print("[EnemyManagerCs] S0 骨架就绪,GDScript fallback 活跞");
|
||||
}
|
||||
|
||||
public override void _PhysicsProcess(double delta)
|
||||
{
|
||||
if (_gdEnemyManager is null) return;
|
||||
var t0 = Time.GetTicksUsec();
|
||||
|
||||
var activeCount = _gdEnemyManager.Get("_active_count").AsInt32();
|
||||
_ = activeCount;
|
||||
|
||||
_accumMs += (Time.GetTicksUsec() - t0) / 1000.0;
|
||||
_frames++;
|
||||
if (_frames >= 60)
|
||||
{
|
||||
GD.Print($"[EnemyManagerCs] 帧时间均倦: {_accumMs / _frames:F3} ms " +
|
||||
$"(active={activeCount} TODO S1: 启用 Boid 热路径)");
|
||||
_accumMs = 0; _frames = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
uid://c8c4u4etym08g
|
||||
@@ -0,0 +1,98 @@
|
||||
// SpatialGridCs — 2D 空间哈希网格 C# 实现
|
||||
// 职责:固定 Cell 网格 dirty-list 方案重建 + query_circle
|
||||
// 权威:architecture_design.md §4.3, ADR-L1
|
||||
//
|
||||
// ADR-L1 规厙1:内层循环体内禁止 GodotObject.Call() / .Set()
|
||||
using Godot;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
public partial class SpatialGridCs : Node
|
||||
{
|
||||
private const int CellSize = 64;
|
||||
private const int GridWidth = 64; // 64 格×64 格 = 4096×4096 像素覆盖范围
|
||||
private const int GridHeight = 64;
|
||||
private const int GridTotal = GridWidth * GridHeight;
|
||||
|
||||
// 网格主体:一维数组模拟二维,直接索引 grid[x + y * GridWidth]
|
||||
private readonly List<int>[] _grid = new List<int>[GridTotal];
|
||||
|
||||
// dirty-list:本帧有变化的格子索引
|
||||
private readonly HashSet<int> _dirty = new();
|
||||
|
||||
// 实体位置缓存:entity_id → cell_index
|
||||
private readonly Dictionary<int, int> _entityCell = new();
|
||||
|
||||
private GodotObject _gdSpatialGrid;
|
||||
|
||||
public override void _Ready()
|
||||
{
|
||||
for (int i = 0; i < GridTotal; i++)
|
||||
_grid[i] = new List<int>(4);
|
||||
|
||||
_gdSpatialGrid = Engine.GetSingleton("SpatialGrid");
|
||||
_gdSpatialGrid.Set("_cs_node", this);
|
||||
|
||||
GD.Print($"[SpatialGridCs] 初始化完成,{GridWidth}×{GridHeight} 格,格子 {CellSize} px");
|
||||
}
|
||||
|
||||
// 插入或更新实体位置(由 EnemyManager 每帧调用)
|
||||
public void Insert(int entityId, Vector2 pos)
|
||||
{
|
||||
int cx = Mathf.Clamp((int)(pos.X / CellSize), 0, GridWidth - 1);
|
||||
int cy = Mathf.Clamp((int)(pos.Y / CellSize), 0, GridHeight - 1);
|
||||
int cellIdx = cx + cy * GridWidth;
|
||||
|
||||
if (_entityCell.TryGetValue(entityId, out int oldCell))
|
||||
{
|
||||
if (oldCell == cellIdx) return; // 未跨格
|
||||
_grid[oldCell].Remove(entityId);
|
||||
_dirty.Add(oldCell);
|
||||
}
|
||||
_grid[cellIdx].Add(entityId);
|
||||
_entityCell[entityId] = cellIdx;
|
||||
_dirty.Add(cellIdx);
|
||||
}
|
||||
|
||||
// 重建(仅清空 dirty 集合)
|
||||
public void Rebuild() => _dirty.Clear();
|
||||
|
||||
// 圆形查询:返回 center 半径 radius 内的所有 entity_id
|
||||
// ADR-L1 规厙1:内层 foreach 内禁止 Call()
|
||||
public int[] QueryCircle(Vector2 center, float radius)
|
||||
{
|
||||
int minCx = Mathf.Clamp((int)((center.X - radius) / CellSize), 0, GridWidth - 1);
|
||||
int maxCx = Mathf.Clamp((int)((center.X + radius) / CellSize), 0, GridWidth - 1);
|
||||
int minCy = Mathf.Clamp((int)((center.Y - radius) / CellSize), 0, GridHeight - 1);
|
||||
int maxCy = Mathf.Clamp((int)((center.Y + radius) / CellSize), 0, GridHeight - 1);
|
||||
|
||||
var result = new List<int>(32);
|
||||
float radSq = radius * radius;
|
||||
|
||||
for (int cy = minCy; cy <= maxCy; cy++)
|
||||
for (int cx = minCx; cx <= maxCx; cx++)
|
||||
{
|
||||
// ADR-L1 规厙1:内层循环约束—不得调用 GodotObject
|
||||
foreach (int id in _grid[cx + cy * GridWidth])
|
||||
result.Add(id);
|
||||
}
|
||||
return result.ToArray();
|
||||
}
|
||||
|
||||
// 移除实体
|
||||
public void Remove(int entityId)
|
||||
{
|
||||
if (!_entityCell.TryGetValue(entityId, out int cell)) return;
|
||||
_grid[cell].Remove(entityId);
|
||||
_entityCell.Remove(entityId);
|
||||
_dirty.Add(cell);
|
||||
}
|
||||
|
||||
// 清空(关卡重置)
|
||||
public void Clear()
|
||||
{
|
||||
foreach (var cell in _grid) cell.Clear();
|
||||
_entityCell.Clear();
|
||||
_dirty.Clear();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
uid://d3j8ulrpquwkh
|
||||
Reference in New Issue
Block a user