I'm struggling with false Gravity in a Cylinder or Capsule. Basically I thought I could take the same Code as for spherical gravity, which does not work. So I changed some lines to get a better result.
[SerializeField] float gravity = 10;
public void Attract ( Transform target )
{
Vector3 gravityUp = (target.position - new Vector3(transform.position.x, transform.position.y,target.position.z)).normalized;
Vector3 bodyDown = -target.up;
Rigidbody rb = target.GetComponent<Rigidbody>();
rb.AddForce(gravityUp * gravity);
Quaternion targetRotation = Quaternion.FromToRotation(bodyDown, gravityUp) * target.rotation;
targetRotation.x = 0;
target.rotation = Quaternion.Slerp(target.rotation, targetRotation, 30.0f * Time.deltaTime);
}
This worked OK on the first try. But the Player(target) can't rotate one the Y-Axis. Does anyone have any ideas?
OK, I tried the following.
Quaternion targetRotation = Quaternion.FromToRotation(bodyDown, gravityUp) * target.rotation;
targetRotation.x = 0;
target.rotation = Quaternion.Slerp(target.rotation, targetRotation, 30.0f * Time.deltaTime);
Now I use the Surface normal to rotate the Player.
if (Physics.Raycast(attractedBody.transform.position + attractedBody.transform.forward, -attractedBody.transform.up, out hit, distance))
{
surfaceNorm = hit.normal;
}
But without Rigidbody Contrains the Player starts rotating without any Input. So I have to use:
rb.constraints = RigidbodyConstraints.FreezeRotation;
This works.
Related
I've been toying with iterations of this for a few weeks, but it's still busted so I thought id reach out for some help.
This performs as expected except for one glaring issue: the output sensitivity appears to be super pitch dependent.
`
//Grab mouse input
float rotateHorizontal = Input.GetAxis("Mouse X");
float rotateVertical = -Input.GetAxis("Mouse Y");
Vector3 rotation = new Vector3(rotateVertical, rotateHorizontal, 0);
transform.Rotate(sensitivity * Time.deltaTime * rotation);
Vector3 eAngles = transform.eulerAngles;
float pitch = eAngles.x;
pitch = ClampAngle(pitch, -camClamp, camClamp);
transform.localEulerAngles = new Vector3(pitch, eAngles.y, eAngles.z);
`
What am I missing? I'm pretty sure it's not the clamp angle function since I stole it from a reputable source.
Thanks for the help.
Rotations are hard!
I think it was a rotation order problem.
Switched to quaternions, problem remained, then found this lovely thread and reordered the operands.
originalRotation = transform.localRotation;
//Grab mouse input
float rotateHorizontal = Input.GetAxis("Mouse X");
float rotateVertical = Input.GetAxis("Mouse Y");
Quaternion xQuaternion = Quaternion.AngleAxis(rotateHorizontal, Vector3.up);
Quaternion yQuaternion = Quaternion.AngleAxis(rotateVertical, -Vector3.right);
transform.localRotation = xQuaternion * originalRotation * yQuaternion;
Vector3 eAngles = transform.eulerAngles;
float pitch = eAngles.x;
pitch = ClampAngle(pitch, -camClamp, camClamp);
transform.localEulerAngles = new Vector3(pitch, eAngles.y, eAngles.z);
Thank you freya holmer
Order matters in quaternion multiplication!
I would like to have a Gameobject point to another only on the Local X-axis.
void FixedUpdate()
{
if(started){
Vector3 targetPosition = target.position;
Vector3 direction = Vector3.ProjectOnPlane(targetPosition - transform.position, transform.right);
Quaternion lookRot = Quaternion.LookRotation(direction, transform.right);
transform.rotation = Quaternion.RotateTowards(transform.rotation, lookRot, Time.fixedDeltaTime * 45);
}
On the y-axis it worked with transform.up instead of transform.right, but on the x-axis the Gameobject only rotates permanently around the z-axis.
You could do something like this:
Vector3 beforeRot = transform.eulerAngles;
transform.LookAt(gameObjectToLookAt.transform);
transform.localEulerAngles = new Vector3(transform.localEulerAngles.x, beforeRot.y, beforeRot.z);
This will take a record of the position before looking at the object.
Make it look at the object.
Reset rotation back to its original except the X rotation
I am trying to create a 3rd person movement script using Cinemachine as camera, I followed Brackeys "THIRD PERSON MOVEMENT in Unity" YouTube tutorial. I then Changed the base of it from character controller to rigidbody and the movement works perfectly fine. However my code sets the velocity of the rigidbody's y axis to 0 when I move the player which fights the gravity making the player jitter slowly to the ground when I move. The Character however does drop to the ground when the player stops moving. All I need is for the script to ignore the y axis and simply listen to unity's gravity.
void Update()
{
if (!photonView.isMine)
{
Destroy(GetComponentInChildren<Camera>().gameObject);
Destroy(GetComponentInChildren<Cinemachine.CinemachineFreeLook>().gameObject);
return;
}
float horizontal = Input.GetAxisRaw("Horizontal");
float vertical = Input.GetAxisRaw("Vertical");
isGrounded = Physics.CheckSphere(new Vector3(transform.position.x, transform.position.y - 1, transform.position.z), 0.01f, layerMask);
Vector3 inputVector = new Vector3(horizontal, 0f, vertical).normalized;
if (inputVector.magnitude >= 0.1f)
{
float targetAngle = Mathf.Atan2(inputVector.x, inputVector.z) * Mathf.Rad2Deg + cam.eulerAngles.y;
float angle = Mathf.SmoothDampAngle(transform.eulerAngles.y, targetAngle, ref turnSmoothVelocity, turnSmoothTime);
transform.rotation = Quaternion.Euler(0f, angle, 0f);
Vector3 moveDir = Quaternion.Euler(0f, targetAngle, 0f) * Vector3.forward;
rb.velocity = moveDir.normalized * speed;
}
if (Input.GetKeyDown(KeyCode.Space) && isGrounded)
{
rb.AddForce(Vector3.up * jumpForce);
}
}
The player jitters because, in your movement section, you set the y velocity to 0, since Vector3.forward returns new Vector3(0, 0, 1) and you only rotate the vector around the y axis. Instead of this, consider doing:
Vector3 moveDir = new Vector3(transform.forward.x, rb.velocity.y, transform.forward.z);
This will preserve the velocity as it was, removing the jittering.
Note: transform.forward automatically gets the forward vector for the player.
Plenty of questions asked around the same topic, but nothing seems to be working for me.
The problem is simple, a player and an enemy are on the x,y plane. I want to launch my projectile at a calculated angle in such way that the projectile will hit the enemy at it's coordinates.
I've tried implementing both
Angle of Reach and Angle required to hit x,y
Both of these implementation end up doing the same for me; Shooting but not hitting the target in this manner
Any help or pointers would be much appreciated! Thank you
Here is the code:
public Rigidbody projectile;
public float projectileSpeed;
public float Firerate = 9f;
private float nextfire;
private GameObject enemy;
private float gravity = Physics.gravity.y;
private Vector3 directionalVector;
// Start is called before the first frame update
void Start()
{
enemy = GameObject.FindGameObjectWithTag("enemy");
}
void Update()
{
directionalVector = enemy.transform.position - transform.position;
}
void FixedUpdate()
{
nextfire = Time.time + (1 / Firerate);
float projectileSpeed2 = projectileSpeed * projectileSpeed;
float projectileSpeed4 = projectileSpeed2 * projectileSpeed2;
float x = enemy.transform.position.x;
float y = enemy.transform.position.y;
float x2 = x * x;
float theta = Mathf.Atan(projectileSpeed2-Mathf.Sqrt(projectileSpeed4-gravity*(gravity*x2+2*y*projectileSpeed2))/gravity*x);
print(theta);
Vector3 releaseVector = (Quaternion.AngleAxis(theta, Vector3.up) * directionalVector).normalized;
Debug.DrawRay(transform.position, releaseVector, Color.red,0.5f);
Rigidbody instantiatedProjectile = Instantiate(projectile, transform.position, transform.rotation) as Rigidbody;
instantiatedProjectile.velocity = releaseVector * projectileSpeed;
}
}
Why not avoid the problem of finding the angle, and just move the bullet based on the direction on where it first saw the enemy.
(target.transform.position - transform.position).normalized;
It will return a Vector direction to the target.
When the projectile moves, just move it based on this direction.
No headache needed in calculating angles :)
Edit
I made a function before to 'convert' an angle to direction:
protected Vector2 DetermineBulletMoveDirection(float shootingAngle) {
// Determine the direction of the bullet travel on the x and y axis.
float bulletDirectionX = transform.position.x + Mathf.Sin((shootingAngle * Mathf.PI) / 180);
float bulletDirectionY = transform.position.y + Mathf.Cos((shootingAngle * Mathf.PI) / 180);
// Determines the direction this bullet should be moving.
Vector2 bulletDirection = new Vector2(bulletDirectionX, bulletDirectionY);
return (bulletDirection - (Vector2)transform.position).normalized;
}
It takes in an angle, and converts it into a direction based on where the shooter is currently at.
The angle should start from Vector.down, and rotates clockwise.
The next problem is to find out the angle between you and the enemy.
This is the simplest solution I could think of, here is a diagram first:
Notice that you can use TOACAHSOH on this?
So all you have to do, is to 'virtually' align the Y axis of the shooter to the origin.(Apply the movement to the shooter too!)
Do the same thing for the shooter, but on the x-axis this time.
And you would be able to achieve that state where you have a triangle with a 90-degree.
From there on, you can calculate the angle to rotate from Vector.down to the enemy.
Just make sure you move both of the objects back to it's initial position.
After fighting this for a while I found a solution.
In the end I ended up using the Angle of Reach. The second error was that Mathf.Atan returns radians and not degrees, while Quantion.AngleAxis takes in angles. The third and the final one was the fact that Unity uses left hand coordinate system as opposed to the usual right hand system which I was used to.
Here is the final piece of code:
public class TargetAndShoot : MonoBehaviour
{
public Rigidbody projectile;
public float projectileSpeed;
public float firerate;
private float nextfire;
private GameObject enemy;
private float gravity = Physics.gravity.y;
// Start is called before the first frame update
void Start()
{
enemy = GameObject.FindGameObjectWithTag("enemy");
}
void Update()
{
if (Time.time >= nextfire)
{
nextfire = Time.time + (1 / firerate);
float distance = enemy.transform.position.x - transform.position.x;
Vector3 directionalVector = enemy.transform.position - transform.position;
float v2 = projectileSpeed * projectileSpeed;
float v4 = v2 * v2;
float x = enemy.transform.position.x;
float x2 = x * x;
float y = enemy.transform.position.y;
float theta = 0.5f*Mathf.Asin((gravity * distance) / (projectileSpeed * projectileSpeed));
Vector3 releaseVector = (Quaternion.AngleAxis(theta * Mathf.Rad2Deg, -Vector3.forward) * directionalVector).normalized;
Debug.DrawRay(transform.position, releaseVector*5, Color.cyan, 0.5f);
Rigidbody instantiatedProjectile = Instantiate(projectile, transform.position, transform.rotation) as Rigidbody;
instantiatedProjectile.velocity = releaseVector * projectileSpeed;
}
}
}
i have start a project in unity 3d.I want to make a spaceship that moving forward,but when i pressed the ArrowUp then i want to change its y postion to
( currentpos+ 1.5 ) but i want this smoothly.
this is my code
transform.position += transform.forward * Time.deltaTime * 10f;
if (Input.GetKey (KeyCode.UpArrow))
transform.position = new Vector3 (transform.position.x, 5f,
transform.position.z);
through the above code the Y position of object can b changed but it work so fast and i want to make it smooth.
so please help me.
I think the best solution to your problem is to use Mathf.SmoothDamp.
Example:
private float targetY = 0f;
private float verticalVelocity = 0f;
private const float smoothTime = 1f;
private void Update()
{
transform.position += transform.forward * Time.deltaTime * 10f;
if (Input.GetKey(KeyCode.UpArrow))
{
targetY = 5f;
}
float y = Mathf.SmoothDamp(transform.position.y, targetY, ref verticalVelocity, smoothTime);
transform.position = new Vector3 (transform.position.x, y, transform.position.z);
}
This example will smoothly change the y coordinate to 5 over the course of 1 second (you can change the smoothTime constant for a different time).
Based in your own code the easiest way for you to work it out could be something like this
//this sets the X position
transform.position += transform.forward * Time.deltaTime * 10f;
//if the button is pressed then modify Y
if (Input.GetKey (KeyCode.UpArrow))
transform.position += new Vector3 (0, 5f * Time.deltaTime * y_speed,0);
y_speed could be a public float y_speed = 1.0f in your script so you could modify it from the inspector to get the effect you want to achieve.
Hope it helps!
Assuming your spaceship is a rigidbody, you should take a look at Rigidbody.AddForce
https://docs.unity3d.com/ScriptReference/Rigidbody.AddForce.html
By working with forces, you can get a smooth movement in all directions very easily, and tweak it within the Rigidbody's parameters (like mass) without fiddling in the script again. It's part of the Unity physics model.
If you only want to move in y-direction, input a vector like (0,1,0) but you can also input the Transform.forward vector of your spaceship's Gameobject. That way, it will always move the direction it is facing in.