Essentially, I'm trying to move an object between two "hands" that are currently fully functional. I can pick objects up and manipulate them both within the room space and rotationally.
However, the issue I'm having is transferring the object between the controllers.
I can hand over the object to the other hand, however if I let go of the trigger on the original hand, the object then becomes Non-Kinematic and drops back to the floor.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.XR;
public class HandGrab : MonoBehaviour
{
// Variable for Controller button register
public string buttonName;
// Allows objects to be transferred between hands
public HandGrab secondaryHandRef;
// Determines the referenced object type
public XRNode nodeType;
// This affects the distance of object grab from controller
public Vector3 objectGrabOffset;
// Default grab distance relative to the controller, (Where the collider sphere is) This can be changed to affect the visuals and where the items get manipulated from
public float grabDistance = 0.1f;
// Force at which an object is thrown
public float throwingMultiplier = 1.5f;
// To determine if object has been grabbed or cant be grabbed
public string grabName = "Grabbing";
// Stores the values of the currently grabbed object
public Transform CurrentGrabObject
{
get { return _currentlyHeldObject; }
set { _currentlyHeldObject = value; }
}
// Does as it says and stores the last frame and is used to work out velocity calculations
private Vector3 _lastFramePosition;
// Determines the transform values for the currently selected objects
private Transform _currentlyHeldObject;
// bolean to determine if an object is currently being held
private bool _isHeld;
// *Hopefully gets the roation of the controllers and matches that to the roation of the item*
// *private Transform _objectRotation;*
// Use this for initialization
void Start()
{
// Clearing all variables back to zero
_currentlyHeldObject = null;
_lastFramePosition = transform.position;
_isHeld = false;
// Determining the tracking space available
XRDevice.SetTrackingSpaceType(TrackingSpaceType.RoomScale);
}
// Update is called once per frame
void Update()
{
// rotation and position tracking. Updates every frame, should be 90fps
transform.localPosition = InputTracking.GetLocalPosition(nodeType);
transform.localRotation = InputTracking.GetLocalRotation(nodeType);
// Check the object in hand variable. If hand is empty, check to see if we can pick anything up (Object highlight posibilites)
if (_currentlyHeldObject == null)
{
// Check to see if there are any colliders in the proximity of hands
Collider[] colliders = Physics.OverlapSphere(transform.position, grabDistance);
if (colliders.Length > 0)
{
// If there is a collision, then pickup the object if we press the grab button and give it the tag "Grabbing"
if (Input.GetAxis(buttonName) >= 0.5F && colliders[0].transform.CompareTag(grabName))
{
// Now we set to grabbing to true
if (_isHeld)
{
return;
}
_isHeld = true;
// Sets the current object to the object we have just picked up and binds it as a child object (this should allow all rotation to mimic the hands)
colliders[0].transform.SetParent(transform);
// If there is no RigidBody attached to the current object, then assign it a RigidBody variable
if (colliders[0].GetComponent<Rigidbody>() == null)
{
colliders[0].gameObject.AddComponent<Rigidbody>();
}
// Disable all physics on the grabbed object until it is released (May need to change if we want to enable objects to be swung and hit other "live objects")
colliders[0].GetComponent<Rigidbody>().isKinematic = true;
// Save the current object for a reference later
_currentlyHeldObject = colliders[0].transform;
// If other hand grabs the object, then release the object to be able to be re-parented above
if (secondaryHandRef.CurrentGrabObject != null)
{
secondaryHandRef.CurrentGrabObject = null;
}
}
}
}
else
// We must now have an object in our hand at this point. We now update its current position to the current hand poistion + the offset definded above
{
//If we release the grab button, we drop the object here
if (Input.GetAxis(buttonName) < 0.1f)
{
// Return the value of the object back to Non-Kinematic and re-enable physics
Rigidbody _objectRB = _currentlyHeldObject.GetComponent<Rigidbody>();
_objectRB.isKinematic = false;
_objectRB.collisionDetectionMode = CollisionDetectionMode.Continuous;
// Fun stuff starts here, we now calculate the hands current velocity
Vector3 velocity = (transform.position - _lastFramePosition) / Time.deltaTime;
// We now set that object velocity to the current velocity of the hand (I may adjust this to improve physics simulation)
_objectRB.velocity = velocity * throwingMultiplier;
// Kill the reference and revert the hands back to being empty and remove its parent
_currentlyHeldObject.SetParent(null);
_currentlyHeldObject = null;
}
}
//Release the grab function
if (Input.GetAxis(buttonName) < 0.5f && _isHeld)
{
_isHeld = false;
}
// Store the current position of the hand for velocity calculation in the upcoming frame
_lastFramePosition = transform.position;
//Tying off all loose ends here
}
}
In your update function:
if (_currentlyHeldObject != null && !_currentlyHeldObject.GetComponent<Rigidbody>().isKinematic) {
_currentlyHeldObject.GetComponent<Rigidbody>().isKinematic = true;
}
Related
Hi!
i am making something like 8 ball pool in 3d with unity 3d C#.
A is que ball and I know Dir1. I want to calculate Dir2.I am using Raycast to i can get point of contact.
If you really want to go manual
Instead of a Raycast you would rather use a Physics.SphereCast (according to your balls shape) or alternatively you could also use Rigidbody.SweepTest in order to get hit information for if you object would move.
Both provide you with the information
whether something is hit or not (e.g. check via tag if the first thing hit is a wall or another ball)
both give you a RaycastHit which contains detailed information such as
point the exact contact point
distance how far the ball traveled before hitting something (e.g. for calculating the already applied damping/table friction)
normal of the surface you hit so you can calculate your two balls new direction from that
and of course most importantly which object was hit so you can start a new calculation for that ball as well
The rest is The Math and Physics of Billard or Physics of Billard etc and you will find that a complete answer is way to complex for this page ;)
There are way to many things to consider when calculating physics manually and first of all you will need to decide how realistic you want to go actually. There are spins, frictions, jumps and you know in the real world there doesn't exist any fully elastic collision at all ... So boy if you are going to pre-calculate all this by hand you can as well just write your own physics engine ;)
Use the existing physics without calculating yourself at all
Now as a complete alternative approach to all that, which doesn't require you to calculate anything at all:
You could simulate the whole physics!
You could
Store all balls current positions (in order to restore them later)
Use Physics.Simulate in a loop, as condition checking if any sphere has moved between the two calls -> If not (or after certain preview time) then break.
Every step track the positions of all balls
After breaking of the loop reset all positions and velocities
=> You already get all the tracked points for each balls LineRenderer ;)
You could even use the positions in order to move the balls yourself instead of use the physics again to show the actual movement ;)
For large systems this would of course cause immense lag (depends also on your target preview time range). But I'd say for only a limited amount of balls this should be fine.
Here a little code I cluched together in a few minutes (way not perfect of course)
// little helper component for storing some references and identify clearly as a ball
// (rather then allowing to reference just any GameObject)
public class Ball : MonoBehaviour
{
[SerializeField]
private Rigidbody _rigidbody;
public Rigidbody Rigidbody => _rigidbody;
[SerializeField]
private LineRenderer _line;
public LineRenderer Line => _line;
private void Awake()
{
if (!_rigidbody) _rigidbody = GetComponent<Rigidbody>();
if (_line) _line = GetComponentInChildren<LineRenderer>(true);
}
}
and
public class Example : MonoBehaviour
{
// A simple data container for transform values
private class TransformData
{
public Vector3 Position;
public Quaternion Rotation;
private readonly Rigidbody _rigidbody;
public TransformData(Rigidbody rigidbody)
{
_rigidbody = rigidbody;
Update();
}
public void Update()
{
Position = _rigidbody.position;
Rotation = _rigidbody.rotation;
}
}
// all balls
public Ball[] balls;
// the white ball in particular
public Ball whiteBall;
// direction to shoot in
public Vector3 direction;
// force to apply
public float force;
// How far to predict into the future
public float maxPreviewTime = 10;
// shall this be done every frame (careful with performance!)
public bool continousUpdates;
// stores all initial transform values before starting the prediction in order to restore the state later
private readonly Dictionary<Ball, TransformData> initialPositions = new Dictionary<Ball, TransformData>();
// stores all the predicted positions in order - but for now without the information about the exact frame
// for simplicity and performance we only store WHERE the balls move, not exactly WHEN (could change that though if you wish)
private readonly Dictionary<Ball, List<Vector3>> simulatedPositions = new Dictionary<Ball, List<Vector3>>();
private void Awake()
{
// Initialize empty data sets for the existing balls
foreach (var ball in balls)
{
initialPositions.Add(ball, new TransformData(ball.Rigidbody));
simulatedPositions.Add(ball, new List<Vector3>());
}
}
private void Update()
{
// you could call this every frame e.g. in order to see prediction lines while the player changes force and direction
// have performance in mind though!
if (continousUpdates)
{
UpdateLines();
}
// for demo we shoot on space key
if (Input.GetKeyDown(KeyCode.Space))
{
ShootWhiteBall();
}
}
private void ShootWhiteBall()
{
whiteBall.Rigidbody.AddForce(direction.normalized * force, ForceMode.Impulse);
}
[ContextMenu("Update Preview Lines")]
public void UpdateLines()
{
// update all balls initial transform values to the current ones
foreach (var transformData in initialPositions.Values)
{
transformData.Update();
}
// clear all prediction values
foreach (var list in this.simulatedPositions.Values)
{
list.Clear();
}
// disable autosimulation - the API is a bit unclear whether this is required when manually calling "Physics.Simulate"
// so just to be sure
Physics.autoSimulation = false;
// we will track if any transform values changed during the prediction
// if not we break out of the loop immediately to avoid unnecessary overhead
var somethingChanged = true;
// as second break condition we use time - we don't want o get stuck in prediction forever
var simulatedTime = 0f;
// Do the same thing as you would later for shooting the white ball
// preferably even actually use the exact same method to avoid double maintenance
ShootWhiteBall();
while (somethingChanged && simulatedTime < maxPreviewTime)
{
// Simulate a physics step
Physics.Simulate(Time.fixedDeltaTime);
// always assume there was no change (-> would break out of prediction loop)
somethingChanged = false;
foreach (var kvp in simulatedPositions)
{
var ball = kvp.Key;
var positions = kvp.Value;
var currentPosition = ball.Rigidbody.position;
// either this is the first frame or the current position is different from the previous one
var hasChanged = positions.Count == 0 || currentPosition != positions[positions.Count - 1];
if (hasChanged)
{
positions.Add(currentPosition);
}
// it is enough for only one ball to be moving to keep running the prediction loop
somethingChanged = somethingChanged || hasChanged;
}
// increase the counter by one physics step
simulatedTime += Time.fixedDeltaTime;
}
// Reset all balls to the initial state
foreach (var kvp in initialPositions)
{
kvp.Key.Rigidbody.velocity = Vector3.zero;
kvp.Key.Rigidbody.angularVelocity = Vector3.zero;
kvp.Key.Rigidbody.position = kvp.Value.Position;
kvp.Key.Rigidbody.rotation = kvp.Value.Rotation;
}
// apply the line renderers
foreach (var kvp in simulatedPositions)
{
var ball = kvp.Key;
var positions = kvp.Value;
ball.Line.positionCount = positions.Count;
ball.Line.SetPositions(positions.ToArray());
}
// re-enable the physics
Physics.autoSimulation = true;
}
}
as you can see it is not exactly 100% accurate, tbh no sure why but it is probably something that can be tweaked out.
In my game I have a game object called ExclamationMark which I want to spawn above enemies heads when the player gets into range and they become "Alerted".
I've made this simple script to do that, but for some reason it will only work on one game object.
My enemy script:
void CheckForPlayer()
{
// Define player and get position
var player = GameObject.FindWithTag("Player");
var playerPos = (int)player.transform.position.x;
if (transform.Find("Graphics"))
{
// Define gameobject position
var enemyPos = transform.Find("Graphics").gameObject.transform.position.x;
// Define range to spawn tiles in
var range = 5;
var rangeInfront = enemyPos + range;
var rangeBehind = enemyPos - range;
if (playerPos >= rangeBehind && playerPos <= rangeInfront)
{
enemyIsActive = true;
if (transform.Find("ExclamationMark"))
{
var exMark = transform.Find("ExclamationMark").gameObject.GetComponent<ExclamationMarkSpawn>();
exMark.SpawnExclamationMark();
}
}
else
{
enemyIsActive = false;
}
}
}
My ! script:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class ExclamationMarkSpawn : MonoBehaviour {
public GameObject spawnPos;
public GameObject exclamationMark;
public GameObject exclamationMarkAudio;
public void SpawnExclamationMark()
{
StartCoroutine(GameObject.FindGameObjectWithTag("MainCamera").GetComponent<CameraShake>().Shake(0.2f, 0.2f, 0.2f));
Instantiate(exclamationMark, spawnPos.transform.position, Quaternion.identity);
if (exclamationMarkAudio)
Instantiate(exclamationMarkAudio, spawnPos.transform.position, Quaternion.identity);
StartCoroutine(DestroyExclamationMark());
}
IEnumerator DestroyExclamationMark()
{
yield return new WaitForSeconds(1);
var children = new List<GameObject>();
foreach (Transform child in transform) children.Add(child.gameObject);
children.ForEach(child => Destroy(child));
}
}
Just to be sure: I assume every player has its own instance of both of your scripts attached (some maybe nested further in their own hierarchy).
I assume that since you are using transform.Find which looks for the object by name within it's own children.
In general using Find and GetComponent over and over again is very inefficient! You should in both classes rather store them to fields and re-use them. Best would be if you can actually already reference them via the Inspector and not use Find and GetComponent at all.
In general finding something by name is always error prone. Are you sure they are all called correctly? Or are others maybe further nested?
Note: Find does not perform a recursive descend down a Transform hierarchy.
I would prefer to go by the attached components. You say it has e.g. a RigidBody. If this is the only Rigidbody component in the hierarchy below your objects (usually this should be the case) then you could instead rather simply use
// pass in true to also get disabled or inactive children
Rigidbody graphics = GetComponentInChildren<Rigidbody>(true);
the same for the ExclamationMarkSpawn
// Would be even beter if you already reference these in the Inspector
[SerializeField] private Rigidbody graphics;
[SerializeField] private ExclamationMarkSpawn exclamationMark;
[SerializeField] private Transform player;
private void Awake()
{
if(!player) player = GameObject.FindWithTag("Player");
if(!graphics) graphics = GetComponentInChildren<Rigidbody>(true);
if(!exclamationMark) exclamationMark = GetComponentInChildren<ExclamationMarkSpawn>(true);
}
private void CheckForPlayer()
{
// If really needed you can also after Awake still use a lazy initialization
// this adds a few later maybe unnecessary if checks but is still
// cheaper then using Find over and over again
if(!player) player = FindWithTag("Player");
if(!graphics) graphics = GetComponentInChildren<Rigidbody>(true);
if(!exclamationMark) exclamationMark = GetComponentInChildren<ExclamationMarkSpawn>(true);
var playerPos = (int)player.position.x;
// always if making such a check also give a hint that something might be missing
if (!graphics)
{
// by adding "this" you can now simply click on the message
// in the console and it highlights the object where this is happening in the hierarchy
Debug.LogWarning("graphics is missing here :'( ", this);
return;
}
// Define gameobject position
var enemyPos = graphics.transform.position.x;
// Define range to spawn tiles in
// this entire block can be shrinked down to
if (Mathf.Abs(playerPos - enemyPos) <= 5)
{
enemyIsActive = true;
if (exclamationMark) exclamationMark.SpawnExclamationMark();
}
else
{
enemyIsActive = false;
}
}
The same also in ExclamationMarkSpawn.cs.
I would additionally only allow 1 exclamation mark being visible at the same time. For example when a player jitters in the distance especially assuming both, the player and the enemy, I would move the entire instantiation to the routine and use a flag. Especially since this is called every frame in Update while the player stays in the range!
Also re-check and make sure your enemies are not maybe referencing the same spawnPos and thus all instantiating their exclamation marks on top of each other.
public class ExclamationMarkSpawn : MonoBehaviour
{
public Transform spawnPos;
public GameObject exclamationMark;
public GameObject exclamationMarkAudio;
[SerializeField] private CameraShake cameraShake;
// only serialized for debug
[SerializeField] private bool isShowingExclamation;
private void Awake()
{
if(!cameraShake) cameraShake = Camera.main.GetComponent<CameraShake>();
// or assuming this component exists only once in the entire scene anyway
if(!cameraShake) cameraShake = FindObjectOfType<CameraShake>();
}
public void SpawnExclamationMark()
{
StartCoroutine(ShowExclamationMark());
}
private IEnumerator ShowExclamationMark()
{
// block concurrent routine call
if(isShowingExclamation) yield brake;
// set flag blocking concurrent routines
isShowingExclamation = true;
// NOTE: Also for this one you might want to rather have a flag
// multiple enemy instances might call this so you get concurrent coroutines also here
StartCoroutine(cameraShake.Shake(0.2f, 0.2f, 0.2f));
Instantiate(exclamationMark, spawnPos.position, Quaternion.identity);
if (exclamationMarkAudio) Instantiate(exclamationMarkAudio, spawnPos.position, Quaternion.identity);
yield return new WaitForSeconds(1);
var children = new List<GameObject>();
foreach (var child in transform.ToList()) children.Add(child.gameObject);
children.ForEach(child => Destroy(child));
// give the flag free
isShowingExclamation = false;
}
}
Try this;
if (transform.Find("ExclamationMark"))
{
var exMark = transform.Find("ExclamationMark").gameObject.GetComponent<ExclamationMarkSpawn>();
exMark.SpawnExclamationMark(transform.position); //Add transform.position here
}
public void SpawnExclamationMark(Vector3 EnemyPos)
{
StartCoroutine(GameObject.FindGameObjectWithTag("MainCamera").GetComponent<CameraShake>().Shake(0.2f, 0.2f, 0.2f));
Instantiate(exclamationMark, EnemyPos, Quaternion.identity);
if (exclamationMarkAudio)
Instantiate(exclamationMarkAudio, EnemyPos, Quaternion.identity);
StartCoroutine(DestroyExclamationMark());
}
Closed. This question needs details or clarity. It is not currently accepting answers.
Want to improve this question? Add details and clarify the problem by editing this post.
Closed 3 years ago.
Improve this question
I'm trying to implement laser beam reflection behavior. I have a laser object named "BeamPoint" with RaySenderScript attached that simply casts a ray upwards.
RaySenderScript
public class RaySenderScript : MonoBehaviour
{
void FixedUpdate()
{
if (gameObject.name == "BeamPoint" ||
gameObject.GetComponent<RayReceiverScript>().isHitByRay)
{
RaycastHit2D hit = Physics2D.Raycast(transform.position, transform.up);
Debug.DrawLine(transform.position, hit.point);
if (hit.collider.tag == "Effector")
{
RayReceiverScript rayReceiver =
hit.collider.gameObject.GetComponent<RayReceiverScript>();
if (rayReceiver != null)
{
rayReceiver.HitWithRay();
}
}
}
}
}
Another object is "Mirror" that has to reflect a laser beam if gets hit by it. Mirror object has attached both RaySenderScript and RayReceiverScript.
RayReceiverScript
public class RayReceiverScript : MonoBehaviour
{
const float hitByRayRefreshTime = .01f;
private float rayRunsOutTime;
public bool isHitByRay = false;
void Start()
{
rayRunsOutTime = Time.time;
}
void FixedUpdate()
{
if(rayRunsOutTime < Time.time)
{
isHitByRay = false;
}
}
public void HitWithRay()
{
isHitByRay = true;
rayRunsOutTime = Time.time + hitByRayRefreshTime;
}
}
Also whole area is surrounded by a wall (tagged wall) with collider - ray always will hit something. So, in my case, when the ray from "BeamPoint" hits the "Mirror" (with tag effector) the mirror does not cast a ray at all. And also isHitByRay variable doesn't turn into false after ray stops casting on mirror - I think it goes in cycles. It is the main question: why doesn't the mirror cast a ray when is getting hit by other ray, and when not - why doesn't the isHitByRay get false?
There are a lot of small issue points in your code:
As I understood every of your objects has both scripts. So first of all I would remove the repeated GetComponent call for the objects own RayReceiver
Also add a check since it is possible that rayReceiver is simply null and therefore your method HitWithRay just never called. At least I would add a warning here.
Then using Physics2D.Raycast it is pretty sure that the first hit collider is actually the one of your sender object itself! So you should rather use Physics2D.RaycastNonAlloc, filter the result and then use the first hit that is not the object itself.
There is also no warranty in which order both FixedUpdate methods will be called. I would rather do it event based and enable a Coroutine. This saves resources while there is no hit anyway.
Also note that your hitByRayRefreshTime of 0.01f is very small and probably even smaller then the rate of FixedUpdate itself (usually 0.02f). The original post you linked rather used 1f as value here. So your rays were simply immediately turned off all the time.
Currently those are also not really "mirrors". If you really want to have a mirrored ray I would additionally pass in the exact hit position and direction and use it later for sending the new ray
So I would change your classes to something like:
public class RaySender : MonoBehaviour
{
// would be even better if you reference this via the Inspector
[SerializeField] private RayReceiver myReceiver;
// enable this for the initial sender
// (using a simple bool is more efficient than string comparing the name)
[SerializeField] private bool alwaysSends;
// store the last hit object!
private Transform _lastHit;
private RayReceiver _currentHitReceiver;
private void Start()
{
// Get your own component ONCE
if (!myReceiver) myReceiver = GetComponent<RayReceiver>();
}
private void FixedUpdate()
{
// if not always on or receiver not hit do nothing
if (!alwaysSends && !myReceiver.isHitByRay) return;
// except fot the first sender use hitpoint and mirrored direction
var laserStartPoint = alwaysSends ? transform.position : transform.TransformPoint(myReceiver.incomingHitPoint);
var laserDirection = alwaysSends ? (Vector2)transform.up : Vector2.Reflect(transform.TransformDirection(myReceiver.incomingHitDirection), transform.up);
// give space for 2 hits in case you hit yourself
var hits = new RaycastHit2D[2];
Physics2D.RaycastNonAlloc(laserStartPoint, laserDirection, hits);
// get the first one that is not yourself
var currentHit = hits.FirstOrDefault(h => h.transform != transform);
if (!currentHit.collider || !currentHit.collider.CompareTag("Effector")) return;
// something was hit
Debug.DrawLine(laserStartPoint, currentHit.point);
if (_lastHit != currentHit.transform)
{
// update currentHitReceiver
_currentHitReceiver = currentHit.collider.GetComponent<RayReceiver>(); ;
_lastHit = currentHit.transform;
}
if (_currentHitReceiver)
{
_currentHitReceiver.HitWithRay(currentHit.point, currentHit.point - (alwaysSends ? (Vector2)transform.position : myReceiver.incomingHitPoint));
}
else
{
// ouch! Hit somthing but RayReceiver not found here
Debug.LogWarning("Ouch! Something without a RayReceiver was hit ...", this");
}
}
}
and
public class RayReceiver : MonoBehaviour
{
[Header("Settings")]
public float turnOffDelay = 0.5f;
[Header("Debug")]
public bool isHitByRay;
public Vector2 incomingHitDirection;
public Vector2 incomingHitPoint;
private void Start()
{
isHitByRay = false;
}
public void HitWithRay(Vector2 hitPoint, Vector2 hitDirection)
{
// stop evtl. running routines so we don't get concurrent routines
StopAllCoroutines();
isHitByRay = true;
// get point and direction relative to this object
incomingHitPoint = transform.InverseTransformPoint(hitPoint);
incomingHitDirection = transform.InverseTransformDirection(hitDirection);
// start the turn off routine
StartCoroutine(TurnOffAfterDelay());
}
// after the given delay turn off the raycast
private IEnumerator TurnOffAfterDelay()
{
yield return new WaitForSeconds(turnOffDelay);
isHitByRay = false;
}
}
I have written a script to pick an object with a rigidbody component by the ray casted from the controller and move it around.
I make the object, the child of the controller to move it in the scene then. I already have a script to detect the object by the ray casted from a controller, picking it up and moving it up and down and left and right with the controller.
Now , I want to make that selected object along the z-axis by using the touchpad of oculus go. However I am not sure how to do it.
This is the function i am using it to attach to the parent:
public virtual void Store(Transform NewParent)
{
//The following stops the object being effected by physics while it's in
the players hand
rb.isKinematic = true;
//And fixes it to the new parent it is given by the player script to
follow.
transform.parent = NewParent;
//It then resets it's position and rotation to match it's new parent
object
//transform.localRotation = Quaternion.identity;
//transform.localPosition = Vector3.zero;
}
and then i use it in the pointer class to attach it to the ray:
void Intract()
{
//We set up the input "OculusPrimaryIndexTrigger" in the Input manager
if (OVRInput.GetDown(OVRInput.Button.PrimaryIndexTrigger))
{
selectVisual.ClearObject();
//Check if you are holding something you can throw first
if (inHand != null)
{
inHand.Release(controllerRef.forward, throwForce);
inHand = null;
//We do this check here to prevent Errors if you have nothing
selected
}
else if (selectedObject != null)
{
//Check if you can pick up the selected object second
if (selectedObject.GetComponent<PickUp>())
{
//Beacuse PickUp is a child of PropBase, we can ask InHand
to store selectedObject as PickUp, rather than use GetComponent
inHand = selectedObject as PickUp;
inHand.Store(holdingRef);
//If non of the above were valid then simple call the
trigger function of the selected object
}
else
{
selectedObject.Trigger();
}
}
//If you have a object that you need to hold down a button to
intract with
}
else if (pointerOver != null)
{
if (pointerOver.GetComponent<PropBase>())
{
selectedObject = pointerOver.GetComponent<PropBase>();
}
else
{
selectedObject = null;
}
}
else
{
selectedObject = null;
}
}
}
If anyone could point me in a right direction or help me with this, I would greatly appreciate it!
Thanks in advance
I am attempting to make a physics sandbox-type game for the Vive, but the velocity of an object is completely reset and just begins falling when you let go of an object you were previously holding, making throwing impossible.
The system I am using currently will disable gravity on an object, and disable colliders of on an object when you pick it up. It will also child the object to your controller, making it like holding the object. When you let go of the button to release the object, it will enable gravity, enable colliders, and then set its parent object to null. This works to pick up and release an object, but it does not work at all for throwing objects. I have played around with timing and order of the components of the code, and nothing works.
Is there any way to find the velocity of an object and the directional velocity, without the object using gravity? Velocity doesn't work if gravity is disabled.
Here is my code:
using UnityEngine;
using System.Collections;
public class WandController : MonoBehaviour
{
//Basic Controller tracking stuff
private Valve.VR.EVRButtonId gripButton = Valve.VR.EVRButtonId.k_EButton_Grip;
public bool gripButtonDown = false;
public bool gripButtonUp = false;
public bool gripButtonPressed = false;
private Valve.VR.EVRButtonId triggerButton = Valve.VR.EVRButtonId.k_EButton_SteamVR_Trigger;
public bool triggerButtonDown = false;
public bool triggerButtonUp = false;
public bool triggerButtonPressed = false;
private SteamVR_Controller.Device controller { get { return SteamVR_Controller.Input((int)trackedObj.index); } }
private SteamVR_TrackedObject trackedObj;
//Game Variables
public GameObject wouldSelect; //What is in the select zone, has tiny script for the zone that sets the newest triggerenter to this variable
public GameObject isHolding; //When you hold something, it goes from wouldselect to isholding
public bool holding = false;
public GameObject holdingZone; //The holding zone, also where objects go if they are picked up
// Use this for initialization
void Start()
{
trackedObj = GetComponent<SteamVR_TrackedObject>();
}
// Update is called once per frame
void Update()
{
//Basic Controller configuration & button management stuff
if (controller == null)
{
Debug.Log("Controller not initialized");
return;
}
gripButtonDown = controller.GetPressDown(gripButton);
gripButtonUp = controller.GetPressUp(gripButton);
gripButtonPressed = controller.GetPress(gripButton);
triggerButtonDown = controller.GetPressDown(triggerButton);
triggerButtonUp = controller.GetPressUp(triggerButton);
triggerButtonPressed = controller.GetPress(triggerButton);
if (gripButtonDown)
{
Debug.Log("Grip Button was just pressed");
}
if (gripButtonUp)
{
Debug.Log("Grip Button was just unpressed");
}
if (triggerButtonDown)
{
Debug.Log("Trigger Button was just pressed");
}
if (triggerButtonUp)
{
Debug.Log("Trigger Button was just unpressed");
}
//Calling void that allows you to grab
CanGrab();
}
void CanGrab ()
{
if(wouldSelect != null && wouldSelect.tag == "Object" && triggerButtonDown == true && holding == false)
{
wouldSelect.GetComponent<Collider>().enabled = false;
wouldSelect.GetComponent<Rigidbody>().useGravity = false;
isHolding = wouldSelect;
wouldSelect.transform.SetParent(this.transform);
wouldSelect.transform.position = holdingZone.transform.position;
holding = true;
}
if(holding == true && triggerButtonUp == true)
{
wouldSelect.GetComponent<Collider>().enabled = true;
isHolding.GetComponent<Rigidbody>().useGravity = true;
isHolding.transform.SetParent(null);
holding = false;
wouldSelect = null;
isHolding = null;
}
}
}
I had a similar issue and solved it by:
tracking the position of the object at every frame, and storing it as lastPosition
when the object is let go, using (transform.position - lastPosition) to give me a rough estimate of the velocity of that object
From there, you can add an impulse force if you have a rigidbody, etc, I found a thread with some details on the various ways Unity does it -- http://answers.unity3d.com/questions/696068/difference-between-forcemodeforceaccelerationimpul.html
I did a number of throwing mechanics in VR using the same parameters you had (Setting the object as a child of your controller, disabling gravity, disabling colliders).
The way I did it was to record the position of the object on the previous and current frame, and take the difference between them as the velocity. However, there are three main factors to consider:
There might be some jitter in the tracking of the controller, and sometimes the object does not fly in the direction of throw.
There might be a lag time between the user pressing/letting go of the throwing button and the button press/release getting recorded, and so the object is thrown only towards the falling arc of the throw. (This happens a lot in my playtests)
There is a peak force during a throw during which an object gains the most velocity for its flight, and sometimes people let go of the object a split second after. This is rather inconsequential, but you should definitely consider if you want very realistic throwing (Take a look at The Lab demo and you will know what I mean)
My implementation:
Record the last x frames (for me the sweet spot is between 10-15 frames) for the object's position.
Take the difference in the first and last frame in the window and use that to calculate the velocity.
transform.velocity = position[n] - position[0];
If I want a slightly more accurate implementation, I'll calculate the force of the throw by taking the differences in velocities for adjacent frames. If there is a time window between the peak force and the release of the object, do not take the velocities in the later half of the window.
For example, if I decide to record the last 10 frames of a throw, and there are 4 frames between the peak force and the release, I will take frame current-12 to current-2, instead of frame current-10 to current for the velocity.