mirror of
https://github.com/overte-org/overte.git
synced 2025-04-30 01:42:40 +02:00
651 lines
22 KiB
JavaScript
651 lines
22 KiB
JavaScript
"use strict";
|
|
//
|
|
// makeUserConnetion.js
|
|
// scripts/system
|
|
//
|
|
// Created by David Kelly on 3/7/2017.
|
|
// Copyright 2017 High Fidelity, Inc.
|
|
//
|
|
// Distributed under the Apache License, Version 2.0.
|
|
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
|
//
|
|
(function() { // BEGIN LOCAL_SCOPE
|
|
|
|
const version = 0.1;
|
|
const label = "makeUserConnection";
|
|
const MAX_AVATAR_DISTANCE = 1.25;
|
|
const GRIP_MIN = 0.05;
|
|
const MESSAGE_CHANNEL = "io.highfidelity.makeUserConnection";
|
|
const STATES = {
|
|
inactive : 0,
|
|
waiting: 1,
|
|
friending: 2,
|
|
makingFriends: 3
|
|
};
|
|
const STATE_STRINGS = ["inactive", "waiting", "friending", "makingFriends"];
|
|
const WAITING_INTERVAL = 100; // ms
|
|
const FRIENDING_INTERVAL = 100; // ms
|
|
const MAKING_FRIENDS_TIMEOUT = 1000; // ms
|
|
const FRIENDING_TIME = 2000; // ms
|
|
const FRIENDING_HAPTIC_STRENGTH = 0.5;
|
|
const FRIENDING_SUCCESS_HAPTIC_STRENGTH = 1.0;
|
|
const HAPTIC_DURATION = 20;
|
|
const MODEL_URL = "http://hifi-content.s3.amazonaws.com/alan/dev/Test/sphere-3-color.fbx";
|
|
const TEXTURES = [
|
|
{"Texture": "http://hifi-content.s3.amazonaws.com/alan/dev/Test/sphere-3-color.fbx/sphere-3-color.fbm/green-50pct-opaque-64.png"},
|
|
{"Texture": "http://hifi-content.s3.amazonaws.com/alan/dev/Test/sphere-3-color.fbx/sphere-3-color.fbm/blue-50pct-opaque-64.png"},
|
|
{"Texture": "http://hifi-content.s3.amazonaws.com/alan/dev/Test/sphere-3-color.fbx/sphere-3-color.fbm/red-50pct-opaque-64.png"}
|
|
];
|
|
const PARTICLE_EFFECT_PROPS = {
|
|
"color": {
|
|
"blue": 0,
|
|
"green": 104,
|
|
"red": 0
|
|
},
|
|
"colorFinish": {
|
|
"blue": 172,
|
|
"green": 75,
|
|
"red": 255
|
|
},
|
|
"colorStart": {
|
|
"blue": 0,
|
|
"green": 104,
|
|
"red": 255
|
|
},
|
|
"dimensions": {
|
|
"x": 0.03,
|
|
"y": 0.03,
|
|
"z": 0.03
|
|
},
|
|
"emitOrientation": {
|
|
"w": 0.707,
|
|
"x": -0.707,
|
|
"y": 0.0,
|
|
"z": 0.0
|
|
},
|
|
"emitRate": 360,
|
|
"emitSpeed": 0.0003,
|
|
"emitterShouldTrail": 1,
|
|
"maxParticles": 1370,
|
|
"polarFinish": 1,
|
|
"radiusSpread": 9,
|
|
"radiusStart": 0.04,
|
|
"radiusFinish": 0.02,
|
|
"speedSpread": 0.09,
|
|
"textures": "http://hifi-content.s3.amazonaws.com/alan/dev/Particles/Bokeh-Particle.png",
|
|
"type": "ParticleEffect"
|
|
};
|
|
|
|
var currentHand;
|
|
var state = STATES.inactive;
|
|
var friendingInterval;
|
|
var waitingInterval;
|
|
var makingFriendsTimeout;
|
|
var overlay;
|
|
var animHandlerId;
|
|
var entityDimensionMultiplier = 1.0;
|
|
var friendingId;
|
|
var friendingHand;
|
|
var waitingList = {};
|
|
var particleEffect;
|
|
var waitingBallScale;
|
|
|
|
function debug() {
|
|
var stateString = "<" + STATE_STRINGS[state] + ">";
|
|
var versionString = "v" + version;
|
|
var friending = "[" + friendingId + "/" + friendingHand + "]";
|
|
print.apply(null, [].concat.apply([label, versionString, stateString, JSON.stringify(waitingList), friending], [].map.call(arguments, JSON.stringify)));
|
|
}
|
|
|
|
function handToString(hand) {
|
|
if (hand === Controller.Standard.RightHand) {
|
|
return "RightHand";
|
|
} else if (hand === Controller.Standard.LeftHand) {
|
|
return "LeftHand";
|
|
}
|
|
return "";
|
|
}
|
|
|
|
function stringToHand(hand) {
|
|
if (hand == "RightHand") {
|
|
return Controller.Standard.RightHand;
|
|
} else if (hand == "LeftHand") {
|
|
return Controller.Standard.LeftHand;
|
|
}
|
|
debug("stringToHand called with bad hand string:", hand);
|
|
return 0;
|
|
}
|
|
|
|
function handToHaptic(hand) {
|
|
if (hand === Controller.Standard.RightHand) {
|
|
return 1;
|
|
} else if (hand === Controller.Standard.LeftHand) {
|
|
return 0;
|
|
}
|
|
return -1;
|
|
}
|
|
|
|
function stopWaiting() {
|
|
if (waitingInterval) {
|
|
waitingInterval = Script.clearInterval(waitingInterval);
|
|
}
|
|
}
|
|
|
|
function stopFriending() {
|
|
if (friendingInterval) {
|
|
friendingInterval = Script.clearInterval(friendingInterval);
|
|
}
|
|
}
|
|
|
|
function stopMakingFriends() {
|
|
if (makingFriendsTimeout) {
|
|
makingFriendsTimeout = Script.clearTimeout(makingFriendsTimeout);
|
|
}
|
|
}
|
|
|
|
// This returns the position of the palm, really. Which relies on the avatar
|
|
// having the expected middle1 joint. TODO: fallback for when this isn't part
|
|
// of the avatar?
|
|
function getHandPosition(avatar, hand) {
|
|
if (!hand) {
|
|
debug("calling getHandPosition with no hand! (returning avatar position but this is a BUG)");
|
|
debug(new Error().stack);
|
|
return avatar.position;
|
|
}
|
|
var jointName = handToString(hand) + "Middle1";
|
|
return avatar.getJointPosition(avatar.getJointIndex(jointName));
|
|
}
|
|
|
|
function shakeHandsAnimation(animationProperties) {
|
|
// all we are doing here is moving the right hand to a spot
|
|
// that is in front of and a bit above the hips. Basing how
|
|
// far in front as scaling with the avatar's height (say hips
|
|
// to head distance)
|
|
var headIndex = MyAvatar.getJointIndex("Head");
|
|
var offset = 0.5; // default distance of hand in front of you
|
|
var result = {};
|
|
if (headIndex) {
|
|
offset = 0.8 * MyAvatar.getAbsoluteJointTranslationInObjectFrame(headIndex).y;
|
|
}
|
|
var handPos = Vec3.multiply(offset, {x: -0.25, y: 0.8, z: 1.3});
|
|
result.rightHandPosition = handPos;
|
|
result.rightHandRotation = Quat.fromPitchYawRollDegrees(90, 0, 90);
|
|
return result;
|
|
}
|
|
|
|
function positionFractionallyTowards(posA, posB, frac) {
|
|
return Vec3.sum(posA, Vec3.multiply(frac, Vec3.subtract(posB, posA)));
|
|
}
|
|
|
|
// this is called frequently, but usually does nothing
|
|
function updateVisualization() {
|
|
if (state != STATES.waiting) {
|
|
if (overlay) {
|
|
overlay = Overlays.deleteOverlay(overlay);
|
|
}
|
|
}
|
|
if (state == STATES.inactive || state == STATES.waiting) {
|
|
if (particleEffect) {
|
|
particleEffect = Entities.deleteEntity(particleEffect);
|
|
}
|
|
}
|
|
if (state == STATES.inactive) {
|
|
return;
|
|
}
|
|
|
|
var textures = TEXTURES[state-1];
|
|
var position = getHandPosition(MyAvatar, currentHand);
|
|
|
|
// TODO: make the size scale with avatar, up to
|
|
// the actual size of MAX_AVATAR_DISTANCE
|
|
var wrist = MyAvatar.getJointPosition(MyAvatar.getJointIndex(handToString(currentHand)));
|
|
var d = Math.abs(entityDimensionMultiplier) * Vec3.distance(wrist, position);
|
|
if (friendingId) {
|
|
// put the position between the 2 hands, if we have a friendingId
|
|
var other = AvatarList.getAvatar(friendingId);
|
|
if (other) {
|
|
var otherHand = getHandPosition(other, stringToHand(friendingHand));
|
|
position = positionFractionallyTowards(position, otherHand, entityDimensionMultiplier);
|
|
}
|
|
}
|
|
if (state == STATES.waiting) {
|
|
var dimension = {x: d, y: d, z: d};
|
|
if (!overlay) {
|
|
waitingBallScale = 1.0/32.0;
|
|
var props = {
|
|
url: MODEL_URL,
|
|
position: position,
|
|
dimensions: Vec3.multiply(waitingBallScale, dimension),
|
|
textures: textures
|
|
};
|
|
overlay = Overlays.addOverlay("model", props);
|
|
} else {
|
|
waitingBallScale = Math.min(1.0, waitingBallScale * 1.1);
|
|
Overlays.editOverlay(overlay, {textures: textures});
|
|
Overlays.editOverlay(overlay, {dimensions: Vec3.multiply(waitingBallScale, dimension), position: position});
|
|
}
|
|
} else {
|
|
var particleProps = {};
|
|
particleProps.position = position;
|
|
if (!particleEffect) {
|
|
particleProps = PARTICLE_EFFECT_PROPS;
|
|
particleEffect = Entities.addEntity(particleProps);
|
|
} else {
|
|
if (state == STATES.makingFriends) {
|
|
particleProps.colorFinish = {red: 0xFF, green: 0x00, blue: 0x00};
|
|
particleProps.color = particleProps.colorFinish;
|
|
}
|
|
Entities.editEntity(particleEffect, particleProps);
|
|
}
|
|
}
|
|
}
|
|
|
|
function isNearby(id, hand) {
|
|
if (currentHand) {
|
|
var handPos = getHandPosition(MyAvatar, currentHand);
|
|
var avatar = AvatarList.getAvatar(id);
|
|
if (avatar) {
|
|
var otherHand = stringToHand(hand);
|
|
var distance = Vec3.distance(getHandPosition(avatar, otherHand), handPos);
|
|
return (distance < MAX_AVATAR_DISTANCE);
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
function findNearestWaitingAvatar() {
|
|
var handPos = getHandPosition(MyAvatar, currentHand);
|
|
var minDistance = MAX_AVATAR_DISTANCE;
|
|
var nearestAvatar = {};
|
|
Object.keys(waitingList).forEach(function (identifier) {
|
|
var avatar = AvatarList.getAvatar(identifier);
|
|
if (avatar) {
|
|
var hand = stringToHand(waitingList[identifier]);
|
|
var distance = Vec3.distance(getHandPosition(avatar, hand), handPos);
|
|
if (distance < minDistance) {
|
|
minDistance = distance;
|
|
nearestAvatar = {avatar: identifier, hand: hand};
|
|
}
|
|
}
|
|
});
|
|
return nearestAvatar;
|
|
}
|
|
|
|
|
|
// As currently implemented, we select the closest waiting avatar (if close enough) and send
|
|
// them a friendRequest. If nobody is close enough we send a waiting message, and wait for a
|
|
// friendRequest. If the 2 people who want to connect are both somewhat out of range when they
|
|
// initiate the shake, they will race to see who sends the friendRequest after noticing the
|
|
// waiting message. Either way, they will start friending eachother at that point.
|
|
function startHandshake(fromKeyboard) {
|
|
if (fromKeyboard) {
|
|
debug("adding animation");
|
|
// just in case order of press/unpress is broken
|
|
if (animHandlerId) {
|
|
animHandlerId = MyAvatar.removeAnimationStateHandler(animHandlerId);
|
|
}
|
|
animHandlerId = MyAvatar.addAnimationStateHandler(shakeHandsAnimation, []);
|
|
}
|
|
debug("starting handshake for", currentHand);
|
|
state = STATES.waiting;
|
|
entityDimensionMultiplier = 1.0;
|
|
friendingId = undefined;
|
|
friendingHand = undefined;
|
|
// just in case
|
|
stopWaiting();
|
|
stopFriending();
|
|
stopMakingFriends();
|
|
|
|
var nearestAvatar = findNearestWaitingAvatar();
|
|
if (nearestAvatar.avatar) {
|
|
friendingId = nearestAvatar.avatar;
|
|
friendingHand = handToString(nearestAvatar.hand);
|
|
debug("sending friendRequest to", friendingId);
|
|
messageSend({
|
|
key: "friendRequest",
|
|
id: friendingId,
|
|
hand: handToString(currentHand)
|
|
});
|
|
} else {
|
|
// send waiting message
|
|
debug("sending waiting message");
|
|
messageSend({
|
|
key: "waiting",
|
|
hand: handToString(currentHand)
|
|
});
|
|
lookForWaitingAvatar();
|
|
}
|
|
}
|
|
|
|
function endHandshake() {
|
|
debug("ending handshake for", currentHand);
|
|
currentHand = undefined;
|
|
// note that setting the state to inactive should really
|
|
// only be done here, unless we change how the triggering works,
|
|
// as we ignore the key release event when inactive. See updateTriggers
|
|
// below.
|
|
state = STATES.inactive;
|
|
friendingId = undefined;
|
|
friendingHand = undefined;
|
|
stopWaiting();
|
|
stopFriending();
|
|
stopMakingFriends();
|
|
// send done to let friend know you are not making friends now
|
|
messageSend({
|
|
key: "done"
|
|
});
|
|
|
|
if (animHandlerId) {
|
|
debug("removing animation");
|
|
MyAvatar.removeAnimationStateHandler(animHandlerId);
|
|
}
|
|
}
|
|
|
|
function updateTriggers(value, fromKeyboard, hand) {
|
|
if (currentHand && hand !== currentHand) {
|
|
debug("currentHand", currentHand, "ignoring messages from", hand);
|
|
return;
|
|
}
|
|
if (!currentHand) {
|
|
currentHand = hand;
|
|
}
|
|
// ok now, we are either initiating or quitting...
|
|
var isGripping = value > GRIP_MIN;
|
|
if (isGripping) {
|
|
if (state != STATES.inactive) {
|
|
return;
|
|
} else {
|
|
startHandshake(fromKeyboard);
|
|
}
|
|
} else {
|
|
// TODO: should we end handshake even when inactive? Ponder
|
|
if (state != STATES.inactive) {
|
|
endHandshake();
|
|
} else {
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
function messageSend(message) {
|
|
Messages.sendMessage(MESSAGE_CHANNEL, JSON.stringify(message));
|
|
}
|
|
|
|
function lookForWaitingAvatar() {
|
|
// we started with nobody close enough, but maybe I've moved
|
|
// or they did. Note that 2 people doing this race, so stop
|
|
// as soon as you have a friendingId (which means you got their
|
|
// message before noticing they were in range in this loop)
|
|
|
|
// just in case we reenter before stopping
|
|
stopWaiting();
|
|
debug("started looking for waiting avatars");
|
|
waitingInterval = Script.setInterval(function () {
|
|
if (state == STATES.waiting && !friendingId) {
|
|
// find the closest in-range avatar, and send friend request
|
|
// TODO: this is same code as in startHandshake - get this
|
|
// cleaned up.
|
|
var nearestAvatar = findNearestWaitingAvatar();
|
|
if (nearestAvatar.avatar) {
|
|
friendingId = nearestAvatar.avatar;
|
|
friendingHand = handToString(nearestAvatar.hand);
|
|
debug("sending friendRequest to", friendingId);
|
|
messageSend({
|
|
key: "friendRequest",
|
|
id: friendingId,
|
|
hand: handToString(currentHand)
|
|
});
|
|
}
|
|
} else {
|
|
// something happened, stop looking for avatars to friend
|
|
stopWaiting();
|
|
debug("stopped looking for waiting avatars");
|
|
}
|
|
}, WAITING_INTERVAL);
|
|
}
|
|
|
|
// this should be where we make the appropriate friend call. For now just make the
|
|
// visualization change.
|
|
function makeFriends(id) {
|
|
// send done to let the friend know you have made friends.
|
|
messageSend({
|
|
key: "done",
|
|
friendId: id
|
|
});
|
|
Controller.triggerHapticPulse(FRIENDING_SUCCESS_HAPTIC_STRENGTH, HAPTIC_DURATION, handToHaptic(currentHand));
|
|
state = STATES.makingFriends;
|
|
// now that we made friends, reset everything
|
|
makingFriendsTimeout = Script.setTimeout(function () {
|
|
friendingId = undefined;
|
|
friendingHand = undefined;
|
|
entityDimensionMultiplier = 1.0;
|
|
makingFriendsTimeout = undefined;
|
|
}, MAKING_FRIENDS_TIMEOUT);
|
|
}
|
|
|
|
// we change states, start the friendingInterval where we check
|
|
// to be sure the hand is still close enough. If not, we terminate
|
|
// the interval, go back to the waiting state. If we make it
|
|
// the entire FRIENDING_TIME, we make friends.
|
|
function startFriending(id, hand) {
|
|
var count = 0;
|
|
debug("friending", id, "hand", hand);
|
|
// do we need to do this?
|
|
friendingId = id;
|
|
friendingHand = hand;
|
|
state = STATES.friending;
|
|
Controller.triggerHapticPulse(FRIENDING_HAPTIC_STRENGTH, HAPTIC_DURATION, handToHaptic(currentHand));
|
|
|
|
// send message that we are friending them
|
|
messageSend({
|
|
key: "friending",
|
|
id: id,
|
|
hand: handToString(currentHand)
|
|
});
|
|
|
|
friendingInterval = Script.setInterval(function () {
|
|
count += 1;
|
|
entityDimensionMultiplier = Math.abs(Math.sin(Math.PI * 2 * 2 * count * FRIENDING_INTERVAL / FRIENDING_TIME));
|
|
if (state != STATES.friending) {
|
|
debug("stopping friending interval, state changed");
|
|
stopFriending();
|
|
} else if (!isNearby(id, hand)) {
|
|
// gotta go back to waiting
|
|
debug(id, "moved, back to waiting");
|
|
stopFriending();
|
|
messageSend({
|
|
key: "done"
|
|
});
|
|
startHandshake();
|
|
} else if (count > FRIENDING_TIME/FRIENDING_INTERVAL) {
|
|
debug("made friends with " + id);
|
|
makeFriends(id);
|
|
stopFriending();
|
|
}
|
|
}, FRIENDING_INTERVAL);
|
|
}
|
|
/*
|
|
A simple sequence diagram: NOTE that the FriendAck is somewhat
|
|
vestigial, and probably should be removed shortly.
|
|
|
|
Avatar A Avatar B
|
|
| |
|
|
| <-----(waiting) ----- startHandshake
|
|
startHandshake -- (FriendRequest) -> |
|
|
| |
|
|
| <-------(FriendAck) --------- |
|
|
| <--------(friending) -- startFriending
|
|
startFriending -- (friending) ---> |
|
|
| |
|
|
| friends
|
|
friends |
|
|
| <--------- (done) ---------- |
|
|
| ---------- (done) ---------> |
|
|
*/
|
|
function messageHandler(channel, messageString, senderID) {
|
|
if (channel !== MESSAGE_CHANNEL) {
|
|
return;
|
|
}
|
|
if (MyAvatar.sessionUUID === senderID) { // ignore my own
|
|
return;
|
|
}
|
|
var message = {};
|
|
try {
|
|
message = JSON.parse(messageString);
|
|
} catch (e) {
|
|
debug(e);
|
|
}
|
|
switch (message.key) {
|
|
case "waiting":
|
|
// add this guy to waiting object. Any other message from this person will
|
|
// remove it from the list
|
|
waitingList[senderID] = message.hand;
|
|
break;
|
|
case "friendRequest":
|
|
delete waitingList[senderID];
|
|
if (state == STATES.waiting && message.id == MyAvatar.sessionUUID && (!friendingId || friendingId == senderID)) {
|
|
// you were waiting for a friend request, so send the ack. Or, you and the other
|
|
// guy raced and both send friendRequests. Handle that too
|
|
friendingId = senderID;
|
|
friendingHand = message.hand;
|
|
messageSend({
|
|
key: "friendAck",
|
|
id: senderID,
|
|
hand: handToString(currentHand)
|
|
});
|
|
} else {
|
|
if (state == STATES.waiting && friendingId == senderID) {
|
|
// the person you are trying to friend sent a request to someone else. See the
|
|
// if statement above. So, don't cry, just start the handshake over again
|
|
startHandshake();
|
|
}
|
|
}
|
|
break;
|
|
case "friendAck":
|
|
delete waitingList[senderID];
|
|
if (state == STATES.waiting && (!friendingId || friendingId == senderID)) {
|
|
if (message.id == MyAvatar.sessionUUID) {
|
|
// start friending...
|
|
friendingId = senderID;
|
|
friendingHand = message.hand;
|
|
stopWaiting();
|
|
startFriending(senderID, message.hand);
|
|
} else {
|
|
if (friendingId) {
|
|
// this is for someone else (we lost race in friendRequest),
|
|
// so lets start over
|
|
startHandshake();
|
|
}
|
|
}
|
|
}
|
|
// TODO: check to see if we are waiting for this but the person we are friending sent it to
|
|
// someone else, and try again
|
|
break;
|
|
case "friending":
|
|
delete waitingList[senderID];
|
|
if (state == STATES.waiting && senderID == friendingId) {
|
|
// temporary logging
|
|
if (friendingHand != message.hand) {
|
|
debug("friending hand", friendingHand, "not same as friending hand in message", message.hand);
|
|
}
|
|
friendingHand = message.hand;
|
|
if (message.id != MyAvatar.sessionUUID) {
|
|
// the person we were trying to friend is friending someone else
|
|
// so try again
|
|
startHandshake();
|
|
break;
|
|
}
|
|
startFriending(senderID, message.hand);
|
|
}
|
|
break;
|
|
case "done":
|
|
delete waitingList[senderID];
|
|
if (state == STATES.friending && friendingId == senderID) {
|
|
// if they are done, and didn't friend us, terminate our
|
|
// friending
|
|
if (message.friendId !== MyAvatar.sessionUUID) {
|
|
stopFriending();
|
|
// now just call startHandshake. Should be ok to do so without a
|
|
// value for isKeyboard, as we should not change the animation
|
|
// state anyways (if any)
|
|
startHandshake();
|
|
}
|
|
} else {
|
|
// if waiting or inactive, lets clear the friending id. If in makingFriends,
|
|
// do nothing (so you see the red for a bit)
|
|
if (state != STATES.makingFriends && friendingId == senderID) {
|
|
friendingId = undefined;
|
|
friendingHand = undefined;
|
|
if (state != STATES.inactive) {
|
|
startHandshake();
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
default:
|
|
debug("unknown message", message);
|
|
break;
|
|
}
|
|
}
|
|
|
|
Messages.subscribe(MESSAGE_CHANNEL);
|
|
Messages.messageReceived.connect(messageHandler);
|
|
|
|
|
|
function makeGripHandler(hand, animate) {
|
|
// determine if we are gripping or un-gripping
|
|
if (animate) {
|
|
return function(value) {
|
|
updateTriggers(value, true, hand);
|
|
};
|
|
|
|
} else {
|
|
return function (value) {
|
|
updateTriggers(value, false, hand);
|
|
};
|
|
}
|
|
}
|
|
|
|
function keyPressEvent(event) {
|
|
if ((event.text === "x") && !event.isAutoRepeat) {
|
|
updateTriggers(1.0, true, Controller.Standard.RightHand);
|
|
}
|
|
}
|
|
function keyReleaseEvent(event) {
|
|
if ((event.text === "x") && !event.isAutoRepeat) {
|
|
updateTriggers(0.0, true, Controller.Standard.RightHand);
|
|
}
|
|
}
|
|
// map controller actions
|
|
var friendsMapping = Controller.newMapping(Script.resolvePath('') + '-grip');
|
|
friendsMapping.from(Controller.Standard.LeftGrip).peek().to(makeGripHandler(Controller.Standard.LeftHand));
|
|
friendsMapping.from(Controller.Standard.RightGrip).peek().to(makeGripHandler(Controller.Standard.RightHand));
|
|
|
|
// setup keyboard initiation
|
|
Controller.keyPressEvent.connect(keyPressEvent);
|
|
Controller.keyReleaseEvent.connect(keyReleaseEvent);
|
|
|
|
// xbox controller cuz that's important
|
|
friendsMapping.from(Controller.Standard.RB).peek().to(makeGripHandler(Controller.Standard.RightHand, true));
|
|
|
|
// it is easy to forget this and waste a lot of time for nothing
|
|
friendsMapping.enable();
|
|
|
|
// connect updateVisualization to update frequently
|
|
Script.update.connect(updateVisualization);
|
|
|
|
Script.scriptEnding.connect(function () {
|
|
debug("removing controller mappings");
|
|
friendsMapping.disable();
|
|
debug("removing key mappings");
|
|
Controller.keyPressEvent.disconnect(keyPressEvent);
|
|
Controller.keyReleaseEvent.disconnect(keyReleaseEvent);
|
|
debug("disconnecting updateVisualization");
|
|
Script.update.disconnect(updateVisualization);
|
|
if (overlay) {
|
|
overlay = Overlays.deleteOverlay(overlay);
|
|
}
|
|
});
|
|
|
|
}()); // END LOCAL_SCOPE
|
|
|