@gg-web-engine/core 0.0.37 → 0.0.38

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.
@@ -163,15 +163,17 @@ export class GgCarEntity extends IRenderable3dEntity {
163
163
  }
164
164
  }
165
165
  if (brake === 0) {
166
- this.raycastVehicle.applyTractionForce(force);
166
+ this.raycastVehicle.applyTraction('front', force * this.carProperties.tractionBias);
167
+ this.raycastVehicle.applyTraction('rear', force * (1 - this.carProperties.tractionBias));
167
168
  this.raycastVehicle.applyBrake('both', 0);
168
169
  }
169
170
  else {
170
- this.raycastVehicle.applyTractionForce(0);
171
+ this.raycastVehicle.applyTraction('both', 0);
171
172
  this.raycastVehicle.applyBrake('front', brake * this.carProperties.brake.frontAxleForce);
172
173
  this.raycastVehicle.applyBrake('rear', brake * this.carProperties.brake.rearAxleForce);
173
174
  }
174
175
  if (this.handBrake) {
176
+ this.raycastVehicle.applyTraction('rear', 0);
175
177
  this.raycastVehicle.applyBrake('rear', this.carProperties.brake.handbrakeForce);
176
178
  }
177
179
  }
@@ -40,7 +40,7 @@ export declare type Gg3dMapGraphEntityOptions = {
40
40
  export declare class MapGraph3dEntity<VTypeDoc extends VisualTypeDocRepo3D = VisualTypeDocRepo3D, PTypeDoc extends PhysicsTypeDocRepo3D = PhysicsTypeDocRepo3D> extends IRenderable3dEntity<VTypeDoc, PTypeDoc> {
41
41
  readonly mapGraph: MapGraph;
42
42
  readonly tickOrder = TickOrder.POST_RENDERING;
43
- readonly loaderCursorEntity$: BehaviorSubject<IPositionable3d | null>;
43
+ readonly loaderCursorEntity$: BehaviorSubject<(IEntity & IPositionable3d) | null>;
44
44
  readonly loaded: Map<MapGraphNodeType, (IEntity & IPositionable3d)[]>;
45
45
  private _initialLoadComplete$;
46
46
  get initialLoadComplete$(): Observable<boolean>;
@@ -7,7 +7,7 @@ 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, NEVER, startWith, Subject, switchMap } from 'rxjs';
10
+ import { BehaviorSubject, NEVER, startWith, Subject, switchMap, takeUntil } from 'rxjs';
11
11
  import { distinctUntilChanged, map, tap, throttleTime } from 'rxjs/operators';
12
12
  import { Graph, Qtrn, TickOrder } from '../../base';
13
13
  import { IRenderable3dEntity } from './i-renderable-3d.entity';
@@ -108,11 +108,10 @@ export class MapGraph3dEntity extends IRenderable3dEntity {
108
108
  }
109
109
  onSpawned(world) {
110
110
  super.onSpawned(world);
111
- // TODO takeUntil removed from world?
112
111
  this.loaderCursorEntity$
113
- .pipe(switchMap(entity => entity
112
+ .pipe(takeUntil(this._onRemoved$), switchMap(entity => entity
114
113
  ? this.tick$.pipe(startWith(null), // map will perform initial loading even if world not started yet. Handy to preload map
115
- throttleTime(1000), map(() => entity.position))
114
+ takeUntil(entity.onRemoved$), throttleTime(1000), map(() => entity.position))
116
115
  : NEVER), map(pos => this.mapGraph.getNearestDummy(this.mapGraphNodes, pos)), tap(node => this._nearestDummy$.next(node)), distinctUntilChanged())
117
116
  .subscribe(currentChunk => {
118
117
  let haveToBeLoaded = new Set();
@@ -22,8 +22,12 @@ export declare type RVEntityAxleOptions = {
22
22
  axlePosition: number;
23
23
  axleHeight: number;
24
24
  } & RVEntitySharedWheelOptions;
25
+ export declare enum RVEntityTractionBias {
26
+ FWD = 1,
27
+ RWD = 0
28
+ }
25
29
  export declare type RVEntityProperties = {
26
- typeOfDrive: 'RWD' | 'FWD' | '4WD';
30
+ tractionBias: RVEntityTractionBias | number;
27
31
  suspension: SuspensionOptions;
28
32
  } & ({
29
33
  wheelBase: {
@@ -42,21 +46,20 @@ export declare type RVEntityProperties = {
42
46
  export declare class RaycastVehicle3dEntity<VTypeDoc extends VisualTypeDocRepo3D = VisualTypeDocRepo3D, PTypeDoc extends PhysicsTypeDocRepo3D = PhysicsTypeDocRepo3D> extends Entity3d<VTypeDoc, PTypeDoc> {
43
47
  readonly carProperties: RVEntityProperties;
44
48
  readonly chassis3D: IDisplayObject3dComponent | null;
45
- readonly chassisBody: IRaycastVehicleComponent;
49
+ readonly vehicleComponent: IRaycastVehicleComponent;
46
50
  protected readonly wheels: (Entity3d<VTypeDoc, PTypeDoc> | null)[];
47
51
  protected readonly wheelLocalRotation: (Point4 | null)[];
48
52
  protected readonly frontWheelsIndices: number[];
49
53
  protected readonly rearWheelsIndices: number[];
50
- protected readonly tractionWheelIndices: number[];
51
54
  getSpeed(): number;
52
55
  readonly tractionWheelRadius: number;
53
56
  private _steeringAngle;
54
57
  get steeringAngle(): number;
55
58
  set steeringAngle(value: number);
56
- applyTractionForce(force: number): void;
59
+ applyTraction(axle: 'front' | 'rear' | 'both', force: number): void;
57
60
  applyBrake(axle: 'front' | 'rear' | 'both', force: number): void;
58
61
  /** car mesh and physics body direction has to be pointing: y front, z up*/
59
- constructor(carProperties: RVEntityProperties, chassis3D: IDisplayObject3dComponent | null, chassisBody: IRaycastVehicleComponent);
62
+ constructor(carProperties: RVEntityProperties, chassis3D: IDisplayObject3dComponent | null, vehicleComponent: IRaycastVehicleComponent);
60
63
  protected runTransformBinding(objectBody: IRigidBody3dComponent, object3D: IDisplayObject3dComponent): void;
61
64
  get isTouchingGround(): boolean;
62
65
  resetTo(options?: {
@@ -1,5 +1,10 @@
1
1
  import { Box, Pnt3, Qtrn } from '../../base';
2
2
  import { Entity3d } from './entity-3d';
3
+ export var RVEntityTractionBias;
4
+ (function (RVEntityTractionBias) {
5
+ RVEntityTractionBias[RVEntityTractionBias["FWD"] = 1] = "FWD";
6
+ RVEntityTractionBias[RVEntityTractionBias["RWD"] = 0] = "RWD";
7
+ })(RVEntityTractionBias || (RVEntityTractionBias = {}));
3
8
  const wheeelDefaults = {
4
9
  tyreWidth: 0.3,
5
10
  tyreRadius: 0.4,
@@ -9,16 +14,15 @@ const wheeelDefaults = {
9
14
  };
10
15
  export class RaycastVehicle3dEntity extends Entity3d {
11
16
  /** car mesh and physics body direction has to be pointing: y front, z up*/
12
- constructor(carProperties, chassis3D, chassisBody) {
13
- super(chassis3D, chassisBody);
17
+ constructor(carProperties, chassis3D, vehicleComponent) {
18
+ super(chassis3D, vehicleComponent);
14
19
  this.carProperties = carProperties;
15
20
  this.chassis3D = chassis3D;
16
- this.chassisBody = chassisBody;
21
+ this.vehicleComponent = vehicleComponent;
17
22
  this.wheels = [];
18
23
  this.wheelLocalRotation = [];
19
24
  this.frontWheelsIndices = [];
20
25
  this.rearWheelsIndices = [];
21
- this.tractionWheelIndices = [];
22
26
  this._steeringAngle = 0;
23
27
  let wheelFullOptions = 'wheelBase' in carProperties
24
28
  ? [
@@ -47,15 +51,11 @@ export class RaycastVehicle3dEntity extends Entity3d {
47
51
  else {
48
52
  this.rearWheelsIndices.push(i);
49
53
  }
50
- if (wheelOpts.isFront && this.carProperties.typeOfDrive != 'RWD') {
51
- this.tractionWheelIndices.push(i);
52
- }
53
- if (!wheelOpts.isFront && this.carProperties.typeOfDrive != 'FWD') {
54
- this.tractionWheelIndices.push(i);
55
- }
56
- this.chassisBody.addWheel(wheelOpts, this.carProperties.suspension);
54
+ this.vehicleComponent.addWheel(wheelOpts, this.carProperties.suspension);
57
55
  });
58
- this.tractionWheelRadius = wheelFullOptions[this.tractionWheelIndices[0]].tyreRadius;
56
+ this.tractionWheelRadius =
57
+ wheelFullOptions[this.frontWheelsIndices[0]].tyreRadius * this.carProperties.tractionBias +
58
+ wheelFullOptions[this.rearWheelsIndices[0]].tyreRadius * (1 - this.carProperties.tractionBias);
59
59
  for (const options of wheelFullOptions) {
60
60
  const display = options.display || {};
61
61
  if (!display.displayObject) {
@@ -96,11 +96,11 @@ export class RaycastVehicle3dEntity extends Entity3d {
96
96
  this.wheels.push(new Entity3d(entity));
97
97
  }
98
98
  this.addChildren(...this.wheels.filter(x => !!x));
99
- this.chassisBody.entity = this;
99
+ this.vehicleComponent.entity = this;
100
100
  }
101
101
  // m/s
102
102
  getSpeed() {
103
- return this.chassisBody.wheelSpeed;
103
+ return this.vehicleComponent.wheelSpeed;
104
104
  }
105
105
  get steeringAngle() {
106
106
  return this._steeringAngle;
@@ -109,17 +109,22 @@ export class RaycastVehicle3dEntity extends Entity3d {
109
109
  if (this._steeringAngle != value) {
110
110
  this._steeringAngle = value;
111
111
  }
112
- this.frontWheelsIndices.forEach(index => this.chassisBody.setSteering(index, value));
112
+ this.frontWheelsIndices.forEach(index => this.vehicleComponent.setSteering(index, value));
113
113
  }
114
- applyTractionForce(force) {
115
- this.tractionWheelIndices.forEach(index => this.chassisBody.applyEngineForce(index, force));
114
+ applyTraction(axle, force) {
115
+ if (axle != 'rear') {
116
+ this.frontWheelsIndices.forEach(index => this.vehicleComponent.applyEngineForce(index, force));
117
+ }
118
+ if (axle != 'front') {
119
+ this.rearWheelsIndices.forEach(index => this.vehicleComponent.applyEngineForce(index, force));
120
+ }
116
121
  }
117
122
  applyBrake(axle, force) {
118
123
  if (axle != 'rear') {
119
- this.frontWheelsIndices.forEach(index => this.chassisBody.applyBrake(index, force));
124
+ this.frontWheelsIndices.forEach(index => this.vehicleComponent.applyBrake(index, force));
120
125
  }
121
126
  if (axle != 'front') {
122
- this.rearWheelsIndices.forEach(index => this.chassisBody.applyBrake(index, force));
127
+ this.rearWheelsIndices.forEach(index => this.vehicleComponent.applyBrake(index, force));
123
128
  }
124
129
  }
125
130
  runTransformBinding(objectBody, object3D) {
@@ -128,7 +133,7 @@ export class RaycastVehicle3dEntity extends Entity3d {
128
133
  if (!wheel) {
129
134
  continue;
130
135
  }
131
- let { position, rotation } = this.chassisBody.getWheelTransform(i);
136
+ let { position, rotation } = this.vehicleComponent.getWheelTransform(i);
132
137
  if (this.wheelLocalRotation[i]) {
133
138
  rotation = Qtrn.combineRotations(rotation, this.wheelLocalRotation[i]);
134
139
  }
@@ -138,14 +143,24 @@ export class RaycastVehicle3dEntity extends Entity3d {
138
143
  }
139
144
  get isTouchingGround() {
140
145
  // is at least one traction wheel touches the ground
141
- return this.tractionWheelIndices
142
- .map(i => this.chassisBody.isWheelTouchesGround(i))
143
- .reduce((prev, cur) => cur || prev, false);
146
+ if (this.carProperties.tractionBias != RVEntityTractionBias.RWD) {
147
+ if (this.frontWheelsIndices
148
+ .map(i => this.vehicleComponent.isWheelTouchesGround(i))
149
+ .reduce((prev, cur) => cur || prev, false)) {
150
+ return true;
151
+ }
152
+ }
153
+ if (this.carProperties.tractionBias != RVEntityTractionBias.FWD) {
154
+ if (this.rearWheelsIndices
155
+ .map(i => this.vehicleComponent.isWheelTouchesGround(i))
156
+ .reduce((prev, cur) => cur || prev, false)) {
157
+ return true;
158
+ }
159
+ }
160
+ return false;
144
161
  }
145
162
  // TODO delete and let game application do all the steps
146
163
  resetTo(options = {}) {
147
- this.chassisBody.resetMotion();
148
- this.chassisBody.resetSuspension();
149
164
  if (options.position) {
150
165
  this.position = options.position;
151
166
  }
@@ -153,5 +168,6 @@ export class RaycastVehicle3dEntity extends Entity3d {
153
168
  this.rotation = options.rotation;
154
169
  }
155
170
  this.steeringAngle = 0;
171
+ this.vehicleComponent.resetMotion();
156
172
  }
157
173
  }
@@ -1,5 +1,5 @@
1
1
  import { GgWorld, PhysicsTypeDocRepo, VisualTypeDocRepo } from '../gg-world';
2
- import { Subject } from 'rxjs';
2
+ import { Observable, Subject } from 'rxjs';
3
3
  import { IWorldComponent } from '../components/i-world-component';
4
4
  /**
5
5
  * Engine's default tick orders: the less value, the earlier tick will be run.
@@ -48,6 +48,8 @@ export declare abstract class IEntity<D = any, R = any, VTypeDoc extends VisualT
48
48
  removeComponents(components: IWorldComponent<D, R, VTypeDoc, PTypeDoc>[], dispose?: boolean): void;
49
49
  protected _onSpawned$: Subject<void>;
50
50
  protected _onRemoved$: Subject<void>;
51
+ get onSpawned$(): Observable<void>;
52
+ get onRemoved$(): Observable<void>;
51
53
  onSpawned(world: GgWorld<D, R, VTypeDoc, PTypeDoc>): void;
52
54
  onRemoved(): void;
53
55
  dispose(): void;
@@ -100,6 +100,12 @@ export class IEntity {
100
100
  }
101
101
  }
102
102
  }
103
+ get onSpawned$() {
104
+ return this._onSpawned$.asObservable();
105
+ }
106
+ get onRemoved$() {
107
+ return this._onRemoved$.asObservable();
108
+ }
103
109
  onSpawned(world) {
104
110
  this._world = world;
105
111
  for (const c of this._components) {
@@ -12,12 +12,20 @@ export declare class Pnt2 {
12
12
  static get nY(): Point2;
13
13
  /** clone point */
14
14
  static clone(p: Point2): Point2;
15
+ /** spread point components */
16
+ static spr(p: Point2): [number, number];
17
+ /** get negation of the point */
18
+ static neg(p: Point2): Point2;
15
19
  /** add point b to point a */
16
20
  static add(a: Point2, b: Point2): Point2;
17
21
  /** subtract point b from point a */
18
22
  static sub(a: Point2, b: Point2): Point2;
23
+ /** scale point b by point. The result is the point, where each component is a product of appropriate components of input points */
24
+ static scale(a: Point2, s: Point2): Point2;
19
25
  /** average point between a and b */
20
26
  static avg(a: Point2, b: Point2): Point2;
27
+ /** round point components */
28
+ static round(p: Point2): Point2;
21
29
  /** calculate vector length (squared) */
22
30
  static lenSq(v: Point2): number;
23
31
  /** calculate vector length */
@@ -32,4 +40,8 @@ export declare class Pnt2 {
32
40
  static lerp(a: Point2, b: Point2, t: number): Point2;
33
41
  /** angle between vectors in radians */
34
42
  static angle(a: Point2, b: Point2): number;
43
+ /** rotate point around zero by provided angle */
44
+ static rot(p: Point2, angle: number): Point2;
45
+ /** rotate point around pivot by provided angle */
46
+ static rotAround(p: Point2, pivot: Point2, angle: number): Point2;
35
47
  }
@@ -23,6 +23,14 @@ export class Pnt2 {
23
23
  static clone(p) {
24
24
  return { x: p.x, y: p.y };
25
25
  }
26
+ /** spread point components */
27
+ static spr(p) {
28
+ return [p.x, p.y];
29
+ }
30
+ /** get negation of the point */
31
+ static neg(p) {
32
+ return { x: -p.x, y: -p.y };
33
+ }
26
34
  /** add point b to point a */
27
35
  static add(a, b) {
28
36
  return { x: a.x + b.x, y: a.y + b.y };
@@ -31,10 +39,18 @@ export class Pnt2 {
31
39
  static sub(a, b) {
32
40
  return { x: a.x - b.x, y: a.y - b.y };
33
41
  }
42
+ /** scale point b by point. The result is the point, where each component is a product of appropriate components of input points */
43
+ static scale(a, s) {
44
+ return { x: a.x * s.x, y: a.y * s.y };
45
+ }
34
46
  /** average point between a and b */
35
47
  static avg(a, b) {
36
48
  return { x: (a.x + b.x) / 2, y: (a.y + b.y) / 2 };
37
49
  }
50
+ /** round point components */
51
+ static round(p) {
52
+ return { x: Math.round(p.x), y: Math.round(p.y) };
53
+ }
38
54
  /** calculate vector length (squared) */
39
55
  static lenSq(v) {
40
56
  return v.x * v.x + v.y * v.y;
@@ -75,4 +91,23 @@ export class Pnt2 {
75
91
  const magnitudeProduct = Math.sqrt(a.x ** 2 + a.y ** 2) * Math.sqrt(b.x ** 2 + b.y ** 2);
76
92
  return Math.acos(dotProduct / magnitudeProduct);
77
93
  }
94
+ /** rotate point around zero by provided angle */
95
+ static rot(p, angle) {
96
+ const cos = Math.cos(angle);
97
+ const sin = Math.sin(angle);
98
+ return {
99
+ x: p.x * cos - p.y * sin,
100
+ y: p.x * sin + p.y * cos,
101
+ };
102
+ }
103
+ /** rotate point around pivot by provided angle */
104
+ static rotAround(p, pivot, angle) {
105
+ const cos = Math.cos(angle);
106
+ const sin = Math.sin(angle);
107
+ const p0 = { x: p.x - pivot.x, y: p.y - pivot.y };
108
+ return {
109
+ x: p0.x * cos - p0.y * sin + pivot.x,
110
+ y: p0.x * sin + p0.y * cos + pivot.y,
111
+ };
112
+ }
78
113
  }
@@ -16,12 +16,20 @@ export declare class Pnt3 {
16
16
  static get nZ(): Point3;
17
17
  /** clone point */
18
18
  static clone(p: Point3): Point3;
19
+ /** spread point components */
20
+ static spr(p: Point3): [number, number, number];
21
+ /** get negation of the point */
22
+ static neg(p: Point3): Point3;
19
23
  /** add point b to point a */
20
24
  static add(a: Point3, b: Point3): Point3;
21
25
  /** subtract point b from point a */
22
26
  static sub(a: Point3, b: Point3): Point3;
27
+ /** scale point b by point. The result is the point, where each component is a product of appropriate components of input points */
28
+ static scale(a: Point3, s: Point3): Point3;
23
29
  /** average point between a and b */
24
30
  static avg(a: Point3, b: Point3): Point3;
31
+ /** round point components */
32
+ static round(p: Point3): Point3;
25
33
  /** calculate vector length (squared) */
26
34
  static lenSq(v: Point3): number;
27
35
  /** calculate vector length */
@@ -32,6 +32,14 @@ export class Pnt3 {
32
32
  static clone(p) {
33
33
  return { x: p.x, y: p.y, z: p.z };
34
34
  }
35
+ /** spread point components */
36
+ static spr(p) {
37
+ return [p.x, p.y, p.z];
38
+ }
39
+ /** get negation of the point */
40
+ static neg(p) {
41
+ return { x: -p.x, y: -p.y, z: -p.z };
42
+ }
35
43
  /** add point b to point a */
36
44
  static add(a, b) {
37
45
  return { x: a.x + b.x, y: a.y + b.y, z: a.z + b.z };
@@ -40,10 +48,18 @@ export class Pnt3 {
40
48
  static sub(a, b) {
41
49
  return { x: a.x - b.x, y: a.y - b.y, z: a.z - b.z };
42
50
  }
51
+ /** scale point b by point. The result is the point, where each component is a product of appropriate components of input points */
52
+ static scale(a, s) {
53
+ return { x: a.x * s.x, y: a.y * s.y, z: a.z * s.z };
54
+ }
43
55
  /** average point between a and b */
44
56
  static avg(a, b) {
45
57
  return { x: (a.x + b.x) / 2, y: (a.y + b.y) / 2, z: (a.z + b.z) / 2 };
46
58
  }
59
+ /** round point components */
60
+ static round(p) {
61
+ return { x: Math.round(p.x), y: Math.round(p.y), z: Math.round(p.z) };
62
+ }
47
63
  /** calculate vector length (squared) */
48
64
  static lenSq(v) {
49
65
  return v.x * v.x + v.y * v.y + v.z * v.z;
@@ -18,6 +18,8 @@ export declare class Qtrn {
18
18
  * @returns A new Point4 instance with the same values as the given Point4 object.
19
19
  */
20
20
  static clone(q: Point4): Point4;
21
+ /** spread quaternion components */
22
+ static spr(p: Point4): [number, number, number, number];
21
23
  /**
22
24
  * Returns the sum of two Point4 objects.
23
25
  * @param a The first Point4 object to add.
@@ -23,6 +23,10 @@ export class Qtrn {
23
23
  static clone(q) {
24
24
  return { x: q.x, y: q.y, z: q.z, w: q.w };
25
25
  }
26
+ /** spread quaternion components */
27
+ static spr(p) {
28
+ return [p.x, p.y, p.z, p.w];
29
+ }
26
30
  /**
27
31
  * Returns the sum of two Point4 objects.
28
32
  * @param a The first Point4 object to add.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@gg-web-engine/core",
3
- "version": "0.0.37",
3
+ "version": "0.0.38",
4
4
  "description": "An attempt to create open source game engine for browser",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
@@ -27,11 +27,11 @@
27
27
  },
28
28
  "homepage": "https://github.com/AndyGura/gg-web-engine#readme",
29
29
  "dependencies": {
30
- "rxjs": "7.8.0",
30
+ "rxjs": "7.8.1",
31
31
  "stats.js": "0.17.0"
32
32
  },
33
33
  "devDependencies": {
34
- "@types/jest": "^29.5.0",
34
+ "@types/jest": "^29.5.12",
35
35
  "@types/stats.js": "^0.17.0",
36
36
  "jest": "^29.5.0",
37
37
  "jest-environment-jsdom": "^29.5.0",