Created
August 21, 2022 00:35
-
-
Save DraconInteractive/892ab734bf5a9a19632a4eb7630f26fc to your computer and use it in GitHub Desktop.
Basic logic flow for VR Bow mechanic
This file contains hidden or 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 System; | |
using System.Collections; | |
using System.Collections.Generic; | |
using UltimateXR.Avatar; | |
using UltimateXR.Core; | |
using UltimateXR.Core.Components.Composite; | |
using UltimateXR.Devices; | |
using UltimateXR.Manipulation; | |
using UnityEngine; | |
using UnityEngine.Events; | |
public class BowModule : UxrGrabbableObjectComponent<BowModule> | |
{ | |
[Header("Objects")] | |
public UxrGrabbableObject notch; | |
public LineRenderer bowString; | |
[Header("Settings")] | |
public float notchRecoverySpeed; | |
public float minimumForce; | |
[Header("Targets")] | |
public Transform top, bottom, rest; | |
[Header("Arrows")] | |
public GameObject[] arrowPrefabs; | |
public int arrowIndex; | |
public Arrow spawnedArrow; | |
[Header("Aim Assist")] | |
[Range(0, 1)] | |
public float velocityAssist = 0.1f; | |
public Vector2 aimAssist; | |
private UxrGrabber grabber; | |
private Coroutine grabR; | |
#region Helper Properties | |
Vector3 ArrowDir => (rest.position - notch.transform.position).normalized; | |
float LaunchForce => Mathf.Clamp01(Vector3.Distance(notch.transform.position, Vector3.Lerp(top.position, bottom.position, 0.5f))); | |
BowTarget BestTarget() | |
{ | |
Vector3 notchPosition = notch.transform.position; | |
Vector3 arrowDirection = ArrowDir; | |
float bestDot = -1; | |
BowTarget bestTarget = null; | |
foreach (var t in BowTarget.All) | |
{ | |
float dot = t.Dot(notchPosition, arrowDirection); | |
if (dot > bestDot && dot > 0.2f) | |
{ | |
bestDot = dot; | |
bestTarget = t; | |
} | |
} | |
return bestTarget; | |
} | |
#endregion | |
protected override void Start() | |
{ | |
base.Start(); | |
// Subscribe to notch interaction events | |
notch.Grabbed += (sender, args) => NotchGrab(); | |
notch.Released += (sender, args) => NotchRelease(); | |
} | |
private void LateUpdate() | |
{ | |
// If not holding notch, move it back towards rest point (located halfway between top and bottom points) | |
if (!notch.IsBeingGrabbed) | |
{ | |
Vector3 targetPos = Vector3.Lerp(top.position, bottom.position, 0.5f); | |
notch.transform.position = Vector3.MoveTowards(notch.transform.position, targetPos, notchRecoverySpeed * Time.deltaTime); | |
} | |
// Notch looks at rest so that when/if an arrow spawns, it also looks at rest to begin | |
// This also influences the axis of constrained pull-back | |
// May not function with Uxr (I dont want to break things!) | |
notch.transform.LookAt(rest); | |
// Calculate and set string line renderer positions | |
// Simple 3 point string feels fine, havent found the need for more points | |
Vector3[] pos = new Vector3[3] | |
{ | |
top.position, | |
notch.transform.position, | |
bottom.position | |
}; | |
bowString.SetPositions(pos); | |
} | |
protected override void OnEnable() | |
{ | |
base.OnEnable(); | |
UxrManager.AvatarsUpdated += UxrManager_AvatarsUpdated; | |
} | |
protected override void OnDisable() | |
{ | |
base.OnDisable(); | |
UxrManager.AvatarsUpdated -= UxrManager_AvatarsUpdated; | |
} | |
protected override void OnObjectGrabbed(UxrManipulationEventArgs e) | |
{ | |
base.OnObjectGrabbed(e); | |
// Cache the grabbing object for later use | |
grabber = e.Grabber; | |
} | |
protected override void OnObjectReleased(UxrManipulationEventArgs e) | |
{ | |
base.OnObjectReleased(e); | |
// Release the cached object | |
grabber = null; | |
} | |
private void UxrManager_AvatarsUpdated() | |
{ | |
if (!IsBeingGrabbed) return; | |
// if being grabbed, check if the user clicks the 1st button on the controller. If so, move to the next arrow prefab | |
bool buttonPressDown = UxrAvatar.LocalAvatarInput.GetButtonsPressDown(grabber.Side, UxrInputButtons.Button1); | |
if (buttonPressDown) | |
{ | |
arrowIndex++; | |
if (arrowIndex >= arrowPrefabs.Length) | |
{ | |
arrowIndex = 0; | |
} | |
} | |
} | |
public void NotchGrab() | |
{ | |
if (IsBeingGrabbed) | |
{ | |
// Create arrow, reset its local rotation (incase of prefab shenanigans) | |
// Start the handler routine | |
spawnedArrow = Instantiate(arrowPrefabs[arrowIndex], notch.transform.position, notch.transform.rotation, notch.transform).GetComponent<Arrow>(); | |
spawnedArrow.transform.localRotation = Quaternion.identity; | |
grabR = StartCoroutine(GrabRoutine()); | |
} | |
} | |
public void NotchRelease() | |
{ | |
// If arrow exists, launch it, stop the handler routine | |
if (spawnedArrow != null) | |
{ | |
Launch(); | |
StopCoroutine(grabR); | |
} | |
} | |
// Handler routine. Previously used for aim visualisation, still working on that | |
IEnumerator GrabRoutine() | |
{ | |
while (true) | |
{ | |
/* | |
if (spawnedArrow != null) | |
{ | |
var bestTarget = BestTarget(); | |
if (bestTarget != null) | |
{ | |
Vector3 targetPoint = bestTarget.transform.position; | |
Quaternion targetRot = Quaternion.Lerp(spawnedArrow.transform.rotation, Quaternion.LookRotation((targetPoint - spawnedArrow.transform.position).normalized, Vector3.up), aimAssist.y); | |
Vector3 aimPos = spawnedArrow.tip.position; | |
aimObject.transform.rotation = targetRot; | |
aimPos = aimObject.transform.forward * Vector3.Distance(spawnedArrow.tip.position, targetPoint); | |
aimObject.transform.position = Vector3.Lerp(aimPos, targetPoint, aimAssist.y); | |
} | |
} | |
*/ | |
yield return null; | |
} | |
} | |
// Launch arrow (called on notch release) | |
void Launch() | |
{ | |
// Calculate force from base * distance from notch rest point | |
float force = LaunchForce; | |
// If that distance is too small, dont launch | |
if (force < minimumForce) | |
{ | |
Destroy(spawnedArrow.gameObject); | |
return; | |
} | |
// If its a 'proper' release, find the best target, check how much aim assist to use, | |
// then send a launch instruction to the arrow with this info | |
var bestTarget = BestTarget(); | |
float triggerPressAmount = UxrAvatar.LocalAvatarInput.GetInput1D(grabber.Side, UxrInput1D.Trigger); | |
float assist = (triggerPressAmount - aimAssist.x) / (aimAssist.y - aimAssist.x); | |
spawnedArrow.GetComponent<Arrow>().Launch(force, bestTarget, assist); | |
spawnedArrow = null; | |
// TODO: Add 'active' arrow list for arrows that have been launched and are still alive. | |
// Could be some fun control mechanics there. | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment