载入中...
搜索中...
未找到
RTSCrowdMotion.cpp
浏览该文件的文档.
1#include "crowd/Crowd.h"
3
4#include <algorithm>
5#include <cmath>
6#include <vector>
7
8namespace eve::rts {
13
15 if (step.delta.nanoseconds() < 0)
17 DiagnosticCode::InvalidArgument, "RTS crowd step delta must be non-negative", "step.delta"));
18 struct LinkedUnit {
19 Unit* unit;
20 Unit::Motion* motion;
21 Unit::Navigation* navigation;
23 Unit::Combat* combat;
24 Unit::Supply* supply;
25 Unit::Morale* morale;
26 Unit::Tactics* tactics;
27 Unit::Command* command;
28 Unit::Effects* effects;
29 OrderComponent* orders;
30 };
31 std::vector<LinkedUnit> linked;
32 std::vector<std::string> keys;
35 for (auto it = view.begin(); it != view.end(); ++it) {
36 auto [identity, motion, navigation, orders, settings, combat, containment, supply, morale, tactics, command,
37 effects] = *it;
38 auto* unit = identity == nullptr ? nullptr : dynamic_cast<Unit*>(ecs::try_get(identity->self));
39 if (unit == nullptr || &*unit->identity() != identity) continue;
40 if (!settings->link.isBound()) continue;
41 if (containment->container.isBound()) {
42 // Contained units have no world-space footprint. Recreate the
43 // projection from their authoritative motion when they disembark.
44 crowd.removeNamedAgent(settings->link.key());
45 continue;
46 }
47 const std::string& key = settings->link.key();
48 if (std::find(keys.begin(), keys.end(), key) != keys.end())
50 Diagnostic::error(DiagnosticCode::Conflict, "RTS crowd agent keys must be unique", "unit.crowd.link"));
51 keys.push_back(key);
52 linked.push_back(
53 {unit, motion, navigation, settings, combat, supply, morale, tactics, command, effects, &orders->values});
54 }
56
57 const auto isWaiting = [](const LinkedUnit& entry) {
58 return (entry.navigation->trafficWaiting && !entry.navigation->trafficRecoveryTarget) ||
59 entry.supply->convoyWaiting || (entry.morale->retreating && entry.tactics->retreatCovering);
60 };
61 for (const auto& entry : linked) {
62 const std::string& key = entry.settings->link.key();
63 int agent = crowd.getNamedAgentIndex(key);
64 if (agent < 0)
65 agent = crowd.addNamedAgent(key, entry.motion->x, entry.motion->y, entry.settings->heading,
66 entry.settings->radius);
67 if (agent < 0)
69 Diagnostic::error(DiagnosticCode::Failed, "canonical Crowd rejected an RTS agent", "unit.crowd.link"));
70 crowd.setAgentPosition(agent, entry.motion->x, entry.motion->y);
71 crowd.setAgentRadius(agent, entry.settings->radius);
72 auto priority = crowd.setAgentAvoidancePriority(agent, entry.navigation->movementPriority);
73 if (!priority) return Result<std::size_t>::failure(priority.status());
74 const float speedFactor =
75 entry.morale->active ? std::clamp(entry.morale->suppressedSpeedFactor, 0.0f, 1.0f) : 1.0f;
76 const float commandFactor =
77 entry.command->requiresCommand && !entry.command->inCommand ? entry.command->outOfCommandSpeedFactor : 1.0f;
78 const float effectFactor = static_cast<float>(entry.effects->values.multiplier("speedMultiplier"));
79 crowd.setAgentSpeed(agent, entry.motion->speed * speedFactor * commandFactor * effectFactor *
80 entry.navigation->formationSpeedFactor);
81 auto current = readCurrent(*entry.orders);
82 if (!current) return Result<std::size_t>::failure(current.status());
83 auto record = std::move(current).takeValue();
84 auto interaction = crowd.getAgentInteraction(agent);
85 if (!interaction) return Result<std::size_t>::failure(interaction.status());
86 auto policy = interaction.value();
87 policy.holdPosition = record && record->kind == OrderKind::HoldPosition;
88 auto configured = crowd.setAgentInteraction(agent, policy);
89 if (!configured) return Result<std::size_t>::failure(configured.status());
90 bool attackMoveEngaged = false;
91 if (record && record->kind == OrderKind::AttackMove && entry.combat->engagementRange > 0.0f &&
92 !entry.navigation->trafficRecoveryTarget) {
93 auto engaged = entityPosition(entry.combat->target);
94 attackMoveEngaged = engaged && distanceSquared(entry.motion->x, entry.motion->y, engaged->x, engaged->y) <=
95 entry.combat->engagementRange * entry.combat->engagementRange;
96 }
97 if (record && isMovementOrder(record->kind) && !isWaiting(entry) && !attackMoveEngaged) {
98 WorldPosition target = record->target;
99 if (record->kind == OrderKind::Attack || record->kind == OrderKind::Resupply ||
100 record->kind == OrderKind::SupplyRelay) {
101 auto liveTarget = entityPosition(record->targetEntity);
102 if (liveTarget) target = *liveTarget;
103 } else if (record->kind == OrderKind::Escort && entry.tactics->guardSet) {
104 target = {entry.tactics->guardX, entry.tactics->guardY};
105 } else if (record->kind == OrderKind::Patrol && entry.navigation->patrolInitialized &&
106 !entry.navigation->patrolTowardTarget) {
107 target = entry.navigation->patrolOrigin;
108 }
109 if (entry.navigation->plannedOrderId == record->id && !entry.navigation->unreachable &&
110 entry.navigation->waypointIndex < entry.navigation->waypoints.size())
111 target = entry.navigation->waypoints[entry.navigation->waypointIndex];
112 if (record->kind == OrderKind::Move && entry.navigation->formationTarget)
113 target = *entry.navigation->formationTarget;
114 if (entry.navigation->trafficRecoveryTarget) target = *entry.navigation->trafficRecoveryTarget;
115 crowd.setAgentTarget(agent, target.x, target.y);
116 crowd.setAgentAction(agent, "seek");
117 } else {
118 crowd.clearAgentTarget(agent);
119 crowd.setAgentAction(agent, "idle");
120 }
121 }
122 auto advanced = crowd.advance(static_cast<float>(step.delta.seconds()));
123 if (!advanced) return Result<std::size_t>::failure(advanced.status());
124 for (const auto& entry : linked) {
125 const int agent = crowd.getNamedAgentIndex(entry.settings->link.key());
126 const auto state = crowd.getAgentState(agent);
127 if (state.action < 0)
129 DiagnosticCode::InvariantViolation, "canonical Crowd lost an RTS agent", "unit.crowd.link"));
130 entry.motion->x = state.x;
131 entry.motion->y = state.y;
132 entry.settings->heading = state.heading;
133 auto current = readCurrent(*entry.orders);
134 if (!current) return Result<std::size_t>::failure(current.status());
135 auto record = std::move(current).takeValue();
136 if (!record || !isMovementOrder(record->kind)) {
137 entry.motion->arrived = true;
138 continue;
139 }
140 if (isWaiting(entry) || entry.navigation->trafficRecoveryTarget) {
141 entry.motion->arrived = false;
142 continue;
143 }
144 WorldPosition target = record->target;
145 if (record->kind == OrderKind::Attack || record->kind == OrderKind::Resupply ||
146 record->kind == OrderKind::SupplyRelay) {
147 auto liveTarget = entityPosition(record->targetEntity);
148 if (liveTarget) target = *liveTarget;
149 } else if (record->kind == OrderKind::Escort && entry.tactics->guardSet) {
150 target = {entry.tactics->guardX, entry.tactics->guardY};
151 }
152 const float remaining = std::sqrt(distanceSquared(state.x, state.y, target.x, target.y));
153 float arrivalRadius = entry.motion->arrivalRadius;
154 if (record->kind == OrderKind::Attack)
155 arrivalRadius = std::max(arrivalRadius, entry.combat->engagementRange);
156 else if (record->kind == OrderKind::Resupply || record->kind == OrderKind::SupplyRelay)
157 arrivalRadius = std::max(arrivalRadius, entry.supply->range * 0.8f);
158 entry.motion->arrived = remaining <= arrivalRadius;
159 if (entry.motion->arrived) {
160 // Arrival is a gameplay tolerance, not permission to undo collision resolution.
161 crowd.setAgentAction(agent, "idle");
162 }
163 }
165}
166
167} // namespace eve::rts
LogicalId target
int priority
std::uint32_t key
glm::mat4 view
double current
TacticalUnit * unit
TerrainThermalSettings settings
float step
Definition TreeMesh.cpp:314
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.
Definition Diagnostic.h:125
Move-only operation result carrying either a value or Status.
Definition Result.h:155
static Result success(T value)
Construct a successful result owning value.
Definition Result.h:164
static Result failure(Status status)
Construct a failed result from a structured status.
Definition Result.h:175
static Status success(StatusCode code=StatusCode::Ok)
Construct a successful status with an explicit non-error outcome.
Definition Status.h:81
群体行为模块:连续流场寻路 + 海量单位移动/转向/行动 + Boids 鸟群。
Definition Crowd.h:116
AgentState getAgentState(int id) const
读取单位状态快照(非法 id 返回 action=-1)。
Definition Crowd.cpp:317
Result< void > setAgentInteraction(int id, AgentInteraction policy)
Atomically replace the local interaction policy for a current compact slot.
bool setAgentPosition(int id, float x, float y)
直接放置单位。
Definition Crowd.cpp:310
int getNamedAgentIndex(const std::string &stableId) const
Resolve a stable logical identifier to the current compact slot.
Definition Crowd.cpp:139
Result< StepReport > advance(float dt)
Advance using bounded simultaneous integration and contact projection.
Definition CrowdStep.cpp:6
int addNamedAgent(const std::string &stableId, float x, float y, float heading, float radius)
Add an agent with an editor/game-stable logical identifier.
Definition Crowd.cpp:125
Result< AgentInteraction > getAgentInteraction(int id) const
Read an owning policy snapshot, or NotFound for an invalid compact slot. @thread Simulation thread on...
bool removeNamedAgent(const std::string &stableId)
Remove an agent by stable logical identifier.
Definition Crowd.cpp:148
bool clearAgentTarget(int id)
清除目标点。
Definition Crowd.cpp:257
bool setAgentSpeed(int id, float speed)
设置最大速度(世界单位/秒)。
Definition Crowd.cpp:263
bool setAgentAction(int id, const std::string &action)
设置行动:"idle" | "flow" | "seek" | "boids"。
Definition Crowd.cpp:236
bool setAgentRadius(int id, float radius)
设置半径。
Definition Crowd.cpp:281
Result< void > setAgentAvoidancePriority(int id, int priority)
Set overlap-resolution priority; higher values yield less.
Definition Crowd.cpp:298
bool setAgentTarget(int id, float tx, float ty)
设置世界目标点(seek 直接寻点,boids 作迁移偏置)。
Definition Crowd.cpp:249
static Result< std::size_t > step(const SimulationStep &step, crowd::Crowd &crowd)
Push ECS targets into Crowd, advance it once, and project positions back to Unit::Motion.
Generic orders component adapter; the queue remains the sole order owner.
Definition RTSTypes.h:385
RTS unit domain root.
Definition RTSTypes.h:495
float distanceSquared(float ax, float ay, float bx, float by)
Result< std::optional< OrderRecord > > readCurrent(OrderComponent &orders)
std::optional< WorldPosition > entityPosition(const ecs::EntityHandle &handle)
One deterministic fixed-step emitted by SimulationClock.
Definition Time.h:158
RTS stance and pursuit limits used by automatic combat policies.
Definition RTSTypes.h:610
Command-network policy and derived connection state for a mobile unit or relay.
Definition RTSTypes.h:655
Unit containment state for transports and building garrisons.
Definition RTSTypes.h:693
Crowd provider link.
Definition RTSTypes.h:538
Lifecycle-only active effects.
Definition RTSTypes.h:522
Runtime identity and logical unit definition.
Definition RTSTypes.h:504
Suppression, recovery, retreat policy, and friendly morale aura.
Definition RTSTypes.h:724
Authoritative kinematic state used by the phase-one move slice.
Definition RTSTypes.h:560
RTS projection of a canonical map path; the map provider remains the grid authority.
Definition RTSTypes.h:569
Generic command/order state.
Definition RTSTypes.h:526
Tactical ammunition logistics; weapon ammunition remains authoritative in WeaponEntity.
Definition RTSTypes.h:699
Escort geometry and combat-group coordination state.
Definition RTSTypes.h:764
Two-dimensional deterministic RTS world position.
Definition RTSTypes.h:48