New Tutorial and entity scripts
Showing the changes to scripts/tutorials/entity_scripts (2 of 7 files). Show all
Unified · Split
| @@ -0,0 +1,116 @@ | ||
| 1 | +// | |
| 2 | +// springHold.js | |
| 3 | +// | |
| 4 | +// Created by Philip Rosedale on March 18, 2017 | |
| 5 | +// Copyright 2017 High Fidelity, Inc. | |
| 6 | +// | |
| 7 | +// Attach this entity script to a model or basic shape and grab it. The object will | |
| 8 | +// follow your hand like a spring, allowing you, for example, to swordfight with others. | |
| 9 | +// | |
| 10 | +// Distributed under the Apache License, Version 2.0. | |
| 11 | +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html | |
| 12 | +// | |
| 13 | + | |
| 14 | +(function() { | |
| 15 | + | |
| 16 | + var TIMESCALE = 0.03; | |
| 17 | + var ACTION_TTL = 10; | |
| 18 | + var _this; | |
| 19 | + | |
| 20 | + function SpringHold() { | |
| 21 | + _this = this; | |
| 22 | + } | |
| 23 | + | |
| 24 | + function updateSpringAction(timescale) { | |
| 25 | + | |
| 26 | + var targetProps = Entities.getEntityProperties(_this.entityID); | |
| 27 | + var props = { | |
| 28 | + targetPosition: targetProps.position, | |
| 29 | + targetRotation: targetProps.rotation, | |
| 30 | + linearTimeScale: timescale, | |
| 31 | + angularTimeScale: timescale, | |
| 32 | + ttl: ACTION_TTL | |
| 33 | + }; | |
| 34 | + Entities.updateAction(_this.copy, _this.actionID, props); | |
| 35 | + } | |
| 36 | + | |
| 37 | + function createSpringAction(timescale) { | |
| 38 | + | |
| 39 | + var targetProps = Entities.getEntityProperties(_this.entityID); | |
| 40 | + var props = { | |
| 41 | + targetPosition: targetProps.position, | |
| 42 | + targetRotation: targetProps.rotation, | |
| 43 | + linearTimeScale: timescale, | |
| 44 | + angularTimeScale: timescale, | |
| 45 | + ttl: ACTION_TTL | |
| 46 | + }; | |
| 47 | + _this.actionID = Entities.addAction("spring", _this.copy, props); | |
| 48 | + } | |
| 49 | + | |
| 50 | + function createCopy() { | |
| 51 | + var originalProps = Entities.getEntityProperties(_this.entityID); | |
| 52 | + var props = { | |
| 53 | + type: originalProps.type, | |
| 54 | + color: originalProps.color, | |
| 55 | + modelURL: originalProps.modelURL, | |
| 56 | + dimensions: originalProps.dimensions, | |
| 57 | + dynamic: true, | |
| 58 | + damping: 0.0, | |
| 59 | + angularDamping: 0.0, | |
| 60 | + collidesWith: 'dynamic,static,kinematic', | |
| 61 | + rotation: originalProps.rotation, | |
| 62 | + position: originalProps.position, | |
| 63 | + shapeType: originalProps.shapeType, | |
| 64 | + visible: true, | |
| 65 | + userData:JSON.stringify({ | |
| 66 | + grabbableKey:{ | |
| 67 | + grabbable:false | |
| 68 | + } | |
| 69 | + }) | |
| 70 | + }; | |
| 71 | + _this.copy = Entities.addEntity(props); | |
| 72 | + } | |
| 73 | + | |
| 74 | + function deleteCopy() { | |
| 75 | + Entities.deleteEntity(_this.copy); | |
| 76 | + } | |
| 77 | + | |
| 78 | + function makeOriginalInvisible() { | |
| 79 | + Entities.editEntity(_this.entityID, { | |
| 80 | + visible: false, | |
| 81 | + collisionless: true | |
| 82 | + }); | |
| 83 | + } | |
| 84 | + | |
| 85 | + function makeOriginalVisible() { | |
| 86 | + Entities.editEntity(_this.entityID, { | |
| 87 | + visible: true, | |
| 88 | + collisionless: false | |
| 89 | + }); | |
| 90 | + } | |
| 91 | + | |
| 92 | + function deleteSpringAction() { | |
| 93 | + Entities.deleteAction(_this.copy, _this.actionID); | |
| 94 | + } | |
| 95 | + | |
| 96 | + SpringHold.prototype = { | |
| 97 | + preload: function(entityID) { | |
| 98 | + _this.entityID = entityID; | |
| 99 | + }, | |
| 100 | + startNearGrab: function(entityID, data) { | |
| 101 | + createCopy(); | |
| 102 | + createSpringAction(TIMESCALE); | |
| 103 | + makeOriginalInvisible(); | |
| 104 | + }, | |
| 105 | + continueNearGrab: function() { | |
| 106 | + updateSpringAction(TIMESCALE); | |
| 107 | + }, | |
| 108 | + releaseGrab: function() { | |
| 109 | + deleteSpringAction(); | |
| 110 | + deleteCopy(); | |
| 111 | + makeOriginalVisible(); | |
| 112 | + } | |
| 113 | + }; | |
| 114 | + | |
| 115 | + return new SpringHold(); | |
| 116 | +}); | |
| @@ -0,0 +1,206 @@ | ||
| 1 | +(function() { | |
| 2 | + | |
| 3 | + // touch.js | |
| 4 | + // | |
| 5 | + // Sample file using spring action, haptic vibration, and color change to demonstrate two spheres | |
| 6 | + // That can give a sense of touch to the holders. | |
| 7 | + // Create two standard spheres, make them grabbable, and attach this entity script. Grab them and touch them together. | |
| 8 | + // | |
| 9 | + // Created by Philip Rosedale on March 18, 2017 | |
| 10 | + // Copyright 2017 High Fidelity, Inc. | |
| 11 | + // | |
| 12 | + // Distributed under the Apache License, Version 2.0. | |
| 13 | + // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html | |
| 14 | + // | |
| 15 | + | |
| 16 | + var TIMESCALE = 0.03; | |
| 17 | + var ACTION_TTL = 10; | |
| 18 | + var _this; | |
| 19 | + | |
| 20 | + var RIGHT_HAND = 1; | |
| 21 | + var LEFT_HAND = 0; | |
| 22 | + var HAPTIC_PULSE_FIRST_STRENGTH = 0.5; | |
| 23 | + var HAPTIC_PULSE_MIN_STRENGTH = 0.20; | |
| 24 | + var HAPTIC_PULSE_MAX_STRENGTH = 0.5; | |
| 25 | + var HAPTIC_PULSE_FIRST_DURATION = 1.0; | |
| 26 | + var HAPTIC_PULSE_DURATION = 16.0; | |
| 27 | + var HAPTIC_PULSE_DISTANCE = 0.0; | |
| 28 | + var MAX_PENETRATION = 0.02; | |
| 29 | + var HAPTIC_MIN_VELOCITY = 0.002; | |
| 30 | + var HAPTIC_MAX_VELOCITY = 0.5; | |
| 31 | + var PENETRATION_PULLBACK_FACTOR = 0.65; | |
| 32 | + var FRAME_TIME = 0.016; | |
| 33 | + | |
| 34 | + var isColliding = false; | |
| 35 | + | |
| 36 | + var hand = LEFT_HAND; | |
| 37 | + var lastHapticPulseLocation = { x:0, y:0, z:0 }; | |
| 38 | + | |
| 39 | + | |
| 40 | + var GRAY = { red: 128, green: 128, blue: 128 }; | |
| 41 | + var RED = { red: 255, green: 0, blue: 0 }; | |
| 42 | + | |
| 43 | + var targetColor = { x: GRAY.red, y: GRAY.green, z: GRAY.blue }; | |
| 44 | + | |
| 45 | + var lastPosition = { x: 0, y: 0, z: 0 }; | |
| 46 | + var velocity = { x: 0, y: 0, z: 0 }; | |
| 47 | + | |
| 48 | + var lastOtherPosition = { x: 0, y: 0, z: 0 }; | |
| 49 | + var otherVelocity = { x: 0, y: 0, z: 0 }; | |
| 50 | + | |
| 51 | + | |
| 52 | + function TouchExample() { | |
| 53 | + _this = this; | |
| 54 | + } | |
| 55 | + | |
| 56 | + function updateSpringAction(timescale) { | |
| 57 | + var targetProps = Entities.getEntityProperties(_this.entityID); | |
| 58 | + // | |
| 59 | + // Look for nearby entities to touch | |
| 60 | + // | |
| 61 | + var copyProps = Entities.getEntityProperties(_this.copy); | |
| 62 | + var nearbyEntities = Entities.findEntities(copyProps.position, copyProps.dimensions.x * 2); | |
| 63 | + var wasColliding = isColliding; | |
| 64 | + var targetAdjust = { x: 0, y: 0, z: 0 }; | |
| 65 | + | |
| 66 | + isColliding = false; | |
| 67 | + for (var i = 0; i < nearbyEntities.length; i++) { | |
| 68 | + if (_this.copy != nearbyEntities[i] && _this.entityID != nearbyEntities[i]) { | |
| 69 | + var otherProps = Entities.getEntityProperties(nearbyEntities[i]); | |
| 70 | + var penetration = Vec3.distance(copyProps.position, otherProps.position) - (copyProps.dimensions.x / 2 + otherProps.dimensions.x / 2); | |
| 71 | + if (otherProps.type === 'Sphere' && penetration < 0 && penetration > -copyProps.dimensions.x * 3) { | |
| 72 | + isColliding = true; | |
| 73 | + targetAdjust = Vec3.sum(targetAdjust, Vec3.multiply(Vec3.normalize(Vec3.subtract(targetProps.position, otherProps.position)), -penetration * PENETRATION_PULLBACK_FACTOR)); | |
| 74 | + if (!wasColliding && false) { | |
| 75 | + targetColor = { x: RED.red, y: RED.green, z: RED.blue }; | |
| 76 | + } else { | |
| 77 | + targetColor = { x: 200 + Math.min(-penetration / MAX_PENETRATION, 1.0) * 55, y: GRAY.green, z: GRAY.blue }; | |
| 78 | + } | |
| 79 | + if (Vec3.distance(targetProps.position, lastHapticPulseLocation) > HAPTIC_PULSE_DISTANCE || !wasColliding) { | |
| 80 | + if (!wasColliding) { | |
| 81 | + velocity = { x: 0, y: 0, z: 0}; | |
| 82 | + otherVelocity = { x: 0, y: 0, z: 0 }; | |
| 83 | + Controller.triggerHapticPulse(HAPTIC_PULSE_FIRST_STRENGTH, HAPTIC_PULSE_FIRST_DURATION, hand); | |
| 84 | + } else { | |
| 85 | + velocity = Vec3.distance(targetProps.position, lastPosition) / FRAME_TIME; | |
| 86 | + otherVelocity = Vec3.distance(otherProps.position, lastOtherPosition) / FRAME_TIME; | |
| 87 | + var velocityStrength = Math.min(velocity + otherVelocity / HAPTIC_MAX_VELOCITY, 1.0); | |
| 88 | + var strength = HAPTIC_PULSE_MIN_STRENGTH + Math.min(-penetration / MAX_PENETRATION, 1.0) * (HAPTIC_PULSE_MAX_STRENGTH - HAPTIC_PULSE_MIN_STRENGTH); | |
| 89 | + Controller.triggerHapticPulse(velocityStrength * strength, HAPTIC_PULSE_DURATION, hand); | |
| 90 | + } | |
| 91 | + lastPosition = targetProps.position; | |
| 92 | + lastOtherPosition = otherProps.position; | |
| 93 | + lastHapticPulseLocation = targetProps.position; | |
| 94 | + } | |
| 95 | + } | |
| 96 | + } | |
| 97 | + } | |
| 98 | + if ((wasColliding != isColliding) && !isColliding) { | |
| 99 | + targetColor = { x: GRAY.red, y: GRAY.green, z: GRAY.blue }; | |
| 100 | + } | |
| 101 | + // Interpolate color toward target color | |
| 102 | + var currentColor = { x: copyProps.color.red, y: copyProps.color.green, z: copyProps.color.blue }; | |
| 103 | + var newColor = Vec3.sum(currentColor, Vec3.multiply(Vec3.subtract(targetColor, currentColor), 0.1)); | |
| 104 | + Entities.editEntity(_this.copy, { color: { red: newColor.x, green: newColor.y, blue: newColor.z } }); | |
| 105 | + | |
| 106 | + var props = { | |
| 107 | + targetPosition: Vec3.sum(targetProps.position, targetAdjust), | |
| 108 | + targetRotation: targetProps.rotation, | |
| 109 | + linearTimeScale: timescale, | |
| 110 | + angularTimeScale: timescale, | |
| 111 | + ttl: ACTION_TTL | |
| 112 | + }; | |
| 113 | + var success = Entities.updateAction(_this.copy, _this.actionID, props); | |
| 114 | + } | |
| 115 | + | |
| 116 | + function createSpringAction(timescale) { | |
| 117 | + | |
| 118 | + var targetProps = Entities.getEntityProperties(_this.entityID); | |
| 119 | + var props = { | |
| 120 | + targetPosition: targetProps.position, | |
| 121 | + targetRotation: targetProps.rotation, | |
| 122 | + linearTimeScale: timescale, | |
| 123 | + angularTimeScale: timescale, | |
| 124 | + ttl: ACTION_TTL | |
| 125 | + }; | |
| 126 | + _this.actionID = Entities.addAction("spring", _this.copy, props); | |
| 127 | + return; | |
| 128 | + } | |
| 129 | + | |
| 130 | + function createCopy() { | |
| 131 | + var originalProps = Entities.getEntityProperties(_this.entityID); | |
| 132 | + var props = { | |
| 133 | + type: originalProps.type, | |
| 134 | + modelURL: originalProps.modelURL, | |
| 135 | + dimensions: originalProps.dimensions, | |
| 136 | + color: GRAY, | |
| 137 | + dynamic: true, | |
| 138 | + damping: 0.0, | |
| 139 | + angularDamping: 0.0, | |
| 140 | + collidesWith: 'static', | |
| 141 | + rotation: originalProps.rotation, | |
| 142 | + position: originalProps.position, | |
| 143 | + shapeType: originalProps.shapeType, | |
| 144 | + visible: true, | |
| 145 | + userData:JSON.stringify({ | |
| 146 | + grabbableKey:{ | |
| 147 | + grabbable:false | |
| 148 | + } | |
| 149 | + }) | |
| 150 | + }; | |
| 151 | + _this.copy = Entities.addEntity(props); | |
| 152 | + } | |
| 153 | + | |
| 154 | + function deleteCopy() { | |
| 155 | + print("Delete copy"); | |
| 156 | + Entities.deleteEntity(_this.copy); | |
| 157 | + } | |
| 158 | + | |
| 159 | + function makeOriginalInvisible() { | |
| 160 | + Entities.editEntity(_this.entityID, { | |
| 161 | + visible: false, | |
| 162 | + collisionless: true | |
| 163 | + }); | |
| 164 | + } | |
| 165 | + | |
| 166 | + function makeOriginalVisible() { | |
| 167 | + Entities.editEntity(_this.entityID, { | |
| 168 | + visible: true, | |
| 169 | + collisionless: false | |
| 170 | + }); | |
| 171 | + } | |
| 172 | + | |
| 173 | + function deleteSpringAction() { | |
| 174 | + Entities.deleteAction(_this.copy, _this.actionID); | |
| 175 | + } | |
| 176 | + | |
| 177 | + function setHand(position) { | |
| 178 | + if (Vec3.distance(MyAvatar.getLeftPalmPosition(), position) < Vec3.distance(MyAvatar.getRightPalmPosition(), position)) { | |
| 179 | + hand = LEFT_HAND; | |
| 180 | + } else { | |
| 181 | + hand = RIGHT_HAND; | |
| 182 | + } | |
| 183 | + } | |
| 184 | + | |
| 185 | + TouchExample.prototype = { | |
| 186 | + preload: function(entityID) { | |
| 187 | + _this.entityID = entityID; | |
| 188 | + }, | |
| 189 | + startNearGrab: function(entityID, data) { | |
| 190 | + createCopy(); | |
| 191 | + createSpringAction(TIMESCALE); | |
| 192 | + makeOriginalInvisible(); | |
| 193 | + setHand(Entities.getEntityProperties(_this.entityID).position); | |
| 194 | + }, | |
| 195 | + continueNearGrab: function() { | |
| 196 | + updateSpringAction(TIMESCALE); | |
| 197 | + }, | |
| 198 | + releaseGrab: function() { | |
| 199 | + deleteSpringAction(); | |
| 200 | + deleteCopy(); | |
| 201 | + makeOriginalVisible(); | |
| 202 | + } | |
| 203 | + }; | |
| 204 | + | |
| 205 | + return new TouchExample(); | |
| 206 | +}); | |