Update bow.js

This commit is contained in:
Ryan Huffman 2016-11-28 16:49:43 -08:00
parent dcd52215d1
commit 53a8a0e45b

View file

@ -9,23 +9,21 @@
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
/*global Script, Controller, SoundCache, Entities, getEntityCustomData, setEntityCustomData, MyAvatar, Vec3, Quat, Messages */
(function() {
Script.include("utils.js");
Script.include("/~/system/libraries/utils.js");
var NULL_UUID = "{00000000-0000-0000-0000-000000000000}";
var NOTCH_ARROW_SOUND_URL = 'http://mpassets.highfidelity.com/32fc6d32-27a2-428e-937e-869f3f05e8e1-v1/notch.wav';
var SHOOT_ARROW_SOUND_URL = 'http://mpassets.highfidelity.com/32fc6d32-27a2-428e-937e-869f3f05e8e1-v1/String_release2.L.wav';
var SHOOT_ARROW_SOUND_URL =
'http://mpassets.highfidelity.com/32fc6d32-27a2-428e-937e-869f3f05e8e1-v1/String_release2.L.wav';
var STRING_PULL_SOUND_URL = 'http://mpassets.highfidelity.com/32fc6d32-27a2-428e-937e-869f3f05e8e1-v1/Bow_draw.1.L.wav';
var ARROW_HIT_SOUND_URL = 'http://mpassets.highfidelity.com/32fc6d32-27a2-428e-937e-869f3f05e8e1-v1/Arrow_impact1.L.wav'
var ARROW_HIT_SOUND_URL = 'http://mpassets.highfidelity.com/32fc6d32-27a2-428e-937e-869f3f05e8e1-v1/Arrow_impact1.L.wav';
var ARROW_DIMENSIONS = {
x: 0.02,
y: 0.02,
z: 0.72
};
var ARROW_OFFSET = -0.44;
var ARROW_TIP_OFFSET = 0.32;
var ARROW_TIP_OFFSET = 0.47;
var ARROW_GRAVITY = {
x: 0,
y: -4.8,
@ -37,11 +35,13 @@
"http://mpassets.highfidelity.com/32fc6d32-27a2-428e-937e-869f3f05e8e1-v1/newarrow_collision_hull.obj";
var ARROW_DIMENSIONS = {
x: 0.02,
y: 0.02,
z: 0.64
x: 0.03,
y: 0.03,
z: 0.96
};
var ARROW_LIFETIME = 15; // seconds
var TOP_NOTCH_OFFSET = 0.6;
var BOTTOM_NOTCH_OFFSET = 0.6;
@ -53,9 +53,10 @@
};
var DRAW_STRING_THRESHOLD = 0.80;
var LEFT_TIP = 1;
var RIGHT_TIP = 3;
var DRAW_STRING_PULL_DELTA_HAPTIC_PULSE = 0.09;
var DRAW_STRING_MAX_DRAW = 0.7;
var NEAR_TO_RELAXED_KNOCK_DISTANCE = 0.5; // if the hand is this close, rez the arrow
var NEAR_TO_RELAXED_SCHMITT = 0.05;
var NOTCH_OFFSET_FORWARD = 0.08;
var NOTCH_OFFSET_UP = 0.035;
@ -65,7 +66,7 @@
max1: 0.6,
min2: 1,
max2: 15
}
};
var USE_DEBOUNCE = false;
@ -95,11 +96,10 @@
}
Bow.prototype = {
stringDrawn: false,
topString: null,
aiming: false,
arrowTipPosition: null,
preNotchString: null,
hasArrowNotched: false,
arrow: null,
stringData: {
currentColor: {
@ -108,6 +108,13 @@
blue: 255
}
},
// 0 = start
// 1 = hand close to knock, arrow is drawn
// 2 = arrow is grabbed
// 3 = arrow is grabbed and pulled
state: 0,
sinceLastUpdate: 0,
preload: function(entityID) {
this.entityID = entityID;
@ -115,11 +122,15 @@
this.shootArrowSound = SoundCache.getSound(SHOOT_ARROW_SOUND_URL);
this.arrowHitSound = SoundCache.getSound(ARROW_HIT_SOUND_URL);
this.arrowNotchSound = SoundCache.getSound(NOTCH_ARROW_SOUND_URL);
var userData = Entities.getEntityProperties(this.entityID).userData;
var userData = Entities.getEntityProperties(this.entityID, ["userData"]).userData;
this.userData = JSON.parse(userData);
var children = Entities.getChildrenIDs(this.entityID);
this.preNotchString = children[0];
children.forEach(function(childID) {
var childName = Entities.getEntityProperties(childID, ["name"]).name;
if (childName == "Hifi-Bow-Pre-Notch-String") {
this.preNotchString = children[0];
}
});
},
unload: function() {
@ -127,37 +138,37 @@
Entities.deleteEntity(this.arrow);
},
startNearGrab: function(entityID, args) {
_this.startEquip(entityID, args);
},
continueNearGrab: function(entityID, args) {
_this.continueEquip(entityID, args);
},
releaseGrab: function(entityID, args) {
_this.releaseEquip(entityID, args);
},
startEquip: function(entityID, args) {
this.hand = args[0];
avatarID = args[1];
// var avatarID = args[1];
//disable the opposite hand in handControllerGrab.js by message
var handToDisable = this.hand === 'right' ? 'left' : 'right';
Messages.sendMessage('Hifi-Hand-Disabler', handToDisable);
if (this.hand === 'left') {
this.getStringHandPosition = function() { return _this.getControllerLocation("right").position; };
} else if (this.hand === 'right') {
this.getStringHandPosition = function() { return _this.getControllerLocation("left").position; };
}
var data = getEntityCustomData('grabbableKey', this.entityID, {});
data.grabbable = false;
setEntityCustomData('grabbableKey', this.entityID, data);
Entities.editEntity(_this.entityID, {
collidesWith: ""
})
});
//make sure the string is ready
if (!this.preNotchString) {
this.createPreNotchString();
}
var preNotchStringProps = Entities.getEntityProperties(this.preNotchString);
if (!preNotchStringProps || preNotchStringProps.name != "Hifi-Bow-Pre-Notch-String") {
this.createPreNotchString();
}
Entities.editEntity(this.preNotchString, {
visible: true
});
},
continueEquip: function(entityID, args) {
this.deltaTime = checkInterval();
//debounce during debugging -- maybe we're updating too fast?
if (USE_DEBOUNCE === true) {
this.sinceLastUpdate = this.sinceLastUpdate + this.deltaTime;
@ -169,23 +180,10 @@
}
}
this.bowProperties = Entities.getEntityProperties(this.entityID);
if (this.aiming === true) {
Entities.editEntity(this.preNotchString, {
visible: false
})
} else {
Entities.editEntity(this.preNotchString, {
visible: true
})
}
this.checkStringHand();
},
releaseEquip: function(entityID, args) {
Messages.sendMessage('Hifi-Hand-Disabler', "none")
Messages.sendMessage('Hifi-Hand-Disabler', "none");
this.stringDrawn = false;
this.deleteStrings();
@ -194,11 +192,9 @@
data.grabbable = true;
setEntityCustomData('grabbableKey', this.entityID, data);
Entities.deleteEntity(this.arrow);
this.aiming = false;
this.hasArrowNotched = false;
Entities.editEntity(_this.entityID, {
collidesWith: "static,dynamic,kinematic,otherAvatar,myAvatar"
})
});
},
createArrow: function() {
@ -212,6 +208,7 @@
compoundShapeURL: ARROW_COLLISION_HULL_URL,
dimensions: ARROW_DIMENSIONS,
position: this.bowProperties.position,
parentID: this.entityID,
dynamic: false,
collisionless: true,
collisionSoundURL: ARROW_HIT_SOUND_URL,
@ -226,12 +223,12 @@
var makeArrowStick = function(entityA, entityB, collision) {
Entities.editEntity(entityA, {
angularVelocity: {
localAngularVelocity: {
x: 0,
y: 0,
z: 0
},
velocity: {
localVelocity: {
x: 0,
y: 0,
z: 0
@ -241,20 +238,40 @@
y: 0,
z: 0
},
position: collision.contactPoint,
dynamic: false
})
Script.removeEventHandler(arrow, "collisionWithEntity", makeArrowStick)
}
parentID: entityB,
dynamic: false,
collisionless: true,
collidesWith: ""
});
Script.removeEventHandler(arrow, "collisionWithEntity", makeArrowStick);
};
Script.addEventHandler(arrow, "collisionWithEntity", makeArrowStick);
return arrow
return arrow;
},
createPreNotchString: function() {
this.preNotchString = Entities.addEntity({
"collisionless": 1,
"dimensions": { "x": 5, "y": 5, "z": 5 },
"ignoreForCollisions": 1,
"linePoints": [ { "x": 0, "y": 0, "z": 0 }, { "x": 0, "y": -1.2, "z": 0 } ],
"lineWidth": 5,
"name": "Hifi-Bow-Pre-Notch-String",
"parentID": this.entityID,
"localPosition": { "x": 0, "y": 0.6, "z": 0.1 },
"localRotation": { "w": 1, "x": 0, "y": 0, "z": 0 },
"type": "Line",
"userData": "{\"grabbableKey\":{\"grabbable\":false}}"
});
},
createStrings: function() {
this.createTopString();
this.createBottomString();
if (!this.topString) {
this.createTopString();
Entities.editEntity(this.preNotchString, { visible: false });
}
},
createTopString: function() {
@ -265,6 +282,8 @@
dimensions: LINE_DIMENSIONS,
dynamic: false,
collisionless: true,
lineWidth: 5,
color: this.stringData.currentColor,
userData: JSON.stringify({
grabbableKey: {
grabbable: false
@ -275,30 +294,17 @@
this.topString = Entities.addEntity(stringProperties);
},
createBottomString: function() {
var stringProperties = {
name: 'Hifi-Bow-Bottom-String',
type: 'Line',
position: Vec3.sum(this.bowProperties.position, BOTTOM_NOTCH_OFFSET),
dimensions: LINE_DIMENSIONS,
dynamic: false,
collisionless: true,
userData: JSON.stringify({
grabbableKey: {
grabbable: false
}
})
};
this.bottomString = Entities.addEntity(stringProperties);
},
deleteStrings: function() {
Entities.deleteEntity(this.topString);
Entities.deleteEntity(this.bottomString);
if (this.topString) {
Entities.deleteEntity(this.topString);
this.topString = null;
Entities.editEntity(this.preNotchString, { visible: true });
}
},
updateStringPositions: function() {
drawStrings: function() {
this.createStrings();
var upVector = Quat.getUp(this.bowProperties.rotation);
var upOffset = Vec3.multiply(upVector, TOP_NOTCH_OFFSET);
var downVector = Vec3.multiply(-1, Quat.getUp(this.bowProperties.rotation));
@ -310,90 +316,94 @@
var bottomStringPosition = Vec3.sum(this.bowProperties.position, downOffset);
this.bottomStringPosition = Vec3.sum(bottomStringPosition, backOffset);
Entities.editEntity(this.topString, {
position: this.topStringPosition
});
Entities.editEntity(this.bottomString, {
position: this.bottomStringPosition
});
},
drawStrings: function() {
this.updateStringPositions();
var lineVectors = this.getLocalLineVectors();
Entities.editEntity(this.topString, {
linePoints: [{
x: 0,
y: 0,
z: 0
}, lineVectors[0]],
lineWidth: 5,
color: this.stringData.currentColor
position: this.topStringPosition,
linePoints: [{ x: 0, y: 0, z: 0 }, lineVectors[0], lineVectors[1]]
});
Entities.editEntity(this.bottomString, {
linePoints: [{
x: 0,
y: 0,
z: 0
}, lineVectors[1]],
lineWidth: 5,
color: this.stringData.currentColor
});
},
getLocalLineVectors: function() {
var topVector = Vec3.subtract(this.arrowRearPosition, this.topStringPosition);
var bottomVector = Vec3.subtract(this.arrowRearPosition, this.bottomStringPosition);
var bottomVector = Vec3.subtract(this.bottomStringPosition, this.topStringPosition);
return [topVector, bottomVector];
},
getControllerLocation: function (controllerHand) {
var standardControllerValue =
(controllerHand === "right") ? Controller.Standard.RightHand : Controller.Standard.LeftHand;
var pose = Controller.getPoseValue(standardControllerValue);
var orientation = Quat.multiply(MyAvatar.orientation, pose.rotation);
var position = Vec3.sum(Vec3.multiplyQbyV(MyAvatar.orientation, pose.translation), MyAvatar.position);
return {position: position, orientation: orientation};
},
checkStringHand: function() {
//invert the hands because our string will be held with the opposite hand of the first one we pick up the bow with
var triggerLookup;
if (this.hand === 'left') {
triggerLookup = 1;
this.getStringHandPosition = MyAvatar.getRightPalmPosition;
} else if (this.hand === 'right') {
this.getStringHandPosition = MyAvatar.getLeftPalmPosition;
triggerLookup = 0;
this.triggerValue = Controller.getValue(TRIGGER_CONTROLS[(this.hand === 'left') ? 1 : 0]);
this.bowProperties = Entities.getEntityProperties(this.entityID);
var notchPosition = this.getNotchPosition(this.bowProperties);
var stringHandPosition = this.getStringHandPosition();
var handToNotch = Vec3.subtract(notchPosition, stringHandPosition);
var pullBackDistance = Vec3.length(handToNotch);
if (this.state === 0) {
this.pullBackDistance = 0;
this.deleteStrings();
if (pullBackDistance < (NEAR_TO_RELAXED_KNOCK_DISTANCE - NEAR_TO_RELAXED_SCHMITT) && !this.backHandBusy) {
//the first time aiming the arrow
var handToDisable = (this.hand === 'right' ? 'left' : 'right');
Messages.sendMessage('Hifi-Hand-Disabler', handToDisable);
this.arrow = this.createArrow();
this.playStringPullSound();
this.state = 1;
}
}
if (this.state === 1) {
this.triggerValue = Controller.getValue(TRIGGER_CONTROLS[triggerLookup]);
if (this.triggerValue < DRAW_STRING_THRESHOLD && this.stringDrawn === true) {
// firing the arrow
this.drawStrings();
this.hasArrowNotched = false;
this.aiming = false;
this.stringDrawn = false;
this.updateArrowPositionInNotch(true);
} else if (this.triggerValue > DRAW_STRING_THRESHOLD && this.stringDrawn === true) {
//continuing to aim the arrow
this.aiming = true;
this.drawStrings();
this.updateArrowPositionInNotch();
} else if (this.triggerValue > DRAW_STRING_THRESHOLD && this.stringDrawn === false) {
this.arrow = this.createArrow();
this.playStringPullSound();
//the first time aiming the arrow
this.stringDrawn = true;
this.createStrings();
this.drawStrings();
this.updateArrowPositionInNotch();
if (pullBackDistance >= (NEAR_TO_RELAXED_KNOCK_DISTANCE + NEAR_TO_RELAXED_SCHMITT)) {
// delete the unpulled arrow
Messages.sendMessage('Hifi-Hand-Disabler', "none");
Entities.deleteEntity(this.arrow);
this.arrow = null;
this.state = 0;
} else if (this.triggerValue >= DRAW_STRING_THRESHOLD) {
// they've grabbed the arrow
this.pullBackDistance = 0;
this.state = 2;
} else {
this.drawStrings();
this.updateArrowPositionInNotch(false, false);
}
}
if (this.state === 2) {
if (this.triggerValue < DRAW_STRING_THRESHOLD) {
// they let go without pulling
this.state = 1;
} else if (pullBackDistance >= (NEAR_TO_RELAXED_KNOCK_DISTANCE + NEAR_TO_RELAXED_SCHMITT)) {
// they've grabbed the arrow and pulled it
this.state = 3;
} else {
this.drawStrings();
this.updateArrowPositionInNotch(false, true);
}
}
if (this.state === 3) {
if (pullBackDistance < (NEAR_TO_RELAXED_KNOCK_DISTANCE + NEAR_TO_RELAXED_SCHMITT)) {
// they unpulled without firing
this.state = 2;
} else if (this.triggerValue < DRAW_STRING_THRESHOLD) {
// they've fired the arrow
Messages.sendMessage('Hifi-Hand-Disabler', "none");
this.updateArrowPositionInNotch(true, true);
this.state = 0;
} else {
this.drawStrings();
this.updateArrowPositionInNotch(false, true);
}
}
},
@ -406,59 +416,71 @@
},
updateArrowPositionInNotch: function(shouldReleaseArrow) {
var bowProperties = Entities.getEntityProperties(this.entityID);
//set the notch that the arrow should go through
getNotchPosition: function(bowProperties) {
var frontVector = Quat.getFront(bowProperties.rotation);
var notchVectorForward = Vec3.multiply(frontVector, NOTCH_OFFSET_FORWARD);
var upVector = Quat.getUp(bowProperties.rotation);
var notchVectorUp = Vec3.multiply(upVector, NOTCH_OFFSET_UP);
var notchPosition;
notchPosition = Vec3.sum(bowProperties.position, notchVectorForward);
var notchPosition = Vec3.sum(bowProperties.position, notchVectorForward);
notchPosition = Vec3.sum(notchPosition, notchVectorUp);
return notchPosition;
},
updateArrowPositionInNotch: function(shouldReleaseArrow, doHapticPulses) {
//set the notch that the arrow should go through
var notchPosition = this.getNotchPosition(this.bowProperties);
//set the arrow rotation to be between the notch and other hand
var stringHandPosition = this.getStringHandPosition();
var handToNotch = Vec3.subtract(notchPosition, stringHandPosition);
var arrowRotation = Quat.rotationBetween(Vec3.FRONT, handToNotch);
var backHand = this.hand === 'left' ? 1 : 0;
var pullBackDistance = Vec3.length(handToNotch);
// pulse as arrow is drawn
if (doHapticPulses &&
Math.abs(pullBackDistance - this.pullBackDistance) > DRAW_STRING_PULL_DELTA_HAPTIC_PULSE) {
Controller.triggerHapticPulse(1, 20, backHand);
this.pullBackDistance = pullBackDistance;
}
// this.changeStringPullSoundVolume(pullBackDistance);
if (pullBackDistance > 0.6) {
pullBackDistance = 0.6;
if (pullBackDistance > DRAW_STRING_MAX_DRAW) {
pullBackDistance = DRAW_STRING_MAX_DRAW;
}
// //pull the arrow back a bit
var pullBackOffset = Vec3.multiply(handToNotch, -pullBackDistance);
var arrowPosition = Vec3.sum(notchPosition, pullBackOffset);
// var pullBackOffset = Vec3.multiply(handToNotch, -pullBackDistance);
// var arrowPosition = Vec3.sum(notchPosition, pullBackOffset);
// // move it forward a bit
var pushForwardOffset = Vec3.multiply(handToNotch, -ARROW_OFFSET);
var finalArrowPosition = Vec3.sum(arrowPosition, pushForwardOffset);
// // // move it forward a bit
// var pushForwardOffset = Vec3.multiply(handToNotch, -ARROW_OFFSET);
// var finalArrowPosition = Vec3.sum(arrowPosition, pushForwardOffset);
//we draw strings to the rear of the arrow
this.setArrowRearPosition(finalArrowPosition, arrowRotation);
// this.setArrowRearPosition(finalArrowPosition, arrowRotation);
var halfArrowVec = Vec3.multiply(Vec3.normalize(handToNotch), ARROW_DIMENSIONS.z / 2.0);
var arrowPosition = Vec3.sum(stringHandPosition, halfArrowVec);
this.setArrowRearPosition(arrowPosition, arrowRotation);
//if we're not shooting, we're updating the arrow's orientation
if (shouldReleaseArrow !== true) {
Entities.editEntity(this.arrow, {
position: finalArrowPosition,
position: arrowPosition,
rotation: arrowRotation
})
});
}
//shoot the arrow
if (shouldReleaseArrow === true) {
var arrowProps = Entities.getEntityProperties(this.arrow);
if (shouldReleaseArrow === true) { // && pullBackDistance >= (NEAR_TO_RELAXED_KNOCK_DISTANCE + NEAR_TO_RELAXED_SCHMITT)) {
var arrowAge = Entities.getEntityProperties(this.arrow, ["age"]).age;
//scale the shot strength by the distance you've pulled the arrow back and set its release velocity to be in the direction of the v
//scale the shot strength by the distance you've pulled the arrow back and set its release velocity to be
// in the direction of the v
var arrowForce = this.scaleArrowShotStrength(pullBackDistance);
var handToNotch = Vec3.normalize(handToNotch);
var handToNotchNorm = Vec3.normalize(handToNotch);
var releaseVelocity = Vec3.multiply(handToNotch, arrowForce);
var releaseVelocity = Vec3.multiply(handToNotchNorm, arrowForce);
// var releaseVelocity2 = Vec3.multiply()
//make the arrow physical, give it gravity, a lifetime, and set our velocity
@ -467,27 +489,37 @@
collisionless: false,
collidesWith: "static,dynamic,otherAvatar", // workaround: not with kinematic --> no collision with bow
velocity: releaseVelocity,
parentID: NULL_UUID,
gravity: ARROW_GRAVITY,
lifetime: 10,
// position: arrowProps.position,
// rotation: arrowProps.rotation
lifetime: ARROW_LIFETIME + arrowAge,
};
//actually shoot the arrow and play its sound
Entities.editEntity(this.arrow, arrowProperties);
this.playShootArrowSound();
var backHand = this.hand === 'left' ? 1 : 0;
var haptic = Controller.triggerShortHapticPulse(1, backHand);
Controller.triggerShortHapticPulse(1, backHand);
//clear the strings back to only the single straight one
this.deleteStrings();
Entities.editEntity(this.preNotchString, {
visible: true
Entities.addAction("travel-oriented", this.arrow, {
forward: { x: 0, y: 0, z: -1 },
angularTimeScale: 0.1,
tag: "arrow from hifi-bow",
ttl: ARROW_LIFETIME
});
}
} // else if (shouldReleaseArrow === true) {
// they released without pulling back; just delete the arrow.
// Entities.deleteEntity(this.arrow);
// this.arrow = null;
// }
// if (shouldReleaseArrow === true) {
// //clear the strings back to only the single straight one
// Entities.editEntity(this.preNotchString, {
// visible: true
// });
// }
},
@ -527,7 +559,7 @@
var audioProperties = {
volume: this.scaleSoundVolume(pullBackDistance),
position: this.bowProperties.position
}
};
this.stringPullInjector.options = audioProperties;
},
@ -537,9 +569,37 @@
var min2 = 0;
var max2 = 0.2;
return min2 + (max2 - min2) * ((value - min1) / (max1 - min1));
}
},
handleMessages: function(channel, message, sender) {
if (sender !== MyAvatar.sessionUUID) {
return;
}
if (channel !== 'Hifi-Object-Manipulation') {
return;
}
try {
var data = JSON.parse(message);
var action = data.action;
var hand = data.joint;
var isBackHand = ((_this.hand == "left" && hand == "RightHand") ||
(_this.hand == "right" && hand == "LeftHand"));
if ((action == "equip" || action == "grab") && isBackHand) {
_this.backHandBusy = true;
}
if (action == "release" && isBackHand) {
_this.backHandBusy = false;
}
} catch (e) {
print("WARNING: bow.js -- error parsing Hifi-Object-Manipulation message: " + message);
}
}
};
return new Bow();
var bow = new Bow();
Messages.subscribe('Hifi-Object-Manipulation');
Messages.messageReceived.connect(bow.handleMessages);
return bow;
});