载入中...
搜索中...
未找到
VehicleSystem.cpp
浏览该文件的文档.
3
7
8#include <algorithm>
9#include <cmath>
10#include <unordered_map>
11#include <utility>
12
13namespace eve::vehicle {
14
15namespace {
16
17constexpr float kPi = 3.14159265358979323846f;
18
19std::unordered_map<std::string, IVehicleMobility*>& mobilityRegistry() {
20 static std::unordered_map<std::string, IVehicleMobility*> registry;
21 return registry;
22}
23
24std::unordered_map<std::string, IVehicleDriver*>& driverRegistry() {
25 static std::unordered_map<std::string, IVehicleDriver*> registry;
26 return registry;
27}
28
29std::unordered_map<int, PlayerControl>& playerControlsTable() {
30 static std::unordered_map<int, PlayerControl> table;
31 return table;
32}
33
34VehicleEventSink& eventSink() {
35 static VehicleEventSink sink;
36 return sink;
37}
38
39void pushEvent(const VehicleEvent& e) {
40 if (eventSink()) eventSink()(e);
41}
42
44float angleDelta(float from, float to) {
45 float d = std::fmod(to - from + 180.f, 360.f);
46 if (d < 0.f) d += 360.f;
47 return d - 180.f;
48}
49
50void finishOrder(VehicleEntity& v) {
51 auto orders = v.orders();
52 const VehicleOrder* current = orders->adapter->current();
53 if (current != nullptr) {
54 const VehicleOrder& o = *current;
55 VehicleEvent e;
57 e.vehicleId = v.identity()->id;
58 e.defId = v.identity()->defId;
59 e.orderType = vehicleOrderTypeName(o.type);
60 e.x = o.x;
61 e.y = o.y;
62 if (orders->adapter->completeCurrent()) {
63 pushEvent(e);
64 v.motion()->arrived = true;
65 }
66 }
67 orders->adapter->syncCompatibility(*orders);
68}
69
70float normalizeDeg(float deg) {
71 deg = std::fmod(deg, 360.f);
72 if (deg < 0.f) deg += 360.f;
73 return deg;
74}
75
77void kinematicMove(VehicleEntity& v, float dt) {
78 const VehicleDefinition* def = v.definition()->def;
79 if (def == nullptr) return;
80 auto in = v.input();
81 auto mo = v.motion();
82
83 const float speedFactor = std::clamp(std::fabs(mo->speed) / def->maxSpeed, 0.f, 1.f);
84 const float turnPower = 0.35f + 0.65f * speedFactor; // 低速可原地转向
85 mo->heading = normalizeDeg(mo->heading + in->steer * def->turnRate * turnPower * dt);
86
87 float target = in->throttle * def->maxSpeed;
88 if (in->brake > 0.f || in->handbrake) target = 0.f;
89 const float dv = target - mo->speed;
90 const float maxDv = def->accel * dt;
91 mo->speed += std::clamp(dv, -maxDv, maxDv);
92
93 const float rad = mo->heading * kPi / 180.f;
94 mo->x += std::cos(rad) * mo->speed * dt;
95 mo->y += std::sin(rad) * mo->speed * dt;
96}
97
99void wheelMove(VehicleEntity& v, float dt) {
101 kinematicMove(v, dt);
102}
103
105void trackMove(VehicleEntity& v, float dt) {
106 const VehicleDefinition* def = v.definition()->def;
107 if (def == nullptr) return;
108 auto in = v.input();
109 auto mo = v.motion();
110
112
113 const float left = std::clamp(in->throttle + in->steer, -1.f, 1.f);
114 const float right = std::clamp(in->throttle - in->steer, -1.f, 1.f);
115 const float drive = (left + right) * 0.5f;
116 const float rot = (right - left) * 0.5f;
117
118 float target = drive * def->maxSpeed;
119 if (in->brake > 0.f || in->handbrake) target = 0.f;
120 const float dv = target - mo->speed;
121 const float maxDv = def->accel * dt;
122 mo->speed += std::clamp(dv, -maxDv, maxDv);
123 mo->heading = normalizeDeg(mo->heading + rot * def->turnRate * dt); // 原地转向
124
125 const float rad = mo->heading * kPi / 180.f;
126 if (VehiclePhysics::tryTrackApply(v, rad, mo->speed) == VehiclePhysicsStatus::Applied) return;
127 mo->x += std::cos(rad) * mo->speed * dt;
128 mo->y += std::sin(rad) * mo->speed * dt;
129}
130
132class KinematicMobility : public IVehicleMobility {
133public:
134 const char* name() const override { return "kinematic"; }
135 void update(VehicleEntity& v, float dt) override { kinematicMove(v, dt); }
136};
137
139class WheelMobility : public IVehicleMobility {
140public:
141 const char* name() const override { return "wheel"; }
142 void update(VehicleEntity& v, float dt) override { wheelMove(v, dt); }
143};
144
146class ShipMobility : public IVehicleMobility {
147public:
148 const char* name() const override { return "ship"; }
149 void update(VehicleEntity& v, float dt) override { wheelMove(v, dt); }
150};
151
153class TrackMobility : public IVehicleMobility {
154public:
155 const char* name() const override { return "track"; }
156 void update(VehicleEntity& v, float dt) override { trackMove(v, dt); }
157};
158
159KinematicMobility gKinematic;
160WheelMobility gWheel;
161ShipMobility gShip;
162TrackMobility gTrack;
163
165class PlayerDriver : public IVehicleDriver {
166public:
167 const char* name() const override { return "player"; }
168 bool sample(VehicleEntity&, int occupantId, VehicleInput& out) override {
169 const PlayerControl* pc = VehicleSystem::playerControls(occupantId);
170 if (pc == nullptr) return false;
171 out.throttle = pc->throttle;
172 out.steer = pc->steer;
173 out.brake = pc->brake;
174 out.handbrake = pc->handbrake;
175 out.fire = pc->fire;
176 out.aimYaw = pc->aimYaw;
177 out.aimPitch = pc->aimPitch;
178 return true;
179 }
180};
181
182PlayerDriver gPlayerDriver;
183
184} // namespace
185
187 if (mobility == nullptr) return;
188 mobilityRegistry()[mobility->name()] = mobility;
189}
190
192 auto it = mobilityRegistry().find(name);
193 return it == mobilityRegistry().end() ? nullptr : it->second;
194}
195
196int VehicleSystem::mobilityCount() { return static_cast<int>(mobilityRegistry().size()); }
197
198void VehicleSystem::setEventSink(VehicleEventSink sink) { eventSink() = std::move(sink); }
199
201 v.orders()->adapter->update(dt);
202 v.orders()->adapter->syncCompatibility(*v.orders());
203 processOrders(v, dt);
204 const VehicleDefinition* def = v.definition()->def;
205 IVehicleMobility* mobility = def != nullptr ? findMobility(def->mobility) : nullptr;
206 if (mobility != nullptr) mobility->update(v, dt);
207}
208
210 auto orders = v.orders();
211 const VehicleOrder* current = orders->adapter->current();
212 if (current == nullptr) {
213 return; // 无命令时保留手动/座位输入
214 }
215
216 auto in = v.input();
217 auto mo = v.motion();
218 in->throttle = 0.f;
219 in->steer = 0.f;
220 in->brake = 0.f;
221 in->fire = false;
222 in->aimYaw = 0.f;
223 in->aimPitch = 0.f;
224
225 const VehicleOrder& o = *current;
226 switch (o.type) {
229 const float dx = o.x - mo->x;
230 const float dy = o.y - mo->y;
231 const float dist = std::sqrt(dx * dx + dy * dy);
232 if (dist <= o.arriveRadius) {
233 finishOrder(v);
234 return;
235 }
236 const float desired = std::atan2(dy, dx) * 180.f / kPi;
237 in->steer = steerToward(v, desired);
238 // 大角度转向时减速(缩小转弯半径),接近目标时进一步减速,
239 // 否则转弯半径大于 arriveRadius 会导致载具绕着目标永远到不了。
240 float throttle = 1.f - 0.75f * std::fabs(in->steer);
241 if (dist < 2.5f * o.arriveRadius) {
242 throttle *= dist / (2.5f * o.arriveRadius);
243 }
244 in->throttle = std::clamp(throttle, 0.05f, 1.f);
245 break;
246 }
248 const float dx = o.x - mo->x;
249 const float dy = o.y - mo->y;
250 const float desired = std::atan2(dy, dx) * 180.f / kPi;
251 in->steer = steerToward(v, desired);
252 in->throttle = 0.f; // 原地转向瞄准,不移动
253 break;
254 }
256 case VehicleOrderType::Hold: in->brake = 1.f; break;
257 }
258}
259
261 auto orders = v.orders();
262 const bool replaces = order.type == VehicleOrderType::Move || order.type == VehicleOrderType::AttackMove ||
264 auto queued = replaces ? orders->adapter->replace(order) : orders->adapter->append(order);
265 if (!queued.ok()) return eve::Result<void>::failure(queued.status());
266 const std::string id = std::move(queued).takeValue();
267 if (id.empty()) {
269 eve::DiagnosticCode::InvariantViolation, "vehicle order adapter returned an empty id", "orders"));
270 }
271 orders->adapter->syncCompatibility(*orders);
272 v.motion()->arrived = false;
274}
275
277 auto orders = v.orders();
278 orders->adapter->clear();
279 orders->adapter->syncCompatibility(*orders);
280 v.motion()->arrived = false;
281}
282
283const VehicleOrder* VehicleSystem::currentOrder(VehicleEntity& v) { return v.orders()->adapter->current(); }
284
285float VehicleSystem::steerToward(VehicleEntity& v, float targetHeadingDeg) {
286 const float delta = angleDelta(v.motion()->heading, targetHeadingDeg);
287 return std::clamp(delta / 90.f, -1.f, 1.f);
288}
289
291 if (driver == nullptr) return;
292 driverRegistry()[driver->name()] = driver;
293}
294
296 auto it = driverRegistry().find(name);
297 return it == driverRegistry().end() ? nullptr : it->second;
298}
299
300int VehicleSystem::driverCount() { return static_cast<int>(driverRegistry().size()); }
301
302void VehicleSystem::setPlayerControls(int playerId, const PlayerControl& control) {
303 playerControlsTable()[playerId] = control;
304}
305
307 auto it = playerControlsTable().find(playerId);
308 return it == playerControlsTable().end() ? nullptr : &it->second;
309}
310
311bool VehicleSystem::enterSeat(VehicleEntity& v, int seatIndex, int playerId) {
312 VehicleSeatContainerAdapter adapter(eve::container::ContainerId("vehicle:seat:" + v.identity()->id), &v);
313 auto entered = adapter.enter(eve::container::SlotIndex(seatIndex), playerId);
314 return entered.ok();
315}
316
318 VehicleSeatContainerAdapter adapter(eve::container::ContainerId("vehicle:seat:" + v.identity()->id), &v);
319 auto exited = adapter.exit(eve::container::SlotIndex(seatIndex));
320 return exited.ok();
321}
322
324 auto seats = v.seats();
325 for (size_t i = 0; i < seats->list.size(); ++i) {
326 if (seats->list[i].occupied && seats->list[i].occupant == playerId) return static_cast<int>(i);
327 }
328 return -1;
329}
330
331} // namespace eve::vehicle
332
333// 静态注册内置移动模型(链接期执行,模块加载即生效)。
334namespace eve::vehicle {
335namespace {
336
337struct BuiltinMobilityRegistrar {
338 BuiltinMobilityRegistrar() {
344 VehicleSystem::registerDriver(&gPlayerDriver);
345 }
346};
347
348BuiltinMobilityRegistrar gBuiltinRegistrar;
349
350} // namespace
351} // namespace eve::vehicle
LogicalId target
std::string from
float v
HexVec3 left
HexVec3 right
HexCoordinates to
Cell the unit walks towards on this segment.
Definition HexUnits.cpp:64
std::string name
std::vector< std::int32_t > order
size_t queued
Definition OnnxGpgpu.cpp:60
float d
double current
float dy
float dx
std::string_view adapter
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
Strong identity of one runtime or persistent container.
Definition Container.h:44
Explicit slot number; it never implicitly converts to an integer.
Definition Container.h:117
驾驶者:把某乘客的控制转成载具输入。
virtual const char * name() const =0
稳定名字("player" 等)。
移动模型:读输入、更新运动状态。
virtual const char * name() const =0
稳定名字("kinematic" / "wheel" / "track" / "hover" / ...)。
virtual void update(VehicleEntity &v, float dt)=0
每帧推进:把 v.input() 转成 v.motion() 的变化。
载具实体:数据全部在组件里,行为在 VehicleSystem。
static VehiclePhysicsStatus tryWheelMove(VehicleEntity &v, float dt)
Step wheel/ship mobility from an attached body.
static void registerBuiltinMobility()
Register suspension mobility when physics is in the build.
static void syncTrackFromBody(VehicleEntity &v)
Copy attached body pose into motion for tracked vehicles.
static VehiclePhysicsStatus tryTrackApply(VehicleEntity &v, float headingRad, float speed)
Apply tracked heading/speed to an attached body.
Transactional adapter over one VehicleEntity::Seats component.
static void update(VehicleEntity &v, float dt)
每帧推进:命令 → 输入 → 移动模型。自动瞄准由 Vehicle 模块完成。
static eve::Result< void > pushOrder(VehicleEntity &v, const VehicleOrder &order)
追加一条命令;Move/AttackMove 会清空旧命令(RTS 习惯)。
static float steerToward(VehicleEntity &v, float targetHeadingDeg)
按角度差生成转向输入(-1..1)。
static void setPlayerControls(int playerId, const PlayerControl &control)
写入某玩家的控制状态(游戏每帧填充)。
static IVehicleDriver * findDriver(const std::string &name)
按名字取驾驶者;未注册返回 nullptr。
static const VehicleOrder * currentOrder(VehicleEntity &v)
当前命令;无命令返回 nullptr。
static void registerDriver(IVehicleDriver *driver)
注册驾驶者实现;同名替换。
static int driverCount()
已注册驾驶者数量。
static bool exitSeat(VehicleEntity &v, int seatIndex)
Exit seat.
static IVehicleMobility * findMobility(const std::string &name)
按名字取移动模型;未注册返回 nullptr。
static const PlayerControl * playerControls(int playerId)
取某玩家的控制状态;未设置返回 nullptr。
static void setEventSink(VehicleEventSink sink)
注册事件汇(替换旧的;传 nullptr 清空)。
static void registerMobility(IVehicleMobility *mobility)
注册移动模型实现;同名替换。
static int findSeatByPlayer(VehicleEntity &v, int playerId)
Finds seat by player.
static void clearOrders(VehicleEntity &v)
清空命令队列。
static void processOrders(VehicleEntity &v, float dt)
处理命令队列:把队首命令转成 v.input()。
static bool enterSeat(VehicleEntity &v, int seatIndex, int playerId)
座位:进入 / 离开 / 按玩家找座位。
static int mobilityCount()
已注册移动模型数量。
double sample(const Heightmap &map, double u, double v)
Sample.
std::unordered_map< std::string, SkillDefinition > & table()
Definition Skill.cpp:65
驾驶者接口与玩家控制状态。
const char * vehicleOrderTypeName(VehicleOrderType type)
命令类型的字符串名("move" | "attack_move" | ...)。
Definition Vehicle.cpp:79
std::function< void(const VehicleEvent &)> VehicleEventSink
事件汇:由 Vehicle 模块(或测试)注册,把事件写入自己的队列。
@ Applied
Physics handled the request.
一名玩家的原始控制(归一化,游戏侧填充)。角度为度。
载具模板(registerVehiclesFromJson 注册,进程级注册表)。
std::string mobility
IVehicleMobility 注册名;默认 "kinematic"。
一条 Vehicle 领域命令(生命周期由 Vehicle order adapter 执行)。
Vehicle adapters for the common Container/Zone/Transfer protocol.