|
@@ -0,0 +1,536 @@
|
|
|
+import * as THREE from 'https://cdn.jsdelivr.net/npm/[email protected]/build/three.module.js';
|
|
|
+import {game} from './game.js';
|
|
|
+import {graphics} from './graphics.js';
|
|
|
+import {math} from './math.js';
|
|
|
+import {visibility} from './visibility.js';
|
|
|
+import {particles} from './particles.js';
|
|
|
+import {blaster} from './blaster.js';
|
|
|
+import {OBJLoader} from 'https://cdn.jsdelivr.net/npm/[email protected]/examples/jsm/loaders/OBJLoader.js';
|
|
|
+
|
|
|
+let _APP = null;
|
|
|
+
|
|
|
+const _NUM_BOIDS = 300;
|
|
|
+const _BOID_SPEED = 25;
|
|
|
+const _BOID_ACCELERATION = _BOID_SPEED / 2.5;
|
|
|
+const _BOID_FORCE_MAX = _BOID_ACCELERATION / 20.0;
|
|
|
+const _BOID_FORCE_ORIGIN = 50;
|
|
|
+const _BOID_FORCE_ALIGNMENT = 10;
|
|
|
+const _BOID_FORCE_SEPARATION = 20;
|
|
|
+const _BOID_FORCE_COLLISION = 50;
|
|
|
+const _BOID_FORCE_COHESION = 5;
|
|
|
+const _BOID_FORCE_WANDER = 3;
|
|
|
+
|
|
|
+
|
|
|
+class LineRenderer {
|
|
|
+ constructor(game) {
|
|
|
+ this._game = game;
|
|
|
+
|
|
|
+ this._materials = {};
|
|
|
+ this._group = new THREE.Group();
|
|
|
+
|
|
|
+ this._game._graphics.Scene.add(this._group);
|
|
|
+ }
|
|
|
+
|
|
|
+ Reset() {
|
|
|
+ this._lines = [];
|
|
|
+ this._group.remove(...this._group.children);
|
|
|
+ }
|
|
|
+
|
|
|
+ Add(pt1, pt2, hexColour) {
|
|
|
+ const geometry = new THREE.Geometry();
|
|
|
+ geometry.vertices.push(pt1);
|
|
|
+ geometry.vertices.push(pt2);
|
|
|
+
|
|
|
+ let material = this._materials[hexColour];
|
|
|
+ if (!material) {
|
|
|
+ this._materials[hexColour] = new THREE.LineBasicMaterial(
|
|
|
+ {color: hexColour});
|
|
|
+ material = this._materials[hexColour];
|
|
|
+ }
|
|
|
+
|
|
|
+ const line = new THREE.Line(geometry, material);
|
|
|
+ this._lines.push(line);
|
|
|
+ this._group.add(line);
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+class ExplodeParticles {
|
|
|
+ constructor(game) {
|
|
|
+ this._particleSystem = new particles.ParticleSystem(
|
|
|
+ game, {texture: "./resources/blaster.jpg"});
|
|
|
+ this._particles = [];
|
|
|
+ }
|
|
|
+
|
|
|
+ Splode(origin) {
|
|
|
+ for (let i = 0; i < 128; i++) {
|
|
|
+ const p = this._particleSystem.CreateParticle();
|
|
|
+ p.Position.copy(origin);
|
|
|
+ p.Velocity = new THREE.Vector3(
|
|
|
+ math.rand_range(-1, 1),
|
|
|
+ math.rand_range(-1, 1),
|
|
|
+ math.rand_range(-1, 1)
|
|
|
+ );
|
|
|
+ p.Velocity.normalize();
|
|
|
+ p.Velocity.multiplyScalar(125);
|
|
|
+ p.TotalLife = 2.0;
|
|
|
+ p.Life = p.TotalLife;
|
|
|
+ p.Colours = [new THREE.Color(0xFF8000), new THREE.Color(0x800000)];
|
|
|
+ p.Sizes = [3, 12];
|
|
|
+ p.Size = p.Sizes[0];
|
|
|
+ this._particles.push(p);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ Update(timeInSeconds) {
|
|
|
+ this._particles = this._particles.filter(p => {
|
|
|
+ return p.Alive;
|
|
|
+ });
|
|
|
+ for (const p of this._particles) {
|
|
|
+ p.Life -= timeInSeconds;
|
|
|
+ if (p.Life <= 0) {
|
|
|
+ p.Alive = false;
|
|
|
+ }
|
|
|
+ p.Position.add(p.Velocity.clone().multiplyScalar(timeInSeconds));
|
|
|
+ p.Velocity.multiplyScalar(0.75);
|
|
|
+ p.Size = math.lerp(p.Life / p.TotalLife, p.Sizes[0], p.Sizes[1]);
|
|
|
+ p.Colour.copy(p.Colours[0]);
|
|
|
+ p.Colour.lerp(p.Colours[1], 1.0 - p.Life / p.TotalLife);
|
|
|
+ }
|
|
|
+ this._particleSystem.Update();
|
|
|
+ }
|
|
|
+};
|
|
|
+
|
|
|
+
|
|
|
+class Boid {
|
|
|
+ constructor(game, params) {
|
|
|
+ this._mesh = new THREE.Mesh(
|
|
|
+ params.geometry,
|
|
|
+ new THREE.MeshStandardMaterial({color: 0x808080}));
|
|
|
+ this._mesh.castShadow = true;
|
|
|
+ this._mesh.receiveShadow = false;
|
|
|
+
|
|
|
+ this._group = new THREE.Group();
|
|
|
+ this._group.add(this._mesh);
|
|
|
+ this._group.position.set(
|
|
|
+ math.rand_range(-250, 250),
|
|
|
+ math.rand_range(-250, 250),
|
|
|
+ math.rand_range(-250, 250));
|
|
|
+ this._direction = new THREE.Vector3(
|
|
|
+ math.rand_range(-1, 1),
|
|
|
+ math.rand_range(-1, 1),
|
|
|
+ math.rand_range(-1, 1));
|
|
|
+ this._velocity = this._direction.clone();
|
|
|
+
|
|
|
+ const speedMultiplier = math.rand_range(params.speedMin, params.speedMax);
|
|
|
+ this._maxSteeringForce = params.maxSteeringForce * speedMultiplier;
|
|
|
+ this._maxSpeed = params.speed * speedMultiplier;
|
|
|
+ this._acceleration = params.acceleration * speedMultiplier;
|
|
|
+
|
|
|
+ const scale = 1.0 / speedMultiplier;
|
|
|
+ this._radius = scale;
|
|
|
+ this._mesh.scale.setScalar(scale * params.scale);
|
|
|
+ //this._mesh.rotateX(Math.PI / 2);
|
|
|
+
|
|
|
+ this._game = game;
|
|
|
+ game._graphics.Scene.add(this._group);
|
|
|
+ this._visibilityIndex = game._visibilityGrid.UpdateItem(
|
|
|
+ this._mesh.uuid, this);
|
|
|
+
|
|
|
+ this._wanderAngle = 0;
|
|
|
+ this._seekGoal = params.seekGoal;
|
|
|
+ this._fireCooldown = 0.0;
|
|
|
+ this._params = params;
|
|
|
+ }
|
|
|
+
|
|
|
+ DisplayDebug() {
|
|
|
+ const geometry = new THREE.SphereGeometry(10, 64, 64);
|
|
|
+ const material = new THREE.MeshBasicMaterial({
|
|
|
+ color: 0xFF0000,
|
|
|
+ transparent: true,
|
|
|
+ opacity: 0.25,
|
|
|
+ });
|
|
|
+ const mesh = new THREE.Mesh(geometry, material);
|
|
|
+ this._group.add(mesh);
|
|
|
+
|
|
|
+ this._mesh.material.color.setHex(0xFF0000);
|
|
|
+ this._displayDebug = true;
|
|
|
+ this._lineRenderer = new LineRenderer(this._game);
|
|
|
+ }
|
|
|
+
|
|
|
+ _UpdateDebug(local) {
|
|
|
+ this._lineRenderer.Reset();
|
|
|
+ this._lineRenderer.Add(
|
|
|
+ this.Position, this.Position.clone().add(this._velocity),
|
|
|
+ 0xFFFFFF);
|
|
|
+ for (const e of local) {
|
|
|
+ this._lineRenderer.Add(this.Position, e.Position, 0x00FF00);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ get Position() {
|
|
|
+ return this._group.position;
|
|
|
+ }
|
|
|
+
|
|
|
+ get Velocity() {
|
|
|
+ return this._velocity;
|
|
|
+ }
|
|
|
+
|
|
|
+ get Direction() {
|
|
|
+ return this._direction;
|
|
|
+ }
|
|
|
+
|
|
|
+ get Radius() {
|
|
|
+ return this._radius;
|
|
|
+ }
|
|
|
+
|
|
|
+ Step(timeInSeconds) {
|
|
|
+ const local = this._game._visibilityGrid.GetLocalEntities(
|
|
|
+ this.Position, 15);
|
|
|
+
|
|
|
+ this._ApplySteering(timeInSeconds, local);
|
|
|
+
|
|
|
+ const frameVelocity = this._velocity.clone();
|
|
|
+ frameVelocity.multiplyScalar(timeInSeconds);
|
|
|
+ this._group.position.add(frameVelocity);
|
|
|
+
|
|
|
+ this._group.quaternion.setFromUnitVectors(
|
|
|
+ new THREE.Vector3(0, 1, 0), this.Direction);
|
|
|
+
|
|
|
+ this._visibilityIndex = this._game._visibilityGrid.UpdateItem(
|
|
|
+ this._mesh.uuid, this, this._visibilityIndex);
|
|
|
+
|
|
|
+ if (this._displayDebug) {
|
|
|
+ this._UpdateDebug(local);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ _ApplySteering(timeInSeconds, local) {
|
|
|
+ const separationVelocity = this._ApplySeparation(local);
|
|
|
+
|
|
|
+ // Only apply alignment and cohesion to allies
|
|
|
+ const allies = local.filter((e) => {
|
|
|
+ return this._seekGoal.equals(e._seekGoal);
|
|
|
+ });
|
|
|
+
|
|
|
+ const enemies = local.filter((e) => {
|
|
|
+ return !this._seekGoal.equals(e._seekGoal);
|
|
|
+ });
|
|
|
+
|
|
|
+ this._fireCooldown -= timeInSeconds;
|
|
|
+ if (enemies.length > 0 && this._fireCooldown <= 0) {
|
|
|
+ const p = this._game._blasters.CreateParticle();
|
|
|
+ p.Start = this.Position.clone();
|
|
|
+ p.End = this.Position.clone();
|
|
|
+ p.Velocity = this.Direction.clone().multiplyScalar(300);
|
|
|
+ p.Length = 50;
|
|
|
+ p.Colours = [
|
|
|
+ this._params.colour.clone(), new THREE.Color(0.0, 0.0, 0.0)];
|
|
|
+ p.Life = 2.0;
|
|
|
+ p.TotalLife = 2.0;
|
|
|
+ p.Width = 0.25;
|
|
|
+
|
|
|
+ if (Math.random() < 0.025) {
|
|
|
+ this._game._explosionSystem.Splode(enemies[0].Position);
|
|
|
+ }
|
|
|
+ this._fireCooldown = 0.25;
|
|
|
+ }
|
|
|
+
|
|
|
+ const alignmentVelocity = this._ApplyAlignment(allies);
|
|
|
+ const cohesionVelocity = this._ApplyCohesion(allies);
|
|
|
+ const originVelocity = this._ApplySeek(this._seekGoal);
|
|
|
+ const wanderVelocity = this._ApplyWander();
|
|
|
+ const collisionVelocity = this._ApplyCollisionAvoidance();
|
|
|
+
|
|
|
+ const steeringForce = new THREE.Vector3(0, 0, 0);
|
|
|
+ steeringForce.add(separationVelocity);
|
|
|
+ steeringForce.add(alignmentVelocity);
|
|
|
+ steeringForce.add(cohesionVelocity);
|
|
|
+ steeringForce.add(originVelocity);
|
|
|
+ steeringForce.add(wanderVelocity);
|
|
|
+ steeringForce.add(collisionVelocity);
|
|
|
+
|
|
|
+ steeringForce.multiplyScalar(this._acceleration * timeInSeconds);
|
|
|
+
|
|
|
+ // Clamp the force applied
|
|
|
+ if (steeringForce.length() > this._maxSteeringForce) {
|
|
|
+ steeringForce.normalize();
|
|
|
+ steeringForce.multiplyScalar(this._maxSteeringForce);
|
|
|
+ }
|
|
|
+
|
|
|
+ this._velocity.add(steeringForce);
|
|
|
+
|
|
|
+ // Clamp velocity
|
|
|
+ if (this._velocity.length() > this._maxSpeed) {
|
|
|
+ this._velocity.normalize();
|
|
|
+ this._velocity.multiplyScalar(this._maxSpeed);
|
|
|
+ }
|
|
|
+
|
|
|
+ this._direction = this._velocity.clone();
|
|
|
+ this._direction.normalize();
|
|
|
+ }
|
|
|
+
|
|
|
+ _ApplyCollisionAvoidance() {
|
|
|
+ const colliders = this._game._visibilityGrid.GetGlobalItems();
|
|
|
+
|
|
|
+ const ray = new THREE.Ray(this.Position, this.Direction);
|
|
|
+ const force = new THREE.Vector3(0, 0, 0);
|
|
|
+
|
|
|
+ for (const c of colliders) {
|
|
|
+ if (c.Position.distanceTo(this.Position) > c.QuickRadius) {
|
|
|
+ continue;
|
|
|
+ }
|
|
|
+
|
|
|
+ const result = ray.intersectBox(c.AABB, new THREE.Vector3());
|
|
|
+ if (result) {
|
|
|
+ const distanceToCollision = result.distanceTo(this.Position);
|
|
|
+ if (distanceToCollision < 2) {
|
|
|
+ let a = 0;
|
|
|
+ }
|
|
|
+ const dirToCenter = c.Position.clone().sub(this.Position).normalize();
|
|
|
+ const dirToCollision = result.clone().sub(this.Position).normalize();
|
|
|
+ const steeringDirection = dirToCollision.sub(dirToCenter).normalize();
|
|
|
+ steeringDirection.multiplyScalar(_BOID_FORCE_COLLISION);
|
|
|
+ force.add(steeringDirection);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ return force;
|
|
|
+ }
|
|
|
+
|
|
|
+ _ApplyWander() {
|
|
|
+ this._wanderAngle += 0.1 * math.rand_range(-2 * Math.PI, 2 * Math.PI);
|
|
|
+ const randomPointOnCircle = new THREE.Vector3(
|
|
|
+ Math.cos(this._wanderAngle),
|
|
|
+ 0,
|
|
|
+ Math.sin(this._wanderAngle));
|
|
|
+ const pointAhead = this._direction.clone();
|
|
|
+ pointAhead.multiplyScalar(5);
|
|
|
+ pointAhead.add(randomPointOnCircle);
|
|
|
+ pointAhead.normalize();
|
|
|
+ return pointAhead.multiplyScalar(_BOID_FORCE_WANDER);
|
|
|
+ }
|
|
|
+
|
|
|
+ _ApplySeparation(local) {
|
|
|
+ if (local.length == 0) {
|
|
|
+ return new THREE.Vector3(0, 0, 0);
|
|
|
+ }
|
|
|
+
|
|
|
+ const forceVector = new THREE.Vector3(0, 0, 0);
|
|
|
+ for (let e of local) {
|
|
|
+ const distanceToEntity = Math.max(
|
|
|
+ e.Position.distanceTo(this.Position) - 1.5 * (this.Radius + e.Radius),
|
|
|
+ 0.001);
|
|
|
+ const directionFromEntity = new THREE.Vector3().subVectors(
|
|
|
+ this.Position, e.Position);
|
|
|
+ const multiplier = (_BOID_FORCE_SEPARATION / distanceToEntity);
|
|
|
+ directionFromEntity.normalize();
|
|
|
+ forceVector.add(
|
|
|
+ directionFromEntity.multiplyScalar(multiplier));
|
|
|
+ }
|
|
|
+ return forceVector;
|
|
|
+ }
|
|
|
+
|
|
|
+ _ApplyAlignment(local) {
|
|
|
+ const forceVector = new THREE.Vector3(0, 0, 0);
|
|
|
+
|
|
|
+ for (let e of local) {
|
|
|
+ const entityDirection = e.Direction;
|
|
|
+ forceVector.add(entityDirection);
|
|
|
+ }
|
|
|
+
|
|
|
+ forceVector.normalize();
|
|
|
+ forceVector.multiplyScalar(_BOID_FORCE_ALIGNMENT);
|
|
|
+
|
|
|
+ return forceVector;
|
|
|
+ }
|
|
|
+
|
|
|
+ _ApplyCohesion(local) {
|
|
|
+ const forceVector = new THREE.Vector3(0, 0, 0);
|
|
|
+
|
|
|
+ if (local.length == 0) {
|
|
|
+ return forceVector;
|
|
|
+ }
|
|
|
+
|
|
|
+ const averagePosition = new THREE.Vector3(0, 0, 0);
|
|
|
+ for (let e of local) {
|
|
|
+ averagePosition.add(e.Position);
|
|
|
+ }
|
|
|
+
|
|
|
+ averagePosition.multiplyScalar(1.0 / local.length);
|
|
|
+
|
|
|
+ const directionToAveragePosition = averagePosition.clone().sub(
|
|
|
+ this.Position);
|
|
|
+ directionToAveragePosition.normalize();
|
|
|
+ directionToAveragePosition.multiplyScalar(_BOID_FORCE_COHESION);
|
|
|
+
|
|
|
+ // HACK: Floating point error from accumulation of positions.
|
|
|
+ directionToAveragePosition.y = 0;
|
|
|
+
|
|
|
+ return directionToAveragePosition;
|
|
|
+ }
|
|
|
+
|
|
|
+ _ApplySeek(destination) {
|
|
|
+ const distance = Math.max(0,((
|
|
|
+ this.Position.distanceTo(destination) - 50) / 500)) ** 2;
|
|
|
+ const direction = destination.clone().sub(this.Position);
|
|
|
+ direction.normalize();
|
|
|
+
|
|
|
+ const forceVector = direction.multiplyScalar(
|
|
|
+ _BOID_FORCE_ORIGIN * distance);
|
|
|
+ return forceVector;
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+class OpenWorldDemo extends game.Game {
|
|
|
+ constructor() {
|
|
|
+ super();
|
|
|
+ }
|
|
|
+
|
|
|
+ _OnInitialize() {
|
|
|
+ this._entities = [];
|
|
|
+
|
|
|
+ this._bloomPass = this._graphics.AddPostFX(
|
|
|
+ graphics.PostFX.UnrealBloomPass,
|
|
|
+ {
|
|
|
+ threshold: 0.75,
|
|
|
+ strength: 2.5,
|
|
|
+ radius: 0,
|
|
|
+ resolution: {
|
|
|
+ x: 1024,
|
|
|
+ y: 1024,
|
|
|
+ }
|
|
|
+ });
|
|
|
+
|
|
|
+ this._glitchPass = this._graphics.AddPostFX(
|
|
|
+ graphics.PostFX.GlitchPass, {});
|
|
|
+ this._glitchCooldown = 15;
|
|
|
+
|
|
|
+ this._glitchPass.enabled = false;
|
|
|
+
|
|
|
+ this._LoadBackground();
|
|
|
+
|
|
|
+ const geometries = {};
|
|
|
+ const loader = new OBJLoader();
|
|
|
+ loader.load("./resources/fighter.obj", (result) => {
|
|
|
+ geometries.fighter = result.children[0].geometry;
|
|
|
+ loader.load("./resources/cruiser.obj", (result) => {
|
|
|
+ geometries.cruiser = result.children[0].geometry;
|
|
|
+ this._CreateBoids(geometries);
|
|
|
+ });
|
|
|
+ });
|
|
|
+ this._CreateEntities();
|
|
|
+ }
|
|
|
+
|
|
|
+ _LoadBackground() {
|
|
|
+ const loader = new THREE.CubeTextureLoader();
|
|
|
+ const texture = loader.load([
|
|
|
+ './resources/space-posx.jpg',
|
|
|
+ './resources/space-negx.jpg',
|
|
|
+ './resources/space-posy.jpg',
|
|
|
+ './resources/space-negy.jpg',
|
|
|
+ './resources/space-posz.jpg',
|
|
|
+ './resources/space-negz.jpg',
|
|
|
+ ]);
|
|
|
+ this._graphics._scene.background = texture;
|
|
|
+ }
|
|
|
+
|
|
|
+ _CreateEntities() {
|
|
|
+ // This is 2D but eh, whatever.
|
|
|
+ this._visibilityGrid = new visibility.VisibilityGrid(
|
|
|
+ [new THREE.Vector3(-500, 0, -500), new THREE.Vector3(500, 0, 500)],
|
|
|
+ [100, 100]);
|
|
|
+
|
|
|
+ this._explosionSystem = new ExplodeParticles(this);
|
|
|
+
|
|
|
+ this._blasters = new blaster.BlasterSystem(
|
|
|
+ this, {texture: "./resources/blaster.jpg"});
|
|
|
+ }
|
|
|
+
|
|
|
+ _CreateBoids(geometries) {
|
|
|
+ const positions = [
|
|
|
+ new THREE.Vector3(-200, 50, -100),
|
|
|
+ new THREE.Vector3(0, 0, 0)];
|
|
|
+ const colours = [
|
|
|
+ new THREE.Color(0.5, 0.5, 4.0),
|
|
|
+ new THREE.Color(4.0, 0.5, 0.5)
|
|
|
+ ];
|
|
|
+ for (let i = 0; i < 2; i++) {
|
|
|
+ const p = positions[i];
|
|
|
+ const cruiser = new THREE.Mesh(
|
|
|
+ geometries.cruiser,
|
|
|
+ new THREE.MeshStandardMaterial({
|
|
|
+ color: 0x404040
|
|
|
+ }));
|
|
|
+ cruiser.position.set(p.x, p.y, p.z);
|
|
|
+ cruiser.castShadow = true;
|
|
|
+ cruiser.receiveShadow = true;
|
|
|
+ cruiser.rotation.x = Math.PI / 2;
|
|
|
+ cruiser.scale.setScalar(10, 10, 10);
|
|
|
+ cruiser.updateWorldMatrix();
|
|
|
+ this._graphics.Scene.add(cruiser);
|
|
|
+
|
|
|
+ cruiser.geometry.computeBoundingBox();
|
|
|
+ const b = cruiser.geometry.boundingBox.clone().applyMatrix4(
|
|
|
+ cruiser.matrixWorld);
|
|
|
+
|
|
|
+ this._visibilityGrid.AddGlobalItem({
|
|
|
+ Position: p,
|
|
|
+ AABB: b,
|
|
|
+ QuickRadius: 200,
|
|
|
+ Velocity: new THREE.Vector3(0, 0, 0),
|
|
|
+ Direction: new THREE.Vector3(0, 1, 0),
|
|
|
+ });
|
|
|
+
|
|
|
+ let params = {
|
|
|
+ geometry: geometries.fighter,
|
|
|
+ speedMin: 1.0,
|
|
|
+ speedMax: 1.0,
|
|
|
+ speed: _BOID_SPEED,
|
|
|
+ maxSteeringForce: _BOID_FORCE_MAX,
|
|
|
+ acceleration: _BOID_ACCELERATION,
|
|
|
+ scale: 0.4,
|
|
|
+ seekGoal: p,
|
|
|
+ colour: colours[i]
|
|
|
+ };
|
|
|
+ for (let i = 0; i < _NUM_BOIDS; i++) {
|
|
|
+ const e = new Boid(this, params);
|
|
|
+ this._entities.push(e);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ //this._entities[0].DisplayDebug();
|
|
|
+ }
|
|
|
+
|
|
|
+ _OnStep(timeInSeconds) {
|
|
|
+ timeInSeconds = Math.min(timeInSeconds, 1 / 10.0);
|
|
|
+
|
|
|
+ this._blasters.Update(timeInSeconds);
|
|
|
+ this._explosionSystem.Update(timeInSeconds);
|
|
|
+
|
|
|
+ this._glitchCooldown -= timeInSeconds;
|
|
|
+ if (this._glitchCooldown < 0) {
|
|
|
+ this._glitchCooldown = math.rand_range(5, 10);
|
|
|
+ this._glitchPass.enabled = !this._glitchPass.enabled;
|
|
|
+ }
|
|
|
+
|
|
|
+ this._StepEntities(timeInSeconds);
|
|
|
+ }
|
|
|
+
|
|
|
+ _StepEntities(timeInSeconds) {
|
|
|
+ if (this._entities.length == 0) {
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ for (let e of this._entities) {
|
|
|
+ e.Step(timeInSeconds);
|
|
|
+ }
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+function _Main() {
|
|
|
+ _APP = new OpenWorldDemo();
|
|
|
+}
|
|
|
+
|
|
|
+_Main();
|