Created
July 7, 2020 09:49
-
-
Save fredgido/8bd9ceca92d9337fa98179e360bafcf0 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| <!DOCTYPE html> | |
| <html lang="en"> | |
| <head> | |
| <meta charset="UTF-8"> | |
| <meta name="viewport" content="width=device-width, initial-scale=1.0"> | |
| <meta http-equiv="X-UA-Compatible" content="ie=edge"> | |
| <title>8Ball Pool by Fred & Rui</title> | |
| <link rel="stylesheet" href="./css/main.css"> | |
| <style> | |
| html, | |
| body { | |
| margin: 0; | |
| padding: 0; | |
| overflow: hidden; | |
| } | |
| </style> | |
| </head> | |
| <body> | |
| <div id="p1">Player 1: Ball Type ?</div> | |
| <div id="p2">Player 2: Ball Type ?</div> | |
| <div id="victory"></div> | |
| <script src="./js/quadtree.js"></script> | |
| <script type="module"> | |
| import * as THREE from './js/three.module.js'; | |
| import { | |
| PointerLockControls | |
| } from './js/PointerLockControls.js'; | |
| import { | |
| GLTFLoader | |
| } from 'https://threejs.org/examples/jsm/loaders/GLTFLoader.js'; | |
| import { | |
| OBJLoader | |
| } from 'https://threejs.org/examples/jsm/loaders/OBJLoader.js'; | |
| import { | |
| MTLLoader | |
| } from 'https://threejs.org/examples/jsm/loaders/MTLLoader.js'; | |
| //import Quadtree from './js/quadtree.js'; | |
| function promisifyLoader(loader, onProgress) { | |
| function promiseLoader(url) { | |
| return new Promise((resolve, reject) => { | |
| loader.load(url, resolve, onProgress, reject); | |
| }); | |
| } | |
| return { | |
| originalLoader: loader, | |
| load: promiseLoader, | |
| }; | |
| } | |
| let keyForward = false; | |
| let keyBackward = false; | |
| let keyLeft = false; | |
| let keyRight = false; | |
| let keyRotate = false; | |
| let keyAntirotate = false; | |
| let globalcontrols = 0; | |
| let gameStateGlobal = null; | |
| let cueaudio = new Audio('sounds/taco.wav'); | |
| let ballaudio = new Audio('sounds/bola.wav'); | |
| let pocketaudio = new Audio('sounds/pocketball.mp3') | |
| let objs; | |
| class Obj extends THREE.Object3D { | |
| constructor(x, y, z) { | |
| super(); | |
| this.position.set(x, y, z) | |
| } | |
| update() {} | |
| } | |
| class Light extends THREE.Object3D { | |
| constructor(position) { | |
| super(); | |
| } | |
| getLight() { | |
| return this.light; | |
| } | |
| } | |
| /* | |
| * Classe que simula a luz da Lua | |
| */ | |
| class MoonLight extends Light { | |
| constructor(position) { | |
| super(position); | |
| this.light = new THREE.PointLight("#ffffff", 0.6, 1000, 0.5); | |
| this.light.position.set(position.x, position.y, position.z); | |
| this.light.castShadow = true; | |
| this.light.shadow.camera.near = 6; | |
| this.light.shadow.camera.far = 2000; | |
| } | |
| } | |
| class Cave extends Obj { | |
| constructor(position) { | |
| super(); | |
| this.addCave(position.x, position.y, position.z); | |
| this.mesh.rotation.y = Math.PI; | |
| } | |
| addCave(x, y, z) { | |
| this.mesh = new THREE.Object3D(); | |
| let geometry = new THREE.SphereGeometry(200, 200, 100); //radius, widthSegments, heightSegments | |
| let material = new THREE.MeshPhongMaterial({ | |
| color: '#D3D3D3', | |
| side: THREE.DoubleSide | |
| }); | |
| let blob = (new THREE.Mesh(geometry, material)); | |
| geometry = new THREE.CylinderGeometry(50, 50, 200, 16); | |
| material = new THREE.MeshPhongMaterial({ | |
| color: '#D3D3D3', | |
| side: THREE.DoubleSide | |
| }); | |
| let tube = (new THREE.Mesh(geometry, material)); | |
| tube.rotation.z += Math.PI / 2; | |
| tube.position.x += 100; | |
| this.mesh.add(blob); | |
| this.mesh.add(tube); | |
| this.mesh.position.set(x, y, z); | |
| } | |
| addEntrance(x, y, z) { | |
| } | |
| getMesh() { | |
| return this.mesh; | |
| } | |
| } | |
| class Bonfire extends Obj { | |
| constructor(position) { | |
| super(); | |
| this.addBonfire(position.x, position.y, position.z); | |
| } | |
| // Método para criar o objecto composto Fogueira (criando troncos, pedras, fogo e luz proveniente do fogo) | |
| addBonfire(x, y, z) { | |
| this.mesh = new THREE.Object3D(); | |
| this.addLog(-2, 7, 2, 0.05, 0, 0); | |
| this.addLog(2, 7, -2, 0.95, 0, 0); | |
| this.addLog(0, 3, 0, 0, 0, 0.25); | |
| this.addRock(0, 2, 20); | |
| this.addRock(10, 2, 16); | |
| this.addRock(19, 2, 10); | |
| this.addRock(20, 2, -1); | |
| this.addRock(18, 2, -12); | |
| this.addRock(10, 2, -18); | |
| this.addRock(0, 2, -20); | |
| this.addRock(-10, 2, -16); | |
| this.addRock(-19, 2, -10); | |
| this.addRock(-20, 2, 1); | |
| this.addRock(-18, 2, 12); | |
| this.addRock(-10, 2, 18); | |
| this.addAsh(0, 0, 0); | |
| this.addFire(0, 10, 0, 0.25, 0, 0, false); | |
| this.addFire(0, 10, 0, 0, 0.25, 0, false); | |
| this.addFire(0, 10, 0, 0, 0, 0.25, false); | |
| this.addFire(0, 10, 0, 0.25, 0, 0, true); | |
| this.addFire(0, 10, 0, 0, 0.25, 0, true); | |
| this.addFire(0, 10, 0, 0, 0, 0.25, true); | |
| this.fireLight(0, 10, 0); | |
| this.mesh.position.set(x, y, z); | |
| } | |
| // Luz do fogo | |
| fireLight(x, y, z) { | |
| let light = new THREE.PointLight('#FF9217', 1.5, 150, 2); //Color, intensity, distance, decay | |
| light.position.set(x, y, z); | |
| light.castShadow = true; | |
| light.shadow.camera.near = 6; | |
| light.shadow.camera.far = 150; | |
| this.mesh.add(light); | |
| } | |
| // Fogo | |
| addFire(x, y, z, rX, rY, rZ, rot) { | |
| let geometry = new THREE.PlaneGeometry(30, 30); //width, height | |
| let texture = new THREE.TextureLoader().load('texture/fire.png'); | |
| texture.wrapS = THREE.RepeatWrapping; | |
| texture.wrapT = THREE.RepeatWrapping; | |
| texture.repeat.set(1, 1); | |
| let material = new THREE.MeshBasicMaterial({ | |
| map: texture, | |
| transparent: true, | |
| depthWrite: false, | |
| opacity: 0.5, | |
| /*depthTest: false ao colocar false a textura fica por cima tudo */ | |
| }); | |
| material.side = THREE.DoubleSide; | |
| let mesh = new THREE.Mesh(geometry, material); | |
| mesh.rotation.x = Math.PI / 2; | |
| mesh.rotation.x += Math.PI * 2 * rX; | |
| mesh.rotation.y += Math.PI * 2 * rY; | |
| mesh.rotation.z += Math.PI * 2 * rZ; | |
| mesh.position.set(x, y, z); | |
| mesh.name = "Fire"; | |
| mesh.rotopt = rot; | |
| this.mesh.add(mesh); | |
| } | |
| // Cinza na fogueira | |
| addAsh(x, y, z) { | |
| let geometry = new THREE.SphereGeometry(20, 32, 32); //radius, width, height | |
| let texture = new THREE.TextureLoader().load('texture/ashes.jpg'); | |
| texture.wrapS = THREE.RepeatWrapping; | |
| texture.wrapT = THREE.RepeatWrapping; | |
| texture.repeat.set(8, 8); | |
| let material = new THREE.MeshPhongMaterial({ | |
| map: texture, | |
| side: THREE.DoubleSide | |
| }); | |
| let mesh = new THREE.Mesh(geometry, material); | |
| mesh.position.set(x, y, z); | |
| mesh.scale.set(1, 0.01, 1); | |
| this.mesh.add(mesh); | |
| } | |
| // Cria pedras à volta da fogueira | |
| addRock(x, y, z) { | |
| let geometry = new THREE.SphereGeometry(5, 32, 32); //radius, width, height | |
| let material = new THREE.MeshPhongMaterial({ | |
| color: '#666666' | |
| }); | |
| let mesh = new THREE.Mesh(geometry, material); | |
| mesh.position.set(x, y, z); | |
| mesh.scale.set(1, 0.5, 1); | |
| this.mesh.add(mesh); | |
| mesh.castShadow = true; | |
| } | |
| // Cria troncos de madeira na fogueira | |
| addLog(x, y, z, rX, rY, rZ) { | |
| let geometry = new THREE.CylinderGeometry(3, 3, 30, 6); //radiusTop, radiusBottom, height, radialSegments | |
| let material = new THREE.MeshPhongMaterial({ | |
| color: '#814808' | |
| }); | |
| let mesh = new THREE.Mesh(geometry, material); | |
| mesh.position.set(x, y, z); | |
| mesh.rotation.x = Math.PI / 2; | |
| mesh.rotation.x += Math.PI * 2 * rX; | |
| mesh.rotation.y += Math.PI * 2 * rY; | |
| mesh.rotation.z += Math.PI * 2 * rZ; | |
| this.mesh.add(mesh); | |
| } | |
| update() { | |
| this.mesh.traverse(function(child) { | |
| if (child instanceof THREE.Object3D) { | |
| if (child.name == 'Fire' && child.rotopt == true) { | |
| child.rotation.z += .1; | |
| let scalethis = child.scale; | |
| child.scale.set(((child.scale.x + 0.1 * (Math.random() - 0.5)) + 0.1) / 1.1, ((child.scale.y + 0.1 * (Math.random() - 0.5)) + 0.1) / 1.1, ((child.scale.z + 0.1 * (Math.random() - 0.5)) + 0.1) / 1.1); | |
| } | |
| if (child.name == 'Fire' && child.rotopt == false) { | |
| child.rotation.z -= .1; | |
| let scalethis = child.scale; | |
| child.scale.set(((child.scale.x + 0.1 * (Math.random() - 0.5)) + 0.1) / 1.1, ((child.scale.y + 0.1 * (Math.random() - 0.5)) + 0.1) / 1.1, ((child.scale.z + 0.1 * (Math.random() - 0.5)) + 0.1) / 1.1); | |
| } | |
| } | |
| }); | |
| } | |
| getMesh() { | |
| return this.mesh; | |
| } | |
| } | |
| /* | |
| * Classe que instancia o Chão | |
| */ | |
| class Floor extends Obj { | |
| constructor(position) { | |
| super(); | |
| let floorTexture = new THREE.TextureLoader().load('models/grass.png'); | |
| floorTexture.wrapS = floorTexture.wrapT = THREE.RepeatWrapping; | |
| floorTexture.repeat.set(300, 300); | |
| let floorMaterial = new THREE.MeshPhongMaterial({ | |
| map: floorTexture, | |
| side: THREE.DoubleSide | |
| }); | |
| var floorGeometry = new THREE.PlaneGeometry(10000, 10000, 1, 1); | |
| this.mesh = new THREE.Mesh(floorGeometry, floorMaterial); | |
| this.mesh.receiveShadow = true; | |
| this.mesh.rotation.x = -Math.PI / 2; | |
| this.mesh.position.set(position.x, position.y, position.z); | |
| } | |
| getMesh() { | |
| return this.mesh; | |
| } | |
| } | |
| class Cue extends Obj { | |
| constructor(position) { | |
| super(); | |
| this.branca = position.branca; | |
| this.mesh = new THREE.Object3D(); | |
| this.Cue = this.addCue(this.branca.position.x, this.branca.position.y, this.branca.position.z); | |
| this.mesh.add(this.Cue); | |
| this.mesh.position.set(this.branca.mesh.position.x, this.branca.mesh.position.y, this.branca.mesh.position.z); | |
| } | |
| hideCue(x, y, z) { | |
| if (this.Cue.scale.x === 0) | |
| this.Cue.scale.set(1, 1, 1); | |
| else | |
| this.Cue.scale.set(0, 0, 0); | |
| this.mesh.position.set(this.branca.mesh.position.x, this.branca.mesh.position.y, this.branca.mesh.position.z); | |
| } | |
| update() { | |
| if (this.polling && this.Cue.position.z < 30) { | |
| this.Cue.position.z += 0.5; | |
| } | |
| if (this.Cue.scale.x == 0) | |
| return 0; | |
| if (keyAntirotate) | |
| this.mesh.rotation.y += 0.03; | |
| if (keyRotate) | |
| this.mesh.rotation.y -= 0.03; | |
| } | |
| addCue(x, y, z) { | |
| this.keyEvent = this.keyEvent.bind(this); | |
| let cueMesh = new THREE.Object3D(); | |
| this.polling = false; | |
| document.body.addEventListener('mousedown', this.keyEvent); | |
| document.body.addEventListener('mouseup', this.keyEvent); | |
| let mtl = new MTLLoader(); | |
| mtl.setPath("models/"); | |
| mtl.load('10522_Pool_Cue_v1_L3.mtl', function(materials) { | |
| materials.preload(); | |
| let obj = new OBJLoader(); | |
| obj.setMaterials(materials); | |
| obj.setPath("models/"); | |
| obj.load('10522_Pool_Cue_v1_L3.obj', function(object) { | |
| let mesh = object; | |
| mesh.rotation.y = Math.PI; | |
| mesh.position.set(0, 0.5, 108); | |
| mesh.scale.set(0.7, 0.7, 0.7); | |
| cueMesh.add(mesh); | |
| }.bind(this)); | |
| }.bind(this)); | |
| return cueMesh; | |
| } | |
| keyEvent(arg) { | |
| if (globalcontrols.isLocked === false) | |
| return 0; | |
| if (this.Cue.scale.x === 0) | |
| return 0; | |
| if (arg.type === 'mousedown') { | |
| this.polling = true; | |
| } | |
| if (arg.type === 'mouseup') { | |
| cueaudio.play(); | |
| gameStateGlobal.ballHit = true; | |
| console.log(gameStateGlobal); | |
| this.polling = false; | |
| let impact = Math.pow(this.Cue.position.z / 2, 1.5); | |
| let speed = new THREE.Vector3(0, 0, -1); | |
| speed.applyAxisAngle(new THREE.Vector3(0, 1, 0), this.mesh.rotation.y); | |
| speed.multiplyScalar(impact); | |
| this.branca.velocity = speed; | |
| this.Cue.position.z = 0; | |
| } | |
| } | |
| getMesh() { | |
| return this.mesh; | |
| } | |
| } | |
| class Tree extends Obj { | |
| constructor(position) { | |
| super(); | |
| this.addTree(position.x, position.y, position.z); | |
| } | |
| addTree(x, y, z) { | |
| this.mesh = new THREE.Object3D(); | |
| this.addTrunk(0, 20, 0); | |
| this.addCopa(0, 99, 0); | |
| this.mesh.position.set(x, y, z); | |
| } | |
| // Cria o tronco da árvore | |
| addTrunk(x, y, z) { | |
| let geometry = new THREE.CylinderGeometry(10, 10, 100, 6); //radiusTop, radiusBottom, height, radialSegments | |
| let material = new THREE.MeshPhongMaterial({ | |
| color: '#814808', | |
| }); | |
| let mesh = new THREE.Mesh(geometry, material); | |
| mesh.position.set(x, y, z); | |
| this.mesh.add(mesh); | |
| mesh.castShadow = true; | |
| mesh.receiveShadow = true; | |
| } | |
| // Cria a copa da árvore | |
| addCopa(x, y, z) { | |
| let geometry = new THREE.ConeGeometry(50, 80, 32); //radius, height, radialSegments | |
| let material = new THREE.MeshLambertMaterial({ | |
| color: '#34eb34', | |
| }); | |
| let mesh = new THREE.Mesh(geometry, material); | |
| mesh.position.set(x, y, z); | |
| this.mesh.add(mesh); | |
| mesh.castShadow = true; | |
| mesh.receiveShadow = true; | |
| } | |
| getMesh() { | |
| return this.mesh; | |
| } | |
| } | |
| class Branca extends Obj { | |
| constructor(position, file, radius, type, number) { | |
| super(); | |
| this.file = file; | |
| this.radius = radius; | |
| this.type = type; | |
| this.number = number; | |
| this.in = false; | |
| this.velocity = new THREE.Vector3(0, 0, 0); | |
| this.mesh = new THREE.Object3D(); | |
| let ballMaterial; | |
| if (file.charAt(0) === "#") | |
| ballMaterial = new THREE.MeshPhongMaterial({ | |
| color: file, | |
| side: THREE.DoubleSide | |
| }); | |
| else { | |
| let ballTexture = new THREE.TextureLoader().load(file); | |
| ballTexture.wrapS = ballTexture.wrapT = THREE.RepeatWrapping; | |
| ballTexture.repeat.set(1, 1); | |
| ballMaterial = new THREE.MeshPhongMaterial({ | |
| map: ballTexture, | |
| side: THREE.DoubleSide | |
| }); | |
| } | |
| let geometry = new THREE.SphereGeometry(this.radius, 40, 40); //radius, widthSegments, heightSegments | |
| let material = new THREE.MeshPhongMaterial({ | |
| color: '#D3D3D3', | |
| side: THREE.DoubleSide | |
| }); | |
| this.bloba = new THREE.Mesh(geometry, ballMaterial); | |
| this.bloba.position.set(0, 0, 0); | |
| this.bloba.castShadow = true; | |
| this.mesh.add(this.bloba); | |
| this.mesh.position.set(position.x, position.y, position.z); | |
| } | |
| update() { | |
| this.mesh.position.add(this.velocity.clone().multiplyScalar(0.2)); | |
| this.velocity.multiplyScalar(0.99); | |
| let rotateAxis = this.velocity.clone().applyAxisAngle(new THREE.Vector3(0, 1, 0), Math.PI / 2); | |
| rotateAxis.normalize(); | |
| this.mesh.rotateOnAxis(rotateAxis, this.velocity.length() * 0.04); | |
| } | |
| getMesh() { | |
| return this.mesh; | |
| } | |
| } | |
| class Hole extends Obj { | |
| constructor(position) { | |
| super(); | |
| this.radius = 3; | |
| this.mesh = new THREE.Object3D(); | |
| this.mesh.position.set(position.x, position.y, position.z); | |
| } | |
| getMesh() { | |
| return this.mesh; | |
| } | |
| } | |
| class tableLight extends Light { | |
| constructor(position, color) { | |
| super(position); | |
| this.light = new THREE.PointLight(color, 6, 100, 2); | |
| this.light.position.set(position.x, position.y, position.z); | |
| this.light.castShadow = true; | |
| } | |
| } | |
| /* | |
| * Classe que faz load | |
| */ | |
| class PoolTable extends Obj { | |
| constructor(position) { | |
| super(); | |
| this.mesh = new THREE.Object3D(); | |
| let mtl = new MTLLoader(); | |
| mtl.setPath("models/"); | |
| mtl.load('poolTableNoBalls.mtl', function(materials) { | |
| materials.preload(); | |
| let obj = new OBJLoader(); | |
| obj.setMaterials(materials); | |
| obj.setPath("models/"); | |
| obj.load('poolTableNoBalls.obj', function(object) { | |
| let mesh = object; | |
| mesh.castShadow = true; | |
| mesh.receiveShadow = true; | |
| mesh.traverse(function(child) { | |
| child.castShadow = true; | |
| }); | |
| mesh.traverse(function(child) { | |
| child.receiveShadow = true; | |
| }); | |
| mesh.position.set(0, 10, 0); | |
| mesh.scale.set(0.4, 0.4, 0.4); | |
| this.mesh.add(mesh); | |
| }.bind(this)); | |
| }.bind(this)); | |
| this.mesh.position.set(position.x, position.y, position.z); | |
| } | |
| getMesh() { | |
| return this.mesh; | |
| } | |
| } | |
| class Seagull extends Obj { | |
| constructor(position) { | |
| super(); | |
| this.mesh = new THREE.Object3D(); | |
| this.seagull; | |
| this.time = 0; | |
| let mtl = new MTLLoader(); | |
| mtl.setPath("models/"); | |
| mtl.load('seagull.mtl', function(materials) { | |
| materials.preload(); | |
| let obj = new OBJLoader(); | |
| obj.setMaterials(materials); | |
| obj.setPath("models/"); | |
| obj.load('seagull.obj', function(object) { | |
| let mesh = object; | |
| mesh.castShadow = true; | |
| mesh.receiveShadow = true; | |
| mesh.traverse(function(child) { | |
| child.castShadow = true; | |
| }); | |
| mesh.traverse(function(child) { | |
| child.receiveShadow = true; | |
| }); | |
| mesh.position.set(0, 0, 0); | |
| mesh.scale.set(0.03, 0.03, 0.03); | |
| this.mesh.add(mesh); | |
| this.seagull = mesh; | |
| }.bind(this)); | |
| }.bind(this)); | |
| this.curve = new THREE.SplineCurve3([ | |
| new THREE.Vector3(-100, 1, 0), | |
| new THREE.Vector3(0, 1, 100), | |
| new THREE.Vector3(100, 1, 0), | |
| new THREE.Vector3(0, 1, -100), | |
| new THREE.Vector3(-100, 1, 0) | |
| ]); | |
| this.mesh.position.set(position.x, position.y, position.z); | |
| } | |
| update() { | |
| if (typeof this.seagull === 'undefined') { | |
| return 0; | |
| } | |
| this.time += 0.005; | |
| this.time = (this.time >= 1) ? this.time - 1 : this.time; | |
| let pt = this.curve.getPoint(this.time); | |
| this.seagull.position.set(pt.x, pt.y, pt.z); | |
| let axis = new THREE.Vector3(); | |
| let tangent = this.curve.getTangent(this.time).normalize(); | |
| axis.crossVectors(new THREE.Vector3(1, 0, 0), tangent).normalize(); | |
| let radians = Math.acos(new THREE.Vector3(1, 0, 0).dot(tangent)); | |
| this.seagull.quaternion.setFromAxisAngle(axis, radians); | |
| } | |
| getMesh() { | |
| return this.mesh; | |
| } | |
| } | |
| class Blimp extends Obj { | |
| constructor(position) { | |
| super(); | |
| this.mesh = new THREE.Object3D(); | |
| this.time = 0; | |
| this.blimp; | |
| let mtl = new MTLLoader(); | |
| mtl.setPath("models/"); | |
| mtl.load('blimp.mtl', function(materials) { | |
| materials.preload(); | |
| let obj = new OBJLoader(); | |
| obj.setMaterials(materials); | |
| obj.setPath("models/"); | |
| obj.load('blimp.obj', function(object) { | |
| let mesh = object; | |
| mesh.traverse(function(child) { | |
| child.castShadow = true; | |
| }); | |
| mesh.traverse(function(child) { | |
| child.receiveShadow = true; | |
| }); | |
| mesh.position.set(0, 0, 0); | |
| mesh.scale.set(0.5, 0.5, 0.5); | |
| this.mesh.add(mesh); | |
| this.blimp = mesh; | |
| }.bind(this)); | |
| }.bind(this)); | |
| this.curve = new THREE.SplineCurve3([ | |
| new THREE.Vector3(-1000, 1, 0), | |
| new THREE.Vector3(0, 1, 1000), | |
| new THREE.Vector3(1000, 1, 0), | |
| new THREE.Vector3(0, 1, -1000), | |
| new THREE.Vector3(-1000, 1, 0) | |
| ]); | |
| this.mesh.position.set(position.x, position.y, position.z); | |
| } | |
| update() { | |
| if (typeof this.blimp === 'undefined') { | |
| return 0; | |
| } | |
| this.time += 0.0003; | |
| this.time = (this.time >= 1) ? this.time - 1 : this.time; | |
| let pt = this.curve.getPoint(this.time); | |
| this.blimp.position.set(pt.x, pt.y, pt.z); | |
| let axis = new THREE.Vector3(); | |
| let tangent = this.curve.getTangent(this.time).normalize(); | |
| axis.crossVectors(new THREE.Vector3(1, 0, 0), tangent).normalize(); | |
| let radians = Math.acos(new THREE.Vector3(1, 0, 0).dot(tangent)); | |
| this.blimp.quaternion.setFromAxisAngle(axis, radians); | |
| } | |
| getMesh() { | |
| return this.mesh; | |
| } | |
| } | |
| class Application { | |
| constructor() { | |
| this.objects = []; | |
| this.createScene(); | |
| this.ballPositions = [ | |
| {x: 0, y: 27, z: 22}, | |
| {x: 64, y: 27, z: 0}, | |
| {x: 64, y: 27, z: -4}, | |
| {x: 64, y: 27, z: -8}, | |
| {x: 64, y: 27, z: -12}, | |
| {x: 64, y: 27, z: -16}, | |
| {x: 64, y: 27, z: -20}, | |
| {x: 64, y: 27, z: -24}, | |
| {x: 68, y: 27, z: 0}, | |
| {x: 60, y: 27, z: 0}, | |
| {x: 60, y: 27, z: -4}, | |
| {x: 60, y: 27, z: -8}, | |
| {x: 60, y: 27, z: -12}, | |
| {x: 60, y: 27, z: -16}, | |
| {x: 60, y: 27, z: -20}, | |
| {x: 60, y: 27, z: -24} | |
| ]; | |
| this.originalBallPositions = [ | |
| {x: 0, y: 27, z: 22}, | |
| {x: -8, y: 27, z: -38.5}, | |
| {x: 6.5, y: 27, z: -34.4}, | |
| {x: -4.5, y: 27, z: -30.9}, | |
| {x: -2.5, y: 27, z: -27.2}, | |
| {x: 0, y: 27, z: -24}, | |
| {x: -4, y: 27, z: -38.5}, | |
| {x: 2.2, y: 27, z: -34.4}, | |
| {x: 0, y: 27, z: -30.9}, | |
| {x: 2.5, y: 27, z: -27.2}, | |
| {x: 0, y: 27, z: -38.5}, | |
| {x: -2.2, y: 27, z: -34.4}, | |
| {x: 4.5, y: 27, z: -30.9}, | |
| {x: 4, y: 27, z: -38.5}, | |
| {x: -6.5, y: 27, z: -34.4}, | |
| {x: 8, y: 27, z: -38.5} | |
| ] | |
| this.gameState = { | |
| "player1Ball": "", | |
| "player2Ball": "", | |
| "turn": "p1", | |
| "firstTouchBall": "", | |
| "ballInType": "", | |
| "ballHit": false, | |
| } | |
| gameStateGlobal = this.gameState; | |
| } | |
| createScene() { | |
| this.scene = new THREE.Scene(); | |
| this.scene.background = new THREE.Color('blue'); | |
| this.camera = new THREE.PerspectiveCamera(60, window.innerWidth / window.innerHeight, 0.1, 10000); | |
| this.camera.position.z = 70; | |
| this.camera.position.y = 70; | |
| this.camera.position.x = 0; | |
| this.camera.rotation.x = -Math.PI / 4; | |
| this.renderer = new THREE.WebGLRenderer({ | |
| antialias: true | |
| }); | |
| this.renderer.shadowMap.enabled = true; | |
| this.renderer.autoClear = false; | |
| this.renderer.shadowMapSoft = true; | |
| this.renderer.shadowMap.type = THREE.PCFSoftShadowMap; | |
| this.renderer.setSize(window.innerWidth, window.innerHeight); | |
| // função que redimensiona a janela de visualização | |
| window.addEventListener('resize', () => { | |
| this.renderer.setSize(window.innerWidth, window.innerHeight); | |
| this.camera.aspect = window.innerWidth / window.innerHeight; | |
| this.camera.updateProjectionMatrix(); | |
| }); | |
| document.body.appendChild(this.renderer.domElement); | |
| // função que permite ao utilizador controlar a perspectiva da câmara consoante a posição do rato | |
| let controls = new PointerLockControls(this.camera, this.renderer.domElement); | |
| globalcontrols = controls; | |
| document.body.addEventListener('click', () => { | |
| controls.lock(); | |
| }); | |
| this.scene.add(controls.getObject()); | |
| // função que permite ao utilizador mover-se no mundo ao introduzir certos inputs | |
| var onKeyDown = function(event) { | |
| switch (event.keyCode) { | |
| case 38: // up | |
| case 87: // w | |
| keyForward = true; | |
| break; | |
| case 37: // left | |
| case 65: // a | |
| keyLeft = true; | |
| break; | |
| case 40: // down | |
| case 83: // s | |
| keyBackward = true; | |
| break; | |
| case 39: // right | |
| case 68: // d | |
| keyRight = true; | |
| break; | |
| case 81: // q | |
| keyRotate = true; | |
| break; | |
| case 69: // e | |
| keyAntirotate = true; | |
| break; | |
| case 32: //spacebar | |
| objs.cue.hideCue(); | |
| break; | |
| case 82: //R | |
| this.resetPosition(); | |
| break; | |
| } | |
| }.bind(this); | |
| var onKeyUp = function(event) { | |
| switch (event.keyCode) { | |
| case 38: // up | |
| case 87: // w | |
| keyForward = false; | |
| break; | |
| case 37: // left | |
| case 65: // a | |
| keyLeft = false; | |
| break; | |
| case 40: // down | |
| case 83: // s | |
| keyBackward = false; | |
| break; | |
| case 39: // right | |
| case 68: // d | |
| keyRight = false; | |
| break; | |
| case 81: // q | |
| keyRotate = false; | |
| break; | |
| case 69: // e | |
| keyAntirotate = false; | |
| break; | |
| } | |
| }; | |
| document.addEventListener('keydown', onKeyDown, false); | |
| document.addEventListener('keyup', onKeyUp, false); | |
| // Criação da skybox | |
| let loader = new THREE.CubeTextureLoader(); | |
| let texture = loader.load([ | |
| 'models/face1.png', | |
| 'models/face2.png', | |
| 'models/face3.png', | |
| 'models/face4.png', | |
| 'models/face5.png', | |
| 'models/face6.png', | |
| ]); | |
| this.scene.background = texture; | |
| this.render(); | |
| } | |
| turnProcess() { | |
| if (typeof this.gameState === "undefined") { | |
| return 0; | |
| } | |
| document.getElementById('victory').innerHTML = "Player " + this.gameState.turn + "'s turn!"; | |
| document.getElementById('victory').style.visibility = "visible"; | |
| if (!this.gameState.ballHit) { | |
| return 0; | |
| } | |
| let wasMoving = false; | |
| Object.values(objs).forEach((object) => { | |
| if (object instanceof Branca) { | |
| console.log(typeof object.velocity); | |
| console.log(object); | |
| if (typeof object.velocity !== "undefined" && object.velocity.length() > 0.000000001) { | |
| wasMoving = true; | |
| console.log("was moving..."); | |
| return 0; | |
| } | |
| } | |
| }); | |
| console.log("Play is over!"); | |
| let myBall = this.gameState.turn === 'p1' ? this.gameState.player1Ball : this.gameState.player2Ball; | |
| if (this.gameState.firstTouchBall !== myBall) { | |
| this.gameState.turn = this.gameState.turn === "p1" ? "p2" : "p1"; | |
| document.getElementById('victory').innerHTML = "Player " + this.gameState.turn + "'s turn!"; | |
| document.getElementById('victory').style.visibility = "visible"; | |
| this.gameState.firstTouchBall = ""; | |
| this.gameState.ballInType = ""; | |
| this.gameState.ballHit = false; | |
| return 0; | |
| } | |
| if (this.gameState.ballInType !== myBall) { | |
| this.gameState.turn = this.gameState.turn === "p1" ? "p2" : "p1"; | |
| document.getElementById('victory').innerHTML = "Player " + this.gameState.turn + "'s turn!"; | |
| document.getElementById('victory').style.visibility = "visible"; | |
| this.gameState.firstTouchBall = ""; | |
| this.gameState.ballInType = ""; | |
| this.gameState.ballHit = false; | |
| return 0; | |
| } | |
| console.log("My turn continues"); | |
| this.gameState.firstTouchBall = ""; | |
| this.gameState.ballInType = ""; | |
| this.gameState.ballHit = false; | |
| } | |
| collisions() { | |
| if (typeof objs === 'undefined') { | |
| return 0; | |
| } | |
| let myTree = new Quadtree({x: -18.6,y: -43.8,width: 2* 18.6,height: 43.8*2}, 10, 4); | |
| Object.values(objs).forEach((object) => { | |
| if (object instanceof Branca || object instanceof Hole) { | |
| myTree.insert({ | |
| x: object.mesh.position.x, | |
| y: object.mesh.position.y, | |
| width: object.radius, | |
| height: object.radius, | |
| o : object | |
| })}}); | |
| Object.values(objs).forEach((object) => { | |
| if (object instanceof Branca && !object.in) { | |
| if (object.mesh.position.z > 43.8 && (object.mesh.position.x < 17.5 && object.mesh.position.x > -17.5)) { | |
| object.velocity.z *= -1; | |
| object.mesh.position.z = 43.8; | |
| } | |
| if (object.mesh.position.z < -43.8 && (object.mesh.position.x < 17.5 && object.mesh.position.x > -17.5)) { | |
| object.velocity.z *= -1; | |
| object.mesh.position.z = -43.8; | |
| } | |
| if (object.mesh.position.x > 18.6 && (object.mesh.position.z < -3 && object.mesh.position.z > -42)) { | |
| object.velocity.x *= -1; | |
| object.mesh.position.x = 18.6 | |
| } | |
| if (object.mesh.position.x > 18.6 && (object.mesh.position.z < 42 && object.mesh.position.z > 3)) { | |
| object.velocity.x *= -1; | |
| object.mesh.position.x = 18.6 | |
| } | |
| if (object.mesh.position.x < -18.6 && (object.mesh.position.z < -3 && object.mesh.position.z > -42)) { | |
| object.velocity.x *= -1; | |
| object.mesh.position.x = -18.6 | |
| } | |
| if (object.mesh.position.x < -18.6 && (object.mesh.position.z < 42 && object.mesh.position.z > 3)) { | |
| object.velocity.x *= -1; | |
| object.mesh.position.x = -18.6 | |
| } | |
| if (object instanceof Branca && object.mesh.position.length() > 200) { | |
| object.mesh.position.set(this.ballPositions[object.number].x, this.ballPositions[object.number].y, this.ballPositions[object.number].z); | |
| } | |
| let elements = myTree.retrieve({ | |
| x: object.mesh.position.x, | |
| y: object.mesh.position.y, | |
| width: object.radius, | |
| height: object.radius | |
| }); | |
| //console.log(elements); | |
| elements.map(z => z.o).forEach((other) => { | |
| if (other instanceof Branca && other != object) { | |
| let conectingvector = other.mesh.position.clone().sub(object.mesh.position) | |
| let d = conectingvector.length() | |
| if (d < object.radius + other.radius) { | |
| conectingvector.normalize(); | |
| let sumVel = (other.velocity.length() + object.velocity.length()) / 2; | |
| other.velocity = conectingvector.clone().multiplyScalar(sumVel); | |
| object.velocity = conectingvector.clone().multiplyScalar(sumVel * -1); | |
| ballaudio.play(); | |
| if (this.gameState.firstTouchBall === "") { | |
| this.gameState.firstTouchBall = object.number === 0 ? other.type : object.type; | |
| } | |
| } | |
| } | |
| if (other instanceof Hole && object.file === "#fff") { | |
| let conectingvector = other.mesh.position.clone().sub(object.mesh.position) | |
| let d = conectingvector.length() | |
| if (d < object.radius + other.radius) { | |
| pocketaudio.play(); | |
| conectingvector.normalize(); | |
| object.mesh.position.set(0, 27, 22); | |
| object.velocity.set(0, 0, 0); | |
| } | |
| } | |
| if (other instanceof Hole) { | |
| let conectingvector = other.mesh.position.clone().sub(object.mesh.position) | |
| let d = conectingvector.length(); | |
| if (d < object.radius + other.radius) { | |
| pocketaudio.play(); | |
| conectingvector.normalize(); | |
| object.mesh.position.set(this.ballPositions[object.number].x, this.ballPositions[object.number].y, this.ballPositions[object.number].z); | |
| object.velocity.set(0, 0, 0); | |
| object.in = true; | |
| object.mesh.rotation.set(0, 0, 0); | |
| if (this.gameState.player1Ball === "") { | |
| this.gameState.player1Ball = object.type; | |
| this.gameState.player2Ball = object.type === "solid" ? "stripe" : "solid"; | |
| document.getElementById('p1').innerHTML = "Player 1: " + this.gameState.player1Ball.charAt(0).toUpperCase() + this.gameState.player1Ball.slice(1); | |
| document.getElementById('p2').innerHTML = "Player 2: " + this.gameState.player2Ball.charAt(0).toUpperCase() + this.gameState.player2Ball.slice(1); | |
| } | |
| if (this.gameState.ballInType !== object.type) { | |
| this.gameState.ballInType = this.gameState.ballInType === "" ? object.type : "all"; | |
| } | |
| console.log(this.gameState); | |
| } | |
| } | |
| }) | |
| } | |
| }); | |
| } | |
| checkWin() { | |
| let blackIn = false; | |
| let allIn = true; | |
| try { | |
| Object.values(objs).forEach((object) => { | |
| if (object instanceof Branca) { | |
| if (object.file === './models/ball8.jpg') { | |
| blackIn = object.in; | |
| } | |
| if (!object.in && object.file != "#fff") | |
| allIn = false; | |
| } | |
| }) | |
| } catch (err) {} | |
| if (blackIn === true && allIn === false) { | |
| console.log("Loss"); | |
| document.getElementById('victory').innerHTML = "Victory! Player " + this.gameState.turn.charAt(1) + " wins!" | |
| document.getElementById("victory").style.visibility = "visible"; | |
| this.resetPosition(); | |
| } | |
| if (blackIn === true && allIn === true) { | |
| document.getElementById('victory').innerHTML = "Victory! Player " + this.gameState.turn.charAt(1) + " wins!" | |
| document.getElementById("victory").style.visibility = "visible"; | |
| this.resetPosition(); | |
| } | |
| } | |
| resetPosition() { | |
| Object.values(objs).forEach((object) => { | |
| if (object instanceof Branca) { | |
| object.mesh.position.set(this.originalBallPositions[object.number].x, | |
| this.originalBallPositions[object.number].y, | |
| this.originalBallPositions[object.number].z); | |
| object.velocity.set(0, 0, 0); | |
| object.in = false; | |
| object.mesh.rotation.set(0, 0, 0); | |
| } | |
| }) | |
| document.getElementById('p1').innerHTML = "Player 1: Ball Type ?"; | |
| document.getElementById('p2').innerHTML = "Player 2: Ball Type ?"; | |
| this.gameState.player1Ball = ""; | |
| this.gameState.player2Ball = ""; | |
| } | |
| render() { | |
| requestAnimationFrame(() => { | |
| this.render(); | |
| }); | |
| this.objects.forEach((object) => { | |
| if (object instanceof Obj) | |
| object.update(); | |
| }); | |
| this.collisions(); | |
| this.checkWin(); | |
| this.turnProcess(); | |
| globalcontrols.moveForward(keyForward ? 0.5 : (keyBackward ? -0.5 : 0)); | |
| globalcontrols.moveRight(keyRight ? 0.5 : (keyLeft ? -0.5 : 0)); | |
| globalcontrols.setZero(keyRotate ? 0.01 : (keyAntirotate ? -0.01 : 0)); | |
| this.renderer.render(this.scene, this.camera); | |
| } | |
| /* função que adiciona "mesh" à cena e em especial às que estão | |
| * pendentes aguarda o loading | |
| */ | |
| async add(mesh) { | |
| if (Array.isArray(mesh)) { | |
| for (var index in mesh) { | |
| this.objects.push(mesh[index]); | |
| if (mesh[index] instanceof Obj) | |
| this.scene.add(mesh[index].getMesh()); | |
| else if (mesh[index] instanceof Light) | |
| this.scene.add(mesh[index].getLight()) | |
| else if (mesh[index].getMesh() instanceof Promise) { | |
| let t = await mesh[index].getMesh(); | |
| mesh[index].mesh = t; | |
| mesh[index].setup(); | |
| this.scene.add(t); | |
| } | |
| } | |
| } else { | |
| this.objects.push(mesh); | |
| this.scene.add(mesh.getMesh()); | |
| } | |
| } | |
| } | |
| let app = new Application(); | |
| objs = {}; | |
| objs.moonlight = new MoonLight({x:0,y:500,z:0}); | |
| objs.floor = new Floor({x:0,y:0,z:0}); | |
| objs.poolTable = new PoolTable({x:0,y:0,z:0}); | |
| objs.bola = new Branca({x:0, y:27, z:22}, "#fff", 2, "solid",0); | |
| objs.ballOne = new Branca({x: -8, y: 27, z: -38.5}, "./models/ball1.jpg", 2, "solid", 1); | |
| objs.ballTwo = new Branca({x: 6.5, y: 27, z: -34.4}, "./models/ball2.png", 2, "solid", 2); | |
| objs.ballThree = new Branca({x: -4.5, y: 27, z: -30.9}, "./models/ball3.jpg", 2, "solid", 3); | |
| objs.ballFour = new Branca({x: -2.5, y: 27, z: -27.2}, "./models/ball4.png", 2, "solid", 4); | |
| objs.ballFive = new Branca({x: 0, y: 27, z: -24}, "./models/ball5.png", 2, "solid", 5); | |
| objs.ballSix = new Branca({x: -4, y: 27, z: -38.5}, "./models/ball6.png", 2, "solid", 6); | |
| objs.ballSeven = new Branca({x: 2.2, y: 27, z: -34.4}, "./models/ball7.jpg", 2, "solid", 7); | |
| objs.ballEight = new Branca({x: 0, y: 27, z: -30.9}, "./models/ball8.jpg", 2, "solid", 8); | |
| objs.ballNine = new Branca({x: 2.5, y: 27, z: -27.2}, "./models/ball9.png", 2, "stripe", 9); | |
| objs.ballTen = new Branca({x: 0, y: 27, z: -38.5}, "./models/ball10.png", 2, "stripe", 10); | |
| objs.ballEleven = new Branca({x: -2.2, y: 27, z: -34.4}, "./models/ball11.png", 2, "stripe", 11); | |
| objs.ballTwelve = new Branca({x: 4.5, y: 27, z: -30.9}, "./models/ball12.png", 2, "stripe", 12); | |
| objs.ballThirteen = new Branca({x: 4, y: 27, z: -38.5}, "./models/ball13.jpg", 2, "stripe", 13); | |
| objs.ballFourteen = new Branca({x: -6.5, y: 27, z: -34.4}, "./models/ball14.png", 2, "stripe", 14); | |
| objs.ballFifteen = new Branca({x: 8, y: 27, z: -38.5}, "./models/ball15.png", 2, "stripe", 15); | |
| objs.cue = new Cue({x:0,y:27,z:0, branca: objs.bola}); | |
| objs.tLight = new tableLight({x:0, y: 90, z:0}, "#fff"); | |
| objs.tLight1 = new tableLight({x: -24, y: 35, z: -47}, "#FF0000"); | |
| objs.tLight2 = new tableLight({x: 24, y: 35, z: 47}, "#0000FF"); | |
| objs.hole = new Hole({x:-24, y:27, z:0}); | |
| objs.hole1 = new Hole({x:24, y:27, z:0}); | |
| objs.hole2 = new Hole({x:22, y:27, z:47}); | |
| objs.hole3 = new Hole({x:-22, y:27, z:47}); | |
| objs.hole4 = new Hole({x:22, y:27, z:-47}); | |
| objs.hole5 = new Hole({x:-22, y:27, z:-47}); | |
| objs.seagull = new Seagull({x: 0, y: 160, z: 0}); | |
| objs.blimp = new Blimp({x: 100, y: 1200, z: -600}); | |
| objs.bonfire = new Bonfire({x: 100, y: 0, z: 0}); | |
| objs.cave = new Cave({x: 350, y: 0, z: 0}); | |
| // Adicionar Árvores à cena aleatoriamente | |
| for (let i = 0; i < 100; i++) { | |
| let cX = 0, | |
| cY = 0, | |
| cZ = 0; | |
| // Previne criação de Árvores no ponto (0,0,0) com lado 80 | |
| while (cX < 130 && cX > -130 && cZ < 130 && cZ > -130) { | |
| cX = 1000 * Math.random() - 500; | |
| cZ = 1000 * Math.random() - 500; | |
| } | |
| objs["Tree" + i] = new Tree({ | |
| x: cX, | |
| y: 0, | |
| z: cZ, | |
| }); | |
| } | |
| app.add(Object.values(objs)); | |
| </script> | |
| </body> | |
| </html> |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment