@needle-tools/car-physics
Version:
Car physics for Needle Engine: Create physical cars with ease
704 lines (703 loc) • 26.4 kB
JavaScript
import { getParam as W, serializable as a, ParticleSystem as Y, Behaviour as I, getBoundingBox as $, getTempVector as p, Mathf as v, getTempQuaternion as M, Gizmos as R, ParticleSystemBaseBehaviour as J, Rigidbody as V, BoxCollider as L, delayForFrames as X, FrameEvent as Z, EventList as Q, OrbitControls as ee, Camera as K, GameObject as te, findObjectOfType as ie, SmoothFollow as se, Application as re } from "@needle-tools/engine";
import { Object3D as O, Vector2 as q, Quaternion as D, Vector3 as B, Euler as z } from "three";
var j = /* @__PURE__ */ ((n) => (n[n.all = 0] = "all", n[n.rear = 1] = "rear", n[n.front = 2] = "front", n))(j || {}), w = /* @__PURE__ */ ((n) => (n[n.front = 0] = "front", n[n.rear = 1] = "rear", n))(w || {}), ne = Object.defineProperty, x = (n, e, i, r) => {
for (var t = void 0, s = n.length - 1, o; s >= 0; s--)
(o = n[s]) && (t = o(e, i, t) || t);
return t && ne(e, i, t), t;
};
const T = W("debugwheels");
class l extends I {
/** The wheel index in the car */
get index() {
return this._wheelIndex;
}
wheelModel;
axle = w.front;
radius = -1;
suspensionRestLength = -1;
maxSuspensionTravel = -1;
suspensionCompression = 3;
suspensionRelax = 5;
suspensionStiff = -1;
maxSuspensionForce = -1;
sideFrictionStiffness = 0.7;
frictionSlip = new q(1, 20);
skidParticle;
skidVisualSideThreshold = 5;
skidVisualBreakThreshold = 0.1;
skidParticleBehaviour;
wheelModelRight;
wheelModelUp;
car;
vehicle;
_wheelIndex = -1;
_activeRadius = -1;
_initialQuaternion;
async initialize(e, i, r) {
this.car = e, this.vehicle = i, this._wheelIndex = r;
const t = this.wheelModel || this.gameObject;
let s = this.radius;
if (s <= 0 && (s = $(t).getSize(p()).y * 0.5), s < 0) {
console.error("CarWheel: Radius is invalid, please set it manually or make sure the wheel is attached to a model");
return;
}
this._activeRadius = Math.max(0.01, s), this._initialQuaternion = t.quaternion.clone(), this.wheelModel?.quaternion.identity(), this.gameObject?.quaternion.identity();
const o = e.gameObject.worldQuaternion.clone();
e.gameObject.worldQuaternion = new D();
const h = new D();
h.copy(e.gameObject.worldQuaternion).multiply(t.worldQuaternion.clone().invert()), e.gameObject.worldQuaternion = o, this.wheelModelUp = new B(0, 1, 0).clone().applyQuaternion(h), this.wheelModelRight = new B(1, 0, 0).clone().applyQuaternion(h);
const _ = t.worldPosition, m = this.car.gameObject.worldToLocal(_);
m.multiply(this.car.gameObject.worldScale), m.y += this._activeRadius * 0.5;
const g = p(0, -1, 0), y = p(-1, 0, 0);
let d = this.suspensionRestLength;
(!d || d <= 0) && (d = this._activeRadius * 0.5);
let f = this.maxSuspensionTravel;
(!f || f <= 0) && (f = this._activeRadius * 0.5);
let c = this.suspensionStiff;
(!c || c <= 0) && (c = 50);
let u = this.maxSuspensionForce;
(!u || u <= 0) && (u = 1e8), T && console.debug(this.name, {
restLength: d,
suspensionTravel: f,
suspensionStiff: c,
maxSupsensionForce: u,
radius: this._activeRadius
}, this), this.vehicle.addWheel(m, g, y, d, this._activeRadius), this.vehicle.setWheelMaxSuspensionTravel(r, f), this.vehicle.setWheelMaxSuspensionForce(r, u), this.vehicle.setWheelSuspensionStiffness(r, c), this.vehicle.setWheelSuspensionCompression(r, this.suspensionCompression), this.vehicle.setWheelSuspensionRelaxation(r, this.suspensionRelax), this.vehicle.setWheelSideFrictionStiffness(r, this.sideFrictionStiffness), this.vehicle.setWheelFrictionSlip(r, this.frictionSlip.y), this.skidParticle && (this.skidParticleBehaviour = new oe(), this.skidParticle.addBehaviour(this.skidParticleBehaviour));
}
applyPhysics(e, i, r) {
this.car.carDrive == j.front && this.axle == w.front || this.car.carDrive == j.rear && this.axle == w.rear || this.car.carDrive == j.all || (e = 0), this.vehicle.setWheelEngineForce(this._wheelIndex, e), this.vehicle.setWheelBrake(this._wheelIndex, i), this.axle == w.front && this.vehicle.setWheelSteering(this._wheelIndex, -r);
let o = p(this.car.velocity).clampLength(0, 1).dot(this.car.gameObject.worldRight);
o = 1 - Math.abs(o);
const h = v.lerp(this.frictionSlip.x, this.frictionSlip.y, o);
this.vehicle.setWheelFrictionSlip(this._wheelIndex, h);
}
updateVisuals() {
const e = this.wheelModel || this.gameObject, i = this.vehicle.wheelRotation(this._wheelIndex), r = this.vehicle.wheelSteering(this._wheelIndex), t = M().setFromAxisAngle(this.wheelModelUp, r), s = M().setFromAxisAngle(this.wheelModelRight, i), o = t.multiply(s);
e.quaternion.copy(o), e.quaternion.multiply(this._initialQuaternion);
const h = this.vehicle.wheelContactPoint(this._wheelIndex), _ = this.vehicle.wheelIsInContact(this._wheelIndex), m = p();
if (h && (T && R.DrawWireSphere(h, 0.02, 16777045, 0, !1), m.copy(this.car.gameObject.worldUp).multiplyScalar(this._activeRadius), m.add(h), e.worldPosition = m), this.skidParticleBehaviour) {
const g = Math.abs(this.vehicle.wheelSideImpulse(this._wheelIndex) ?? 0), y = Math.abs(this.vehicle.wheelBrake(this._wheelIndex) ?? 0), d = g > this.skidVisualSideThreshold || y > this.skidVisualBreakThreshold, f = _ && h != null && d;
if (this.skidParticle && h) {
const c = p(h);
c.y += this.skidParticle.main.startSize.constant / 4, this.skidParticle.worldPosition = c;
}
this.skidParticleBehaviour.isSkidding = f;
}
if (T) {
const g = this._activeRadius * 0.1, y = p(this.car.gameObject.worldRight).multiplyScalar(-1);
y.applyEuler(new z(0, r, 0)), R.DrawCircle(m, y, this._activeRadius, 255, 0, !1);
const d = p(m), f = p(m).add(p(y).multiplyScalar(this._activeRadius));
R.DrawLine(d, f, 16711680, 0, !1), R.DrawSphere(f, g, 16711680, 0, !1);
const c = p(this.car.gameObject.worldForward).multiplyScalar(this._activeRadius * 1);
c.applyEuler(new z(0, r, 0));
const u = p(m).add(c);
R.DrawLine(m, u, 255, 0, !1), R.DrawSphere(u, g, 255, 0, !1);
}
}
}
x([
a(O)
], l.prototype, "wheelModel");
x([
a()
], l.prototype, "axle");
x([
a()
], l.prototype, "radius");
x([
a()
], l.prototype, "suspensionRestLength");
x([
a()
], l.prototype, "maxSuspensionTravel");
x([
a()
], l.prototype, "suspensionCompression");
x([
a()
], l.prototype, "suspensionRelax");
x([
a()
], l.prototype, "suspensionStiff");
x([
a()
], l.prototype, "maxSuspensionForce");
x([
a()
], l.prototype, "sideFrictionStiffness");
x([
a(q)
], l.prototype, "frictionSlip");
x([
a(Y)
], l.prototype, "skidParticle");
x([
a()
], l.prototype, "skidVisualSideThreshold");
x([
a()
], l.prototype, "skidVisualBreakThreshold");
class oe extends J {
isSkidding = !1;
update(e, i) {
const r = e;
if (this.system.trails?.enabled && r) {
this.isSkidding || e.color.setW(0);
let t = r.previous?.tail;
for (; t && t.hasPrev(); ) {
const s = t;
s.data ??= {}, s.data.isSkidding === void 0 && (s.data.isSkidding = this.isSkidding), s.data.isSkidding === !1 && t.data.color?.setW(0), t = t.prev;
}
}
}
}
var ae = Object.defineProperty, C = (n, e, i, r) => {
for (var t = void 0, s = n.length - 1, o; s >= 0; s--)
(o = n[s]) && (t = o(e, i, t) || t);
return t && ae(e, i, t), t;
};
const E = W("debugcar");
class b extends I {
carDrive = j.all;
mass = 500;
maxSteer = 40;
steerSmoothingFactor = 0.1;
accelerationForce = 12;
breakForce = 12;
topSpeed = 25;
wheels = [];
/**
* Steer the car. -1 is full left, 1 is full right
* @param steerAmount -1 to 1
*/
steerImpulse(e) {
this._steerInput += e, this._steerInput = v.clamp(this._steerInput, -1, 1);
}
get currentSteer() {
return this._currentSteer;
}
set currentSteer(e) {
this._currentSteer = e;
}
/**
* Increase or decrease acceleration
* @param accelAmount -1 to 1 where -1 is full brake and 1 is full acceleration
*/
accelerationImpulse(e) {
this._currAcc += e;
}
/**
* This will always apply the break force to the car
* @param breakAmount The amount of break force to apply e.g. 1 for full break
*/
breakImpulse(e) {
this._currBreak += e;
}
/** Rigidbody component */
get rigidbody() {
return this._rigidbody;
}
/**
* Rapier Physics Rigidbody (owned by the rigidbody componenti)
*/
get rapierRigidbody() {
return this.context.physics.engine?.getBody(this._rigidbody);
}
/**
* Rapier Physics Vehicle Controller
*/
get vehicle() {
return this._vehicle;
}
/**
* The rigidbody velocity vector of the car in worldspace
*/
get velocity() {
return this._rigidbody?.getVelocity();
}
/**
* Current vehicle speed
*/
get currentSpeed() {
return this._vehicle?.currentVehicleSpeed() || 0;
}
/**
* Current vehicle speed in km/h
*/
get currentSpeedInKmh() {
return this.currentSpeed * 3.6;
}
/**
* The maximum speed of the car in km/h
*/
get maxSpeedInKmh() {
return this.topSpeed * 3.6;
}
/**
* Current vehicle speed normalized between 0 and 1 where 1 is the top speed
*/
get currentSpeed01() {
return this._vehicle ? this._vehicle.currentVehicleSpeed() / this.topSpeed : 0;
}
/**
* The airtime of the car in seconds
*/
get airtime() {
return this._airtime;
}
set airtime(e) {
this._airtime = e;
}
_vehicle;
_rigidbody;
_currentSteer = 0;
_currAcc = 0;
_currBreak = 0;
_steerInput = 0;
_airtime = 0;
/** @internal */
awake() {
if (this._rigidbody || (this._rigidbody = this.gameObject.addComponent(V)), !this.gameObject.getComponentInChildren(L)) {
const e = L.add(this.gameObject), i = new O();
i.addComponent(e), this.gameObject.add(i), i.position.copy(e.center), e.center.set(0, 0, 0), e.center.y += e.size.y * 0.1, e.size.x *= 0.85, e.size.y *= 0.7, e.size.z *= 0.85, e.updateProperties();
}
}
_physicsRoutine;
/** @internal */
async onEnable() {
if (this.mass <= 0 && (this.mass = 1), this._rigidbody = this.gameObject.getOrAddComponent(V), this._rigidbody.mass = this.mass, this._rigidbody.autoMass = this.mass <= 0, await this.context.physics.engine?.initialize().then(() => X(1)), !this.activeAndEnabled) return;
const e = this.context.physics.engine?.world;
if (!e) {
console.error("[CarPhysics] Physics world not found");
return;
}
if (!this.rapierRigidbody) {
console.error("[CarPhysics] Rigidbody not found");
return;
}
if (this._vehicle || (this._vehicle = e.createVehicleController(this.rapierRigidbody)), this._vehicle.indexUpAxis = 1, this._vehicle.setIndexForwardAxis = 2, this.wheels.length === 0 && this.wheels.push(...this.gameObject.getComponentsInChildren(l).filter((i) => i.activeAndEnabled)), this.wheels.length <= 0) {
console.debug(`[CarPhysics] No wheels found on ${this.gameObject.name}, trying to find them`);
const i = he(this);
i.length > 0 && (console.debug(`[CarPhysics] Found ${i.length} wheels: ${i.map((r) => `${r.name} (${w[r.axle]})`).join(", ")}`), this.wheels.push(...i));
}
this.wheels.length <= 0 && console.warn(`[CarPhysics] No wheels found on ${this.gameObject.name}`), E && console.log(`[CarPhysics] ${this.name} has ${this.wheels.length} wheels:`, this.wheels), this.wheels.forEach((i, r) => {
i.initialize(this, this._vehicle, r);
}), this._physicsRoutine = this.startCoroutine(this.physicsLoop(), Z.PostPhysicsStep);
}
/** @internal */
onDisable() {
this._vehicle && this.context.physics.engine?.world?.removeVehicleController(this._vehicle), this._vehicle?.free(), this._vehicle = null, this._physicsRoutine && this.stopCoroutine(this._physicsRoutine);
}
/** @internal */
onBeforeRender() {
if (!this._vehicle) return;
if (this.steerSmoothingFactor > 0) {
const i = this.context.time.deltaTime / this.steerSmoothingFactor;
this._currentSteer = v.lerp(this._currentSteer, this._steerInput, v.clamp01(i));
} else
this._currentSteer = this._steerInput;
this.applyPhysics(), this._steerInput = 0, this._currAcc = 0, this._currBreak = 0;
let e = !1;
if (this.wheels.forEach((i) => {
i.updateVisuals(), e || (e ||= this._vehicle.wheelIsInContact(i.index));
}), e ? this._airtime = 0 : this._airtime += this.context.time.deltaTime, E) {
const i = this._vehicle.chassis(), r = i.translation(), t = p(r).add(p(0, 2, 0)), s = `vel: ${this._vehicle.currentVehicleSpeed().toFixed(2)}`;
R.DrawLabel(t, s, 0.1, 0, 16777215, 0), this.wheels.forEach((o) => {
const h = this._vehicle.wheelChassisConnectionPointCs(o.index);
h && R.DrawLine(p(r), p(h).applyQuaternion(i.rotation()).add(r), 255, 0, !1);
});
}
}
teleport(e, i, r = !0) {
!this.rapierRigidbody || !this._vehicle || (e && this.rapierRigidbody.setTranslation(e, !0), i && this.rapierRigidbody.setRotation(i, !0), r && this._rigidbody.setVelocity(0, 0, 0));
}
*physicsLoop() {
for (; ; ) {
if (this._vehicle) {
const e = this.context.time.deltaTime;
this._vehicle?.updateVehicle(e);
}
yield null;
}
}
applyPhysics() {
this._currAcc = v.clamp(this._currAcc, -1, 1);
let e = this._currAcc === 0 ? 0.2 : 0, i = 0;
const r = this._rigidbody.getVelocity(), t = this._vehicle.currentVehicleSpeed(), s = t > this.topSpeed, o = this.context.time.deltaTime * this.mass * this.currentSpeed01 * 20;
this._rigidbody.applyImpulse(p(0, -o, 0)), this._currAcc < 0 && t > 0.05 && r.dot(this.gameObject.worldForward) > 0 && (e = this.breakForce * -this._currAcc), e += Math.max(0, this._currBreak) * this.breakForce, this._currAcc != 0 && !s && (i = this.accelerationForce / this.context.time.deltaTime * this._currAcc);
const m = v.lerp(this.maxSteer, this.maxSteer * 0.5, this.currentSpeed01), g = this._currentSteer * m * v.Deg2Rad;
this.wheels.forEach((y) => {
y.applyPhysics(i, e, g);
});
}
}
C([
a()
], b.prototype, "carDrive");
C([
a()
], b.prototype, "mass");
C([
a()
], b.prototype, "maxSteer");
C([
a()
], b.prototype, "steerSmoothingFactor");
C([
a()
], b.prototype, "accelerationForce");
C([
a()
], b.prototype, "breakForce");
C([
a()
], b.prototype, "topSpeed");
C([
a(l)
], b.prototype, "wheels");
function he(n) {
const e = new Array();
if (i(n.gameObject), e.length <= 0) {
const r = n.gameObject.worldPosition, t = n.gameObject.worldQuaternion;
n.gameObject.worldPosition = new B(), n.gameObject.worldQuaternion = new D();
const s = $(n.gameObject);
n.gameObject.worldQuaternion = t, n.gameObject.worldPosition = r;
const o = s.max.y - s.min.y, h = Math.max(s.max.x - s.min.x, s.max.z - s.min.z), _ = o / h, g = s.getSize(new B()).length() * 0.1, y = s.min.y;
let d = (s.max.x - s.min.x) * 0.1, f = (s.max.z - s.min.z) * 0.1;
_ > 1 && (d *= -_ * 1.5, f *= -_ * 1.5);
const c = new O();
c.position.set(s.min.x + d, y, s.max.z - f), c.name = "WheelFrontLeft", e.push(c.addComponent(l, {
axle: w.front,
radius: g
})), n.gameObject.add(c);
const u = new O();
u.position.set(s.max.x - d, y, s.max.z - f), u.name = "WheelFrontRight", e.push(u.addComponent(l, {
axle: w.front,
radius: g
})), n.gameObject.add(u);
const S = new O();
S.position.set(s.min.x + d, y, s.min.z + f), S.name = "WheelRearLeft", e.push(S.addComponent(l, {
axle: w.rear,
radius: g
})), n.gameObject.add(S);
const P = new O();
P.position.set(s.max.x - d, y, s.min.z + f), P.name = "WheelRearRight", e.push(P.addComponent(l, {
axle: w.rear,
radius: g
})), n.gameObject.add(P);
}
return e;
function i(r) {
for (const t of r.children) {
const s = t.name.toLowerCase();
if (s.includes("wheel") && !t.getComponent(l)) {
const o = s.includes("front") || s.includes("fl") || s.includes("fr"), h = t.addComponent(l, {
axle: o ? w.front : w.rear
});
e.push(h);
}
}
for (const t of r.children) {
if (e.length > 0) break;
t instanceof O && i(t);
}
}
}
var ce = Object.defineProperty, F = (n, e, i, r) => {
for (var t = void 0, s = n.length - 1, o; s >= 0; s--)
(o = n[s]) && (t = o(e, i, t) || t);
return t && ce(e, i, t), t;
};
class k extends I {
carPhysics;
autoReset = !0;
manualReset = !0;
onReset = new Q();
/**
* Resets the car to the starting position and orientation
*/
reset() {
this.carPhysics?.teleport(this.posOnStart, this.rotOnStart, !0), this.context.mainCamera.getComponent(ee)?.setCameraTargetPosition(this.camStartPos, !0), this.onReset?.invoke();
}
posOnStart;
rotOnStart;
camStartPos;
start() {
this.posOnStart = this.gameObject.worldPosition.clone(), this.rotOnStart = this.gameObject.worldQuaternion.clone(), this.camStartPos = this.context.mainCamera.position.clone();
}
onEnable() {
this.carPhysics ||= this.gameObject.getComponent(b), window.addEventListener("blur", this.onBlur);
}
onDisable() {
window.removeEventListener("blur", this.onBlur);
}
onBeforeRender() {
this.handleInput(), this.manualReset && this.context.input.isKeyDown("r") && this.reset(), this.autoReset && (this.resetWhenRolledOver(), this.resetWhenFallingoff());
}
onBlur = (e) => {
if (!this.context.application.hasFocus) {
const i = navigator.getGamepads()?.[0];
i && i.vibrationActuator?.playEffect("dual-rumble", {
startDelay: 0,
duration: 0,
weakMagnitude: 1,
strongMagnitude: 1
});
}
};
_lastResetTime = -1;
resetWhenFallingoff() {
this.carPhysics && this.carPhysics.airtime > 5 && this.context.time.realtimeSinceStartup - this._lastResetTime > 5 && (this._lastResetTime = this.context.time.realtimeSinceStartup, this.reset());
}
rolledOverDuration = 0;
resetWhenRolledOver() {
if (!this.carPhysics) return;
const e = this.gameObject.worldUp.dot(p(0, 1, 0)) < 0.65, r = this.carPhysics.rigidbody.getVelocity().length() < 0.1;
e && r ? this.rolledOverDuration += this.context.time.deltaTime : this.rolledOverDuration = 0, this.rolledOverDuration > 1 && this.rescueVehicle();
}
// TODO: add raycast to determine normal of the surface the car is resetting to
async rescueVehicle() {
if (!this.carPhysics) return;
const e = this.gameObject.worldPosition;
e.y += 1;
const i = this.gameObject.worldForward;
i.y = 0, i.normalize();
const r = M().setFromUnitVectors(p(0, 0, -1), i);
this.carPhysics.teleport(e, r);
}
_lastVehicleVelocity = 0;
_lastHeroRumbleTime = -1;
_currentSteer = 0;
_currentSteerAccum = 0;
handleInput() {
if (!this.carPhysics?.vehicle) return;
let e = 0, i = 0, r = 0;
if (this.context.xr) {
i += this.context.xr.rightController?.getButton("a-button")?.value || 0, i -= this.context.xr.leftController?.getButton("x-button")?.value || 0;
const s = this.context.xr.rightController?.getButton("xr-standard-squeeze")?.value || 0, o = this.context.xr.leftController?.getButton("xr-standard-squeeze")?.value || 0;
if (s > 0.5 && o > 0.5) {
const h = this.context.xr.leftController.gripPosition.y - this.context.xr.rightController.gripPosition.y;
e = v.clamp(h, -2, 2);
}
} else
this.context.input.isKeyPressed("a") || this.context.input.isKeyPressed("ArrowLeft") ? e -= 1 : (this.context.input.isKeyPressed("d") || this.context.input.isKeyPressed("ArrowRight")) && (e += 1), (this.context.input.isKeyPressed("s") || this.context.input.isKeyPressed("ArrowDown")) && (i -= 1), (this.context.input.isKeyPressed("w") || this.context.input.isKeyPressed("ArrowUp")) && (i += 1), this.context.input.isKeyPressed(" ") && (r += 1);
const t = navigator.getGamepads()?.[0];
if (t?.connected) {
const s = t.axes[0], o = t.axes[1];
if (Math.abs(s) > 0.01) {
const c = s < 0 ? -1 : 1;
e += Math.pow(s, 2) * c;
}
Math.abs(o) > 0.01 && (i -= o);
const h = t.buttons[0], _ = t.buttons[1], m = t.buttons[6], g = t.buttons[7];
(h.pressed || g.pressed) && (i += 1), (_.pressed || m.pressed) && (i -= 1), t.buttons[2].pressed && this.reset();
const d = this.carPhysics.velocity.length();
if (this.context.time.realtimeSinceStartup - this._lastHeroRumbleTime > 0.3) {
d > 0.01 && t.vibrationActuator?.playEffect("dual-rumble", {
startDelay: 0,
duration: this.context.time.deltaTime,
weakMagnitude: 0.1,
strongMagnitude: 0.1
});
const c = this.carPhysics.wheels, u = 200;
let S = 0;
for (const P of c) {
const A = this.carPhysics.vehicle.wheelSuspensionForce(P.index);
if (A && A < u) {
const N = 1 - A / u;
S = Math.max(S, N);
}
}
if (S > 0) {
const P = Math.pow(S, 2);
t.vibrationActuator?.playEffect("dual-rumble", {
startDelay: 0,
duration: S * 500,
weakMagnitude: P * 1,
strongMagnitude: P * 1
});
}
}
if (d) {
const c = this._lastVehicleVelocity;
this._lastVehicleVelocity = d;
const u = c - d;
u > 1 && (this._lastHeroRumbleTime = this.context.time.realtimeSinceStartup, t.vibrationActuator?.playEffect("dual-rumble", {
startDelay: 0,
duration: 150,
weakMagnitude: v.clamp01(u / 3),
strongMagnitude: v.clamp01(u / 3)
}));
}
}
e *= Math.max(0.2, Math.min(1, 2 * Math.abs(this.carPhysics.currentSteer))), this._currentSteer = v.lerp(this._currentSteer, e, this.context.time.deltaTime / 0.12), this.carPhysics.steerImpulse(this._currentSteer), this.carPhysics.breakImpulse(r), this.carPhysics.accelerationImpulse(i);
}
}
F([
a(b)
], k.prototype, "carPhysics");
F([
a()
], k.prototype, "autoReset");
F([
a()
], k.prototype, "manualReset");
F([
a(Q)
], k.prototype, "onReset");
var le = Object.defineProperty, de = (n, e, i, r) => {
for (var t = void 0, s = n.length - 1, o; s >= 0; s--)
(o = n[s]) && (t = o(e, i, t) || t);
return t && le(e, i, t), t;
};
class H extends I {
carPhysics;
steerLeftState = 0;
steerRightState = 0;
throttleState = 0;
breakState = 0;
update() {
this.throttleInput(), this.steerInput();
}
throttleInput() {
this.carPhysics?.accelerationImpulse(v.clamp(this.throttleState + this.breakState, -1, 1));
}
steerInput() {
this.carPhysics?.steerImpulse(v.clamp(this.steerLeftState + this.steerRightState, -1, 1));
}
// ---
steerLeftPress() {
this.steerLeftState = -1;
}
steerLeftRelease() {
this.steerLeftState = 0;
}
steerRightPress() {
this.steerRightState = 1;
}
steerRightRelease() {
this.steerRightState = 0;
}
throttlePress() {
this.throttleState = 1;
}
throttleRelease() {
}
brakePress() {
this.throttleState = 0, this.breakState = -1;
}
brakeRelease() {
this.breakState = 0;
}
}
de([
a(b)
], H.prototype, "carPhysics");
var ue = Object.defineProperty, U = (n, e, i, r) => {
for (var t = void 0, s = n.length - 1, o; s >= 0; s--)
(o = n[s]) && (t = o(e, i, t) || t);
return t && ue(e, i, t), t;
};
class G extends I {
cameraRig = null;
cars;
awake() {
this.cars ??= [];
}
start() {
this.cars?.length || (this.cars = [...te.findObjectsOfType(k)]), this.cars.length > 0 && this.selectCarByIndex(0);
}
onEnable() {
this.context.input.addEventListener("keyup", this.onKey), this.context.domElement.addEventListener("click", this.onClick);
}
onDisable() {
this.context.input.removeEventListener("keyup", this.onKey), this.context.domElement.removeEventListener("click", this.onClick);
}
selectCar(e) {
this.cars || (this.cars = []);
let i = this.cars.indexOf(e);
i === -1 && (this.cars.push(e), i = this.cars.length - 1), this.selectCarByIndex(i);
}
gamepadButtonDown = !1;
update() {
const e = navigator.getGamepads()?.[0];
if (e)
if (e.buttons?.[3]?.pressed) {
if (!this.gamepadButtonDown) {
this.gamepadButtonDown = !0;
const r = this.cars.find((t) => t.activeAndEnabled);
if (r) {
const s = ((r ? this.cars.indexOf(r) : -1) + 1) % this.cars.length;
this.selectCarByIndex(s);
}
}
} else this.gamepadButtonDown && (this.gamepadButtonDown = !1);
}
onKey = (e) => {
const i = parseInt(e.key) - 1;
i >= 0 && i < this.cars.length && this.selectCarByIndex(i);
};
onClick = (e) => {
if (!this.cars?.length || e instanceof MouseEvent && e.button != 0)
return;
const i = this.context.physics.raycast();
if (i.length) {
const r = i[0]?.object.getComponentInParent(k), t = r ? this.cars.indexOf(r) : -1;
t >= 0 && this.selectCarByIndex(t);
}
};
selectCarByIndex(e) {
for (const r of this.cars)
r && (r.enabled = !1);
const i = this.cars[e];
if (i) {
i.enabled = !0;
const r = ie(H);
r && (r.carPhysics = i.gameObject.getComponentInChildren(b) || void 0);
const t = i.gameObject.getComponentInChildren(K);
if (t)
this.context.setCurrentCamera(t);
else if (this.cameraRig) {
this.context.setCurrentCamera(this.cameraRig);
const s = this.cameraRig.gameObject.getComponentInParent(se);
s && (s.target = i.gameObject);
}
}
}
}
U([
a(K)
], G.prototype, "cameraRig");
U([
a(k)
], G.prototype, "cars");
var pe = Object.defineProperty, me = (n, e, i, r) => {
for (var t = void 0, s = n.length - 1, o; s >= 0; s--)
(o = n[s]) && (t = o(e, i, t) || t);
return t && pe(e, i, t), t;
};
class fe extends I {
url = "https://stream.laut.fm/gta-classics";
_audio = null;
onEnable() {
this.url && (this._audio = new Audio(this.url), this._audio.autoplay = !0, re.registerWaitForInteraction(() => {
this.enabled && this._audio?.play();
}));
}
onDisable() {
this._audio?.pause();
}
}
me([
a()
], fe.prototype, "url");
export {
w as CarAxle,
k as CarController,
j as CarDrive,
b as CarPhysics,
fe as CarRadio,
G as CarSelection,
l as CarWheel,
oe as SkidTrailBehaviour
};