Rotation is changing object's x axis - c#

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 → 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).

Related

Unity Physics Sphere Movement (wheel movement)

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.

(Unity) How does vector multiplication and position updates work in unity?

This piece of code involves the multiplication of a Vector3 moveVector with a float moveSpeed and another float, Time.deltaTime. do these floats get multiplied to every value of the Vector3 (x, y, z)? Furthermore, if I write transform.position instead of GameObject.transform.position, am I right that the transform.position transforms the position of the global object, thereby updating the position of whatever GameObject/prefab this movement script is attached to?
void Move(Vector3 desiredDirection)
{
moveVector.Set(desiredDirection.x, 0f, desiredDirection.z);
moveVector = moveVector * moveSpeed * Time.deltaTime;
transform.position += moveVector;
}
Yes. moveVector * moveSpeed * Time.deltaTime; takes each number from the vector and multiplies it with the move speed then again with Time.deltaTime.
So if we have a vector 3, 2, 1 each axis is multiplied with the value:
3 * speed * deltaTme
2 * speed * deltaTme
1 * speed * deltaTime
transform.position is the same as writing gameObject.transform.position. Because the script is attached to the gameObject.
Notice the difference between the GameObject and gameObject.
gameObject is the current object the script is attached to
GameObject is the base class of the object

Can't limit Object's Rotation Unity3D

I have an object in my scene that move forward and rotate with input.GetAxis, and I want to limit its X rotation between -45 and 45 degree. So I tried the Clamp method but the object can't rotate anymore! is there something wrong in my code?
float Speed = 10f;
// Use this for initialization
void Start () {
}
// Update is called once per frame
void Update () {
transform.Rotate(-Input.GetAxis("Vertical") * 2f, 0, -Input.GetAxis("Horizontal"));
float rotationX = Mathf.Clamp(transform.rotation.x, -45.0f, 45.0f);
transform.localEulerAngles = new Vector3(rotationX, transform.localEulerAngles.y, transform.localEulerAngles.z);
}
private void FixedUpdate()
{
transform.position += transform.forward * Speed * Time.fixedDeltaTime;
}
Personally I wouldn't try setting localEulerAngles directly, I'd reset the rotation to Quaternion.Identity (no rotation) or to another reference transform's rotation and then use Transform.rotate rotationX degrees around the desired axis whenever the rotation changes
The main issue your having atm is transform.rotation is a Quaternion and not a vector3. Since Quaternions have a min/max value of -1/1 the vector3 X rotation can't ever be outside that range.
You probably wanted to do: float rotationX = Mathf.Clamp(transform.localEulerAngles.x, -45.0f, 45.0f);

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.

Categories