@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.
- package/dist/2d/components/physics/i-rigid-body-2d.component.d.ts +1 -0
- package/dist/2d/gg-2d-world.d.ts +1 -2
- package/dist/2d/gg-2d-world.js +4 -5
- package/dist/3d/components/physics/i-raycast-vehicle.component.d.ts +3 -6
- package/dist/3d/components/physics/i-rigid-body-3d.component.d.ts +1 -0
- package/dist/3d/entities/controllers/input/car-keyboard-handling.controller.d.ts +11 -13
- package/dist/3d/entities/controllers/input/car-keyboard-handling.controller.js +29 -72
- package/dist/3d/entities/controllers/input/gg-car-keyboard-handling.controller.d.ts +18 -0
- package/dist/3d/entities/controllers/input/gg-car-keyboard-handling.controller.js +96 -0
- package/dist/3d/entities/gg-car/gg-car.entity.d.ts +79 -0
- package/dist/3d/entities/gg-car/gg-car.entity.js +228 -0
- package/dist/3d/entities/raycast-vehicle-3d.entity.d.ts +43 -76
- package/dist/3d/entities/raycast-vehicle-3d.entity.js +78 -299
- package/dist/3d/gg-3d-world.d.ts +1 -2
- package/dist/3d/gg-3d-world.js +4 -5
- package/dist/3d/index.d.ts +2 -0
- package/dist/3d/index.js +2 -0
- package/dist/base/components/physics/i-body.component.d.ts +14 -0
- package/dist/base/components/physics/i-body.component.js +1 -0
- package/dist/base/components/physics/i-rigid-body.component.d.ts +4 -11
- package/dist/base/components/physics/i-trigger.component.d.ts +3 -1
- package/dist/base/entities/i-entity.js +1 -1
- package/dist/base/gg-world.d.ts +8 -12
- package/dist/base/gg-world.js +32 -64
- package/dist/base/index.d.ts +2 -3
- package/dist/base/index.js +2 -3
- package/dist/base/math/point2.d.ts +4 -0
- package/dist/base/math/point2.js +8 -0
- package/dist/base/math/point3.d.ts +6 -0
- package/dist/base/math/point3.js +12 -0
- package/dist/base/math/splines.d.ts +1 -0
- package/dist/base/math/splines.js +7 -0
- package/dist/{base/ui → dev}/gg-console.ui.d.ts +1 -4
- package/dist/{base/ui → dev}/gg-console.ui.js +35 -17
- package/dist/dev/gg-debugger.ui.d.ts +11 -0
- package/dist/dev/gg-debugger.ui.js +111 -0
- package/dist/dev/gg-static.d.ts +32 -0
- package/dist/dev/gg-static.js +141 -0
- package/dist/dev/index.d.ts +3 -0
- package/dist/dev/index.js +3 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +1 -0
- package/package.json +1 -1
- package/dist/base/gg-static.d.ts +0 -9
- package/dist/base/gg-static.js +0 -36
- package/dist/base/ui/gg-debugger.ui.d.ts +0 -10
- 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
|
}
|
package/dist/2d/gg-2d-world.d.ts
CHANGED
|
@@ -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
|
-
|
|
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
|
}
|
package/dist/2d/gg-2d-world.js
CHANGED
|
@@ -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
|
|
15
|
-
super(visualScene, physicsWorld
|
|
14
|
+
constructor(visualScene, physicsWorld) {
|
|
15
|
+
super(visualScene, physicsWorld);
|
|
16
16
|
this.visualScene = visualScene;
|
|
17
17
|
this.physicsWorld = physicsWorld;
|
|
18
|
-
|
|
19
|
-
|
|
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 {
|
|
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 {
|
|
2
|
-
import { DirectionKeyboardInput, DirectionKeyboardKeymap,
|
|
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
|
-
|
|
7
|
-
|
|
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
|
-
|
|
16
|
-
|
|
17
|
-
|
|
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 {
|
|
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,
|
|
13
|
+
constructor(keyboard, options = {
|
|
19
14
|
keymap: 'arrows',
|
|
20
|
-
|
|
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
|
-
|
|
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
|
|
46
|
-
this.
|
|
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(
|
|
71
|
-
|
|
72
|
-
|
|
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.
|
|
76
|
-
|
|
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
|
+
}
|