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 | }); | ||