123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406 |
- import * as THREE from 'https://cdn.jsdelivr.net/npm/[email protected]/build/three.module.js';
- import {ColladaLoader} from 'https://cdn.jsdelivr.net/npm/[email protected]/examples/jsm/loaders/ColladaLoader.js';
- import {FBXLoader} from 'https://cdn.jsdelivr.net/npm/[email protected]/examples/jsm/loaders/FBXLoader.js';
- import {GLTFLoader} from 'https://cdn.jsdelivr.net/npm/[email protected]/examples/jsm/loaders/GLTFLoader.js';
- import {GUI} from 'https://cdn.jsdelivr.net/npm/[email protected]/examples/jsm/libs/dat.gui.module.js';
- import {BufferGeometryUtils} from 'https://cdn.jsdelivr.net/npm/[email protected]/examples/jsm/utils/BufferGeometryUtils.js';
- import {agent} from './agent.js';
- import {controls} from './controls.js';
- import {game} from './game.js';
- import {math} from './math.js';
- import {terrain} from './terrain.js';
- import {visibility} from './visibility.js';
- import {particles} from './particles.js';
- import {blaster} from './blaster.js';
- let _APP = null;
- const _NUM_BOIDS = 100;
- const _BOID_SPEED = 100;
- 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 PlayerEntity {
- constructor(params) {
- this._model = params.model;
- this._params = params;
- this._game = params.game;
- this._fireCooldown = 0.0;
- this._velocity = new THREE.Vector3(0, 0, 0);
- this._direction = new THREE.Vector3(0, 0, -1);
- this._health = 1000.0;
- const x = 2.75;
- const y1 = 1.5;
- const y2 = 0.4;
- const z = 4.0;
- this._offsets = [
- new THREE.Vector3(-x, y1, -z),
- new THREE.Vector3(x, y1, -z),
- new THREE.Vector3(-x, -y2, -z),
- new THREE.Vector3(x, -y2, -z),
- ];
- this._offsetIndex = 0;
- this._visibilityIndex = this._game._visibilityGrid.UpdateItem(
- this._model.uuid, this);
- }
- get Enemy() {
- return false;
- }
- get Velocity() {
- return this._velocity;
- }
- get Direction() {
- return this._direction;
- }
- get Position() {
- return this._model.position;
- }
- get Radius() {
- return 1.0;
- }
- get Health() {
- return this._health;
- }
- get Dead() {
- return (this._health <= 0.0);
- }
- TakeDamage(dmg) {
- this._params.game._entities['_explosionSystem'].Splode(this.Position);
- this._health -= dmg;
- if (this._health <= 0.0) {
- this._game._visibilityGrid.RemoveItem(this._model.uuid, this._game._visibilityIndex);
- }
- }
- Fire() {
- if (this._fireCooldown > 0.0) {
- return;
- }
- this._fireCooldown = 0.05;
- const p = this._params.game._entities['_blasterSystem'].CreateParticle();
- p.Start = this._offsets[this._offsetIndex].clone();
- p.Start.applyQuaternion(this._model.quaternion);
- p.Start.add(this.Position);
- p.End = p.Start.clone();
- p.Velocity = this.Direction.clone().multiplyScalar(500.0);
- p.Length = 50.0;
- p.Colours = [
- new THREE.Color(4.0, 0.5, 0.5), new THREE.Color(0.0, 0.0, 0.0)];
- p.Life = 2.0;
- p.TotalLife = 2.0;
- p.Width = 0.25;
- this._offsetIndex = (this._offsetIndex + 1) % this._offsets.length;
- }
- Update(timeInSeconds) {
- if (this.Dead) {
- return;
- }
- this._visibilityIndex = this._game._visibilityGrid.UpdateItem(
- this._model.uuid, this, this._visibilityIndex);
- this._fireCooldown -= timeInSeconds;
- this._burstCooldown = Math.max(this._burstCooldown, 0.0);
- this._direction.copy(this._velocity);
- this._direction.normalize();
- this._direction.applyQuaternion(this._model.quaternion);
- }
- }
- class ExplodeParticles {
- constructor(game) {
- this._particleSystem = new particles.ParticleSystem(
- game, {texture: "./resources/explosion.png"});
- this._particles = [];
- }
- Splode(origin) {
- for (let i = 0; i < 96; 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(50);
- p.TotalLife = 2.0;
- p.Life = p.TotalLife;
- p.Colours = [new THREE.Color(0xFF8010), new THREE.Color(0xFF8010)];
- p.Sizes = [4, 16];
- p.Size = p.Sizes[0];
- this._particles.push(p);
- }
- }
- Update(timeInSeconds) {
- const _V = new THREE.Vector3();
- 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));
- _V.copy(p.Velocity);
- _V.multiplyScalar(10.0 * timeInSeconds);
- const velocityLength = p.Velocity.length();
- if (_V.length() > velocityLength) {
- _V.normalize();
- _V.multiplyScalar(velocityLength)
- }
- p.Velocity.sub(_V);
- 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);
- p.Opacity = math.smootherstep(p.Life / p.TotalLife, 0.0, 1.0);
- }
- this._particleSystem.Update();
- }
- };
- class ProceduralTerrain_Demo extends game.Game {
- constructor() {
- super();
- }
- _OnInitialize() {
- this._CreateGUI();
- this._userCamera = new THREE.Object3D();
- this._userCamera.position.set(4100, 0, 0);
- this._graphics.Camera.position.set(10340, 880, -2130);
- this._graphics.Camera.quaternion.set(-0.032, 0.885, 0.062, 0.46);
- this._score = 0;
- // This is 2D but eh, whatever.
- this._visibilityGrid = new visibility.VisibilityGrid(
- [new THREE.Vector3(-10000, 0, -10000), new THREE.Vector3(10000, 0, 10000)],
- [100, 100]);
- this._entities['_explosionSystem'] = new ExplodeParticles(this);
- this._entities['_blasterSystem'] = new blaster.BlasterSystem(
- {
- game: this,
- texture: "./resources/blaster.jpg",
- visibility: this._visibilityGrid,
- });
- this._entities['_terrain'] = new terrain.TerrainChunkManager({
- camera: this._graphics.Camera,
- scene: this._graphics.Scene,
- gui: this._gui,
- guiParams: this._guiParams,
- game: this
- });
- this._library = {};
- let loader = new GLTFLoader();
- loader.setPath('./resources/models/x-wing/');
- loader.load('scene.gltf', (gltf) => {
- const model = gltf.scene.children[0];
- model.scale.setScalar(0.5);
- const group = new THREE.Group();
- group.add(model);
- this._graphics.Scene.add(group);
- this._entities['player'] = new PlayerEntity(
- {model: group, camera: this._graphics.Camera, game: this});
- this._entities['_controls'] = new controls.ShipControls({
- target: this._entities['player'],
- camera: this._graphics.Camera,
- scene: this._graphics.Scene,
- domElement: this._graphics._threejs.domElement,
- gui: this._gui,
- guiParams: this._guiParams,
- });
- });
- loader = new GLTFLoader();
- loader.setPath('./resources/models/tie-fighter-gltf/');
- loader.load('scene.gltf', (obj) => {
- // This is bad, but I only want the mesh and I know this only has 1.
- // This is what you get when you don't have an art pipeline and don't feel like making one.
- obj.scene.traverse((c) => {
- if (c.isMesh) {
- const model = obj.scene.children[0];
- model.scale.setScalar(0.05);
- model.rotateX(Math.PI);
- const mat = new THREE.MeshStandardMaterial({
- map: new THREE.TextureLoader().load(
- './resources/models/tie-fighter-gltf/textures/hullblue_baseColor.png'),
- normalMap: new THREE.TextureLoader().load(
- './resources/models/tie-fighter-gltf/textures/hullblue_normal.png'),
- });
- model.material = mat;
- this._library['tie-fighter'] = model;
- }
- if (this._library['tie-fighter']) {
- this._CreateEnemyShips();
- }
- });
- });
- this._LoadBackground();
- }
- _CreateEnemyShips() {
- const positions = [
- new THREE.Vector3(8000, 0, 0),
- new THREE.Vector3(-7000, 50, -100),
- ];
- const colours = [
- new THREE.Color(4.0, 0.5, 0.5),
- new THREE.Color(0.5, 0.5, 4.0),
- ];
- for (let j = 0; j < 2; j++) {
- const p = positions[j];
- let loader = new GLTFLoader();
- loader.setPath('./resources/models/star-destroyer/');
- loader.load('scene.gltf', (gltf) => {
- const model = gltf.scene.children[0];
- model.scale.setScalar(20.0);
- model.rotateZ(Math.PI / 2.0);
- const cruiser = model;
- cruiser.position.set(p.x, p.y, p.z);
- cruiser.castShadow = true;
- cruiser.receiveShadow = true;
- cruiser.updateWorldMatrix();
- this._graphics.Scene.add(cruiser);
- });
- for (let i = 0; i < _NUM_BOIDS; i++) {
- let params = {
- mesh: this._library['tie-fighter'].clone(),
- speedMin: 1.0,
- speedMax: 1.0,
- speed: _BOID_SPEED,
- maxSteeringForce: _BOID_FORCE_MAX,
- acceleration: _BOID_ACCELERATION,
- seekGoal: p,
- colour: colours[j],
- };
-
- const e = new agent.Agent(this, params);
- this._entities['_boid_' + i] = e;
- }
- break;
- }
- }
- EnemyDied() {
- this._score++;
- document.getElementById('scoreText').innerText = this._score;
- }
- _CreateGUI() {
- this._CreateGameGUI();
- this._CreateControlGUI();
- }
- _CreateGameGUI() {
- const guiDiv = document.createElement('div');
- guiDiv.className = 'guiRoot guiBox';
- const scoreDiv = document.createElement('div');
- scoreDiv.className = 'vertical';
- const scoreTitle = document.createElement('div');
- scoreTitle.className = 'guiBigText';
- scoreTitle.innerText = 'KILLS';
- const scoreText = document.createElement('div');
- scoreText.className = 'guiSmallText';
- scoreText.innerText = '0';
- scoreText.id = 'scoreText';
- scoreDiv.appendChild(scoreTitle);
- scoreDiv.appendChild(scoreText);
- guiDiv.appendChild(scoreDiv);
- document.body.appendChild(guiDiv);
- }
- _CreateControlGUI() {
- this._guiParams = {
- general: {
- },
- };
- this._gui = new GUI();
- this._gui.hide();
- const generalRollup = this._gui.addFolder('General');
- this._gui.close();
- }
- _LoadBackground() {
- this._graphics.Scene.background = new THREE.Color(0xFFFFFF);
- 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;
- }
- _OnStep(timeInSeconds) {
- }
- }
- function _Main() {
- _APP = new ProceduralTerrain_Demo();
- }
- _Main();
|