From 3393b60aba92bfc9caa84a1ce0cdb5cf2ddc5383 Mon Sep 17 00:00:00 2001 From: Eric Levin Date: Thu, 14 May 2015 11:39:04 -0700 Subject: [PATCH] added hydraHockeyGrab.js, which constrains moving object to xz plane and disables rotation --- examples/example/games/hydraGrabHockey.js | 284 ++++++++++++++++++++++ 1 file changed, 284 insertions(+) create mode 100644 examples/example/games/hydraGrabHockey.js diff --git a/examples/example/games/hydraGrabHockey.js b/examples/example/games/hydraGrabHockey.js new file mode 100644 index 0000000000..d995b7014f --- /dev/null +++ b/examples/example/games/hydraGrabHockey.js @@ -0,0 +1,284 @@ +//same as hydraGrab script, but only x-z plane and no rotation +//Also tighter fall off force so can move puck faster + +var addedVelocity, newVelocity, angularVelocity, dT, cameraEntityDistance; +var RIGHT = 1; +var LASER_WIDTH = 3; +var LASER_COLOR = { + red: 50, + green: 150, + blue: 200 +}; +var LASER_HOVER_COLOR = { + red: 200, + green: 50, + blue: 50 +}; + +var DROP_DISTANCE = 5.0; +var DROP_COLOR = { + red: 200, + green: 200, + blue: 200 +}; + +var FULL_STRENGTH = 0.2; +var LASER_LENGTH_FACTOR = 500; +var CLOSE_ENOUGH = 0.001; +var SPRING_RATE = 1.5; +var DAMPING_RATE = 0.8; +var SCREEN_TO_METERS = 0.001; +var DISTANCE_SCALE_FACTOR = 1000 + +var grabSound = SoundCache.getSound("https://hifi-public.s3.amazonaws.com/eric/sounds/CloseClamp.wav"); +var releaseSound = SoundCache.getSound("https://hifi-public.s3.amazonaws.com/eric/sounds/ReleaseClamp.wav"); + +function getRayIntersection(pickRay) { + var intersection = Entities.findRayIntersection(pickRay); + return intersection; +} + + +function controller(side) { + this.triggerHeld = false; + this.triggerThreshold = 0.9; + this.side = side; + this.palm = 2 * side; + this.tip = 2 * side + 1; + this.trigger = side; + this.originalGravity = { + x: 0, + y: 0, + z: 0 + }; + + this.laser = Overlays.addOverlay("line3d", { + start: { + x: 0, + y: 0, + z: 0 + }, + end: { + x: 0, + y: 0, + z: 0 + }, + color: LASER_COLOR, + alpha: 1, + lineWidth: LASER_WIDTH, + anchor: "MyAvatar" + }); + + this.dropLine = Overlays.addOverlay("line3d", { + color: DROP_COLOR, + alpha: 1, + visible: false, + lineWidth: 2 + }); + + + this.update = function(deltaTime) { + this.updateControllerState(); + this.moveLaser(); + this.checkTrigger(); + this.checkEntityIntersection(); + if (this.grabbing) { + this.updateEntity(deltaTime); + } + + this.oldPalmPosition = this.palmPosition; + this.oldTipPosition = this.tipPosition; + } + + this.updateEntity = function(deltaTime) { + this.dControllerPosition = Vec3.subtract(this.palmPosition, this.oldPalmPosition); + this.dControllerPosition.y = 0; + this.cameraEntityDistance = Vec3.distance(Camera.getPosition(), this.currentPosition); + this.targetPosition = Vec3.sum(this.targetPosition, Vec3.multiply(this.dControllerPosition, this.cameraEntityDistance * SCREEN_TO_METERS * DISTANCE_SCALE_FACTOR)); + + this.entityProps = Entities.getEntityProperties(this.grabbedEntity); + this.currentPosition = this.entityProps.position; + this.currentVelocity = this.entityProps.velocity; + + var dPosition = Vec3.subtract(this.targetPosition, this.currentPosition); + this.distanceToTarget = Vec3.length(dPosition); + if (this.distanceToTarget > CLOSE_ENOUGH) { + // compute current velocity in the direction we want to move + this.velocityTowardTarget = Vec3.dot(this.currentVelocity, Vec3.normalize(dPosition)); + this.velocityTowardTarget = Vec3.multiply(Vec3.normalize(dPosition), this.velocityTowardTarget); + // compute the speed we would like to be going toward the target position + + this.desiredVelocity = Vec3.multiply(dPosition, (1.0 / deltaTime) * SPRING_RATE); + // compute how much we want to add to the existing velocity + this.addedVelocity = Vec3.subtract(this.desiredVelocity, this.velocityTowardTarget); + //If target is to far, roll off force as inverse square of distance + if(this.distanceToTarget/ this.cameraEntityDistance > FULL_STRENGTH) { + this.addedVelocity = Vec3.multiply(this.addedVelocity, Math.pow(FULL_STRENGTH/ this.distanceToTarget, 2.0)); + } + this.newVelocity = Vec3.sum(this.currentVelocity, this.addedVelocity); + this.newVelocity = Vec3.subtract(this.newVelocity, Vec3.multiply(this.newVelocity, DAMPING_RATE)); + } else { + this.newVelocity = { + x: 0, + y: 0, + z: 0 + }; + } + Entities.editEntity(this.grabbedEntity, { + velocity: this.newVelocity, + }); + + this.updateDropLine(this.targetPosition); + + } + + + this.updateControllerState = function() { + this.palmPosition = Controller.getSpatialControlPosition(this.palm); + this.tipPosition = Controller.getSpatialControlPosition(this.tip); + this.triggerValue = Controller.getTriggerValue(this.trigger); + } + + this.checkTrigger = function() { + if (this.triggerValue > this.triggerThreshold && !this.triggerHeld) { + this.triggerHeld = true; + } else if (this.triggerValue < this.triggerThreshold && this.triggerHeld) { + this.triggerHeld = false; + if (this.grabbing) { + this.release(); + } + } + } + + + this.updateDropLine = function(position) { + + Overlays.editOverlay(this.dropLine, { + visible: true, + start: { + x: position.x, + y: position.y + DROP_DISTANCE, + z: position.z + }, + end: { + x: position.x, + y: position.y - DROP_DISTANCE, + z: position.z + } + }); + + } + + this.checkEntityIntersection = function() { + + var pickRay = { + origin: this.palmPosition, + direction: Vec3.normalize(Vec3.subtract(this.tipPosition, this.palmPosition)) + }; + var intersection = getRayIntersection(pickRay); + if (intersection.intersects && intersection.properties.collisionsWillMove) { + this.laserWasHovered = true; + if (this.triggerHeld && !this.grabbing) { + this.grab(intersection.entityID); + } + Overlays.editOverlay(this.laser, { + color: LASER_HOVER_COLOR + }); + } else if (this.laserWasHovered) { + this.laserWasHovered = false; + Overlays.editOverlay(this.laser, { + color: LASER_COLOR + }); + } + } + + this.grab = function(entityId) { + this.grabbing = true; + this.grabbedEntity = entityId; + this.entityProps = Entities.getEntityProperties(this.grabbedEntity); + this.targetPosition = this.entityProps.position; + this.currentPosition = this.targetPosition; + this.oldPalmPosition = this.palmPosition; + this.originalGravity = this.entityProps.gravity; + Entities.editEntity(this.grabbedEntity, { + gravity: { + x: 0, + y: 0, + z: 0 + } + }); + Overlays.editOverlay(this.laser, { + visible: false + }); + Audio.playSound(grabSound, { + position: this.entityProps.position, + volume: 0.25 + }); + } + + this.release = function() { + this.grabbing = false; + this.grabbedEntity = null; + Overlays.editOverlay(this.laser, { + visible: true + }); + Overlays.editOverlay(this.dropLine, { + visible: false + }); + + Audio.playSound(releaseSound, { + position: this.entityProps.position, + volume: 0.25 + }); + + // only restore the original gravity if it's not zero. This is to avoid... + // 1. interface A grabs an entity and locally saves off its gravity + // 2. interface A sets the entity's gravity to zero + // 3. interface B grabs the entity and saves off its gravity (which is zero) + // 4. interface A releases the entity and puts the original gravity back + // 5. interface B releases the entity and puts the original gravity back (to zero) + if(vectorIsZero(this.originalGravity)) { + Entities.editEntity(this.grabbedEntity, { + gravity: this.originalGravity + }); + } + } + + this.moveLaser = function() { + var inverseRotation = Quat.inverse(MyAvatar.orientation); + var startPosition = Vec3.multiplyQbyV(inverseRotation, Vec3.subtract(this.palmPosition, MyAvatar.position)); + // startPosition = Vec3.multiply(startPosition, 1 / MyAvatar.scale); + var direction = Vec3.multiplyQbyV(inverseRotation, Vec3.subtract(this.tipPosition, this.palmPosition)); + direction = Vec3.multiply(direction, LASER_LENGTH_FACTOR / (Vec3.length(direction) * MyAvatar.scale)); + var endPosition = Vec3.sum(startPosition, direction); + + Overlays.editOverlay(this.laser, { + start: startPosition, + end: endPosition + }); + + } + + this.cleanup = function() { + Overlays.deleteOverlay(this.laser); + Overlays.deleteOverlay(this.dropLine); + } +} + +function update(deltaTime) { + rightController.update(deltaTime); +} + +function scriptEnding() { + rightController.cleanup(); +} + +function vectorIsZero(v) { + return v.x === 0 && v.y === 0 && v.z === 0; +} + +var rightController = new controller(RIGHT); + + +Script.update.connect(update); +Script.scriptEnding.connect(scriptEnding); \ No newline at end of file