Moving Rigidbody using player input - unity3d

Im trying to move an object in a 3d world using a controller, but think I am missing something cus it just clips away as soon as I give any input:
private void Update()
{
float h = Input.GetAxisRaw("Horizontal");
float v = Input.GetAxisRaw("Vertical");
Vector3 movement = new Vector3(h, 0, v).normalized * Time.deltaTime * speed;
if(h != 0 || v != 0)
_rigidBody.MovePosition(movement);
}
Works fine using _rigidbody.velocity, but as I understand it that should be avoided for these types of things.

Rigidbody.MovePosition sets the position of the rigidbody with interpolation. It looks like you want to offset the position by movement, so you should probably set the velocity. If you do still want to use MovePosition, you should do _rigidBody.MovePosition(transform.position + movement);.

"using a controller"
Do you mean Character Controller Component? You have 2 options and they are both well explained in Unity Documentation.
Second is by Rigidbody Component.

Related

Movement using a Rigidbody

I've been using the transform property for movement so far and that left my Physics a little choppy on collision. To counter this I switched movement using Rigidbody.velocity. This is a sample of my code.
//Start
rb = GetComponent<RigidBody>();
jumpForce = 5;
//Update
horizontalInput = Input.GetAxis("HorizontalInput");
Vector3 movement = new Vector3(horizontalInput, 0.0f, 0.0f);
rb.velocity = movement * speed;
this worked when it came to left and right, however when I want to jump I use this function and my character does not move, is there something wrong I'm doing or am I too much of a beginner to understand?
if (Input.GetKeyDown(KeyCode.UpArrow)) {
rb.AddForce(Vector3.up * jumpForce, ForceMode.Impulse);
}
Looks like you're overriding the velocity when you do horizontal movement so the jump velocity has no effect. You should use rb.AddForce() for horizontal movement too.
You have to be careful not to add too much force though. For an example see this answer.
Vector3.up is the same as new Vector3(0f, 1, 0f) and since X and Z axis are 0 => your character will not move when jumping.
You need to save existing X and Z velocity in rb.velocity without any changing them when jumping:
if (Input.GetKeyDown(KeyCode.UpArrow))
{
Vector3 velocity = rb.velocity;
rb.velocity = new Vector3(velocity.x, jumpForce, velocity.z);
}
Since you change velocity instantly - you'll get instant velocity change which is the same as using AddForce with ForceMode.Impulse

Reverse movement done with transform.RotateAround()

I am making a third person mobile game with the help of joysticks. I have it set up where joystick.vertical moves the character forward or backwards depending on where he is looking and joystick.horizontal turns the character. Since the camera is parented to the character the camera always stays behind the character.
Swiping across the screen rotates the camera around the player with a touch panel using Camera.main.transform.RotateAround() function and the transform.LookAt() ensures I am looking at my character always.
My issue: I would like when the swipe is let go the camera to return to its original position behind the character but in a smooth motion or at a set speed moving around the player.
My Solution 1: To make an empty gameObject parented to the character and place it in the position where I want the camera to revert back to and call this position when the mouse is let go.
float spanCamera = -Joystick.Horizontal;
Camera.main.transform.LookAt(rb.transform.position);
if (spanCamera != 0)
Camera.main.transform.RotateAround(rb.position, Vector3.up, spanCamera * Time.fixedDeltaTime * spanSpeed);
else if (Input.touchCount <= 1)
{
float var6 = var5 * Time.deltaTime;
Camera.main.transform.position = camPos.transform.position;
Camera.main.transform.LookAt(camLookAt.transform.position);
}
This piece of code moves the camera back to the start position immediately without smoothing.
Solution 2: use a Vector3.MoveTowards() instead
// Camera.main.transform.position = camPos.transform.position;
Camera.main.transform.position = Vector3.MoveTowards(Camera.main.transform.position, camPos.transform.position, var6);
This code allows me to smoothly move to the start position in a straight line. I want it to go around the player.
So I tried a different method where I assign a float value 1 if the camera turns right and check if the rotate button is let go and if the value is 1 within an if block.
public float axisDir;
...
else if (spanCamera == 0 && axisDir == 1)
{
Camera.main.transform.RotateAround(rb.position, Vector3.up, -1 * Time.fixedDeltaTime * spanSpeed);
}
But this results in an infinite spin because I do not know how to check if the desired position has been reached.
I hope someone can help. This is quite a long post. :(
You could have an dummy gameObject as a child of the player, and the camera as a child of the dummy.
That way, the camera is looking at the centre of the gameObject: where the player is. The camera would also rotate with the player, so it would always be behind the player.
Now, you know that when Mathf.Approximately(dummy.transform.localEulerAngles.y, 0.0f), the camera is behind the player. [1][2]
Knowing that, you can check when the player stopped swiping and start slowly rotating it back.
// If the player let go and camera is not behind the player
if (!playerIsSwiping && !Mathf.Approximately(dummy.transform.localEulerAngles.y, 0.0f))
{
// Slowly rotate until the camera is behind the player
dummy.transform.RotateAround(Vector3.zero, Vector3.up, rotationSpeed * Time.deltaTime);
}
try this buddy :
// Maximum turn rate in degrees per second.
public float turningRate = 30f;
// Rotation we should blend towards.
private Quaternion _targetRotation = Quaternion.identity;
// Call this when you want to turn the object smoothly.
public void SetBlendedEulerAngles(Vector3 angles)
{
_targetRotation = Quaternion.Euler(angles);
}
private void Update()
{
// Turn towards our target rotation.
transform.rotation = Quaternion.RotateTowards(transform.rotation, _targetRotation, turningRate * Time.deltaTime);
}
i found it here
from my own experience using LERP works quite well, you lookin for a smooth transition, or between two points, known as interpolation, in this case linear interpolation.

How to lock position of physic body in z axis in Unity 3D

I am developing a 2.5D game. In that game I want my character (which has Rigidbody component attached to) to just move on x and y axises. So I use this code snippet:
private void LockZAxis () {
Vector3 currentPosition = _rigidbody.position;
currentPosition.z = 0;
_rigidbody.position = currentPosition;
}
I call this LockZAxis method in the end of both Update, FixedUpdate and LateUpdate. But it doesn't work. When my character run forward for a while, its z position is still changed.
For additional information, in my code, there are two times I manipulate the position of RegidBody. The first is when my character jump, that time I use this:
jumpVelocityVector = Vector3.up * jumpForceUp + transform.forward * jumpForceForward;
_rigidbody.velocity = jumpVelocityVector;
And each frame when I want my character to move a bit faster, so in the update method, I have this:
void Update () {
Vector3 newPosition = transform.position + transform.forward * speed * Time.deltaTime;
newPosition.z = 0;
_rigidbody.MovePosition (newPosition);
LockZAxis ();
}
A rigidbody is used to simulate physics, by setting the rigidbody's position every frame you're essentially teleporting the character every frame. You can restrict movement in z-axis, this will prevent it to move in z-axis when physics is applied, which is what a rigidbody typically is used for.
Here is how to restrict rigidbody positional change:
If you run your LockZAxis() after you've changed the position it should teleport the object to the z-position of 0 every frame. Please make sure that the z-axis is the correct axis. You can debug this by pausing a running game and manipulating the Transform values to see how each axis moves your Object.
Here is how you can do it with C# Script:
Freeze All Positions
rigidbody.constraints = RigidbodyConstraints.FreezePosition;
Freeze Specific Positions:
rigidbody.constraints = RigidbodyConstraints.FreezePositionY | RigidbodyConstraints.FreezePositionZ;
Unity Documentation
Is physics gravity set to only affect the Y position ?
Physics.gravity = new Vector3(0, -1.0F, 0);
And set these also
rigidbody.angularVelocity = Vector3.zero;
rigidbody.velocity.z=0;
make sure your rigidbody is set to kinematic since you are using Rigidbody.moveposition() and using moveposition() will directly effect velocity internally on a kinematic rigidbody
Try using moveposition() for you jump instead of velocity

assigning velocity to rigidbody doesn't do anything

I have a script attached to a mesh with a kinematic, rigid body with a convex mesh collider that I'd like to move around. Here's what I call in my update function:
if (Input.GetKey(forwards)) {
Debug.Log("forwards!!");
//get current velocity in local space
Vector3 localVel = transform.InverseTransformDirection(body.velocity);
//alter so that forward component = speed
localVel = new Vector3(localVel.x, localVel.y, linearSpeed);
//convert back into world space and set to body
Vector3 worldVel = transform.TransformDirection(localVel);
body.velocity = worldVel;
}
Other Info:
body is a Rigidbody variable that I assign in Start() using GetComponent<Rigidbody>();
linearSpeed is a float with value 1
I'm getting the Debug.Log output, but my mesh is not moving. Is there anything obvious I'm missing here? This is my first script for a 3D, as opposed to a 2D game.
public float speed = 20;
Rigidbody r;
void Start(){
r = gameObject.GetComponent<Rigidbody> (); //Put's reference to local rigidbody into variable "r"
}
void FixedUpdate () {
Vector3 direction = Vector3.zero; //set's current direction to none
//Adds vectors in case that user is pressing the button
if(Input.GetKey(KeyCode.W)) direction += Vector3.forward;
if(Input.GetKey(KeyCode.S)) direction -= Vector3.forward;
if(Input.GetKey(KeyCode.D)) direction += Vector3.right;
if(Input.GetKey(KeyCode.A)) direction -= Vector3.right;
//Normalizez direction (put's it's magnitude to 1) so object moves at same speeds in all directions
r.AddForce (direction.normalized * speed); //Adds direction with magnitude of "speed" to rigidbody
}
Rigidbody MUST be attached to same GO as this script. This script uses world directions, because working with local directions is much harder (the object is rotating and changes directions rapidly, you can use it if you want just by replacing reference to Vector3 to transform like this:
if(Input.GetKey(KeyCode.W)) direction += transform.forward;
Of course for all direction.
This is very basic way to move the object along it's local axises, to do it more better you need to write specific scripts for specific sets of objects, it all depends what kind of object are you moving and how you want to move it. (is it sphere, cube..., will it ever fly up, should it rotate....).
If the RigidBody is Kinematic it is meant to be moved by means other than the physics system; animations, transform.position, etc. Make your rigid body non-kinematic and it should move when you set velocity.

How to prevent colliders from passing through each other?

I am having trouble keeping game objects inside of a contained space. When they reach the edge, there is some momentary push back but then they will go right through the wall.
I am using a Box Collider on the player, and a Mesh Collider for the level's wall. I am having issues with both a Player Character (a space ship) that the movement is controlled by the player. And with projectiles, which are fire and forget moving at a constant speed.
This is my movement code for my player. It is being run in the FixedUpdate() function.
//Movement
haxis = Input.GetAxis("Horizontal") * speed;
vaxis = Input.GetAxis("Vertical") * speed;
moveVector.x = haxis;
moveVector.z = vaxis;
if(moveVector.magnitude > 1)
{
moveVector.Normalize();
}
rigidbody.MovePosition(transform.position + moveVector * speed);
With the bullets, they are given a velocity and the engine calculates their moviements. They are using Box Collider and it is set as a Trigger so they don't have physics. But I use OnTriggerEnter to destroy them.
//Projectiles without physics collisiions
function OnTriggerEnter (other : Collider) {
Destroy(gameObject);
}
Some, but not all of the bullets will be destroyed when hitting the mesh collider wall. The player will sometimes hit it and stop, but can usually push through it. How can I make the collisions with the mesh collider work every time?
Collision with fast-moving objects is always a problem. A good way to ensure that you detect all collision is to use Raycasting instead of relying on the physics simulation. This works well for bullets or small objects, but will not produce good results for large objects.
http://unity3d.com/support/documentation/ScriptReference/Physics.Raycast.html
Pseudo-codeish (I don't have code-completion here and a poor memory):
void FixedUpdate()
{
Vector3 direction = new Vector3(transform.position - lastPosition);
Ray ray = new Ray(lastPosition, direction);
RaycastHit hit;
if (Physics.Raycast(ray, hit, direction.magnitude))
{
// Do something if hit
}
this.lastPosition = transform.position;
}
I have a pinball prototype that also gave me much trouble in the same areas. These are all the steps I've taken to almost (but not yet entirely) solve these problems:
For fast moving objects:
Set the rigidbody's Interpolate to 'Interpolate' (this does not affect the actual physics simulation, but updates the rendering of the object properly - use this only on important objects from a rendering point of view, like the player, or a pinball, but not for projectiles)
Set Collision Detection to Continuous Dynamic
Attach the script DontGoThroughThings (https://www.auto.tuwien.ac.at/wordpress/?p=260) to your object. This script cleverly uses the Raycasting solution I posted in my other answer to pull back offending objects to before the collision points.
In Edit -> Project Settings -> Physics:
Set Min Penetration for Penalty to a very low value. I've set mine to 0.001
Set Solver Iteration Count to a higher value. I've set mine to 50, but you can probably do ok with much less.
All that is going to have a penalty in performace, but that's unavoidable. The defaults values are soft on performance but are not really intented for proper simulation of small and fast-moving objects.
How about set the Collision Detection of rigidbody to Continuous or Continuous Dynamic?
http://unity3d.com/support/documentation/Components/class-Rigidbody.html
So I haven't been able to get the Mesh Colliders to work. I created a composite collider using simple box colliders and it worked exactly as expected.
Other tests with simple Mesh Colliders have come out the same.
It looks like the best answer is to build a composite collider out of simple box/sphere colliders.
For my specific case I wrote a Wizard that creates a Pipe shaped compound collider.
#script AddComponentMenu("Colliders/Pipe Collider");
class WizardCreatePipeCollider extends ScriptableWizard
{
public var outterRadius : float = 200;
public var innerRadius : float = 190;
public var sections : int = 12;
public var height : float = 20;
#MenuItem("GameObject/Colliders/Create Pipe Collider")
static function CreateWizard()
{
ScriptableWizard.DisplayWizard.<WizardCreatePipeCollider>("Create Pipe Collider");
}
public function OnWizardUpdate() {
helpString = "Creates a Pipe Collider";
}
public function OnWizardCreate() {
var theta : float = 360f / sections;
var width : float = outterRadius - innerRadius;
var sectionLength : float = 2 * outterRadius * Mathf.Sin((theta / 2) * Mathf.Deg2Rad);
var container : GameObject = new GameObject("Pipe Collider");
var section : GameObject;
var sectionCollider : GameObject;
var boxCollider : BoxCollider;
for(var i = 0; i < sections; i++)
{
section = new GameObject("Section " + (i + 1));
sectionCollider = new GameObject("SectionCollider " + (i + 1));
section.transform.parent = container.transform;
sectionCollider.transform.parent = section.transform;
section.transform.localPosition = Vector3.zero;
section.transform.localRotation.eulerAngles.y = i * theta;
boxCollider = sectionCollider.AddComponent.<BoxCollider>();
boxCollider.center = Vector3.zero;
boxCollider.size = new Vector3(width, height, sectionLength);
sectionCollider.transform.localPosition = new Vector3(innerRadius + (width / 2), 0, 0);
}
}
}
1.) Never use MESH COLLIDER. Use combination of box and capsule collider.
2.) Check constraints in RigidBody. If you tick Freeze Position X than it will pass through the object on the X axis. (Same for y axis).
Old Question but maybe it helps someone.
Go to Project settings > Time and Try dividing the fixed timestep and maximum allowed timestep by two or by four.
I had the problem that my player was able to squeeze through openings smaller than the players collider and that solved it. It also helps with stopping fast moving objects.
Edit ---> Project Settings ---> Time ... decrease "Fixed Timestep" value .. This will solve the problem but it can affect performance negatively.
Another solution is could be calculate the coordinates (for example, you have a ball and wall. Ball will hit to wall. So calculate coordinates of wall and set hitting process according these cordinates )
Try setting the models to environment and static. That fix my issue.