载入中...
搜索中...
未找到
VehiclePhysics.cpp
浏览该文件的文档.
2
3#include "physics/Body.h"
4#include "physics/Body3D.h"
6#include "physics/Shape3D.h"
7#include "physics/World.h"
8#include "physics/World3D.h"
11
12#include <algorithm>
13#include <cmath>
14#include <cstdint>
15#include <memory>
16
17namespace eve::vehicle {
18
20public:
21 enum class Space : std::uint8_t { TwoD, ThreeD };
22
24 std::weak_ptr<const void> lifetime)
25 : link_(link), space_(Space::TwoD), world_(world), lifetime_(std::move(lifetime)) {}
26
28 std::weak_ptr<const void> lifetime)
29 : link_(link), space_(Space::ThreeD), world_(world), lifetime_(std::move(lifetime)) {}
30
32
33 [[nodiscard]] eve::physics::Body* resolve2D() const {
34 if (space_ != Space::TwoD) return nullptr;
35 auto lifetime = lifetime_.lock();
36 if (!lifetime) return nullptr;
37 auto* world = static_cast<eve::physics::World*>(world_);
38 if (!world) return nullptr;
39 auto resolved = link_.resolve(*world);
40 if (!resolved) {
41 resolved.ignore("vehicle physics link became stale");
42 return nullptr;
43 }
44 return std::move(resolved).takeValue();
45 }
46
47 [[nodiscard]] eve::physics::Body3D* resolve3D() const {
48 if (space_ != Space::ThreeD) return nullptr;
49 auto lifetime = lifetime_.lock();
50 if (!lifetime) return nullptr;
51 auto* world = static_cast<eve::physics::World3D*>(world_);
52 if (!world) return nullptr;
53 auto resolved = link_.resolve(*world);
54 if (!resolved) {
55 resolved.ignore("vehicle physics link became stale");
56 return nullptr;
57 }
58 return std::move(resolved).takeValue();
59 }
60
61 [[nodiscard]] bool isAttached() const { return resolve2D() != nullptr || resolve3D() != nullptr; }
62
63 void detach() noexcept {
64 if (auto* body = resolve2D()) body->destroy();
65 if (auto* body = resolve3D()) body->destroy();
66 link_ = {};
67 world_ = nullptr;
68 lifetime_.reset();
69 }
70
71private:
73 Space space_;
74 void* world_ = nullptr;
75 std::weak_ptr<const void> lifetime_;
76};
77
78namespace {
79
80constexpr float kPi = 3.14159265358979323846f;
81
82float normalizeDeg(float deg) {
83 deg = std::fmod(deg, 360.f);
84 if (deg < 0.f) deg += 360.f;
85 return deg;
86}
87
88void wheelMove2D(VehicleEntity& v, eve::physics::Body* b, float dt) {
89 const VehicleDefinition* def = v.definition()->def;
90 if (def == nullptr) return;
91 auto in = v.input();
92 auto mo = v.motion();
93 mo->x = b->getX();
94 mo->y = b->getY();
95
96 const float speedFactor = std::clamp(std::fabs(mo->speed) / def->maxSpeed, 0.f, 1.f);
97 mo->heading = normalizeDeg(mo->heading + in->steer * def->turnRate * (0.35f + 0.65f * speedFactor) * dt);
98
99 float target = in->throttle * def->maxSpeed;
100 if (in->brake > 0.f || in->handbrake) target = 0.f;
101 const float dv = target - mo->speed;
102 const float maxDv = def->accel * dt;
103 mo->speed += std::clamp(dv, -maxDv, maxDv);
104
105 const float rad = mo->heading * kPi / 180.f;
106 b->setAngle(rad);
107 b->setLinearVelocity(std::cos(rad) * mo->speed, std::sin(rad) * mo->speed);
108}
109
110void wheelMove3D(VehicleEntity& v, eve::physics::Body3D* b, float dt) {
111 const VehicleDefinition* def = v.definition()->def;
112 if (def == nullptr) return;
113 auto in = v.input();
114 auto mo = v.motion();
115 mo->x = b->getX();
116 mo->y = b->getZ();
117
118 const float speedFactor = std::clamp(std::fabs(mo->speed) / def->maxSpeed, 0.f, 1.f);
119 mo->heading = normalizeDeg(mo->heading + in->steer * def->turnRate * (0.35f + 0.65f * speedFactor) * dt);
120
121 float target = in->throttle * def->maxSpeed;
122 if (in->brake > 0.f || in->handbrake) target = 0.f;
123 const float dv = target - mo->speed;
124 const float maxDv = def->accel * dt;
125 mo->speed += std::clamp(dv, -maxDv, maxDv);
126
127 const float rad = mo->heading * kPi / 180.f;
128 b->setRotation(0.f, std::sin(rad * 0.5f), 0.f, std::cos(rad * 0.5f));
129 b->setLinearVelocity(std::sin(rad) * mo->speed, 0.f, std::cos(rad) * mo->speed);
130}
131
132void suspensionMove3D(VehicleEntity& v, eve::physics::Body3D* b, float dt) {
133 const VehicleDefinition* def = v.definition()->def;
134 if (def == nullptr) return;
135 eve::physics::World3D* world = b->getWorld();
136 if (world == nullptr) {
137 if (IVehicleMobility* kinematic = VehicleSystem::findMobility("kinematic")) kinematic->update(v, dt);
138 return;
139 }
140
141 auto in = v.input();
142 auto mo = v.motion();
143 mo->x = b->getX();
144 mo->y = b->getZ();
145
146 const float qx = b->getRotX();
147 const float qy = b->getRotY();
148 const float qz = b->getRotZ();
149 const float qw = b->getRotW();
150 const float yaw = std::atan2(2.f * (qw * qy + qx * qz), 1.f - 2.f * (qy * qy + qz * qz));
151 mo->heading = normalizeDeg(yaw * 180.f / kPi);
152 const float yawRad = mo->heading * kPi / 180.f;
153 const float fx = std::sin(yawRad);
154 const float fz = std::cos(yawRad);
155
156 const auto& wheels = def->suspension.wheels;
157 auto sus = v.suspension();
158 if (sus->wheels.size() != wheels.size()) sus->wheels.resize(wheels.size());
159 const uint64_t chassisMask = ~uint64_t{2};
160
161 for (size_t i = 0; i < wheels.size(); ++i) {
162 const SuspensionWheel& w = wheels[i];
163 const float wx = mo->x + w.x * std::cos(yawRad) + w.z * std::sin(yawRad);
164 const float wz = mo->y - w.x * std::sin(yawRad) + w.z * std::cos(yawRad);
165 const float mountY = b->getY() + w.y + w.restLength;
166 const float rayLen = w.restLength + w.radius + def->suspension.maxTravel;
167 const int hit = world->rayCastFiltered(wx, mountY, wz, wx, mountY - rayLen, wz, chassisMask);
168
169 float compression = 0.f;
170 if (hit >= 0) {
171 const float hitDist = mountY - world->getRayHitY();
172 compression = std::clamp(w.restLength - (hitDist - w.radius), 0.f, def->suspension.maxTravel);
173 }
174
175 auto& ws = sus->wheels[i];
176 const float vel = (compression - ws.prevCompression) / std::max(dt, 1e-4f);
177 const float force = w.stiffness * compression + w.damping * vel;
178 if (force > 0.f) b->applyForceAt(0.f, force, 0.f, wx, mountY, wz);
179 ws.prevCompression = compression;
180 ws.grounded = hit >= 0;
181 }
182
183 const float drive = (in->brake > 0.f || in->handbrake) ? 0.f : in->throttle;
184 const float targetSpeed = drive * def->maxSpeed;
185 const float fwd =
186 std::fabs(targetSpeed) > 0.01f ? (b->getLinearVelocityX() * fx + b->getLinearVelocityZ() * fz) : 0.f;
187 float driveForce = def->suspension.driveForce * drive;
188 if (std::fabs(targetSpeed) > 0.01f) {
189 driveForce *= std::clamp(1.f - fwd / targetSpeed, 0.f, 1.f);
190 }
191 b->applyForce(fx * driveForce, 0.f, fz * driveForce);
192 b->setAngularVelocity(0.f, in->steer * def->turnRate * kPi / 180.f, 0.f);
193
194 const float vx = b->getLinearVelocityX();
195 const float vy = b->getLinearVelocityY();
196 const float vz = b->getLinearVelocityZ();
197 const float rx = fz;
198 const float rz = -fx;
199 const float lat = vx * rx + vz * rz;
200 const float grip = std::max(0.f, 1.f - def->suspension.lateralGrip * dt);
201 b->setLinearVelocity(fx * fwd + rx * lat * grip, vy, fz * fwd + rz * lat * grip);
202 mo->speed = fwd;
203}
204
205class SuspensionMobility3D : public IVehicleMobility {
206public:
207 const char* name() const override { return "suspension"; }
208 void update(VehicleEntity& v, float dt) override {
209 const auto binding = v.physicsBody()->binding;
210 if (eve::physics::Body3D* b = binding ? binding->resolve3D() : nullptr) {
211 suspensionMove3D(v, b, dt);
212 return;
213 }
214 if (IVehicleMobility* kinematic = VehicleSystem::findMobility("kinematic")) {
215 kinematic->update(v, dt);
216 }
217 }
218};
219
220} // namespace
221
223 if (v == nullptr || world == nullptr) return VehiclePhysicsStatus::Unavailable;
224 (void)detach(v);
225 const VehicleDefinition* def = v->definition()->def;
226 if (def == nullptr) return VehiclePhysicsStatus::Unavailable;
227 auto mo = v->motion();
228
229 eve::physics::Body* b = world->newBody("dynamic", mo->x, mo->y);
230 if (b == nullptr) return VehiclePhysicsStatus::Unavailable;
231 b->newCircleFixture(def->radius, 1.f, 0.6f, 0.f);
232 b->setAngle(mo->heading * kPi / 180.f);
234 if (!link) {
235 link.ignore("new vehicle body did not produce a valid physics link");
236 b->destroy();
238 }
239 v->physicsBody()->binding = std::make_shared<VehiclePhysicsBinding>(
240 std::move(link).takeValue(), world, world->lifetimeToken());
241 v->physicsBody()->space = "2d";
243}
244
246 if (v == nullptr || world == nullptr) return VehiclePhysicsStatus::Unavailable;
247 (void)detach(v);
248 const VehicleDefinition* def = v->definition()->def;
249 if (def == nullptr) return VehiclePhysicsStatus::Unavailable;
250 auto mo = v->motion();
251
252 eve::physics::Body3D* b = world->newBody("dynamic", mo->x, heightY, mo->y);
253 if (b == nullptr) return VehiclePhysicsStatus::Unavailable;
254 eve::physics::Shape3D* shape = b->newBoxShape(def->radius, 0.35f, def->radius, 1.f, 0.8f, 0.f);
255 if (shape != nullptr) shape->setFilterBits(2, ~uint64_t{0});
256 const float rad = mo->heading * kPi / 180.f;
257 b->setRotation(0.f, std::sin(rad * 0.5f), 0.f, std::cos(rad * 0.5f));
258 b->setAwake(true);
259
261 if (!link) {
262 link.ignore("new vehicle body did not produce a valid physics link");
263 b->destroy();
265 }
266 v->physicsBody()->binding = std::make_shared<VehiclePhysicsBinding>(
267 std::move(link).takeValue(), world, world->lifetimeToken());
268 v->physicsBody()->space = "3d";
269 v->suspension()->wheels.assign(def->suspension.wheels.size(), {});
271}
272
274 if (v == nullptr) return VehiclePhysicsStatus::Unavailable;
275 auto pb = v->physicsBody();
276 const bool had = pb->binding != nullptr;
277 if (pb->binding) pb->binding->detach();
278 pb->binding.reset();
279 pb->space.clear();
281}
282
284 return v != nullptr && v->physicsBody()->binding && v->physicsBody()->binding->isAttached();
285}
286
288 const auto binding = v == nullptr ? nullptr : v->physicsBody()->binding;
289 if (auto* body = binding ? binding->resolve3D() : nullptr) return body->getY();
290 return 0.f;
291}
292
294 const auto binding = v.physicsBody()->binding;
295 if (eve::physics::Body* b = binding ? binding->resolve2D() : nullptr) {
296 wheelMove2D(v, b, dt);
298 }
299 if (eve::physics::Body3D* b = binding ? binding->resolve3D() : nullptr) {
300 wheelMove3D(v, b, dt);
302 }
304}
305
307 auto mo = v.motion();
308 const auto binding = v.physicsBody()->binding;
309 if (eve::physics::Body* b = binding ? binding->resolve2D() : nullptr) {
310 mo->x = b->getX();
311 mo->y = b->getY();
312 } else if (eve::physics::Body3D* b = binding ? binding->resolve3D() : nullptr) {
313 mo->x = b->getX();
314 mo->y = b->getZ();
315 }
316}
317
319 const auto binding = v.physicsBody()->binding;
320 if (eve::physics::Body* b = binding ? binding->resolve2D() : nullptr) {
321 b->setAngle(headingRad);
322 b->setLinearVelocity(std::cos(headingRad) * speed, std::sin(headingRad) * speed);
324 }
325 if (eve::physics::Body3D* b = binding ? binding->resolve3D() : nullptr) {
326 b->setRotation(0.f, std::sin(headingRad * 0.5f), 0.f, std::cos(headingRad * 0.5f));
327 b->setLinearVelocity(std::sin(headingRad) * speed, 0.f, std::cos(headingRad) * speed);
329 }
331}
332
334 static SuspensionMobility3D mobility;
336}
337
338} // namespace eve::vehicle
LogicalId target
float w
Definition AnimClip.cpp:738
ShaderImageInput shape
float v
std::string name
MeleePoint3 b
Definition MeleeHit.cpp:41
eve::action::ActionVfxBinding binding
World3D * world
std::weak_ptr< const void > lifetime
bool hit
std::string body
float vz
float wz
float wx
float vy
float qy
float vx
float qx
float qw
float qz
3D rigid body (Box3D) in meter-space coordinates (+Y up by convention). Owned by a World3D; create pr...
Definition Body3D.h:24
2D rigid body (Box2D) in pixel-space coordinates. Owned by a World; create shapes with newRectangleFi...
Definition Body.h:21
Fixture * newCircleFixture(float radius, float density=1.f, float friction=0.2f, float restitution=0.f)
Creates a circular fixture in pixel-space units.
Definition Body.cpp:256
3D shape (box/sphere/capsule) attached to a Body3D with material settings. Created via Body3D::new*Sh...
Definition Shape3D.h:25
void setFilterBits(uint64_t categoryBits, uint64_t maskBits)
Collision filter bits used by world ray/query filters (Box3D b3Filter). categoryBits 声明本形状属于哪些类别;mask...
Definition Shape3D.cpp:891
Box3D rigid-body world. Script coordinates are meters (Box3D native), unlike 2D World which uses pixe...
Definition World3D.h:63
void update(float dt)
Steps the simulation by dt seconds (default substep count).
Definition World3D.cpp:726
Box2D world wrapper (2D physics) with pixel-space coordinates. Handles stepping, gravity,...
Definition World.h:41
载具实体:数据全部在组件里,行为在 VehicleSystem。
VehiclePhysicsBinding(eve::physics::PhysicsLink link, eve::physics::World *world, std::weak_ptr< const void > lifetime)
eve::physics::Body3D * resolve3D() const
eve::physics::Body * resolve2D() const
VehiclePhysicsBinding(eve::physics::PhysicsLink link, eve::physics::World3D *world, std::weak_ptr< const void > lifetime)
static VehiclePhysicsStatus detach(VehicleEntity *v)
Destroy attached bodies if any.
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 attach2D(VehicleEntity *v, eve::physics::World *world)
Attach a 2D dynamic body when physics is in the build.
static VehiclePhysicsStatus tryTrackApply(VehicleEntity &v, float headingRad, float speed)
Apply tracked heading/speed to an attached body.
static bool isAttached(VehicleEntity *v)
Whether the entity has a live, resolvable physics body.
static VehiclePhysicsStatus attach3D(VehicleEntity *v, eve::physics::World3D *world, float heightY)
Attach a 3D dynamic body when physics is in the build.
static float height(VehicleEntity *v)
3D body height, or 0 when no 3D body is attached.
static IVehicleMobility * findMobility(const std::string &name)
按名字取移动模型;未注册返回 nullptr。
static void registerMobility(IVehicleMobility *mobility)
注册移动模型实现;同名替换。
驾驶者接口与玩家控制状态。
VehiclePhysicsStatus
Outcome of an optional physics attach, detach, or mobility step.
@ Unavailable
No physics module, no body, or invalid arguments.
@ Applied
Physics handled the request.
std::vector< SuspensionWheel > wheels
载具模板(registerVehiclesFromJson 注册,进程级注册表)。