i’d like to pick up ammo when i reach over my shoulder like in HL-A
i guess i could look at position of the none gun holding hand and see is its above and behind the camera but that seems like a hack. is their an elegant way to do it? thanks
i’d like to pick up ammo when i reach over my shoulder like in HL-A
i guess i could look at position of the none gun holding hand and see is its above and behind the camera but that seems like a hack. is their an elegant way to do it? thanks
actually i think i can put a trigger collider as a child of the XR rig behind the camera and simply detect stay events in there
Yes Trigger events is a straightforward way to implement this. I would attach the trigger as a child of the Main Camera, this will make sure it moves when the player turns around.
If you’d like to integrate more with the XR interaction toolkit system, you can put a SimpleInteractable component on it and then use interaction layers to make it only react to Hover and Select events by the direct interactor (assuming you also have rays). That will help you detect when the user uses the grab action behind their back.
Depending on the headset and controller type, controllers sometimes lose tracking when they are not visible by some sensors, however usually it can estimate the controllers based on their acceleration sensors.
thanks i got it working but i think you are correct about the controller behind the head problem. what i find is holding the controller inside the trigger zone i get multiple entry and exit events which cases me to instantiate hundreds of ammo mags
so what i did was use a flag (actually timer = -1 is the flag, see below) in the trigger enter to detect first entry and only the instantiate the mag. i basically ignore exit triggers, instead i use a coroutine started when i instantiate the mag that waits about 1/2 sec. if the player hasn’t grabbed the mag in that time, i destroy it. so long as i keep getting entry triggers, i reset the coroutine timer to 0 so i can wait in the ammo zone as long as necessary
BTW, the instantiated mag is set to child of the hand so i know its location will be in hand even if i can’t see it
public class HandInAmmoZone : MonoBehaviour
{
public GameObject magazinePrefab;
float timer = -1; // -1 means not running
public IEnumerator DetectLeaveAmmonZone()
{
while (timer < 1)
{
// do nothing, just wait
timer += Time.deltaTime;
yield return null;
}
// timer expired, if mag not picked up, destroy it
timer = -1; // so can start over again next entry
// need to find of mag was grabbed
if (!globals.magazineInHand)
yield return null;
globals.SetHUDText("in DetectLeaveAmmonZone destroy ");
Destroy(globals.magazineInHand);
globals.magazineInHand = null;
}
private void OnTriggerEnter(Collider other)
{
Transform t = other.transform.parent;
// globals.SetHUDText("in other = " + other);
// globals.SetHUDText("in OnTriggerEnter parent = " + t);
if (!t)
return;
if (!t.CompareTag("LHand") && !t.CompareTag("RHand"))
return;
if (timer < 0)
{
// start timer ob first entry only (avoid multiple entry/exit events while inside)
timer = 0;
StartCoroutine(DetectLeaveAmmonZone());
}
else
{
// because timer running already, rest set it back to start time
timer = 0;
return;
}
if (t.CompareTag("LHand") || t.CompareTag("RHand"))
{
//activeHandInAmmoZone = ActiveHandInAmmoZone.RHandInAmmoZone;
globals.SetHUDText("in OnTriggerEnter parent = " + t + "tag=" + t.tag);
globals.magazineInHand = Instantiate(magazinePrefab, t.position, t.rotation, t);
}
}
actually, i found the trigger to be a pain in the a because it rotates with the head movement up and down which i don’t want. so i found a better way, attaching a script the hand that looks like this
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using static UnityEngine.GraphicsBuffer;
public class HandInAmmoZone : MonoBehaviour
{
Globals globals;
public GameObject magazinePrefab;
public GameObject magazineOnHand;
bool inAmmonZone = false;
// Start is called before the first frame update
void Start()
{
globals = GameObject.FindObjectOfType<Globals>();
}
// Update is called once per frame
void FixedUpdate()
{
Transform camera = transform.parent.GetChild(0); // camera is 1st child of CameraOffset
Vector3 heading = transform.position - camera.transform.position;
float dot = Vector3.Dot(heading, camera.transform.forward);
//globals.SetHUDText("HandInAmmoZone", "FixedUpdate", "dot = " + dot);
if (dot < 0)
{
//globals.SetHUDText("HandInAmmoZon", "FixedUpdate", "hand pos = " + handPos);
if (inAmmonZone)
return; // alread in, not interested
inAmmonZone = true;
globals.SetHUDText("HandInAmmoZon", "FixedUpdate", "ENTER ZONE hand dot = " + dot);
magazineOnHand = Instantiate(magazinePrefab, transform.position, transform.rotation, transform);
}
else
{
if (!inAmmonZone)
return; // alread out, not interested
inAmmonZone = false;
globals.SetHUDText("HandInAmmoZon", "FixedUpdate", "EXIT ZONE hand dot = " + dot);
if(magazineOnHand)
{
Destroy(magazineOnHand); // leaving without grabbing
magazineOnHand = null;
}
}
}
}
it checks if the hand is behind the camera and if so, instants a mag which you can grab
to use the mag i use this script
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.XR.Interaction.Toolkit;
using UnityEngine.XR.Interaction.Toolkit.AR;
public class Magazine : MonoBehaviour
{
Globals globals;
public int numberOfBullets = 8;
GameObject savedMagInHand;
void Start()
{
globals = GameObject.FindObjectOfType<Globals>();
globals.SetHUDText("Magazine", "Start", "");
XRGrabInteractable grabInteractable = GetComponent<XRGrabInteractable>();
grabInteractable.selectEntered.AddListener(Grab);
grabInteractable.selectExited.AddListener(Release);
}
public void Grab(BaseInteractionEventArgs arg)
{
globals.SetHUDText("public class Magazine : MonoBehaviour\r\n", "Grab", "interactableObject = " + arg.interactableObject.transform);
globals.SetHUDText("public class Magazine : MonoBehaviour\r\n", "Grab", "interactorObjectParent = " +
arg.interactorObject.transform.parent);
HandInAmmoZone handInAmmoZone = arg.interactorObject.transform.parent.GetComponent<HandInAmmoZone>();
if (!handInAmmoZone)
{
globals.SetHUDText("Magazine", "Grab", "no handInAmmoZone");
return;
}
if (!handInAmmoZone.magazineOnHand)
{
globals.SetHUDText("Magazine", "Grab", "no magazineInHand");
return;
}
globals.SetHUDText("Magazine", "Grab", "parent = " + handInAmmoZone.magazineOnHand.transform.parent);
savedMagInHand = handInAmmoZone.magazineOnHand;
handInAmmoZone.magazineOnHand.transform.parent = null;
globals.SetHUDText("Magazine", "Grab", "2 parent = " + handInAmmoZone.magazineOnHand.transform.parent);
handInAmmoZone.magazineOnHand = null; // this will stop the timer in HandInAmmoZone from destroying the mag
}
public void Release(BaseInteractionEventArgs arg)
{
globals.SetHUDText("Magazine", "Release", "enter");
if (!savedMagInHand)
return;
Rigidbody rb = savedMagInHand.GetComponent<Rigidbody>();
rb.isKinematic = false;
globals.SetHUDText("Magazine", "Release", "rb.isKinematic = " + rb.isKinematic);
savedMagInHand = null;
}
public void MagEmpty()
{
transform.GetChild(1).gameObject.SetActive(false);
}
}