@gg-web-engine/core 0.0.24 → 0.0.28

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (47) hide show
  1. package/dist/2d/components/physics/i-rigid-body-2d.component.d.ts +1 -0
  2. package/dist/2d/gg-2d-world.d.ts +1 -2
  3. package/dist/2d/gg-2d-world.js +4 -5
  4. package/dist/3d/components/physics/i-raycast-vehicle.component.d.ts +3 -6
  5. package/dist/3d/components/physics/i-rigid-body-3d.component.d.ts +1 -0
  6. package/dist/3d/entities/controllers/input/car-keyboard-handling.controller.d.ts +11 -13
  7. package/dist/3d/entities/controllers/input/car-keyboard-handling.controller.js +29 -72
  8. package/dist/3d/entities/controllers/input/gg-car-keyboard-handling.controller.d.ts +18 -0
  9. package/dist/3d/entities/controllers/input/gg-car-keyboard-handling.controller.js +96 -0
  10. package/dist/3d/entities/gg-car/gg-car.entity.d.ts +79 -0
  11. package/dist/3d/entities/gg-car/gg-car.entity.js +228 -0
  12. package/dist/3d/entities/raycast-vehicle-3d.entity.d.ts +43 -76
  13. package/dist/3d/entities/raycast-vehicle-3d.entity.js +78 -299
  14. package/dist/3d/gg-3d-world.d.ts +1 -2
  15. package/dist/3d/gg-3d-world.js +4 -5
  16. package/dist/3d/index.d.ts +2 -0
  17. package/dist/3d/index.js +2 -0
  18. package/dist/base/components/physics/i-body.component.d.ts +14 -0
  19. package/dist/base/components/physics/i-body.component.js +1 -0
  20. package/dist/base/components/physics/i-rigid-body.component.d.ts +4 -11
  21. package/dist/base/components/physics/i-trigger.component.d.ts +3 -1
  22. package/dist/base/entities/i-entity.js +1 -1
  23. package/dist/base/gg-world.d.ts +8 -12
  24. package/dist/base/gg-world.js +32 -64
  25. package/dist/base/index.d.ts +2 -3
  26. package/dist/base/index.js +2 -3
  27. package/dist/base/math/point2.d.ts +4 -0
  28. package/dist/base/math/point2.js +8 -0
  29. package/dist/base/math/point3.d.ts +6 -0
  30. package/dist/base/math/point3.js +12 -0
  31. package/dist/base/math/splines.d.ts +1 -0
  32. package/dist/base/math/splines.js +7 -0
  33. package/dist/{base/ui → dev}/gg-console.ui.d.ts +1 -4
  34. package/dist/{base/ui → dev}/gg-console.ui.js +35 -17
  35. package/dist/dev/gg-debugger.ui.d.ts +11 -0
  36. package/dist/dev/gg-debugger.ui.js +111 -0
  37. package/dist/dev/gg-static.d.ts +32 -0
  38. package/dist/dev/gg-static.js +141 -0
  39. package/dist/dev/index.d.ts +3 -0
  40. package/dist/dev/index.js +3 -0
  41. package/dist/index.d.ts +1 -0
  42. package/dist/index.js +1 -0
  43. package/package.json +1 -1
  44. package/dist/base/gg-static.d.ts +0 -9
  45. package/dist/base/gg-static.js +0 -36
  46. package/dist/base/ui/gg-debugger.ui.d.ts +0 -10
  47. package/dist/base/ui/gg-debugger.ui.js +0 -61
@@ -1,4 +1,5 @@
1
1
  import { IRigidBodyComponent, Point2 } from '../../../base';
2
2
  import { IPhysicsWorld2dComponent } from './i-physics-world-2d.component';
3
3
  export interface IRigidBody2dComponent<PW extends IPhysicsWorld2dComponent = IPhysicsWorld2dComponent> extends IRigidBodyComponent<Point2, number, PW> {
4
+ angularVelocity: number;
4
5
  }
@@ -7,8 +7,7 @@ import { Renderer2dEntity } from './entities/renderer-2d.entity';
7
7
  export declare class Gg2dWorld<V extends IVisualScene2dComponent = IVisualScene2dComponent, P extends IPhysicsWorld2dComponent = IPhysicsWorld2dComponent> extends GgWorld<Point2, number, V, P> {
8
8
  readonly visualScene: V;
9
9
  readonly physicsWorld: P;
10
- protected readonly consoleEnabled: boolean;
11
- constructor(visualScene: V, physicsWorld: P, consoleEnabled?: boolean);
10
+ constructor(visualScene: V, physicsWorld: P);
12
11
  addPrimitiveRigidBody(descr: BodyShape2DDescriptor, position?: Point2, rotation?: number): Entity2d<V, P>;
13
12
  addRenderer(canvas?: HTMLCanvasElement, rendererOptions?: Partial<RendererOptions>): Renderer2dEntity;
14
13
  }
@@ -11,13 +11,12 @@ import { GgWorld, Pnt2 } from '../base';
11
11
  import { Entity2d } from './entities/entity-2d';
12
12
  import { Renderer2dEntity } from './entities/renderer-2d.entity';
13
13
  export class Gg2dWorld extends GgWorld {
14
- constructor(visualScene, physicsWorld, consoleEnabled = false) {
15
- super(visualScene, physicsWorld, consoleEnabled);
14
+ constructor(visualScene, physicsWorld) {
15
+ super(visualScene, physicsWorld);
16
16
  this.visualScene = visualScene;
17
17
  this.physicsWorld = physicsWorld;
18
- this.consoleEnabled = consoleEnabled;
19
- if (consoleEnabled) {
20
- this.registerConsoleCommand('ph_gravity', (...args) => __awaiter(this, void 0, void 0, function* () {
18
+ if (window.ggstatic) {
19
+ window.ggstatic.registerConsoleCommand(this, 'ph_gravity', (...args) => __awaiter(this, void 0, void 0, function* () {
21
20
  if (args.length == 1) {
22
21
  args = ['0', args[0]]; // mean Y axis
23
22
  }
@@ -1,6 +1,5 @@
1
- import { AxisDirection3, Point3, Point4 } from '../../../base';
1
+ import { Point3, Point4 } from '../../../base';
2
2
  import { IRigidBody3dComponent } from './i-rigid-body-3d.component';
3
- import { IDisplayObject3dComponent } from '../rendering/i-display-object-3d.component';
4
3
  import { IPhysicsWorld3dComponent } from './i-physics-world-3d.component';
5
4
  export declare type SuspensionOptions = {
6
5
  stiffness: number;
@@ -9,12 +8,10 @@ export declare type SuspensionOptions = {
9
8
  restLength: number;
10
9
  };
11
10
  export declare type WheelOptions = {
12
- tyre_width: number;
13
- tyre_radius: number;
14
- wheelObject?: IDisplayObject3dComponent;
15
- wheelObjectDirection?: AxisDirection3;
16
11
  isLeft: boolean;
17
12
  isFront: boolean;
13
+ tyreWidth: number;
14
+ tyreRadius: number;
18
15
  position: Point3;
19
16
  frictionSlip: number;
20
17
  rollInfluence: number;
@@ -1,4 +1,5 @@
1
1
  import { IRigidBodyComponent, Point3, Point4 } from '../../../base';
2
2
  import { IPhysicsWorld3dComponent } from './i-physics-world-3d.component';
3
3
  export interface IRigidBody3dComponent<PW extends IPhysicsWorld3dComponent = IPhysicsWorld3dComponent> extends IRigidBodyComponent<Point3, Point4, PW> {
4
+ angularVelocity: Point3;
4
5
  }
@@ -1,23 +1,21 @@
1
- import { BehaviorSubject } from 'rxjs';
2
- import { DirectionKeyboardInput, DirectionKeyboardKeymap, IEntity, TickOrder, GgWorld, KeyboardInput } from '../../../../base';
3
- import { RaycastVehicle3dEntity } from '../../raycast-vehicle-3d.entity';
1
+ import { Observable } from 'rxjs';
2
+ import { DirectionKeyboardInput, DirectionKeyboardKeymap, GgWorld, IEntity, KeyboardInput, TickOrder } from '../../../../base';
4
3
  export declare type CarKeyboardControllerOptions = {
5
- keymap: DirectionKeyboardKeymap;
6
- gearUpDownKeys: [string, string];
7
- handbrakeKey: string;
4
+ readonly keymap: DirectionKeyboardKeymap;
5
+ readonly maxSteerDeltaPerSecond: number;
6
+ };
7
+ export declare type CarHandlingOutput = {
8
+ upDown: number;
9
+ leftRight: number;
8
10
  };
9
11
  export declare class CarKeyboardHandlingController extends IEntity {
10
12
  protected readonly keyboard: KeyboardInput;
11
- car: RaycastVehicle3dEntity | null;
12
13
  protected readonly options: CarKeyboardControllerOptions;
13
14
  readonly tickOrder = TickOrder.INPUT_CONTROLLERS;
14
15
  protected readonly directionsInput: DirectionKeyboardInput;
15
- protected readonly x$: BehaviorSubject<number>;
16
- protected readonly y$: BehaviorSubject<number>;
17
- protected lastX: number;
18
- switchingGearsEnabled: boolean;
19
- pairTickerPipe: import("rxjs").UnaryFunction<import("rxjs").Observable<number>, import("rxjs").Observable<number>>;
20
- constructor(keyboard: KeyboardInput, car: RaycastVehicle3dEntity | null, options?: CarKeyboardControllerOptions);
16
+ private _output$;
17
+ get output$(): Observable<CarHandlingOutput>;
18
+ constructor(keyboard: KeyboardInput, options?: CarKeyboardControllerOptions);
21
19
  onSpawned(world: GgWorld<any, any>): Promise<void>;
22
20
  onRemoved(): Promise<void>;
23
21
  }
@@ -7,97 +7,54 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
7
7
  step((generator = generator.apply(thisArg, _arguments || [])).next());
8
8
  });
9
9
  };
10
- import { BehaviorSubject, combineLatest, filter, interval, pipe, takeUntil } from 'rxjs';
11
- import { distinctUntilChanged, map, pairwise, startWith, switchMap, take, tap } from 'rxjs/operators';
10
+ import { combineLatest, Subject, takeUntil } from 'rxjs';
12
11
  import { DirectionKeyboardInput, IEntity, TickOrder, } from '../../../../base';
13
- // TODO pass as settings
14
- // TODO smooth y?
15
- const TICKER_INTERVAL = 16; // TODO when refactored, reflect in CarKeyboardHandlingController test
16
- const TICKER_MAX_STEPS = 10;
17
12
  export class CarKeyboardHandlingController extends IEntity {
18
- constructor(keyboard, car, options = {
13
+ constructor(keyboard, options = {
19
14
  keymap: 'arrows',
20
- gearUpDownKeys: ['KeyA', 'KeyZ'],
21
- handbrakeKey: 'Space',
15
+ maxSteerDeltaPerSecond: 12,
22
16
  }) {
23
17
  super();
24
18
  this.keyboard = keyboard;
25
- this.car = car;
26
19
  this.options = options;
27
20
  this.tickOrder = TickOrder.INPUT_CONTROLLERS;
28
- // emits values -1 - 1; -1 = full turn left; 1 = full turn right
29
- this.x$ = new BehaviorSubject(0);
30
- // emits values -1 - 1; -1 = full brake; 1 = full acceleration
31
- this.y$ = new BehaviorSubject(0);
32
- this.lastX = 0;
33
- this.switchingGearsEnabled = true;
34
- this.pairTickerPipe = pipe(pairwise(), map(([beforeValue, afterValue]) => [this.lastX, afterValue]), switchMap(([beforeValue, afterValue]) => interval(TICKER_INTERVAL).pipe(startWith(-1), take(TICKER_MAX_STEPS), map(count => count + 2), map(count => beforeValue * ((TICKER_MAX_STEPS - count) / TICKER_MAX_STEPS) + afterValue * (count / TICKER_MAX_STEPS)), tap(x => {
35
- this.lastX = x;
36
- }))));
21
+ this._output$ = new Subject();
37
22
  this.directionsInput = new DirectionKeyboardInput(keyboard, options.keymap);
38
23
  }
24
+ get output$() {
25
+ return this._output$.asObservable();
26
+ }
39
27
  onSpawned(world) {
40
28
  const _super = Object.create(null, {
41
29
  onSpawned: { get: () => super.onSpawned }
42
30
  });
43
31
  return __awaiter(this, void 0, void 0, function* () {
44
32
  _super.onSpawned.call(this, world);
45
- let moveDirection = {};
46
- this.keyboard
47
- .bind(this.options.gearUpDownKeys[0])
48
- .pipe(takeUntil(this._onRemoved$), filter(x => this.switchingGearsEnabled && !!x))
49
- .subscribe(() => {
50
- if (this.car && (!this.car.carProperties.transmission.isAuto || this.car.gear <= 0)) {
51
- this.car.gear++;
52
- }
53
- });
54
- this.keyboard
55
- .bind(this.options.gearUpDownKeys[1])
56
- .pipe(takeUntil(this._onRemoved$), filter(x => this.switchingGearsEnabled && !!x))
57
- .subscribe(() => {
58
- if (this.car) {
59
- if (this.car.carProperties.transmission.isAuto && this.car.gear > 1) {
60
- this.car.gear = 0;
61
- }
62
- else {
63
- this.car.gear--;
64
- }
65
- }
66
- });
67
- this.keyboard
68
- .bind(this.options.handbrakeKey)
33
+ let input = { upDown: 0, leftRight: 0 };
34
+ combineLatest([this.directionsInput.output$, this.tick$])
69
35
  .pipe(takeUntil(this._onRemoved$))
70
- .subscribe(isKeyDown => {
71
- if (this.car) {
72
- this.car.handBrake = isKeyDown;
36
+ .subscribe(([d, [_, dt]]) => {
37
+ const direction = { upDown: 0, leftRight: 0 };
38
+ if (d.leftRight !== undefined)
39
+ direction.leftRight = d.leftRight ? 1 : -1;
40
+ if (d.upDown !== undefined)
41
+ direction.upDown = d.upDown ? 1 : -1;
42
+ if (direction.leftRight != input.leftRight) {
43
+ let diff = Math.abs(direction.leftRight - input.leftRight);
44
+ let maxSteerInputDelta = (this.options.maxSteerDeltaPerSecond * dt) / 1000;
45
+ if (diff > maxSteerInputDelta) {
46
+ if (direction.leftRight < input.leftRight) {
47
+ direction.leftRight = input.leftRight - maxSteerInputDelta;
48
+ }
49
+ else {
50
+ direction.leftRight = input.leftRight + maxSteerInputDelta;
51
+ }
52
+ }
73
53
  }
54
+ input = direction;
74
55
  });
75
- this.directionsInput.output$.pipe(takeUntil(this._onRemoved$)).subscribe(d => {
76
- moveDirection = d;
77
- });
78
- this.tick$
79
- .pipe(takeUntil(this._onRemoved$), map(() => {
80
- const direction = [0, 0];
81
- if (moveDirection.leftRight !== undefined)
82
- direction[0] = moveDirection.leftRight ? -1 : 1;
83
- if (moveDirection.upDown !== undefined)
84
- direction[1] = moveDirection.upDown ? 1 : -1;
85
- return direction;
86
- }))
87
- .subscribe(([x, y]) => {
88
- this.x$.next(x);
89
- this.y$.next(y);
90
- });
91
- combineLatest([
92
- this.x$.pipe(takeUntil(this._onRemoved$), distinctUntilChanged(), startWith(0), this.pairTickerPipe),
93
- this.y$.pipe(takeUntil(this._onRemoved$), distinctUntilChanged(), startWith(0)),
94
- ])
95
- .pipe(takeUntil(this._onRemoved$))
96
- .subscribe(([x, y]) => {
97
- if (this.car) {
98
- this.car.setXAxisControlValue(x);
99
- this.car.setYAxisControlValue(y);
100
- }
56
+ this.tick$.pipe(takeUntil(this._onRemoved$)).subscribe(() => {
57
+ this._output$.next(input);
101
58
  });
102
59
  yield this.directionsInput.start();
103
60
  });
@@ -0,0 +1,18 @@
1
+ import { GgWorld, IEntity, KeyboardInput, TickOrder } from '../../../../base';
2
+ import { GgCarEntity } from '../../gg-car/gg-car.entity';
3
+ import { CarKeyboardControllerOptions, CarKeyboardHandlingController } from './car-keyboard-handling.controller';
4
+ export declare type GgCarKeyboardControllerOptions = CarKeyboardControllerOptions & {
5
+ gearUpDownKeys: [string, string];
6
+ autoReverse: boolean;
7
+ handbrakeKey: string;
8
+ };
9
+ export declare class GgCarKeyboardHandlingController extends IEntity {
10
+ protected readonly keyboard: KeyboardInput;
11
+ car: GgCarEntity | null;
12
+ protected readonly options: GgCarKeyboardControllerOptions;
13
+ readonly tickOrder = TickOrder.INPUT_CONTROLLERS;
14
+ protected readonly carHandlingInput: CarKeyboardHandlingController;
15
+ switchingGearsEnabled: boolean;
16
+ constructor(keyboard: KeyboardInput, car: GgCarEntity | null, options?: GgCarKeyboardControllerOptions);
17
+ onSpawned(world: GgWorld<any, any>): Promise<void>;
18
+ }
@@ -0,0 +1,96 @@
1
+ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
2
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
3
+ return new (P || (P = Promise))(function (resolve, reject) {
4
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
5
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
6
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
7
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
8
+ });
9
+ };
10
+ import { filter, takeUntil } from 'rxjs';
11
+ import { IEntity, TickOrder } from '../../../../base';
12
+ import { CarKeyboardHandlingController } from './car-keyboard-handling.controller';
13
+ export class GgCarKeyboardHandlingController extends IEntity {
14
+ constructor(keyboard, car, options = {
15
+ keymap: 'arrows',
16
+ maxSteerDeltaPerSecond: 12,
17
+ gearUpDownKeys: ['KeyA', 'KeyZ'],
18
+ autoReverse: true,
19
+ handbrakeKey: 'Space',
20
+ }) {
21
+ super();
22
+ this.keyboard = keyboard;
23
+ this.car = car;
24
+ this.options = options;
25
+ this.tickOrder = TickOrder.INPUT_CONTROLLERS;
26
+ this.switchingGearsEnabled = true;
27
+ this.carHandlingInput = new CarKeyboardHandlingController(keyboard, options);
28
+ this.addChildren(this.carHandlingInput);
29
+ }
30
+ onSpawned(world) {
31
+ const _super = Object.create(null, {
32
+ onSpawned: { get: () => super.onSpawned }
33
+ });
34
+ return __awaiter(this, void 0, void 0, function* () {
35
+ _super.onSpawned.call(this, world);
36
+ this.carHandlingInput.output$.pipe(takeUntil(this._onRemoved$)).subscribe(({ upDown, leftRight }) => {
37
+ if (this.car) {
38
+ this.car.steeringFactor = leftRight;
39
+ if (upDown !== 0 && this.options.autoReverse && this.car.gear !== 0) {
40
+ if (this.car.gear === -1) {
41
+ if (upDown > 0 && this.car.raycastVehicle.getSpeed() > -1) {
42
+ this.car.gear = 1;
43
+ }
44
+ else {
45
+ upDown = -upDown;
46
+ }
47
+ }
48
+ else {
49
+ if (upDown < 0 && this.car.raycastVehicle.getSpeed() < 1) {
50
+ this.car.gear = -1;
51
+ upDown = -upDown;
52
+ }
53
+ }
54
+ }
55
+ if (upDown > 0) {
56
+ this.car.acceleration = upDown;
57
+ this.car.brake = 0;
58
+ }
59
+ else {
60
+ this.car.acceleration = 0;
61
+ this.car.brake = -upDown;
62
+ }
63
+ }
64
+ });
65
+ this.keyboard
66
+ .bind(this.options.gearUpDownKeys[0])
67
+ .pipe(takeUntil(this._onRemoved$), filter(x => this.switchingGearsEnabled && !!x))
68
+ .subscribe(() => {
69
+ if (this.car && (!this.car.carProperties.transmission.isAuto || this.car.gear <= 0)) {
70
+ this.car.gear++;
71
+ }
72
+ });
73
+ this.keyboard
74
+ .bind(this.options.gearUpDownKeys[1])
75
+ .pipe(takeUntil(this._onRemoved$), filter(x => this.switchingGearsEnabled && !!x))
76
+ .subscribe(() => {
77
+ if (this.car) {
78
+ if (this.car.carProperties.transmission.isAuto && this.car.gear > 1) {
79
+ this.car.gear = 0;
80
+ }
81
+ else {
82
+ this.car.gear--;
83
+ }
84
+ }
85
+ });
86
+ this.keyboard
87
+ .bind(this.options.handbrakeKey)
88
+ .pipe(takeUntil(this._onRemoved$))
89
+ .subscribe(isKeyDown => {
90
+ if (this.car) {
91
+ this.car.handBrake = isKeyDown;
92
+ }
93
+ });
94
+ });
95
+ }
96
+ }
@@ -0,0 +1,79 @@
1
+ import { RaycastVehicle3dEntity, RVEntityProperties } from '../raycast-vehicle-3d.entity';
2
+ import { IRenderableEntity, Point3, Point4 } from '../../../base';
3
+ import { IPositionable3d } from '../../interfaces/i-positionable-3d';
4
+ import { BehaviorSubject, Observable } from 'rxjs';
5
+ import { Gg3dWorld } from '../../gg-3d-world';
6
+ import { IDisplayObject3dComponent } from '../../components/rendering/i-display-object-3d.component';
7
+ import { IRaycastVehicleComponent } from '../../components/physics/i-raycast-vehicle.component';
8
+ export declare type GgCarProperties = RVEntityProperties & {
9
+ mpsToRpmFactor?: number;
10
+ engine: {
11
+ minRpm: number;
12
+ maxRpm: number;
13
+ torques: {
14
+ rpm: number;
15
+ torque: number;
16
+ }[];
17
+ maxRpmIncreasePerSecond: number;
18
+ maxRpmDecreasePerSecond: number;
19
+ };
20
+ brake: {
21
+ frontAxleForce: number;
22
+ rearAxleForce: number;
23
+ handbrakeForce: number;
24
+ };
25
+ transmission: {
26
+ isAuto: boolean;
27
+ reverseGearRatio: number;
28
+ gearRatios: number[];
29
+ drivelineEfficiency: number;
30
+ finalDriveRatio: number;
31
+ upShifts: number[];
32
+ autoHold: boolean;
33
+ };
34
+ maxSteerAngle: number;
35
+ };
36
+ export declare class GgCarEntity extends IRenderableEntity<Point3, Point4> implements IPositionable3d {
37
+ readonly carProperties: GgCarProperties;
38
+ readonly tickOrder: number;
39
+ get position(): Point3;
40
+ set position(value: Point3);
41
+ get rotation(): Point4;
42
+ set rotation(value: Point4);
43
+ protected get engineTorque(): number;
44
+ get transmissionGearRatio(): number;
45
+ protected get mpsToRpm(): number;
46
+ calculateRpmFromCarSpeed(): number;
47
+ protected readonly _rpm$: BehaviorSubject<number>;
48
+ get engineRpm$(): Observable<number>;
49
+ get engineRpm(): number;
50
+ protected get tractionForce(): number;
51
+ private _tailLightsOn;
52
+ get tailLightsOn(): boolean;
53
+ protected setTailLightsOn(value: boolean): void;
54
+ protected _acceleration$: BehaviorSubject<number>;
55
+ get acceleration(): number;
56
+ get acceleration$(): Observable<number>;
57
+ set acceleration(value: number);
58
+ protected _brake$: BehaviorSubject<number>;
59
+ get brake(): number;
60
+ set brake(value: number);
61
+ set steeringFactor(value: number);
62
+ protected handBrake$: BehaviorSubject<boolean>;
63
+ get handBrake(): boolean;
64
+ set handBrake(value: boolean);
65
+ private _gear;
66
+ private _gear$;
67
+ get gear(): number;
68
+ get gear$(): Observable<number>;
69
+ set gear(value: number);
70
+ set isHonking(value: boolean);
71
+ readonly raycastVehicle: RaycastVehicle3dEntity;
72
+ constructor(carProperties: GgCarProperties, chassis3D: IDisplayObject3dComponent | null, chassisBody: IRaycastVehicleComponent);
73
+ onSpawned(world: Gg3dWorld): void;
74
+ protected updateEngine(delta: number): void;
75
+ resetTo(options?: {
76
+ position?: Point3;
77
+ rotation?: Point4;
78
+ }): void;
79
+ }
@@ -0,0 +1,228 @@
1
+ import { RaycastVehicle3dEntity } from '../raycast-vehicle-3d.entity';
2
+ import { cubicSplineInterpolation, IRenderableEntity, TickOrder } from '../../../base';
3
+ import { BehaviorSubject, filter } from 'rxjs';
4
+ import { throttleTime } from 'rxjs/operators';
5
+ export class GgCarEntity extends IRenderableEntity {
6
+ constructor(carProperties, chassis3D, chassisBody) {
7
+ super();
8
+ this.carProperties = carProperties;
9
+ this.tickOrder = TickOrder.PHYSICS_SIMULATION - 5;
10
+ this._rpm$ = new BehaviorSubject(this.carProperties.engine.minRpm);
11
+ this._tailLightsOn = false;
12
+ // 0..1
13
+ this._acceleration$ = new BehaviorSubject(0);
14
+ // 0..1
15
+ this._brake$ = new BehaviorSubject(0);
16
+ this.handBrake$ = new BehaviorSubject(false);
17
+ this._gear = 0;
18
+ this._gear$ = new BehaviorSubject(0);
19
+ this.raycastVehicle = new RaycastVehicle3dEntity(carProperties, chassis3D, chassisBody);
20
+ this.addChildren(this.raycastVehicle);
21
+ }
22
+ get position() {
23
+ return this.raycastVehicle.position;
24
+ }
25
+ set position(value) {
26
+ this.raycastVehicle.position = value;
27
+ }
28
+ get rotation() {
29
+ return this.raycastVehicle.rotation;
30
+ }
31
+ set rotation(value) {
32
+ this.raycastVehicle.rotation = value;
33
+ }
34
+ get engineTorque() {
35
+ const currentRPM = this.engineRpm;
36
+ const torques = this.carProperties.engine.torques;
37
+ if (currentRPM <= torques[0].rpm) {
38
+ return torques[0].torque;
39
+ }
40
+ else if (currentRPM >= torques[torques.length - 1].rpm) {
41
+ return torques[torques.length - 1].torque;
42
+ }
43
+ let index = 0;
44
+ while (currentRPM > torques[index + 1].rpm) {
45
+ index++;
46
+ }
47
+ const x0 = torques[index].rpm;
48
+ const x1 = torques[index + 1].rpm;
49
+ const y0 = torques[index].torque;
50
+ const y1 = torques[index + 1].torque;
51
+ const m0 = index === 0 ? 0 : (y1 - torques[index - 1].torque) / (x1 - torques[index - 1].rpm);
52
+ const m1 = index === torques.length - 2 ? 0 : (torques[index + 2].torque - y0) / (torques[index + 2].rpm - x0);
53
+ return cubicSplineInterpolation(currentRPM, x0, x1, y0, y1, m0, m1);
54
+ }
55
+ get transmissionGearRatio() {
56
+ if (this._gear == -1) {
57
+ return this.carProperties.transmission.reverseGearRatio;
58
+ }
59
+ return this.carProperties.transmission.gearRatios[this._gear - 1] || 0;
60
+ }
61
+ get mpsToRpm() {
62
+ return (this.carProperties.mpsToRpmFactor ||
63
+ (30 * this.carProperties.transmission.finalDriveRatio) / (Math.PI * this.raycastVehicle.tractionWheelRadius));
64
+ }
65
+ calculateRpmFromCarSpeed() {
66
+ return this.raycastVehicle.getSpeed() * this.mpsToRpm * this.transmissionGearRatio;
67
+ }
68
+ get engineRpm$() {
69
+ return this._rpm$.asObservable();
70
+ }
71
+ get engineRpm() {
72
+ return this._rpm$.getValue();
73
+ }
74
+ get tractionForce() {
75
+ return ((this.engineTorque *
76
+ this.transmissionGearRatio *
77
+ this.carProperties.transmission.finalDriveRatio *
78
+ this.carProperties.transmission.drivelineEfficiency) /
79
+ this.raycastVehicle.tractionWheelRadius);
80
+ }
81
+ get tailLightsOn() {
82
+ return this._tailLightsOn;
83
+ }
84
+ setTailLightsOn(value) {
85
+ if (this._tailLightsOn != value) {
86
+ this._tailLightsOn = value;
87
+ }
88
+ }
89
+ get acceleration() {
90
+ return this._acceleration$.getValue();
91
+ }
92
+ get acceleration$() {
93
+ return this._acceleration$.asObservable();
94
+ }
95
+ set acceleration(value) {
96
+ this._acceleration$.next(value);
97
+ }
98
+ get brake() {
99
+ return this._brake$.getValue();
100
+ }
101
+ set brake(value) {
102
+ this._brake$.next(value);
103
+ this.setTailLightsOn(value > 0.2);
104
+ }
105
+ // -1..1
106
+ set steeringFactor(value) {
107
+ this.raycastVehicle.steeringAngle = value * this.carProperties.maxSteerAngle;
108
+ }
109
+ get handBrake() {
110
+ return this.handBrake$.getValue();
111
+ }
112
+ set handBrake(value) {
113
+ this.handBrake$.next(value);
114
+ }
115
+ get gear() {
116
+ return this._gear;
117
+ }
118
+ get gear$() {
119
+ return this._gear$.asObservable();
120
+ }
121
+ set gear(value) {
122
+ value = Math.max(-1, Math.min(this.carProperties.transmission.gearRatios.length, value));
123
+ if (value === this._gear) {
124
+ return;
125
+ }
126
+ this._gear = value;
127
+ this._gear$.next(value);
128
+ }
129
+ // TODO remove
130
+ set isHonking(value) { }
131
+ onSpawned(world) {
132
+ super.onSpawned(world);
133
+ this.tick$.subscribe(([_, delta]) => {
134
+ this.updateEngine(delta);
135
+ if (this.raycastVehicle.isTouchingGround) {
136
+ // TODO 1 - R (1000 rpm) quick switch with acceleration pedal should have the same speed as without acceleration pedal
137
+ let force = 0;
138
+ let brake = this.brake;
139
+ const calculatedRpm = this.calculateRpmFromCarSpeed();
140
+ if (this.gear !== 0 && calculatedRpm > this.carProperties.engine.maxRpm) {
141
+ // engine brake, this is related to clutch, better clutch condition - bigger force
142
+ force = this.gear > 0 ? -12000 : 12000;
143
+ }
144
+ else {
145
+ force =
146
+ this.acceleration > 0
147
+ ? this.tractionForce * this.acceleration // apply torque
148
+ : 0.5 * (this.carProperties.engine.minRpm - calculatedRpm) * (this.gear > 0 ? 1 : -1); // released, use engine brake
149
+ // this functionality makes car stay still (parking gear) when speed is low
150
+ if (this.carProperties.transmission.autoHold) {
151
+ const speedThreshold = 3;
152
+ if (Math.abs(this.raycastVehicle.getSpeed()) < speedThreshold &&
153
+ (this.gear == 0 || this.acceleration <= 0)) {
154
+ brake = Math.max(brake, (0.3 * (speedThreshold - this.raycastVehicle.getSpeed())) / speedThreshold);
155
+ force = 0;
156
+ }
157
+ }
158
+ }
159
+ this.raycastVehicle.applyTractionForce(force);
160
+ this.raycastVehicle.applyBrake('front', brake * this.carProperties.brake.frontAxleForce);
161
+ this.raycastVehicle.applyBrake('rear', brake * this.carProperties.brake.rearAxleForce);
162
+ if (this.handBrake) {
163
+ this.raycastVehicle.applyBrake('rear', this.carProperties.brake.handbrakeForce);
164
+ }
165
+ }
166
+ });
167
+ if (this.carProperties.transmission.isAuto) {
168
+ this.tick$
169
+ .pipe(throttleTime(50), filter(() => this.raycastVehicle.isTouchingGround))
170
+ .subscribe(() => {
171
+ let gear = this.gear;
172
+ let upshifted = false;
173
+ if (gear > 0) {
174
+ let rpm = this.engineRpm;
175
+ while (rpm >= this.carProperties.transmission.upShifts[gear - 1]) {
176
+ rpm *=
177
+ this.carProperties.transmission.gearRatios[gear] / this.carProperties.transmission.gearRatios[gear - 1];
178
+ gear++;
179
+ upshifted = true;
180
+ }
181
+ if (!upshifted) {
182
+ while (gear > 1) {
183
+ rpm *=
184
+ this.carProperties.transmission.gearRatios[gear - 2] /
185
+ this.carProperties.transmission.gearRatios[gear - 1];
186
+ if (rpm > this.carProperties.transmission.upShifts[gear - 2]) {
187
+ break;
188
+ }
189
+ gear--;
190
+ }
191
+ }
192
+ this.gear = gear;
193
+ }
194
+ });
195
+ }
196
+ }
197
+ updateEngine(delta) {
198
+ delta = delta / 1000; // ms -> s
199
+ const acceleration = this._acceleration$.getValue();
200
+ let rpm = this.engineRpm;
201
+ // TODO here I will take perioud between gears and drift into account someday :)
202
+ const gripKoeff = this.gear === 0 || !this.raycastVehicle.isTouchingGround ? 0 : 1;
203
+ if (gripKoeff == 0) {
204
+ rpm +=
205
+ (acceleration * 2 - 1) *
206
+ (acceleration > 0.5
207
+ ? this.carProperties.engine.maxRpmIncreasePerSecond
208
+ : this.carProperties.engine.maxRpmDecreasePerSecond) *
209
+ delta;
210
+ }
211
+ else {
212
+ const rpmFromSpeed = this.calculateRpmFromCarSpeed();
213
+ if (rpmFromSpeed > rpm) {
214
+ rpm = Math.min(rpmFromSpeed, rpm + this.carProperties.engine.maxRpmIncreasePerSecond * delta);
215
+ }
216
+ else {
217
+ rpm = Math.max(rpmFromSpeed, rpm - this.carProperties.engine.maxRpmDecreasePerSecond * delta);
218
+ }
219
+ }
220
+ this._rpm$.next(Math.max(this.carProperties.engine.minRpm, Math.min(this.carProperties.engine.maxRpm, rpm)));
221
+ }
222
+ // TODO delete and let game application do all the steps
223
+ resetTo(options = {}) {
224
+ this.raycastVehicle.resetTo(options);
225
+ this.gear = 0;
226
+ this._rpm$.next(this.carProperties.engine.minRpm);
227
+ }
228
+ }