Path: blob/main/projects/HexGL/bkcore/hexgl/ShipControls.js
4627 views
/*1* HexGL2* @author Thibaut 'BKcore' Despoulain <http://bkcore.com>3* @license This work is licensed under the Creative Commons Attribution-NonCommercial 3.0 Unported License.4* To view a copy of this license, visit http://creativecommons.org/licenses/by-nc/3.0/.5*/67var bkcore = bkcore || {};8bkcore.hexgl = bkcore.hexgl || {};910bkcore.hexgl.ShipControls = function(ctx)11{12var self = this;13var domElement = ctx.document;1415this.active = true;16this.destroyed = false;17this.falling = false;1819this.dom = domElement;20this.mesh = null;2122this.epsilon = 0.00000001;23this.zero = new THREE.Vector3(0,0,0);24this.airResist = 0.02;25this.airDrift = 0.1;26this.thrust = 0.02;27this.airBrake = 0.02;28this.maxSpeed = 7.0;29this.boosterSpeed = this.maxSpeed * 0.2;30this.boosterDecay = 0.01;31this.angularSpeed = 0.005;32this.airAngularSpeed = 0.0065;33this.repulsionRatio = 0.5;34this.repulsionCap = 2.5;35this.repulsionLerp = 0.1;36this.collisionSpeedDecrease = 0.8;37this.collisionSpeedDecreaseCoef = 0.8;38this.maxShield = 1.0;39this.shieldDelay = 60;40this.shieldTiming = 0;41this.shieldDamage = 0.25;42this.driftLerp = 0.35;43this.angularLerp = 0.35;4445this.movement = new THREE.Vector3(0,0,0);46this.rotation = new THREE.Vector3(0,0,0);47this.roll = 0.0;48this.rollAxis = new THREE.Vector3();49this.drift = 0.0;50this.speed = 0.0;51this.speedRatio = 0.0;52this.boost = 0.0;53this.shield = 1.0;54this.angular = 0.0;5556this.currentVelocity = new THREE.Vector3();5758this.quaternion = new THREE.Quaternion();5960this.dummy = new THREE.Object3D();61this.dummy.useQuaternion = true;6263this.collisionMap = null;64this.collisionPixelRatio = 1.0;65this.collisionDetection = false;66this.collisionPreviousPosition = new THREE.Vector3();6768this.heightMap = null;69this.heightPixelRatio = 1.0;70this.heightBias = 0.0;71this.heightLerp = 0.4;72this.heightScale = 1.0;7374this.rollAngle = 0.6;75this.rollLerp = 0.08;76this.rollDirection = new THREE.Vector3(0,0,1);7778this.gradient = 0.0;79this.gradientTarget = 0.0;80this.gradientLerp = 0.05;81this.gradientScale = 4.0;82this.gradientVector = new THREE.Vector3(0,0,5);83this.gradientAxis = new THREE.Vector3(1,0,0);8485this.tilt = 0.0;86this.tiltTarget = 0.0;87this.tiltLerp = 0.05;88this.tiltScale = 4.0;89this.tiltVector = new THREE.Vector3(5,0,0);90this.tiltAxis = new THREE.Vector3(0,0,1);9192this.repulsionVLeft = new THREE.Vector3(1,0,0);93this.repulsionVRight = new THREE.Vector3(-1,0,0);94this.repulsionVFront = new THREE.Vector3(0,0,1);95this.repulsionVScale = 4.0;96this.repulsionAmount = 0.0;97this.repulsionForce = new THREE.Vector3();9899this.fallVector = new THREE.Vector3(0,-20,0);100101this.resetPos = null;102this.resetRot = null;103104this.key = {105forward: false,106backward: false,107left: false,108right: false,109ltrigger: false,110rtrigger: false,111use: false112};113114this.collision = {115front: false,116left: false,117right: false118};119120this.touchController = null;121this.orientationController = null;122this.gamepadController = null123124if(ctx.controlType == 1 && bkcore.controllers.TouchController.isCompatible())125{126this.touchController = new bkcore.controllers.TouchController(127domElement, ctx.width/2,128function(state, touch, event){129if(event.touches.length >= 4)130window.location.reload(false);131else if(event.touches.length == 3)132ctx.restart();133// touch was on the right-hand side of the screen134else if (touch.clientX > (ctx.width / 2)) {135if (event.type === 'touchend')136self.key.forward = false;137else138self.key.forward = true;139}140});141}142else if(ctx.controlType == 4 && bkcore.controllers.OrientationController.isCompatible())143{144this.orientationController = new bkcore.controllers.OrientationController(145domElement, true,146function(state, touch, event){147if(event.touches.length >= 4)148window.location.reload(false);149else if(event.touches.length == 3)150ctx.restart();151else if(event.touches.length < 1)152self.key.forward = false;153else154self.key.forward = true;155});156}157else if(ctx.controlType == 3 && bkcore.controllers.GamepadController.isCompatible())158{159this.gamepadController = new bkcore.controllers.GamepadController(160function(controller){161if (controller.select)162ctx.restart();163else164self.key.forward = controller.acceleration > 0;165self.key.ltrigger = controller.ltrigger > 0;166self.key.rtrigger = controller.rtrigger > 0;167self.key.left = controller.lstickx < -0.1;168self.key.right = controller.lstickx > 0.1;169});170}171else if(ctx.controlType == 2)172{173if(Leap == null)174throw new Error("Unable to reach LeapJS!");175176var leapInfo = this.leapInfo = document.getElementById('leapinfo');177isServerConnected = false;178var lb = this.leapBridge = {179isConnected: true,180hasHands: false,181palmNormal: [0, 0, 0]182};183184function updateInfo()185{186if(!isServerConnected)187{188leapInfo.innerHTML = 'Waiting for the Leap Motion Controller server...'189leapInfo.style.display = 'block';190}191else if(lb.isConnected && lb.hasHands)192{193leapInfo.style.display = 'none';194}195else if(!lb.isConnected)196{197leapInfo.innerHTML = 'Please connect your Leap Motion Controller.'198leapInfo.style.display = 'block';199}200else if(!lb.hasHands)201{202leapInfo.innerHTML = 'Put your hand over the Leap Motion Controller to play.'203leapInfo.style.display = 'block';204}205}206updateInfo();207208var lc = this.leapController = new Leap.Controller({enableGestures: false});209lc.on('connect', function()210{211isServerConnected = true;212updateInfo();213});214lc.on('deviceConnected', function()215{216lb.isConnected = true;217updateInfo();218});219lc.on('deviceDisconnected', function()220{221lb.isConnected = false;222updateInfo();223});224lc.on('frame', function(frame)225{226if(!lb.isConnected) return;227hand = frame.hands[0];228if(typeof hand === 'undefined')229{230if(lb.hasHands)231{232lb.hasHands = false;233updateInfo();234}235lb.palmNormal = [0, 0, 0];236}237else238{239if(!lb.hasHands)240{241lb.hasHands = true;242updateInfo();243}244lb.palmNormal = hand.palmNormal;245}246});247lc.connect();248}249250function onKeyDown(event)251{252switch(event.keyCode)253{254case 38: /*up*/ self.key.forward = true; break;255256case 40: /*down*/self.key.backward = true; break;257258case 37: /*left*/self.key.left = true; break;259260case 39: /*right*/self.key.right = true; break;261262case 81: /*Q*/self.key.ltrigger = true; break;263case 65: /*A*/self.key.ltrigger = true; break;264265case 68: /*D*/self.key.rtrigger = true; break;266case 69: /*E*/self.key.rtrigger = true; break;267}268};269270function onKeyUp(event)271{272switch(event.keyCode)273{274case 38: /*up*/ self.key.forward = false; break;275276case 40: /*down*/self.key.backward = false; break;277278case 37: /*left*/self.key.left = false; break;279280case 39: /*right*/self.key.right = false; break;281282case 81: /*Q*/self.key.ltrigger = false; break;283case 65: /*A*/self.key.ltrigger = false; break;284285case 68: /*D*/self.key.rtrigger = false; break;286case 69: /*E*/self.key.rtrigger = false; break;287}288};289290domElement.addEventListener('keydown', onKeyDown, false);291domElement.addEventListener('keyup', onKeyUp, false);292};293294bkcore.hexgl.ShipControls.prototype.control = function(threeMesh)295{296this.mesh = threeMesh;297this.mesh.martixAutoUpdate = false;298this.dummy.position = this.mesh.position;299};300301bkcore.hexgl.ShipControls.prototype.reset = function(position, rotation)302{303this.resetPos = position;304this.resetRot = rotation;305this.movement.set(0,0,0);306this.rotation.copy(rotation);307this.roll = 0.0;308this.drift = 0.0;309this.speed = 0.0;310this.speedRatio = 0.0;311this.boost = 0.0;312this.shield = this.maxShield;313this.destroyed = false;314315this.dummy.position.copy(position);316this.quaternion.set(rotation.x, rotation.y, rotation.z, 1).normalize();317this.dummy.quaternion.set(0,0,0,1);318this.dummy.quaternion.multiplySelf(this.quaternion);319320this.dummy.matrix.setPosition(this.dummy.position);321this.dummy.matrix.setRotationFromQuaternion(this.dummy.quaternion);322323this.mesh.matrix.identity();324this.mesh.applyMatrix(this.dummy.matrix);325}326327bkcore.hexgl.ShipControls.prototype.terminate = function()328{329this.destroy();330331if(this.leapController != null)332{333this.leapController.disconnect();334this.leapInfo.style.display = 'none';335}336}337338bkcore.hexgl.ShipControls.prototype.destroy = function()339{340bkcore.Audio.play('destroyed');341bkcore.Audio.stop('bg');342bkcore.Audio.stop('wind');343344this.active = false;345this.destroyed = true;346this.collision.front = false;347this.collision.left = false;348this.collision.right = false;349}350351bkcore.hexgl.ShipControls.prototype.fall = function()352{353this.active = false;354this.collision.front = false;355this.collision.left = false;356this.collision.right = false;357this.falling = true;358_this = this;359setTimeout(function(){360_this.destroyed = true;361}, 1500);362}363364bkcore.hexgl.ShipControls.prototype.update = function(dt)365{366if(this.falling)367{368this.mesh.position.addSelf(this.fallVector);369return;370}371372this.rotation.y = 0;373this.movement.set(0,0,0);374this.drift += (0.0 - this.drift) * this.driftLerp;375this.angular += (0.0 - this.angular) * this.angularLerp * 0.5;376377var rollAmount = 0.0;378var angularAmount = 0.0;379var yawLeap = 0.0;380381if(this.leapBridge != null && this.leapBridge.hasHands)382{383rollAmount -= this.leapBridge.palmNormal[0] * 3.5 * this.rollAngle;384yawLeap = -this.leapBridge.palmNormal[2] * 0.6;385}386387if(this.active)388{389390if(this.touchController != null)391{392angularAmount -= this.touchController.stickVector.x/100 * this.angularSpeed * dt;393rollAmount += this.touchController.stickVector.x/100 * this.rollAngle;394}395else if(this.orientationController != null)396{397angularAmount += this.orientationController.beta/45 * this.angularSpeed * dt;398rollAmount -= this.orientationController.beta/45 * this.rollAngle;399}400else if(this.gamepadController != null && this.gamepadController.updateAvailable())401{402angularAmount -= this.gamepadController.lstickx * this.angularSpeed * dt;403rollAmount += this.gamepadController.lstickx * this.rollAngle;404}405else if(this.leapBridge != null && this.leapBridge.hasHands)406{407angularAmount += this.leapBridge.palmNormal[0] * 2 * this.angularSpeed * dt;408this.speed += Math.max(0.0, (0.5 + this.leapBridge.palmNormal[2])) * 3 * this.thrust * dt;409}410else411{412if(this.key.left)413{414angularAmount += this.angularSpeed * dt;415rollAmount -= this.rollAngle;416}417if(this.key.right)418{419angularAmount -= this.angularSpeed * dt;420rollAmount += this.rollAngle;421}422}423424if(this.key.forward)425this.speed += this.thrust * dt;426else427this.speed -= this.airResist * dt;428if(this.key.ltrigger)429{430if(this.key.left)431angularAmount += this.airAngularSpeed * dt;432else433angularAmount += this.airAngularSpeed * 0.5 * dt;434this.speed -= this.airBrake * dt;435this.drift += (this.airDrift - this.drift) * this.driftLerp;436this.movement.x += this.speed * this.drift * dt;437if(this.drift > 0.0)438this.movement.z -= this.speed * this.drift * dt;439rollAmount -= this.rollAngle * 0.7;440}441if(this.key.rtrigger)442{443if(this.key.right)444angularAmount -= this.airAngularSpeed * dt;445else446angularAmount -= this.airAngularSpeed * 0.5 * dt;447this.speed -= this.airBrake * dt;448this.drift += (-this.airDrift - this.drift) * this.driftLerp;449this.movement.x += this.speed * this.drift * dt;450if(this.drift < 0.0)451this.movement.z += this.speed * this.drift * dt;452rollAmount += this.rollAngle * 0.7;453}454}455456this.angular += (angularAmount - this.angular) * this.angularLerp;457this.rotation.y = this.angular;458459this.speed = Math.max(0.0, Math.min(this.speed, this.maxSpeed));460this.speedRatio = this.speed / this.maxSpeed;461this.movement.z += this.speed * dt;462463if(this.repulsionForce.isZero())464{465this.repulsionForce.set(0,0,0);466}467else468{469if(this.repulsionForce.z != 0.0) this.movement.z = 0;470this.movement.addSelf(this.repulsionForce);471this.repulsionForce.lerpSelf(this.zero, dt > 1.5 ? this.repulsionLerp*2 : this.repulsionLerp);472}473474this.collisionPreviousPosition.copy(this.dummy.position);475476this.boosterCheck(dt);477478//this.movement.multiplyScalar(dt);479//this.rotation.multiplyScalar(dt);480481this.dummy.translateX(this.movement.x);482this.dummy.translateZ(this.movement.z);483484485this.heightCheck(dt);486this.dummy.translateY(this.movement.y);487488this.currentVelocity.copy(this.dummy.position).subSelf(this.collisionPreviousPosition);489490this.collisionCheck(dt);491492this.quaternion.set(this.rotation.x, this.rotation.y, this.rotation.z, 1).normalize();493this.dummy.quaternion.multiplySelf(this.quaternion);494495this.dummy.matrix.setPosition(this.dummy.position);496this.dummy.matrix.setRotationFromQuaternion(this.dummy.quaternion);497498if(this.shield <= 0.0)499{500this.shield = 0.0;501this.destroy();502}503504if(this.mesh != null)505{506this.mesh.matrix.identity();507508// Gradient (Mesh only, no dummy physics impact)509var gradientDelta = (this.gradientTarget - (yawLeap + this.gradient)) * this.gradientLerp;510if(Math.abs(gradientDelta) > this.epsilon) this.gradient += gradientDelta;511if(Math.abs(this.gradient) > this.epsilon)512{513this.gradientAxis.set(1,0,0);514this.mesh.matrix.rotateByAxis(this.gradientAxis, this.gradient);515}516517// Tilting (Idem)518var tiltDelta = (this.tiltTarget - this.tilt) * this.tiltLerp;519if(Math.abs(tiltDelta) > this.epsilon) this.tilt += tiltDelta;520if(Math.abs(this.tilt) > this.epsilon)521{522this.tiltAxis.set(0,0,1);523this.mesh.matrix.rotateByAxis(this.tiltAxis, this.tilt);524}525526// Rolling (Idem)527var rollDelta = (rollAmount - this.roll) * this.rollLerp;528if(Math.abs(rollDelta) > this.epsilon) this.roll += rollDelta;529if(Math.abs(this.roll) > this.epsilon)530{531this.rollAxis.copy(this.rollDirection);532this.mesh.matrix.rotateByAxis(this.rollAxis, this.roll);533}534535this.mesh.applyMatrix(this.dummy.matrix);536this.mesh.updateMatrixWorld(true);537}538539//Update listener position540bkcore.Audio.setListenerPos(this.movement);541bkcore.Audio.setListenerVelocity(this.currentVelocity);542};543544bkcore.hexgl.ShipControls.prototype.teleport = function(pos, quat)545{546this.quaternion.copy(quat);547this.dummy.quaternion.copy(this.quaternion);548549this.dummy.position.copy(pos);550this.dummy.matrix.setPosition(this.dummy.position);551552//console.log(pos.x, pos.y, pos.z);553554this.dummy.matrix.setRotationFromQuaternion(this.dummy.quaternion);555556if(this.mesh != null)557{558this.mesh.matrix.identity();559560// Gradient (Mesh only, no dummy physics impact)561var gradientDelta = (this.gradientTarget - this.gradient) * this.gradientLerp;562if(Math.abs(gradientDelta) > this.epsilon) this.gradient += gradientDelta;563if(Math.abs(this.gradient) > this.epsilon)564{565this.gradientAxis.set(1,0,0);566this.mesh.matrix.rotateByAxis(this.gradientAxis, this.gradient);567}568569// Tilting (Idem)570var tiltDelta = (this.tiltTarget - this.tilt) * this.tiltLerp;571if(Math.abs(tiltDelta) > this.epsilon) this.tilt += tiltDelta;572if(Math.abs(this.tilt) > this.epsilon)573{574this.tiltAxis.set(0,0,1);575this.mesh.matrix.rotateByAxis(this.tiltAxis, this.tilt);576}577578this.mesh.applyMatrix(this.dummy.matrix);579this.mesh.updateMatrixWorld(true);580}581}582583bkcore.hexgl.ShipControls.prototype.boosterCheck = function(dt)584{585if(!this.collisionMap || !this.collisionMap.loaded)586return false;587588this.boost -= this.boosterDecay * dt;589if(this.boost < 0){590this.boost = 0.0;591bkcore.Audio.stop('boost');592}593594var x = Math.round(this.collisionMap.pixels.width/2 + this.dummy.position.x * this.collisionPixelRatio);595var z = Math.round(this.collisionMap.pixels.height/2 + this.dummy.position.z * this.collisionPixelRatio);596var pos = new THREE.Vector3(x, 0, z);597598var color = this.collisionMap.getPixel(x, z);599600if(color.r == 255 && color.g < 127 && color.b < 127) {601bkcore.Audio.play('boost');602this.boost = this.boosterSpeed;603}604605this.movement.z += this.boost * dt;606}607608bkcore.hexgl.ShipControls.prototype.collisionCheck = function(dt)609{610if(!this.collisionDetection || !this.collisionMap || !this.collisionMap.loaded)611return false;612613if(this.shieldDelay > 0)614this.shieldDelay -= dt;615616this.collision.left = false;617this.collision.right = false;618this.collision.front = false;619620var x = Math.round(this.collisionMap.pixels.width/2 + this.dummy.position.x * this.collisionPixelRatio);621var z = Math.round(this.collisionMap.pixels.height/2 + this.dummy.position.z * this.collisionPixelRatio);622var pos = new THREE.Vector3(x, 0, z);623624//console.log({c: this.collisionMap.getPixel(414, 670), d: this.dummy.position, x: x, y: y, p: this.collisionMap.getPixel(x, y)})625626var collision = this.collisionMap.getPixelBilinear(x, z);627628if(collision.r < 255)629{630bkcore.Audio.play('crash');631632// Shield633var sr = (this.getRealSpeed() / this.maxSpeed);634this.shield -= sr * sr * 0.8 * this.shieldDamage;635636// Repulsion637this.repulsionVLeft.set(1,0,0);638this.repulsionVRight.set(-1,0,0);639this.dummy.matrix.rotateAxis(this.repulsionVLeft);640this.dummy.matrix.rotateAxis(this.repulsionVRight);641this.repulsionVLeft.multiplyScalar(this.repulsionVScale);642this.repulsionVRight.multiplyScalar(this.repulsionVScale);643644var lPos = this.repulsionVLeft.addSelf(pos);645var rPos = this.repulsionVRight.addSelf(pos);646var lCol = this.collisionMap.getPixel(Math.round(lPos.x), Math.round(lPos.z)).r;647var rCol = this.collisionMap.getPixel(Math.round(rPos.x), Math.round(rPos.z)).r;648649this.repulsionAmount = Math.max(0.8,650Math.min(this.repulsionCap,651this.speed * this.repulsionRatio652)653);654655if(rCol > lCol)656{// Repulse right657this.repulsionForce.x += -this.repulsionAmount;658this.collision.left = true;659}660else if(rCol < lCol)661{// Repulse left662this.repulsionForce.x += this.repulsionAmount;663this.collision.right = true;664}665else666{667//console.log(collision.r+" -- "+fCol+" @ "+lCol+" / "+rCol);668this.repulsionForce.z += -this.repulsionAmount*4;669this.collision.front = true;670this.speed = 0;671}672673// DIRTY GAMEOVER674if(rCol < 128 && lCol < 128)675{676var fCol = this.collisionMap.getPixel(Math.round(pos.x+2), Math.round(pos.z+2)).r;677if(fCol < 128)678{679console.log('GAMEOVER');680this.fall();681}682}683684this.speed *= this.collisionSpeedDecrease;685this.speed *= (1-this.collisionSpeedDecreaseCoef*(1-collision.r/255));686this.boost = 0;687688return true;689}690else691{692return false;693}694}695696bkcore.hexgl.ShipControls.prototype.heightCheck = function(dt)697{698if(!this.heightMap || !this.heightMap.loaded)699return false;700701var x = this.heightMap.pixels.width/2 + this.dummy.position.x * this.heightPixelRatio;702var z = this.heightMap.pixels.height/2 + this.dummy.position.z * this.heightPixelRatio;703var height = this.heightMap.getPixelFBilinear(x, z) / this.heightScale + this.heightBias;704705var color = this.heightMap.getPixel(x, z);706707if(height < 16777)708{709var delta = (height - this.dummy.position.y);710711if(delta > 0)712{713this.movement.y += delta;714}715else716{717this.movement.y += delta * this.heightLerp;718}719}720721// gradient722this.gradientVector.set(0,0,5);723this.dummy.matrix.rotateAxis(this.gradientVector);724this.gradientVector.addSelf(this.dummy.position);725726x = this.heightMap.pixels.width/2 + this.gradientVector.x * this.heightPixelRatio;727z = this.heightMap.pixels.height/2 + this.gradientVector.z * this.heightPixelRatio;728729var nheight = this.heightMap.getPixelFBilinear(x, z) / this.heightScale + this.heightBias;730731if(nheight < 16777)732this.gradientTarget = -Math.atan2(nheight-height, 5.0)*this.gradientScale;733734// tilt735this.tiltVector.set(5,0,0);736this.dummy.matrix.rotateAxis(this.tiltVector);737this.tiltVector.addSelf(this.dummy.position);738739x = this.heightMap.pixels.width/2 + this.tiltVector.x * this.heightPixelRatio;740z = this.heightMap.pixels.height/2 + this.tiltVector.z * this.heightPixelRatio;741742nheight = this.heightMap.getPixelFBilinear(x, z) / this.heightScale + this.heightBias;743744if(nheight >= 16777) // If right project out of bounds, try left projection745{746this.tiltVector.subSelf(this.dummy.position).multiplyScalar(-1).addSelf(this.dummy.position);747748x = this.heightMap.pixels.width/2 + this.tiltVector.x * this.heightPixelRatio;749z = this.heightMap.pixels.height/2 + this.tiltVector.z * this.heightPixelRatio;750751nheight = this.heightMap.getPixelFBilinear(x, z) / this.heightScale + this.heightBias;752}753754if(nheight < 16777)755this.tiltTarget = Math.atan2(nheight-height, 5.0)*this.tiltScale;756};757758bkcore.hexgl.ShipControls.prototype.getRealSpeed = function(scale)759{760return Math.round(761(this.speed+this.boost)762* (scale == undefined ? 1 : scale)763);764};765766bkcore.hexgl.ShipControls.prototype.getRealSpeedRatio = function()767{768return Math.min(769this.maxSpeed,770this.speed+this.boost771) / this.maxSpeed;772};773774bkcore.hexgl.ShipControls.prototype.getSpeedRatio = function()775{776return (this.speed+this.boost)/ this.maxSpeed;777};778779bkcore.hexgl.ShipControls.prototype.getBoostRatio = function()780{781return this.boost / this.boosterSpeed;782};783784bkcore.hexgl.ShipControls.prototype.getShieldRatio = function()785{786return this.shield / this.maxShield;787};788789bkcore.hexgl.ShipControls.prototype.getShield = function(scale)790{791return Math.round(792this.shield793* (scale == undefined ? 1 : scale)794);795};796797bkcore.hexgl.ShipControls.prototype.getPosition = function()798{799return this.dummy.position;800}801802bkcore.hexgl.ShipControls.prototype.getQuaternion = function()803{804return this.dummy.quaternion;805}806807808