I'm creating a VR application in Unity using SteamVR. I have implemented a Snap Rotation Script, so, the user can turn himself in-game without doing it phisicaly.
My hierachy is: [CameraRig] -> (Controller left, Controller right, camera)
Now the problem is: when I'm holding an object and i want to throw it, if i rotated the CameraRig earlier, the object throwed behave strangely. Its direction is out of phase, based on the cumulative rotation of the CameraRig.
If I never rotate the CameraRig, the object throwed behave normally
I'm using FixedJoint component to attach the object to the hand.
How can I fix this?
public void Drop() {
// Null Check
if (!m_CurrentInteractable){
return;
}
// Apply velocity
Rigidbody targetBody = m_CurrentInteractable.GetComponent<Rigidbody>();
targetBody.velocity = m_pose.GetVelocity();
targetBody.angularVelocity = m_pose.GetAngularVelocity();
// Detach
m_Joint.connectedBody = null;
// Clear
m_CurrentInteractable.m_ActiveHand = null;
m_CurrentInteractable = null;
}
When calculating your velocity and angularVelocity, try to add one more step afterwards which incorporates the rig:
velocity = Rig.transform.rotation * velocity;
angularVelocity = Rig.transform.rotation * angularVelocity;
Hope this helps!
Related
i am working on a project and need help making an object follow another object but at the same time one end should point towards the camera.I have tried the lookAT method but it is not working properly.
LookAt works perfectly for 3D objects.
These are the possibilities that LookAt may fail to work as you expect:
You want a 2D object to look at a point.
The visually-considered front-side of the object is not its x+ side. mainly because:
The 3D model is carelessly fabricated using a third party software.
The object is inside another object and is rotated in its place.
You are manipulating the rotation of the object elsewhere.
Pointing the other object is really simple. You only need to use the LookAt, like this:
GameObject target;
transform.LookAt(target.transform);
For following the target, I see multiple apporaches. You can for example make the object also use the script with LookAt and when you move the main oject, move also the follower.
Another more complex way would be set a maximum separation distance where the object will move towards its target. This one would also use LookAt.
If you chose the last one, you can do something like this:
const float MaxDistance = 5.0f // The maximum distance the target can go away before following it.
float speed = 2.0f;
if(Vector3.Distance(transform.position, target.transform.position) > MaxDistance)
{
transform.LookAt(target.transform);
transform.position += Vector3.forward * speed * Time.deltaTime;
}
You should attach this to the Update or a Couroutine.
A specific object goes straight to the target object as follows:
//Declare a variable that points to a specific object and a target obj variable
public GameObject obj1;
public GameObject obj2;
GameObject targetObj;
// Update is called once per frame
void Update()
{
if (Input.GetKeyUp(KeyCode.Alpha1))
OnChangedTarget(obj1);
if (Input.GetKeyUp(KeyCode.Alpha2))
OnChangedTarget(obj2);
UpdateObjPos(Camera.main.gameObject);
}
//Keep distance from target obj
void UpdateObjPos(GameObject followObj, float width = 10f, float height = 10f)
{
if (targetObj == null)
return;
followObj.transform.LookAt(targetObj.transform);
float posX = targetObj.transform.position.x + width;
float posY = targetObj.transform.position.y + width;
float posZ = targetObj.transform.position.z + height;
followObj.transform.position = new Vector3(posX, posY, posZ);
}
//Declare function to select target obj
void OnChangedTarget(GameObject obj)
{
Debug.Log("Target Changed : " + obj.name);
targetObj = obj;
}
Solution
Just inherit your desired object to the main game object withe the main game object which is your player.I never used any c# code script for this purpose. if you want to follow two objects to follow each other just inherit main game object . In that way the second game object will follow the required main player . The camera will be inherit as well if you want camera to follow both of them . As far as the points the camera is concerned you need to play with unity tool and adjust the x,y and z axis according to the position of camera . I would suggest to put camera in front of both game objects and just turn the face of camera toward the game objects .
picture
I'm using bulletsharp (C# wrapper of bullet physics) to do some collision detection. Everything went fine until i tried to change the collision shape of an existing rigid body from box to compound at runtime. I'm doing this to simulate the collision again with a higher accuracy. The problem is: collision of compound shapes is not detected.
Scenario:
DicreteDynamicsWorld containing some RigidBody with BoxShape
Collision of two kinematic RigidBody with BoxShape happens (and is detected)
Change the shapes of these two RigidBody to CompoundShape using the HACD algorithm for convex decomposition
Remove RigidBody from DicreteDynamicsWorld
RigidBody.CollisionShape = CompoundShape
Set Position using RigidBody.MotionState.WorldTransform
Add RigidBody to DicreteDynamicsWorld
Undo the movement which caused the collision (one object doesn't contain the other)
Repeat the movement
Collision is not detected
Remarks:
Changing the CollisionShape from BoxShape to CompoundShape was successful (correct CollisionShape and correct position)
For collision detection i'm using DicreteDynamicsWorld.Dispatcher.NumManifolds > 0 after a DicreteDynamicsWorld.StepSimulation(...)
Some code snippets as requested:
If you need something particular, please tell me. My solution is too big and too complexe to post the complete code...
RigidBody creation:
// Create rigid body
MotionState motionState = new DefaultMotionState(startTransform);
RigidBodyConstructionInfo rbInfo = new RigidBodyConstructionInfo(0.0f, motionState, collisionShape);
RigidBody rigidBody = new RigidBody(rbInfo);
rbInfo.Dispose();
// Kinematic body: mass=0 -> static/kinematic -> use flag
bool isKinematicBody = (compModel.Children[i].Type ==...) || ... ;
rigidBody.CollisionFlags = isKinematicBody ? CollisionFlags.KinematicObject : CollisionFlags.StaticObject;
rigidBody.ActivationState = ActivationState.DisableDeactivation;
Basic steps:
// Get old collision data
if (compModel.Children[i].Container.TryGetValue(ContainerType.Collision, out container))
collisionData = ((ContainerCollision) container).CollisionData;
// Get geometry
if (compModel.Children[i].Container.TryGetValue(ContainerType.Geometry, out container))
{
verticesGeo = ((ContainerGeometry) container).GeometryData.Vertices;
trianglesGeo = ((ContainerGeometry) container).GeometryData.Triangles;
}
// Remove rigid body from world
_world.RemoveRigidBody(collisionData.RigidBody);
// Create new shape
List<Vector3> vertices = Utility.ToBulletVector3List(verticesGeo);
List<int> indices = Utility.ListIntArrayToListInt(trianglesGeo);
CompoundShape collisionShape = ConvexDecomposition(compModel.Children[i].Id, vertices, indices);
// Set collision shape
collisionData.RigidBody.CollisionShape = collisionShape;
// Set position
collisionData.RigidBody.MotionState.WorldTransform *= collisionData.PositionDifference;
// Add rigid body to world
_world.AddRigidBody(collisionData.RigidBody, collisionData.CollisionGroup, collisionData.CollisionMask);
CollisionContainer:
public interface IContainer
{
ContainerType Type { get; }
}
public struct ContainerCollision : IContainer
{
public ContainerType Type
{
get { return ContainerType.Collision; }
}
public CollisionData CollisionData;
}
Structure CollisionData:
public struct CollisionData
{
public BulletSharp.RigidBody RigidBody;
public BulletSharp.Matrix PositionDifference;
public BulletSharp.Vector3 ZeroPosition;
public short CollisionGroup;
public short CollisionMask;
}
Any ideas, what i'm doing wrong?
Thanks.
Assuming that all your change in shape is happening here
CompoundShape collisionShape = ConvexDecomposition(compModel.Children[i].Id, vertices, indices);
// Set collision shape
collisionData.RigidBody.CollisionShape = collisionShape;
// Set position
collisionData.RigidBody.MotionState.WorldTransform *= collisionData.PositionDifference;
// Add rigid body to world
_world.AddRigidBody(collisionData.RigidBody, collisionData.CollisionGroup, collisionData.CollisionMask);
Probably you will have to recalculate the entire RigidBody from start, I have no clue how you do that in the first place so I will show you an example how I do it.
public virtual RigidBody LocalCreateRigidBody(float mass, Matrix startTransform, CollisionShape shape)
{
//rigidbody is dynamic if and only if mass is non zero, otherwise static
bool isDynamic = (mass != 0.0f);
Vector3 localInertia = Vector3.Zero;
if (isDynamic)
shape.CalculateLocalInertia(mass, out localInertia);
//using motionstate is recommended, it provides interpolation capabilities, and only synchronizes 'active' objects
DefaultMotionState myMotionState = new DefaultMotionState(startTransform);
RigidBodyConstructionInfo rbInfo = new RigidBodyConstructionInfo(mass, myMotionState, shape, localInertia);
RigidBody body = new RigidBody(rbInfo);
rbInfo.Dispose();
return body;
}
With that being said, take a note at your "collisionData" (Which I guess its a class?) and the naming of RigidBody (it might have a conflict with the BulletSharp class?)
Best reguards with your progress and feel free to contact me if you have any questions!
I am having an issue where only the first NavMeshAgent can be created.
First of all, I selected all mesh renderers for the NavMesh bake objects:
I fine-tuned the bake a bit so it had just the right amount of detail. The black gaps around the various objects would be larger if I used a larger radius in the baked agent options:
I have two spawners placed on either side of the back ramp (the one with the white sphere in it). These are invisible but in a script, NavMeshObjects are placed onto these locations on an interval and have their destinations set to one of the other gameobjects.
The first one works fine, but none of the other 17 NavMeshObjects in the spawner queue can have their destinations set.
public class MinionSpawner : MonoBehaviour {
public void spawn (GameObject minion, GameObject target_position_obj) {
// Find the target position from the passed gameobject
MinionTargetPosition target_position = target_position_obj.GetComponent<MinionTargetPosition>();
if (!target_position) { throw new System.Exception("no valid target position given"); }
// Instantiate the given minion at the spawner's origin point
GameObject new_minion = Object.Instantiate(minion, transform.position, new Quaternion(0,0,0,0));
new_minion.SetActive(true);
// Mark the minion at it's target position, for lookup upon collision or elsewhere
MinionAttributes minion_attrs = new_minion.GetComponentInChildren<MinionAttributes>(true);
if (!minion_attrs) { throw new System.Exception("object passed as minion does not have MinionAttributes"); }
minion_attrs.target_position = target_position;
// Configure a NavMeshAgent to navigate the minion from origin to target position.
NavMeshAgent nav_mesh_agent = minion_attrs.gameObject.GetComponent<NavMeshAgent>();
if (!nav_mesh_agent) { throw new System.Exception("minion doesn't have a nav mesh agent"); };
nav_mesh_agent.Warp(transform.position);
// ================================================================
// THIS LINE FAILS:
nav_mesh_agent.destination = target_position.transform.position;
// ================================================================
// mark the minion's position as occupied in the turret's dictionary
Turret turret = target_position.turret;
turret.minion_slots[target_position] = true;
// set the minion's parent to the minion spawner, for organization's sake.
minion.transform.parent = transform;
}
}
I'm getting the error "SetDestination" can only be called on an active agent that has been placed on a NavMesh. and I know there are questions out there about this. But I've tried really everything I've found suggested, including baking the NavMesh, setting the NavMeshAgent's initial position with Warp, and making sure the points are on the mesh.
The issue was something unrelated to the NavMeshagent. It was this line:
minion.transform.parent = transform;
I needed to change it to:
new_minion.transform.parent = transform;
Basically minion is the object that gets cloned and new_minion is the clone. So if I set the parent of minion, I am messing it up for future clones, I guess.
Basically, i have player object and bunch of other. For this moment they all have RigitBody2D, and colliders, code for creating:
obj = new GameObject();
obj.tag = "web";
obj.name = "web";
float randomX = Random.Range (Utilities.getMainCameraBounds ().min.x, Utilities.getMainCameraBounds ().max.x);
float randomY = Random.Range (Utilities.getMainCameraBounds ().min.y, Utilities.getMainCameraBounds ().max.y);
obj.transform.position = new Vector3(randomX, randomY);
obj.transform.localScale = new Vector3 (3.0f, 3.0f, 3.0f);
collider = obj.AddComponent <BoxCollider2D>();
body = obj.AddComponent<Rigidbody2D> ();
body.constraints = RigidbodyConstraints2D.FreezeAll;
joint = obj.AddComponent<HingeJoint2D> ();
renderer = obj.AddComponent<SpriteRenderer>();
renderer.sortingOrder = 1;
renderer.sprite = Resources.Load("Textures/dot_net", typeof(Sprite)) as Sprite;
Purpose for this is to catch collision like this :
void OnCollisionEnter2D(Collision2D collision) {
if (collision.transform.tag == "web") {
Player.getInstance().joint(collision.rigidbody);
}
}
After player collides with some of those objects, method joint called, it's purpose to connect player and web with HingeJoint2D to simulate pendulum behvior. Code of joint:
public void joint(Rigidbody2D body) {
WebCollection.getInstance ().turnOffColliders (body.gameObject);
HingeJoint2D hingeJoint = webLine.getObject ().AddComponent<HingeJoint2D> ();
hingeJoint.connectedBody = body;
hingeJoint.anchor = new Vector2 (0.00120592f, 0.1921938f);
hingeJoint.connectedAnchor = new Vector2 (0.1939605f, 0.03025406f);
}
Pendulum behavior is working just fine, but in moment of it, or just when player is moving towards web, other web objects are colliding with it, creating real colliding behavior(like crushing into each other)
My goal: i want web objects to react on the collision(calling method above), but on the screen to player just go throw them.
What i've already tried: I wanted to turnOff other objects bodies (WebCollection.getInstance ().turnOffColliders (body.gameObject);) and after pendulum movement is over, to turn them back, but it causes very strange behavior.
What also can be a solution is to manually checking if player is colliding with web object right now, and handle it, but this is to costly i think.
To put my comment into an answer for those finding your question:
If you want to have Colliders that only trigger events and do not physically collide with other Colliders, then what you want are Triggers. To make a Collider a trigger you check the Is Trigger checkbox of the collider component in the inspector (you can also set it via script with the Collider.IsTrigger boolean property).
To do something when the trigger fires, use the OnTrigger events (OnTriggerEnter, OnTriggerExit, OnTriggerStay) (see http://docs.unity3d.com/ScriptReference/Collider.html)
There is also a short introduction to Colliders as Triggers by unity: http://unity3d.com/learn/tutorials/topics/physics/colliders-triggers
I am trying to make a "worms"-like game where the player can choose the position of an object (the player can move the object around 180 degrees) , and then the force would be added to the direction the object is facing. I tried using transform.right and transform.forward, but the force was not driven towards where the object is pointing. I have looked around A LOT, and still not found/understand what I can do. Heres the code I use for shooting the object:
void shootIt(){
transform.parent = null;
isPlaying = true;
A.isKinematic = false;
A.AddForce(transform.up*varSpeed*_multiplier);
} //"A" stands for the RigidBody2D component I got by using GetComponent<Rigidbody2D>();
Help is as always greatly appreciated.
Try this:
void shootIt()
{
Vector2 direction = transform.localPosition.normalized;
transform.parent = null;
isPlaying = true;
A.isKinematic = false;
A.AddForce(direction*varSpeed*_multiplier);
}
Also consider forcing yourself to write good names for your variables. A is not very descriptive.