Unity Physics Sphere Movement (wheel movement) - c#

I'm making a sphere move over a plane object. I'm trying to make the movement similar to the movement of a wheel, but I don't want to use the Wheel Collider component. I am using torque to move the sphere back and forth and I am using the rigidbody rotation (Because I read that it is not a good practice to perform these transformations directly on the geometry), but the rotation (steering) part is not working, the sphere continues to follow in same direction even rotating. Here's code below:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class SphereMovement : MonoBehaviour
{
float maxTorque = 30.0f;
float maxSteerAngle = 30.0f;
void Start()
{
}
void FixedUpdate()
{
var deltaRotation = GetComponent<Rigidbody>().rotation * Quaternion.Euler(new Vector3(maxSteerAngle * Input.GetAxis("Horizontal") * Time.deltaTime, 0, 0));
GetComponent<Rigidbody>().rotation = deltaRotation;
GetComponent<Rigidbody>().AddTorque(new Vector3(maxTorque * Input.GetAxis("Vertical") * Time.deltaTime, 0, 0));
}
}
Can someone help me?

Cache your GetComponent calls to improve performance (see below)
You are applying torque into global x direction, you probably want to move "forward" (depending on the rotation of the wheel). transform.forward is your friend
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class SphereMovement : MonoBehaviour
{
float maxTorque = 30.0f;
float maxSteerAngle = 30.0f;
private Rigidbody rb;
void Start()
{
rb = GetComponent<Rigidbody>();
}
void FixedUpdate()
{
var deltaRotation = rb .rotation * Quaternion.Euler(new Vector3(maxSteerAngle * Input.GetAxis("Horizontal") * Time.deltaTime, 0, 0));
GetComponent<Rigidbody>().rotation = deltaRotation;
GetComponent<Rigidbody>().AddTorque(transform.forward * (maxTorque * Input.GetAxis("Vertical") * Time.deltaTime);
}
}
If your setup is otherwise rotated, you can try transform.right or transform.up
edit:
when you rotate your wheel, the "forward" will spin as well, so you need to ignore the y. (assuming an almost flat surface for now, you mentioned a plane)
So you would think this works:
Vector3 direction = transform.forward;
direction.y = 0;
direction = direction.normalized;
GetComponent<Rigidbody>().AddTorque(direction * (maxTorque *
Input.GetAxis("Vertical") * Time.deltaTime);
But Then it would go back or forth, depending on the current rotation.
So that won't work.
Another Idea: You could add an empty parent and rotate that on the y axis to "steer" while the child is rotated to do the actual "forward movement" like a wheel rooling over concrete.
Option without parent:
You simply apply Torque in local space using Rigidbody.AddRelativeTorque
Like this (we use Vector3.forward, don't confuse it with transform.forward! Vector3.forward is simply Vector3(0, 0, 1). but in local space that's fine because the rotation is still considered)
void FixedUpdate()
{
var deltaRotation = rb .rotation * Quaternion.Euler(new Vector3(maxSteerAngle * Input.GetAxis("Horizontal") * Time.deltaTime, 0, 0));
GetComponent<Rigidbody>().rotation = deltaRotation;
GetComponent<Rigidbody>().AddRelativeTorque(Vector3.forward * (maxTorque * Input.GetAxis("Vertical") * Time.deltaTime);
}

The problem is here:
GetComponent<Rigidbody>().AddTorque(new Vector3(maxTorque * Input.GetAxis("Vertical") * Time.deltaTime, 0, 0));
Your torque is always being applied along the global X axis, so it doesn't matter which way the sphere object is rotated. Think of it like this -- no matter which way you rotate a soccer ball, if you spin it in a northwards direction, it will roll north. For your approach to work, you need to take your torque force and apply it along the vector you want to move, rather than always the global X axis.

Related

Can I use RigidBody.Addforce without drifting?

I am trying to create a flight simulator, using rigid bodies. I am using AddForce to make the plane accelerate. However, when rotate the plane, there is a lot of drift. How can I stop this?
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class PlaneController : MonoBehaviour
{
public float maxSpeed = 200f;
public float speed = 90.0f;
public Vector3 rotationSpeedY = new Vector3(0, 0, 40);
public Vector3 rotationSpeedZ = new Vector3(40, 0, 0);
public Rigidbody rb;
private void FixedUpdate()
{
rb.AddRelativeTorque(-Input.GetAxis("Horizontal") * rotationSpeedY * Time.deltaTime);
rb.AddRelativeTorque(Input.GetAxis("Vertical") * rotationSpeedZ * Time.deltaTime);
rb.AddRelativeForce(transform.forward * speed);
speed -= transform.forward.y * Time.deltaTime * 50.0f;
if (rb.velocity.magnitude > maxSpeed)
{
rb.velocity = rb.velocity.normalized * maxSpeed;
}
if (speed < 35.0f)
{
speed = 35.0f;
}
}
}
The reason for this is that you're not modeling aerodynamics in any way. When a real plane banks the wings provide a lift force according to their angle of attack vs the direction of travel and at the same time they produce drag in the opposite direction.
If you want your plane to react like the real world you will have to calculate these two forces which will be pretty simple if you're not looking for accurate aerodynamic modelling based off of wing shape. At the moment your plane is acting as if there is no atmosphere.

Rotation is changing object's x axis

I need to make the object move around the scene and jump, but every time the object rotates it changes the axis of motion. How can I ensure that the rotation of the object does not influence its movement?
public float forca = 300f;
public Rigidbody2D Bola;
public float movdir = 5f;
public float moveesq = -5f;
// Start is called before the first frame update
void Start()
{
}
// Update is called once per frame
void Update()
{
if (Input.GetKeyDown(KeyCode.Space))
{
Bola.AddForce(new Vector2(0, forca * Time.deltaTime), ForceMode2D.Impulse);
}
if (Input.GetKey(KeyCode.RightArrow))
{
Bola.transform.Translate(new Vector2(movdir * Time.deltaTime, 0));
}
if (Input.GetKey(KeyCode.LeftArrow))
{
Bola.transform.Translate(new Vector2(moveesq * Time.deltaTime, 0));
}
transform.Translate takes an optional parameter relativeTo with a default value Space.Self &rightarrow; movement in local X-axis
If relativeTo is left out or set to Space.Self the movement is applied relative to the transform's local axes. (the x, y and z axes shown when selecting the object inside the Scene View.) If relativeTo is Space.World the movement is applied relative to the world coordinate system.
You can convert this into the global X-axis (independent from the objects orientation) by simply passing Space.World as last parameter:
if (Input.GetKey(KeyCode.RightArrow))
{
Bola.transform.Translate(new Vector2(movdir * Time.deltaTime, 0), Space.World);
}
if (Input.GetKey(KeyCode.LeftArrow))
{
Bola.transform.Translate(new Vector2(moveesq * Time.deltaTime, 0), Space.World);
}
AddForce however takes world space coordinates anyway so there you already are independent of the objects orientation.
However, since there is a rigidbody involved you should not set the position using the Transform component at all. You should rather go through Rigidbody2D.MovePosition
if (Input.GetKey(KeyCode.RightArrow))
{
Bola.MovePosition(Bola.position + Vector2.right * movdir * Time.deltaTime);
}
if (Input.GetKey(KeyCode.LeftArrow))
{
Bola.MovePosition(Bola.position + Vector2.right * moveesq * Time.deltaTime);
}
Yoi should this then rather also in FixedUpdate
Multiply by transform.forward
Currently your movement vectors are in absolute coordinates. You want them in local coordinates. The easiest way to do this is to multiple by transform.forward (for forward motion) and transform.right (for strafing motion).

Unity bouncing when colliding with object

I have made a script with movement, that finally works as i want it to, except one thing... I want it to be a first person game, but the way the movement works now, is on the global axis, which means that W is always torwards one specific direction, no matter what direction my camera is turning... How do i fix this? i want the movement to stay how it is, but with the W key to always be forward depending on the way the camera or player is looking.
Please let me know how my script would look edited, or atleast what part i have to change.
I would also like to add that i would love to be able to do a wall jump, but i am not sure how to add that behavior.
Here is my movement script:
public class MovementScript : MonoBehaviour {
public float speed;
public float jumpforce;
public float gravity = 25;
private Vector3 moveVector;
private Vector3 lastMove;
private float verticalVelocity;
private CharacterController controller;
// Use this for initialization
void Start () {
controller = GetComponent<CharacterController> ();
//Låser og gemmer musen
Cursor.lockState = CursorLockMode.Locked;
}
// Update is called once per frame
void Update () {
//Låser musen op
if (Input.GetKeyDown ("escape"))
Cursor.lockState = CursorLockMode.None;
moveVector = Vector3.zero;
moveVector.x = Input.GetAxis("Horizontal");
moveVector.z = Input.GetAxis("Vertical");
if (controller.isGrounded) {
verticalVelocity = -1;
if (Input.GetButton("Jump")) {
verticalVelocity = jumpforce;
}
} else {
verticalVelocity -= gravity * Time.deltaTime;
moveVector = lastMove;
}
moveVector.y = 0;
moveVector.Normalize ();
moveVector *= speed;
moveVector.y = verticalVelocity;
controller.Move (moveVector * Time.deltaTime);
lastMove = moveVector;
}
}
You need to go from local to world space:
for example when you want to move on the x-axis regarding the player's orientation, the local vector is (1, 0, 0), which you get from your input axis, but the CharacterController need a world based direction (see the doc of the Move function)
A more complex move function taking absolute movement deltas.
To get this, use Transform.TransformDirection like this
worldMove = transform.TransformDirection(moveVector);
controller.Move (worldMove * Time.deltaTime);
EDIT regarding your issue with the controller moving a bit after releasing the input:
That's because GetAxis gives you a smoothed value. Replace GetAxis by GetAxisRaw and it should work
Modify the final moving code to
controller.Move(moveVector.z * transform.forward * Time.deltaTime);
controller.Move(moveVector.x * transform.right * Time.deltaTime);
controller.Move(moveVector.y * transform.up * Time.deltaTime);
Alternatively, suppose your character is only rotated about the Y axis, you can look into rotating your moveVector by your character's Y rotation so moveVector's forward points parallel to your chracter's forward.
I found a good explaination of rotating a vector here: https://answers.unity.com/questions/46770/rotate-a-vector3-direction.html
Rotating a vector:
moveVector = Quaternion.Euler(0, transform.eulerAngles.y, 0) * moveVector;

How can i move a cylinder right and left?

Between two points or only to the left nonestop or only to the right nonestop.
In this code i spin the cylinder but i can't move it to the sides:
using UnityEngine;
using System.Collections;
public class MakeTwoPoints3D : MonoBehaviour
{
public float speed = 10f;
public float delta = 15.5f; // Amount to move left and right from the start point
public float moveSpeed = 5.0f;
private Vector3 startPos;
void Start()
{
startPos = transform.position;
}
void Update()
{
transform.Rotate(Vector3.up, speed * Time.deltaTime);
transform.position += transform.right * Time.deltaTime * moveSpeed;
}
}
If i make transform.right it will move the cylinder in circle on place up and down in circle. If i make transform.up it will move it to me i mean like forward but to the camera but at least it will move it. And if i make transform.Forward again it will make circles and will remove the cylinder in circles up down.
I can't figure out how to move it to the sides.
You need use Vector3.right instead of transform.right.
void Update()
{
transform.Rotate(Vector3.up, speed * Time.deltaTime);
transform.position += Vector3.right * Time.deltaTime * moveSpeed;
}
When you use transform.right, the Vector3 will adopt the local rotations of that object's transform. Meaning, if the object was rotated 45 degrees around the Y axis, your transform.right vector would be on an angle. If you keep translating an object along it's local axis while you rotate it, it will travel in a circle.
On the other hand, Vector3.right is always in world space so it will always face "true" right.

Unity: Move camera based on forward horiz direction without changing y position

I'm creating a Google Cardboard VR app in Unity. I want the camera to constantly move forwards horizontally in the direction the camera is facing but not change its Y position, i.e. not rise or fall.
I've managed to get it so that the camera moves forwards in whichever direction it's looking but can go up and down if you look that way, using this line of code:
transform.position = transform.position + cam.transform.forward * WalkingSpeed * Time.deltaTime;
Is there a simple way of fixing the Y axis, so that I get the behaviour I'm looking for? Here's the full code so far:
using UnityEngine;
using System.Collections;
public class CameraMovement : MonoBehaviour {
public float WalkingSpeed = 1.0f;
public bool WalkEnabled = true;
GameObject cam;
void Start () {
// This is the part of the Cardboard camera where I'm getting
// the forward position from:
cam = GameObject.FindWithTag("CCHead");
}
void Update ()
{
if (WalkEnabled)
{
walk();
}
}
void walk()
{
transform.localPosition = transform.localPosition + cam.transform.forward * WalkingSpeed * Time.deltaTime;
}
}
Note: ticking the 'freeze position' y value in rigidbody constraints doesn't work.
Many thanks in advance.
You can try and split the forward vector up and build a new one where the y axis does not change:
transform.position =
transform.position +
new Vector3(
cam.transform.forward.x * WalkingSpeed * Time.deltaTime,
0, // add 0 to keep the y coordinate the same
cam.transform.forward.z * WalkingSpeed * Time.deltaTime);

Categories