I'm using C# (Unity) and can't put variables into my vector3 - c#

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 :)

Related

Clamp RotateAround object Unity

I am making a script to rotate my camera around a sphere but I need to clamp the y axis so the camera does not co over the polls of the sphere I am using rotate around to move my camera.
Thanks!
My current code
public float sensitivity = 1;
public float moveSpeed = 10;
public float maxUp = 45f;
public float maxDown = -45f;
public Transform target;
void Update()
{
transform.LookAt(target);
float HorizontalAxis = Input.GetAxis("Horizontal") * moveSpeed;
float VerticalAxis = Input.GetAxis("Vertical") * moveSpeed;
if (HorizontalAxis >= 1 || VerticalAxis >= 1 || HorizontalAxis <= -1 || VerticalAxis <= -1)
{
Quaternion targetPos = transform.rotation;
targetPos.x += HorizontalAxis * sensitivity;
targetPos.y += VerticalAxis * sensitivity;
transform.RotateAround(target.position, Vector3.left, targetPos.y);
transform.RotateAround(target.position, Vector3.up, targetPos.x);
}
}
Your code makes no sense to begin with.
You do
Quaternion targetPos = transform.rotation;
targetPos.x += HorizontalAxis * sensitivity;
targetPos.y += VerticalAxis * sensitivity;
Just to then use these as parameters in
transform.RotateAround(target.position, Vector3.left, targetPos.y);
transform.RotateAround(target.position, Vector3.up, targetPos.x);
A Quaternion has not three but four components x, y, z and w and they move in ranges between -1 and 1. You never touch the individual component of a Quaternion except you really know exactly what you are doing!
You rather simply want to use the HorizontalAxis and VerticalAxis directly as the parameters to RotateAround.
You could rather simply remember and clamp how much you already rotated like e.g.
private float rotatedY;
private void Update()
{
transform.LookAt(target);
// why two different multipliers anyway though?
var HorizontalAxis = Input.GetAxis("Horizontal") * moveSpeed * sensitivity;
var VerticalAxis = Input.GetAxis("Vertical") * moveSpeed * sensitivity;
// would a positive rotation exceed the maxUp?
if(rotatedY + VerticalAxis > maxUp)
{
// then rotate only so much that you terminate exactly on maxUp
VerticalAxis = maxUp - rotatedY;
}
// would a negative rotation exceed the maxDown?
else if(rotatedY + VerticalAxis < maxDown)
{
// then you rotate only that much that you terminate exactly on maxDown
VerticalAxis = maxDown - rotatedY;
}
transform.RotateAround(target.position, Vector3.left, VerticalAxis);
transform.RotateAround(target.position, Vector3.up, HorizontalAxis);
// sum up how much you already rotated vertically
rotatedY += VerticalAxis;
}

Why this code moving my gameobject in such an odd way?

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);

Unity rigid object receives twice the force it is supposed to receive

I am making a game where bullets can start orbiting a player. The orbits are circular, and not elliptical. Bullets have a drag factor of 0.
At first, I simply calculated the next position in the orbit, and divided the delta position by fixedDeltaTime to obtain its new velocity.
Unfortunately, it meant that fast bullets instead follow polygonal paths, and often miss a target.
I wanted to improve the precision by giving them an inward force, and a velocity nearly tangential to the orbit, so that they follow parabola segments around the player instead of straight lines.
The parabola segments are defined by their start and end point, and their vertices, which must lay on the orbit arc, with a velocity equal to the velocity I was previous using ((endpos - startpos) / fixedDeltaTime).
To calculate the parabola, I calculate the midway points on the arc and segment, and their difference is proportional to the force applied.
so we use the following names
fdt = fixedDeltaTime
t = fdt / 2
f = the force applied during the incoming physics frame
v0 = the velocity at the start of the frame
v1 = the velocity at the middle of the frame (at the vertex of the parabola)
dp1 = the relative position at the middle of the frame (midpos - startpos) and vertex of the parabola
dp2 = the relative position at the end of the frame (endpos - startpos)
The force is defined by these two equations:
// at vertex, only the "lateral" velocity remains
v1 = dp2 / fdt
// the difference between dp2 / 2 and dp1 is what the force can apply over half a frame
dp2 / 2 - dp1 = f * 0.5tt
therefore
(dp2 / 2 - dp1) / (0.5 * t * t) = f
(dp2 / 2 - dp1) / (0.5 * fdt/2 * fdt/2) = f
(dp2 - dp1 * 2) * 4 / (fdt * fdt) = f
//v0 is then easily calculated
v0 = v1 - t * force
v0 = (dp2 / fdt) - force * (fdt / 2)
We then get this working code:
Vector3 startPos = _rigidbody.position;
if (firstFrame == false && Vector3.Distance(predictedNextFramePos, startPos) > 0.01f)
Debug.Log("WTF");
Vector3 nextPosition;
GetLocalOrbitPos(nextTime, out nextPosition);
nextPosition += _parent.GetPosition();
float fdt = Time.fixedDeltaTime;
float halfTime = (time + nextTime) / 2f;
Vector3 halfPosition;
GetLocalOrbitPos(halfTime, out halfPosition);
halfPosition += _parent.GetPosition();
Vector3 dp2 = nextPosition - startPos;
Vector3 dp1 = halfPosition - startPos;
Vector3 force = (dp2 - 2 * dp1) * 4 / (fdt * fdt);
Vector3 v0 = (dp2 / fdt) - (force * fdt / 2f);
Vector3 deltaPosPredicted = PhysicsTools.GetMovement(v0, force, fdt);
if (Vector3.Distance(deltaPosPredicted, dp2) > 0.001f)
Debug.Log("position prediction error: " + Vector3.Distance(deltaPosPredicted, dp2));
predictedNextFramePos = deltaPosPredicted + startPos;
Vector3 deltaHPosPredicted = PhysicsTools.GetMovement(v0, force, fdt / 2f);
if (Vector3.Distance(deltaHPosPredicted, dp1) > 0.001f)
Debug.Log("position prediction error: " + Vector3.Distance(deltaHPosPredicted, dp1));
//drawing the startpos, midpos, endpos triangle
Debug.DrawLine(startPos, startPos + dp2, Color.magenta, Time.fixedDeltaTime * 2);
Debug.DrawLine(startPos, startPos + dp1, Color.red, Time.fixedDeltaTime * 2);
Debug.DrawLine(startPos + dp2, startPos + dp1, Color.red, Time.fixedDeltaTime * 2);
//drawing v0 and force
Debug.DrawLine(startPos, startPos + force, Color.gray, Time.fixedDeltaTime * 2);
Debug.DrawLine(startPos, startPos + v0, Color.blue, Time.fixedDeltaTime * 2);
//drawing the parabola arc
{
Vector3 pos = startPos;
Vector3 vel = v0;
for (int i = 0; i < 10; i++)
{
Vector3 offset = PhysicsTools.GetMovementUpdateVelocity(ref vel, force, Time.fixedDeltaTime / 10f);
Debug.DrawLine(pos, pos + offset, Color.green, Time.fixedDeltaTime * 2);
pos += offset;
}
}
// Old version
// Vector3 deltaPosition = nextPosition - _rigidbody.position;
// Vector3 velocity = deltaPosition / t;
// SetPhysicsState(_rigidbody.position, velocity, time);
//Applying results
SetPhysicsState(startPos, v0, time);
_rigidbody.AddForce(force / 2f, ForceMode.Acceleration);
I am using my physics helper class
public static class PhysicsTools
{
public static Vector3 GetMovement(Vector3 velocity, Vector3 force, float time)
{
return (velocity * time) + 0.5f * force * (time * time);
}
public static Vector3 GetMovementUpdateVelocity(ref Vector3 velocity, Vector3 force, float time)
{
Vector3 ret = (velocity * time) + 0.5f * force * (time * time);
velocity += force * time;
return ret;
}
}
Everything works fine, but if, and only if, I divide the force by two when applying it. My own simulation using PhysicsTools does not require such tampering.
Here's a picture of one of my tests, with the force factor applied to both the physics engine and the PhysicsTools simulation. You can see that the simulated lines go off into the distance, but not the actual projectile, which stays in its weird pentagram, as it should.
Here we can see it working as intended (still with the applied force reduced)
My question, why would I need to divide that damned force by two?
Well here folks is what happen when you make assumptions.
I assumed that ForceMode.Continuous meant that the force would be applied continuously through the frame. That is not the case.
The unity physics engine is incapable of any kind of continuous acceleration or parabola casting. Any object moves in straight lines, and AddForce simply modifies the velocity right then and there.
It turns out that simply dividing the force by two was enough to reset the starting velocity to my previous linear solution to the problem, and that the only reason that objects seemed to react outside of the polygon was that my bullet collider was much wider than I thought it was.
Please read this post for more information: https://answers.unity.com/questions/696068/difference-between-forcemodeforceaccelerationimpul.html
The only solution to the problem is to increase the physics framerate, or to use your own raycasting solution, which comes with a slew of other problems.

Change audio pitch based on rigidbody speed

We have some sound for when the player is moving or rolling being the player is a ball. We want to increase the pitch of the audio the faster the ball goes. I tried the below code but it doesn't do anything. I think it's because the value of p comes out too small.
I remember reading somewhere that there is something built in to handle this but I can't think of where I saw or it what it was called.
Thanks in advance!
void FixedUpdate()
{
#if UNITY_EDITOR || UNITY_STANDALONE
float moveHorizontal = Input.GetAxis("Horizontal");
float moveVertical = Input.GetAxis("Vertical");
Vector3 move = new Vector3(-moveHorizontal, 0.0f, -moveVertical);
move = move * (speed / 15f);
//maxSpeed = maxSpeed / 5;
#else
// Player movement in mobile devices
// Building of force vector
Vector3 move = new Vector3(-Input.acceleration.x, 0.0f, -Input.acceleration.y);
// Adding force to rigidbody
move = move * (speed / 15f);
//move = movement * speed * Time.deltaTime;
#endif
rigidbdy.AddForce(move);
var p = rigidbdy.velocity.magnitude / speed;
audio.pitch = Mathf.Clamp(p, 1.0f, 2.0f); // p is clamped to sane values
//Limits the max speed
if (rigidbdy.velocity.magnitude > maxSpeed)
{
rigidbdy.velocity = rigidbdy.velocity.normalized * maxSpeed;
}
}
You can use the map function for easy control over the pitch value.
float mapValue(float mainValue, float inValueMin, float inValueMax, float outValueMin, float outValueMax)
{
return (mainValue - inValueMin) * (outValueMax - outValueMin) / (inValueMax - inValueMin) + outValueMin;
}
You pass in AudioSource.pitch to the mainValue parameter.
For the inValueMin value, you pass in the default/MIN value of the Rigidbody.velocity.magnitude which is 0.
For the inValueMax value, you pass in the MAX value your ball can go.
You can easily determine this number with Debug.Log("RB: " + ballRigidbody.velocity.magnitude); and running the game. 10 seems to be fine for this. You must determine your own value.
The default AudioSource.pitch value is 1, so outValueMin parameter should be 1.
The outValueMax parameter will be the maximum pitch you think is acceptable to you. I found 1.5 to be ok for this so 1.5 will be used for outValueMax.
Whatever you get from the mapValue function is what you assign to the AudioSource.pitch. This gives you much more control over the pitch of you sound. You can read more about this function on the Arduino site.
Remove your current Audio code and replace it with this:
float rigidBodyMangintude = rigidbdy.velocity.magnitude;
float pitch = mapValue(rigidBodyMangintude, 0f, 10f, 1f, 1.5f);
audio.pitch = pitch;
Debug.Log("Pitch: " + pitch);
The mapValue function is at the top of this answer.

Rotate object around parent object in Unity

I am new to unity and am trying to understand how the code works but am having a problem with my simple project.
I have a Star and a Planet. The planet is a child of the Star like so:
StarĀ¬
Planet
I added a component C# script to Planet that is meant to make it rotate around the Star. But when i press play the planet is not moving.
This is my script:
using UnityEngine;
using System.Collections;
public class Orbit : MonoBehaviour {
public float rotateSpeed = 5.0f;
public float orbitSpeed = 1.0f;
private Vector3 pos;
void Start(){
//get parent object position
pos = transform.root.gameOject;
}
void Update() {
// planet to spin on it's own axis
transform.Rotate (transform.up * rotateSpeed * Time.deltaTime);
// planet to travel along a path that rotates around the sun
transform.RotateAround (pos, Vector3.up, orbitSpeed * Time.deltaTime);
}
}
I am not sure what my mistake is. And am hoping some one can help.
Side question, given i want to eventually have more than one planet, is it efficient to have a component script for every individual planet to rotate or is there a more automative way to do it such as iterate through all the planets in one go?
Unit Version 5.3.2f1
You should consider oscillation. Whether you use sin or cos does not really matter somehow. You will get the same shape but one will start at 0 as sin(0) = 0 and the other starts at 1 as cos(0) = 1.
One nice feature of sin and cos is that the result is lerping and clamped between -1 and 1. We can get a variable that will constantly go from 1 to -1 and back to 1 and so on.
The following is entirely based on basic trigonometry and unit circle
void Update ()
{
transform.localPosition= new Vector3(Mathf.Cos (Time.time ),0,Mathf.Sin (Time.time));
}
this is based on the trig equation:
x = cos(2 * pi * k + t)
y = sin(2 * pi * k + t)
The 2 * PI * k part is shorten to Time.time, result is the same, you would only need those for extra precision if you were to reproduce a real situation.
Other case you want to use the full equation is if you need to control the time it takes for a revolution:
private float twoPi = Mathf.PI * 2f;
void Update ()
{
transform.localPosition= new Vector3(Mathf.Cos (twoPi * Time.time ),0,Mathf.Sin (twoPi * Time.time));
}
This will take 1sec to do the full revolution.
When you use Cos for one, you have to use Sin for the other or your object will not spin around the parent.
You can add distance to spread the planet apart from the star:
private float twoPi = Mathf.PI * 2f;
[SerializeField]private float amplitude = 2.0f;
void Update()
{
float x = amplitude * Mathf.Cos (twoPi * Time.time );
float z = amplitude * Mathf.Sin (twoPi * Time.time );
transform.localPosition= new Vector3(x,0,z);
}
All spinning items will rotate at the same frequency of 1 so you should be able to provide different frequencies to each planet:
private float twoPi = Mathf.PI * 2f;
[SerializeField]private float amplitude = 2.0f;
[SerializeField]private float frequency = 2.0f;
void Update()
{
float x = amplitude * Mathf.Cos (twoPi * Time.time * frequency);
float z = amplitude * Mathf.Sin (twoPi * Time.time * frequency);
transform.localPosition= new Vector3(x,0,z);
}
if you give different frequencies to x and z within the same object, the child will not spin entirely around but will have a horseshoe shape. Frequency could be assimilated to speed as it will define how fast one full revolution is performed.
You can then fully control the period (this is the math term for speed), a period is the time between two peaks on the sinusoidal (the movement can be flattened to a sinusoidal, two actually, x and z). The relation between frequency and period is
frequency = 1 / period;
So the greater the frequency, the shorter the period. If you want your revolution to take 2sec => frequency = 1 / 2 => 0.5. If you need 2 minutes, frequency is always in seconds so 120sec => frequency = 1 / 120 = 0.0083f;
All your planet will rotate at the same position around the star, that is they will all start from left or right so you can apply a phase. This is the k from the initial equation and it is not multiplied but added:
private float twoPi = Mathf.PI * 2f;
[SerializeField] private float amplitude = 2.0f;
[SerializeField] private float periodInSec = 120;
private float frequency = 2.0f;
[SerializeField] private float phase = 0.5f;
void Start()
{
frequency = 1 / periodInSec;
}
void Update()
{
float x = amplitude * Mathf.Cos (twoPi * Time.time * frequency + phase);
float z = amplitude * Mathf.Sin (twoPi * Time.time * frequency + phase);
transform.localPosition= new Vector3(x,0,z);
}
And if you need to provide an elliptic shape (which is the case of most astres), you simply give a different amplitude to x and z:
[SerializeField]private float amplitudeX = 2.0f;
[SerializeField]private float amplitudeZ = 3.0f;
[SerializeField] private float periodInSec = 120;
private float frequency = 2.0f;
[SerializeField] private float phase = 0.5f;
void Start()
{
frequency = 1 / periodInSec;
}
void Update()
{
float x = amplitudeX * Mathf.Cos (twoPi * Time.time * frequency + phase);
float z = amplitudeZ * Mathf.Sin (twoPi * Time.time * frequency + phase);
transform.localPosition= new Vector3(x,0,z);
}
If you need to have many planets around one star and you want the planet to move on all three axis. That is, one spin "flat" while another one spin with a rotation, the easiest is to make the planet a child of a star child.
-Star
-Container
-Earth
-Container
-March
The containers are at (0,0,0) and you can give each container a different rotation and the child planet will rotate on its own ellipse around the star. Just make sure they don't collide, billions of lives at stake.
public Vector3 pos and then Drag the Star in the Inspector.
much easier.

Categories