Created
September 15, 2016 21:47
-
-
Save ghthor/4a19aa6812a3ee869797750054b3dca7 to your computer and use it in GitHub Desktop.
Chordpad For Vive in Unity
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using UnityEngine; | |
using System.Collections; | |
using System.Collections.Generic; | |
[RequireComponent(typeof(SteamVR_TrackedObject))] | |
public class ChordInput : MonoBehaviour | |
{ | |
SteamVR_TrackedObject controller; | |
enum ControllerHand { L, R } | |
enum ChordButtonID | |
{ | |
Trigger_0, | |
Trigger_1, | |
// Used to represent 2 buttons using the trackpad | |
Hemisphere_0_0, | |
Hemisphere_0_1, | |
Hemisphere_1_0, | |
Hemisphere_1_1, | |
// Used to represent 3 buttons using the trackpad | |
Pie_0_0, | |
Pie_0_1, | |
Pie_0_2, | |
Pie_1_0, | |
Pie_1_1, | |
Pie_1_2, | |
}; | |
class ChordMask | |
{ | |
private Dictionary<ChordButtonID, ulong> masks; | |
public readonly ulong All; | |
public ChordMask(ChordButtonID[] inputs) | |
{ | |
masks = new Dictionary<ChordButtonID, ulong>(); | |
foreach (ChordButtonID b in inputs) | |
{ | |
ulong mask = 1ul << masks.Count; | |
masks.Add(b, mask); | |
All |= mask; | |
} | |
} | |
public ulong For(ChordButtonID b) | |
{ | |
return masks[b]; | |
} | |
public string StringFor(ulong mask) | |
{ | |
if (mask == 0) { return "[]"; } | |
List<string> buttonsPressed = new List<string>(); | |
foreach (KeyValuePair<ChordButtonID, ulong> mapping in masks) | |
{ | |
if ((mask & mapping.Value) == 0) { continue; } | |
buttonsPressed.Add(mapping.Key.ToString()); | |
} | |
return "[" + string.Join(",", buttonsPressed.ToArray()) + "]"; | |
} | |
} | |
// TODO: Implement 3-button support | |
class TrackpadButtonMask | |
{ | |
// If the location's distance from center is <= this radius then both buttons are pressed. | |
const float crossoverRadius = 1.0F / 3.0F * 2.0F; | |
// Config for ButtonID => Mask | |
private ChordMask mask; | |
// The Set of ButtonID's for this Hand | |
private ChordButtonID[] buttons; | |
// A Mask for all the Trackpad ButtonID's attached the Hand | |
public readonly ulong All; | |
public TrackpadButtonMask(ChordMask config, ControllerHand hand) | |
{ | |
mask = config; | |
buttons = new[] | |
{ | |
ChordButtonID.Hemisphere_0_0, | |
ChordButtonID.Hemisphere_0_1, | |
}; | |
if (hand == ControllerHand.R) | |
{ | |
buttons = new[] | |
{ | |
ChordButtonID.Hemisphere_1_0, | |
ChordButtonID.Hemisphere_1_1, | |
}; | |
} | |
foreach (ChordButtonID b in buttons) | |
{ | |
All |= mask.For(b); | |
} | |
} | |
public ulong MaskFor(SteamVR_Controller.Device dev) | |
{ | |
// TODO: Add Support Vertical axis Mode | |
Vector2 v = dev.GetAxis(Valve.VR.EVRButtonId.k_EButton_SteamVR_Touchpad); | |
float radius = Mathf.Abs(v.x); | |
if (radius <= crossoverRadius) | |
{ | |
return mask.For(buttons[0]) | mask.For(buttons[1]); | |
} | |
else if (v.x < 0) | |
{ | |
return mask.For(buttons[0]); | |
} | |
else if (v.x > 0) | |
{ | |
return mask.For(buttons[1]); | |
} | |
return 0; | |
} | |
} | |
class ChordMachine | |
{ | |
public enum State | |
{ | |
Building, | |
Playing, | |
Played, | |
} | |
public enum InputEvent | |
{ | |
TriggerPressed, | |
TriggerReleased, | |
TouchpadPressed, | |
TouchpadValueModified, | |
TouchpadReleased, | |
ChordValueOutput, | |
} | |
public static State Transistion(State state, InputEvent inputEvent) | |
{ | |
switch (state) | |
{ | |
case State.Building: | |
if (inputEvent == InputEvent.TriggerReleased | |
|| inputEvent == InputEvent.TouchpadReleased) | |
{ | |
return State.Playing; | |
} | |
break; | |
case State.Playing: | |
if (inputEvent == InputEvent.ChordValueOutput) | |
{ | |
return State.Played; | |
} | |
break; | |
case State.Played: | |
if (inputEvent == InputEvent.TriggerPressed | |
|| inputEvent == InputEvent.TouchpadPressed | |
|| inputEvent == InputEvent.TouchpadValueModified) | |
{ | |
return State.Building; | |
} | |
break; | |
} | |
return state; | |
} | |
} | |
// A Mapping of ChordButtonID's => Unique Mask Values | |
private ChordMask mask; | |
private ControllerHand hand; | |
// Unique set of ChordButtonID's for the current Hand | |
private ChordButtonID trigger; | |
private TrackpadButtonMask trackpad; | |
// Shared Button State between both hands | |
static private ulong prevKeys = 0; | |
static private ulong chordKeys = 0; | |
static private ChordMachine.State status = ChordMachine.State.Building; | |
ChordInput() | |
{ | |
// TODO: Add Runtime Configuration of ChordingButton's used | |
mask = new ChordMask(new ChordButtonID[] { | |
ChordButtonID.Trigger_0, | |
ChordButtonID.Trigger_1, | |
ChordButtonID.Hemisphere_0_0, | |
ChordButtonID.Hemisphere_0_1, | |
ChordButtonID.Hemisphere_1_0, | |
ChordButtonID.Hemisphere_1_1, | |
}); | |
} | |
void Awake() | |
{ | |
controller = GetComponent<SteamVR_TrackedObject>(); | |
setButtonIDs(); | |
Debug.Log("Mask Config: " + mask.StringFor(mask.All)); | |
Debug.Log(string.Join("\n", new[] { | |
controller.name + " setup as " + hand.ToString(), | |
"Trigger => " + trigger.ToString(), | |
"Trackpad => " + mask.StringFor(trackpad.All), | |
})); | |
} | |
private ControllerHand HandForName(string name) | |
{ | |
// TODO: Generalize this resolution of GameObject.name => enum value | |
return controller.name.Contains("Left") ? ControllerHand.L : ControllerHand.R; | |
} | |
private void setButtonIDs() | |
{ | |
hand = HandForName(controller.name); | |
trigger = (hand == ControllerHand.L ? ChordButtonID.Trigger_0 : ChordButtonID.Trigger_1); | |
trackpad = new TrackpadButtonMask(mask, hand); | |
} | |
void FixedUpdate() | |
{ | |
SteamVR_Controller.Device dev = SteamVR_Controller.Input((int)controller.index); | |
// ## Update Phase - Additions to Chord Being Built | |
bool trackpadDown = dev.GetPress(SteamVR_Controller.ButtonMask.Touchpad) | |
|| dev.GetPressDown(SteamVR_Controller.ButtonMask.Touchpad); | |
bool triggerDown = dev.GetTouch(SteamVR_Controller.ButtonMask.Trigger) | |
|| dev.GetTouchDown(SteamVR_Controller.ButtonMask.Trigger); | |
// ### Update Trackpad | |
chordKeys &= ~trackpad.All; | |
chordKeys = trackpadDown ? | |
chordKeys | trackpad.MaskFor(dev) : | |
chordKeys; | |
// ### Update Trigger State | |
chordKeys = triggerDown ? | |
chordKeys | mask.For(trigger) : | |
chordKeys & ~mask.For(trigger); | |
} | |
void Update() | |
{ | |
if (prevKeys == chordKeys) { return; } | |
ulong touchpadKeysDownPrev = (prevKeys & trackpad.All); | |
ulong touchpadKeysDownNow = (chordKeys & trackpad.All); | |
bool touchpadWasDownPrev = touchpadKeysDownPrev != 0; | |
bool touchpadIsDownNow = touchpadKeysDownNow != 0; | |
ulong triggerMask = mask.For(trigger); | |
bool triggerDownPrev = (prevKeys & triggerMask) != 0; | |
bool triggerDownNow = (chordKeys & triggerMask) != 0; | |
if ((touchpadWasDownPrev && touchpadIsDownNow) | |
&& (touchpadKeysDownPrev != touchpadKeysDownNow)) | |
{ | |
status = ChordMachine.Transistion(status, ChordMachine.InputEvent.TouchpadValueModified); | |
} | |
// Press Events | |
if (!triggerDownPrev && triggerDownNow) | |
{ | |
status = ChordMachine.Transistion(status, ChordMachine.InputEvent.TriggerPressed); | |
} | |
if (!touchpadWasDownPrev && touchpadIsDownNow) | |
{ | |
status = ChordMachine.Transistion(status, ChordMachine.InputEvent.TouchpadPressed); | |
} | |
// Release Events | |
if (triggerDownPrev && !triggerDownNow) | |
{ | |
status = ChordMachine.Transistion(status, ChordMachine.InputEvent.TriggerReleased); | |
} | |
if (touchpadWasDownPrev && !touchpadIsDownNow) | |
{ | |
status = ChordMachine.Transistion(status, ChordMachine.InputEvent.TouchpadReleased); | |
} | |
} | |
void LateUpdate() | |
{ | |
if (status == ChordMachine.State.Playing) | |
{ | |
// TODO: Modify Text View | |
Debug.Log(string.Format("Output Chord Value({0}) : {1}", prevKeys, mask.StringFor(prevKeys))); | |
status = ChordMachine.Transistion(status, ChordMachine.InputEvent.ChordValueOutput); | |
} | |
prevKeys = chordKeys; | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment