My Code
Vector2 moveDirection;
moveDirection = (Sensitive.transform.position - gameObject.transform.position).normalized;
float deg = Mathf.Rad2Deg * Mathf.Atan(moveDirection.x / -moveDirection.y);
if (moveDirection.y < 0) deg -= 180;
Vector3 to = new Vector3(0, 0, deg);
transform.eulerAngles = to;
transform.Translate(new Vector3(moveDirection.x, moveDirection.y) * speed * Time.deltaTime);
In the update function intended to look at and move to Sensitive, though it points correctly doesn't move correctly and I can figure out why.
transform.Translate by default interprets the input as a local direction & distance. You're providing the input in world direction & distance, so you should use the optional 2nd parameter and specify Space.World:
transform.Translate(new Vector3(moveDirection.x, moveDirection.y)
* (speed * Time.deltaTime), Space.World);
Related
Full disclosure, I do not know C# at all. I've been coding this just using the internet. I decided to try learning Unity over the summer before I start my first year at university and have been working on walking controls.
I've been trying to make the character turn in the direction they're facing and while I can get them to go forward just fine. If I have them turn though, subsequently after that any attempt to make them go forward (even if I just have them return to a rotation of 0) end up with the error message "rigidbody.force assign attempt for 'Fox' is not valid. Input force is {NaN, 0.000000, NaN}."
Here's the code:
using System.Collections;
using System.Collections.Generic;
using System;
using UnityEngine;
//private quaternion rotation;
public class PlayerController : MonoBehaviour
{
public Rigidbody rb;
public float moveSpeed = 10f;
public float rotationSpeed = 7f;
public float rotation;
private float xInput;
private float zInput;
private float directionInput;
public GameObject player;
// Start is called before the first frame update
void Start()
{
}
// Update is called once per frame
void Update()
{
ProcessInputs();
if (Input.GetKeyDown("a"))
{
player.transform.Rotate(0f, -(rotationSpeed), 0f, Space.World);
}
if (Input.GetKeyDown("d"))
{
player.transform.Rotate(0f, rotationSpeed, 0f, Space.World);
}
}
private void FixedUpdate()
{
//Movement
Move();
}
private void ProcessInputs()
{
xInput = Input.GetAxis("Horizontal");
zInput = Input.GetAxis("Vertical");
}
private void Move()
{
//float xMove;
//float zMove;
rotation = player.transform.rotation.y;
if (rotation == 0)
{
float xMove = 0f;
float zMove = zInput;
rb.AddForce(new Vector3(xMove, 0f, zMove) * moveSpeed);
}
else if (rotation <= 90)
{
float zMove = (float)(Math.Sqrt((zInput*zInput) - ((zInput * (1/(Math.Cos(90 - rotation)))) * (zInput * (1/(Math.Cos(90 - rotation))))) ));
float xMove = (float)(Math.Sqrt((zInput*zInput) - ((zInput * (1/(Math.Sin(90 - rotation)))) * (zInput * (1/(Math.Sin(90 - rotation))))) ));
rb.AddForce(new Vector3(xMove, 0f, zMove) * moveSpeed);
}
//Math.Sqrt((zInput*zInput) - ((zInput * (1/(Math.Cos(rotation)))) * (zInput * (1/(Math.Cos(rotation))))) );
//Math.Sqrt((zInput*zInput) - ((zInput * (1/(Math.Sin(rotation)))) * (zInput * (1/(Math.Sin(rotation))))) );
//rb.AddForce(new Vector3(0f, 0f, zInput) * moveSpeed);
//rb.AddForce(new Vector3(xMove, 0f, zMove) * moveSpeed);
}
}
The problematic code is here:
float zMove = (float)(Math.Sqrt((zInput*zInput) - ((zInput * (1/(Math.Cos(90 - rotation)))) * (zInput * (1/(Math.Cos(90 - rotation))))) ));
float xMove = (float)(Math.Sqrt((zInput*zInput) - ((zInput * (1/(Math.Sin(90 - rotation)))) * (zInput * (1/(Math.Sin(90 - rotation))))) ));
rb.AddForce(new Vector3(xMove, 0f, zMove) * moveSpeed);
There's no MathJax here (why??) so let me just drop the code for a moment and show you what you're doing:
sqrt( z^2 - (z/Cos)*(z/Cos))
But you can multiply out the second term:
sqrt( z^2 - (z^2/Cos^2))
And then you can factor out the z^2:
sqrt( z^2 * (1 - (1/Cos^2))
:EDIT: - Realized a slight issue with my argument here. Sine and cosine can range between -1 and 1, not just 0 and 1, but the rest of the argument still stands - Cosine squared or sine squared will only range 0 to 1, which will give you imaginary numbers.
:Original: (edited)
Now there's the problem. Cosine (or sine, for the other term) can only range between -1 and 1. So when you do something like Cos^2, the negative drops out and it can only range between 0 and 1. That means, when you have that term in the denominator, you get that 1/Cos^2 will range from 1 at the smallest to Infinity in the divide-by-zero case.
So you wind up with your (1/Cos^2) ranging [1 ... INF], and this means that you get two limits on your equation:
sqrt( z^2 * (1 - 1))
and
sqrt( z^2 * (1 - INF))
In the former case, you get zero, and in every other case you get the square root of a negative number. This gives you the imaginary number, which gets your the error you see:
rigidbody.force assign attempt for 'Fox' is not valid. Input force is {NaN, 0.000000, NaN}
because sqrt(-1) is Not a Number (NaN).
It looks like your trying to rotate the force to coincide with the forward and right directions, but you have unit vectors already pointing in those directions, transform.forward for z and transform.right for x, so just scale those by your moveSpeed, add them together, and add that force.
Vector3 zMove = rb.transform.forward * moveSpeed;
Vector3 xMove = rb.transform.right * moveSpeed;
rb.AddForce(zMove+xMove);
I'll caution you though that you're adding a FORCE here, not setting a speed, so this might not get you what you're after. I say that because the variable name in your code is moveSpeed. You might be happier setting
rb.velocity = zMove + xMove;
but it's your game and you can experiment with what you want :)
I am new to unity and I need help with this code. I need some help with this.
if (Input.GetKey("d"))
{
yr = PlayerModel.transform.rotation.y - 1f * rotationSpeed * Time.deltaTime;
PlayerModel.transform.rotation.y = yr;
}
yr is just a float. PlayerModel is my game object. And this is under my Update void.
I'm trying to create almost like a tank turning system. https://imgur.com/a/Fjlm4O2
Why not use Transform.Rotate()?
PlayerModel.transform.Rotate(Vector3 eulers, Space relativeTo = Space.Self);
PlayerModel.transform.Rotate(float xAngle, float yAngle, float zAngle, Space relativeTo = Space.Self);
PlayerModel.transform.Rotate(Vector3 axis, float angle, Space relativeTo = Space.Self);
PlayerModel.transform.Rotate(Vector3 eulers);
PlayerModel.transform.Rotate(Vector3 axis, float angle);
More info:
https://docs.unity3d.com/ScriptReference/Transform.Rotate.html
So in your case, it can be
PlayerModel.transform.Rotate(Vector3.up * -1f * rotationSpeed * Time.deltaTime);
Also you can use Transform.eularAngles = new Vector3(x, y, z);
PlayerModel.transform.eularAngles = new Vector3(
PlayerModel.transform.eularAngles.x,
yr,
PlayerModel.transform.eularAngles.z
);
If you are still want to use transform.rotation,
PlayerModel.transform.rotation = Quaternion.Euler(
PlayerModel.transform.eularAngles.x,
yr,
PlayerModel.transform.eularAngles.z
);
Please keep in mind that to rotate the transform relative to the transform rotation of the parent, you need to use localRotation or localEulerAngles.
if (Input.GetKey("a"))
Tank.transform.eulerAngles -= new Vector3(Tank.transform.eulerAngles.x, 1f * rotationSpeed * Time.deltaTime, Tank.transform.eulerAngles.z);
else if (Input.GetKey("d"))
Tank.transform.eulerAngles += new Vector3(Tank.transform.eulerAngles.x, 1f * rotationSpeed * Time.deltaTime, Tank.transform.eulerAngles.z);
My friend who has a lot of experience in Unity helped me with this, works fine now! Thanks for your help.
One way of setting specific angles in unity:
GameObject PlayerCharacter = GameObject.FindWithTag("ThePlayer");
int RotationY = 0;
int RotationX = 0;
int RotationZ = 0;
PlayerCharacter.transform.eulerAngles = new Vector3(RotationX, RotationY, RotationZ);
i've got a gameobject, which moves straight ahead and can turn left, right, up and down using this function:
void moveTowardsPoint(Vector3 targetPoint)
{
//forward movement
var movementSpeed = Time.deltaTime * speed;
transform.position += transform.forward * movementSpeed;
//rotation
Vector3 dir = targetPoint - transform.position;
Quaternion targetRotation = Quaternion.LookRotation(dir);
var turnSpeed = Time.deltaTime * 2f;
transform.rotation = Quaternion.Lerp(transform.rotation, targetRotation, turnSpeed);
}
i want to make this object roll, proportionally to how much it turns left or right. for 20° turn, i want to roll by 20° aswell (angles relative to my startangle)
would actually be even nicer, if i can set a roll limit and it would turn lets say by 30° and roll by 15°.
Here is a topdown view of how this behaviour looks like:
I am trying to rotate the Player about X, Y, and Z axis. The Y axis should not move from last angle. Example, if I rotate 45 degree's to the left, the player should not rotate back to 0. The players X and Z axis rotate a maximum of 30 degrees, then when Input is no longer in use, settle to 0.
Through trial and error, I have finally gotten my Y angle to not Slerp back to 0. However, X and Z, still consider Y to be 0 degree's. The player is rotated (assume 45 degree's to the left), but movement along X and Z is as if Y is 0 degree's.
I've been reading articles and threads, and watching video's across multiple domains, including but not limited StackOverflow, Unity forums, Unity API, and YouTube video's.
Video of Current Game - notice the engine exhaust - X and Z never change to the new normal of the Camera view / Player Y direction.
void Update()
{
if(!controller.isGrounded)
{
//Three degree's
moveDirection = new Vector3(Input.GetAxis("Horizontal"), Input.GetAxis("Thrust"), Input.GetAxis("Vertical"));
moveDirection *= speed;
//rotate around Y-Axis
transform.Rotate(0, Input.GetAxis("Yaw") * rotationSpeed, 0);
float currentY = transform.eulerAngles.y; //save Y for later
//rotation around X and Z
float tiltAroundX = Input.GetAxis("Vertical") * tiltAngle;
float tiltAroundZ = -1 * (Input.GetAxis("Horizontal") * tiltAngle);
Quaternion targetRotation = Quaternion.Euler(tiltAroundX, currentY, tiltAroundZ);
Vector3 finalRotation = Quaternion.Slerp(transform.rotation, targetRotation, smooth).eulerAngles;
finalRotation.y = currentY; //reintroduce Y
transform.rotation = Quaternion.Euler(finalRotation);
controller.Move(moveDirection * Time.deltaTime);
}
After further research that lead me along different avenues, I discovered that there were two issues. Both issue's revolved around the fact that the Z-axis was never being normalized to the new Y-axis degree after rotation. #Ruzihm, solved the issue of Rotation. I solved the then visible issue of movement. Which became readily visible once rotation was working properly.
In essence, the Z-axis (transform.forward) must be recalculated after any change in the Y-axis rotation (Vector3.up). Once you have the new normal (transform.forward), the movement vector needed to flattened to the plane to keep the player from diving into the surface of the world. Thank you #Ruzihm for all your assistance.
Here is the new code:
//Three degree's
moveDirection = new Vector3(Input.GetAxis("Horizontal"),
Input.GetAxis("Thrust"),
Input.GetAxis("Vertical"));
//Normalize the movement direction and flatten the Plane
moveDirection = transform.TransformDirection(moveDirection);
moveDirection = Vector3.ProjectOnPlane(moveDirection, Vector3.up);
moveDirection *= speed;
// collect inputs
float yaw = Input.GetAxis("Yaw") * rotationSpeed;
float pitch = Input.GetAxis("Vertical") * tiltAngle;
float roll = -1 * (Input.GetAxis("Horizontal") * tiltAngle);
// Get current forward direction projected to plane normal to up (horizontal plane)
Vector3 forwardCurrent = transform.forward
- Vector3.Dot(transform.forward, Vector3.up) * Vector3.up;
// Debug to view forwardCurrent
Debug.DrawRay(transform.position, forwardCurrent * 2, Color.white);
// create rotation based on forward
Quaternion targetRotation = Quaternion.LookRotation(forwardCurrent);
// rotate based on yaw, then pitch, then roll.
// This order prevents changes to the projected forward direction
targetRotation = targetRotation * Quaternion.AngleAxis(yaw, Vector3.up);
// Debug to see forward after applying yaw
Debug.DrawRay(transform.position, targetRotation * Vector3.forward, Color.red);
targetRotation = targetRotation * Quaternion.AngleAxis(pitch, Vector3.right);
targetRotation = targetRotation * Quaternion.AngleAxis(roll, Vector3.forward);
transform.rotation = Quaternion.Slerp(transform.rotation, targetRotation, smooth);
controller.Move(moveDirection * Time.deltaTime);
There seem to be some incorrect assumptions about the order of rotations that apply when working with Euler angles. Roll is applied, then pitch, then finally yaw. This means that keeping the same yaw then setting the roll and pitch to zero (or even just changing roll) can completely change the flattened direction you're facing.
It may help to rotate by yaw, flatten the forward direction (aka project it to a completely horizontal plane) Then create a rotation based off that (using Quaternion.LookRotation) which you can then rotate by each axis manually.
if(!controller.isGrounded)
{
//Three degree's
moveDirection = new Vector3(Input.GetAxis("Horizontal"),
Input.GetAxis("Thrust"),
Input.GetAxis("Vertical"));
moveDirection *= speed;
// collect inputs
float yaw = Input.GetAxis("Yaw") * rotationSpeed;
float pitch = Input.GetAxis("Vertical") * tiltAngle;
float roll = -1 * (Input.GetAxis("Horizontal") * tiltAngle);
// Get current forward direction projected to plane normal to up (horizontal plane)
Vector3 forwardCurrent = transform.forward
- Vector3.Dot(transform.forward,Vector3.up) * Vector3.up;
// Debug to view forwardCurrent
Debug.DrawRay(transform.location, forwardCurrent, Color.white, 0f, false);
// create rotation based on forward
Quaternion targetRotation = Quaternion.LookRotation(forwardCurrent);
// rotate based on yaw, then pitch, then roll.
// This order prevents changes to the projected forward direction
targetRotation = targetRotation * Quaternion.AngleAxis(yaw, Vector3.up);
// Debug to see forward after applying yaw
Debug.DrawRay(transform.location, targetRotation * Vector3.forward, Color.red, 0f, false);
targetRotation = targetRotation * Quaternion.AngleAxis(pitch, Vector3.right);
targetRotation = targetRotation * Quaternion.AngleAxis(roll, Vector3.forward);
transform.rotation = Quaternion.Slerp(transform.rotation, targetRotation, smooth);
//debug new forward/up
Debug.DrawRay(transform.location, Transform.forward, Color.blue, 0f, false);
Debug.DrawRay(transform.location, Transform.up, Color.green, 0f, false);
controller.Move(moveDirection * Time.deltaTime);
}
This may be considered a partial answer because being able to determine a "flattened forward" direction and reorder the process of applying component rotations is useful to answering your question but may not be enough to get the full effect you want depending on the details.
As a sidenote, you may want to consider using Quaternion.RotateTowards instead of Quaternion.Slerp if you want to ensure that it will actually reach the target rotation instead of infinitely approach it.
I have a Space Shuttle that i want to rotate on Vector3.up and Vector3.left, so that the player can look up/down and left/right with the Shuttle.
This is the code i use:
// Rotate the ship
transform.Rotate(Vector3.up, distX * rotateSpeed * Time.deltaTime, Space.Self);
transform.Rotate(Vector3.left, distY * rotateSpeed * Time.deltaTime, Space.Self);
where distX is a value for the strength of the rotation, based on Mouse X - Axis and rotateSpeed is just a value for the speed of the rotation.
However, if i use this script to rotate my ship, it gets rotated by the z-axis, too.
And i have no idea why it's doing this. These are the only lines in my code (yet) that do something with the rotation of the Shuttle.
What i tried yet is to replace Vector3.up by transform.up and Vector3.left by transform.right (also changed distY to -distY in the second case), but didn't work either.
You can assign Euler value of 0 to z like which allows you to restrict rotation in z axis :
distX = Input.GetAxis("Vertical") * rotateSpeed * Time.deltaTime;
distY = Input.GetAxis("Horizontal") * rotateSpeed * Time.deltaTime;
transform.rotation = Quaternion.Euler(distX, distY, 0f);