10#include <unordered_map>
17enum Action : int32_t { kIdle = 0, kFlow = 1, kSeek = 2, kBoids = 3 };
19int actionFromName(
const std::string &
name) {
20 if (
name ==
"idle")
return kIdle;
21 if (
name ==
"flow")
return kFlow;
22 if (
name ==
"seek")
return kSeek;
23 if (
name ==
"boids")
return kBoids;
27const char *actionName(
int action) {
29 case kIdle:
return "idle";
30 case kFlow:
return "flow";
31 case kSeek:
return "seek";
32 case kBoids:
return "boids";
49 impl_->field.resize(
width,
height, cellSize, originX, originY);
53 impl_->field.setBlocked(
cx,
cy, blocked);
57 impl_->field.setCellCost(
cx,
cy,
cost);
63 impl_->field.setGoal(gx, gy);
85 impl_->field.flowAtWorld(
wx,
wy,
v.x,
v.y);
93 impl_->field.flowAtCell(
cx,
cy,
v.x,
v.y);
101 if (
int(
d.actions.size()) >=
d.maxAgents)
return -1;
102 const int id = int(
d.xs.size());
106 d.vxs.push_back(0.f);
107 d.vys.push_back(0.f);
108 d.speeds.push_back(0.f);
110 d.maxSpeeds.push_back(
d.defaultSpeed);
111 d.maxAccels.push_back(
d.defaultSpeed * 2.f);
112 d.turnRates.push_back(
d.defaultTurnRate);
113 d.actions.push_back(kFlow);
114 d.datas.push_back(0);
115 d.avoidancePriorities.push_back(0);
116 d.hasTargets.push_back(0);
117 d.interactions.emplace_back();
118 d.targetXs.push_back(0.f);
119 d.targetYs.push_back(0.f);
120 d.wanderPhases.push_back(
float(
id) * 2.399963f);
121 d.stableIds.emplace_back();
127 if (stableId.empty() ||
d.namedAgents.count(stableId) != 0)
return -1;
129 if (
index < 0)
return -1;
130 d.stableIds[
static_cast<size_t>(
index)] = stableId;
131 d.namedAgents[stableId] =
index;
136 return impl_->namedAgents.count(stableId) != 0;
140 const auto found = impl_->namedAgents.find(stableId);
141 return found == impl_->namedAgents.end() ? -1 :
found->second;
145 return impl_->validId(
index) ? impl_->stableIds[
static_cast<size_t>(
index)] : std::string{};
155 if (!
d.validId(
id))
return false;
156 const int last = int(
d.xs.size()) - 1;
157 const std::string removedStableId =
d.stableIds[
static_cast<size_t>(
id)];
159 d.xs[size_t(
id)] =
d.xs[size_t(last)];
160 d.ys[size_t(
id)] =
d.ys[size_t(last)];
161 d.headings[size_t(
id)] =
d.headings[size_t(last)];
162 d.vxs[size_t(
id)] =
d.vxs[size_t(last)];
163 d.vys[size_t(
id)] =
d.vys[size_t(last)];
164 d.speeds[size_t(
id)] =
d.speeds[size_t(last)];
165 d.radii[size_t(
id)] =
d.radii[size_t(last)];
166 d.maxSpeeds[size_t(
id)] =
d.maxSpeeds[size_t(last)];
167 d.maxAccels[size_t(
id)] =
d.maxAccels[size_t(last)];
168 d.turnRates[size_t(
id)] =
d.turnRates[size_t(last)];
169 d.actions[size_t(
id)] =
d.actions[size_t(last)];
170 d.datas[size_t(
id)] =
d.datas[size_t(last)];
171 d.avoidancePriorities[size_t(
id)] =
d.avoidancePriorities[size_t(last)];
172 d.hasTargets[size_t(
id)] =
d.hasTargets[size_t(last)];
173 d.interactions[size_t(
id)] =
d.interactions[size_t(last)];
174 d.targetXs[size_t(
id)] =
d.targetXs[size_t(last)];
175 d.targetYs[size_t(
id)] =
d.targetYs[size_t(last)];
176 d.wanderPhases[size_t(
id)] =
d.wanderPhases[size_t(last)];
177 d.stableIds[size_t(
id)] =
d.stableIds[size_t(last)];
178 if (!
d.stableIds[
size_t(
id)].empty())
d.namedAgents[
d.stableIds[size_t(
id)]] =
id;
180 if (!removedStableId.empty())
d.namedAgents.erase(removedStableId);
183 d.headings.pop_back();
188 d.maxSpeeds.pop_back();
189 d.maxAccels.pop_back();
190 d.turnRates.pop_back();
191 d.actions.pop_back();
193 d.avoidancePriorities.pop_back();
194 d.hasTargets.pop_back();
195 d.interactions.pop_back();
196 d.targetXs.pop_back();
197 d.targetYs.pop_back();
198 d.wanderPhases.pop_back();
199 d.stableIds.pop_back();
217 d.avoidancePriorities.clear();
218 d.hasTargets.clear();
219 d.interactions.clear();
222 d.wanderPhases.clear();
224 d.namedAgents.clear();
225 d.gridW =
d.gridH = 0;
231 impl_->maxAgents = std::max(maxAgents, 0);
237 if (!impl_->validId(
id))
return false;
238 const int a = actionFromName(
action);
239 if (
a < 0)
return false;
240 impl_->actions[size_t(
id)] =
a;
245 if (!impl_->validId(
id))
return "";
246 return actionName(impl_->actions[
size_t(
id)]);
250 if (!impl_->validId(
id))
return false;
251 impl_->hasTargets[size_t(
id)] = 1;
252 impl_->targetXs[size_t(
id)] = tx;
253 impl_->targetYs[size_t(
id)] = ty;
258 if (!impl_->validId(
id))
return false;
259 impl_->hasTargets[size_t(
id)] = 0;
264 if (!impl_->validId(
id) || speed < 0.f)
return false;
265 impl_->maxSpeeds[size_t(
id)] = speed;
270 if (!impl_->validId(
id) || accel < 0.f)
return false;
271 impl_->maxAccels[size_t(
id)] = accel;
276 if (!impl_->validId(
id) || radPerSec < 0.f)
return false;
277 impl_->turnRates[size_t(
id)] = radPerSec;
282 if (!impl_->validId(
id) ||
radius < 0.f)
return false;
283 impl_->radii[size_t(
id)] =
radius;
288 if (!impl_->validId(
id))
return false;
289 impl_->datas[size_t(
id)] = data;
294 if (!impl_->validId(
id))
return 0;
295 return impl_->datas[size_t(
id)];
299 if (!impl_->validId(
id))
302 impl_->avoidancePriorities[size_t(
id)] =
priority;
307 return impl_->validId(
id) ? impl_->avoidancePriorities[size_t(
id)] : 0;
311 if (!impl_->validId(
id))
return false;
312 impl_->xs[size_t(
id)] =
x;
313 impl_->ys[size_t(
id)] =
y;
319 if (!impl_->validId(
id)) {
323 s.x = impl_->xs[size_t(
id)];
324 s.y = impl_->ys[size_t(
id)];
325 s.heading = impl_->headings[size_t(
id)];
326 s.speed = impl_->speeds[size_t(
id)];
327 s.vx = impl_->vxs[size_t(
id)];
328 s.vy = impl_->vys[size_t(
id)];
329 s.action = impl_->actions[size_t(
id)];
330 s.data = impl_->datas[size_t(
id)];
331 s.avoidancePriority = impl_->avoidancePriorities[size_t(
id)];
340 impl_->defaultTurnRate = std::max(radPerSec, 0.f);
eve::resource::CostSpec cost
#define Module_IMPL(ModuleName, newExpr)
static Diagnostic error(DiagnosticCode code, std::string message, std::string path={}, DiagnosticDetails details={}, std::string source={})
Construct an error diagnostic with the standard error severity.
Move-only operation result carrying either a value or Status.
static Result success(T value)
Construct a successful result owning value.
static Result failure(Status status)
Construct a failed result from a structured status.
static Status success(StatusCode code=StatusCode::Ok)
Construct a successful status with an explicit non-error outcome.
群体行为模块:连续流场寻路 + 海量单位移动/转向/行动 + Boids 鸟群。
FlowVec flowAtCell(int cx, int cy) const
格级流场方向。
void setCohesionWeight(float weight)
Boids 聚合力权重。
bool removeAgent(int id)
删除单位(swap-pop O(1))。
AgentState getAgentState(int id) const
读取单位状态快照(非法 id 返回 action=-1)。
void setClampToField(bool enable)
是否把单位钳制在流场边界内(默认开)。
std::string getAgentAction(int id) const
查询行动名。
void setWanderWeight(float weight)
Boids wander 权重。
void build()
执行 Dijkstra 建场。
void addFlowGoal(int gx, int gy)
追加目标格(多目标)。
bool setAgentPosition(int id, float x, float y)
直接放置单位。
void resizeField(int width, int height, float cellSize, float originX, float originY)
配置流场网格(世界单位/格)。
float costAtWorld(float wx, float wy) const
世界坐标积分代价(场外返回 kUnreachable)。
bool isFieldBuilt() const
是否已建场。
int getNamedAgentIndex(const std::string &stableId) const
Resolve a stable logical identifier to the current compact slot.
int getMaxAgents() const
Returns the max agents.
bool setAgentData(int id, int data)
设置游戏自定义标记。
int getAgentCount() const
当前单位数。
bool setAgentAccel(int id, float accel)
设置加速度上限(默认 maxSpeed×2)。
void setDefaultRadius(float radius)
新单位默认半径。
void setSeparationRadius(float radius)
分离/邻居查询半径。
void setDefaultSpeed(float speed)
新单位默认速度。
void clearFlowGoals()
清空目标列表。
void buildFlowField(int gx, int gy)
单目标快捷建场(clearGoals + addGoal + build)。
int addNamedAgent(const std::string &stableId, float x, float y, float heading, float radius)
Add an agent with an editor/game-stable logical identifier.
int addAgent(float x, float y, float heading, float radius)
添加单位;返回 id(=槽位索引;删除后 id 不稳定)。
void setBlocked(int cx, int cy, bool blocked)
设置/清除某格阻挡。
int getAgentAvoidancePriority(int id) const
Return overlap-resolution priority, or zero for an invalid id.
int getFieldWidth() const
网格信息访问器(调试渲染用)。
bool isReachable(int cx, int cy) const
某格是否可达。
float getCellSize() const
Returns the cell size.
float getFieldOriginY() const
Returns the field origin y.
void setResolveOverlaps(bool enable)
是否做位置重叠消解 pass(默认开)。
void setMaxAgents(int maxAgents)
单位容量上限(默认 100000)。
bool removeNamedAgent(const std::string &stableId)
Remove an agent by stable logical identifier.
std::string getAgentStableId(int index) const
Return the stable logical identifier for a compact slot.
bool clearAgentTarget(int id)
清除目标点。
void clearAgents()
清空全部单位。
bool setAgentSpeed(int id, float speed)
设置最大速度(世界单位/秒)。
FlowVec flowAtWorld(float wx, float wy) const
世界坐标流场方向(双线性插值;场外返回零向量)。
float getFieldOriginX() const
Returns the field origin x.
bool setAgentAction(int id, const std::string &action)
设置行动:"idle" | "flow" | "seek" | "boids"。
float getCellCost(int cx, int cy) const
查询地形代价。
int getFieldHeight() const
Returns the field height.
void setGoalWeight(float weight)
Boids 目标偏置权重。
void setDefaultTurnRate(float radPerSec)
新单位默认转向速率(弧度/秒)。
bool setAgentRadius(int id, float radius)
设置半径。
bool hasNamedAgent(const std::string &stableId) const
Return whether a stable logical agent exists.
Result< void > setAgentAvoidancePriority(int id, int priority)
Set overlap-resolution priority; higher values yield less.
void setSeparationWeight(float weight)
Boids 分离力权重。
void setCellCost(int cx, int cy, float cost)
设置地形代价(0=阻挡,>=1 可走)。
void setArriveRadius(float radius)
seek 到达减速半径。
bool setAgentTurnRate(int id, float radPerSec)
设置转向速率上限(弧度/秒)。
int getAgentData(int id) const
Returns the agent data.
void setPerceptionRadius(float radius)
对齐/聚合感知半径(Boids;默认 64,可大于分离半径)。
void setAlignmentWeight(float weight)
Boids 对齐力权重。
bool setAgentTarget(int id, float tx, float ty)
设置世界目标点(seek 直接寻点,boids 作迁移偏置)。
单个单位的状态快照(脚本 getAgentState 返回值)。 action: 0=idle, 1=flow, 2=seek, 3=boids。
Private crowd runtime storage. @cost Linear in agent count for SOA vectors and transient broadphase/a...
流场采样结果(脚本 flowAtWorld / flowAtCell 返回值)。