Unity approach an object in spiral motion - unity3d

In Unity I can handle circular motion around an object with a simple
transform.RotateAround(GameObject.Find("CubeTest").transform.position, Vector3.up, 20000*Time.deltaTime);
However, I want the object traveling in circular motion to approach this object whilst in orbit. Not entirely sure how to do this without screwing up.

GameObject cube = GameObject.Find("CubeTest");
transform.LookAt(cube.transform);
transform.Translate(transform.forward * Time.deltaTime * approachSpeed);
transform.RotateAround(cube.transform.position, Vector3.up,20000*Time.deltaTime);
I think that could do as you want? It moves towards the rotation point gradually, then rotates, giving the appearance of a deteriorating orbit.

If you came here looking for working 2D solution here you are.
From this blog post. I built this configurable script:
public class SpiralMovement : MonoBehaviour
{
[SerializeField] Transform moveable;
[SerializeField] public Transform destiny;
[SerializeField] float speed;
// As higher as smoother approach, but it can not be more than 90, or it will start getting away.
[SerializeField][Range(0f, 89f)] float angle = 60f;
void Update()
{
Vector3 direction = destiny.position - moveable.position;
direction = Quaternion.Euler(0, 0, angle) * direction;
float distanceThisFrame = speed * Time.deltaTime;
moveable.transform.Translate(direction.normalized * distanceThisFrame, Space.World);
}
}

Related

Top Down Shooter Animations Unity, how do you get the right directional animations to play when moving?

i'm not really sure how to phrase the question sorry about that. I have encountered this hurdle while setting up animations on a character for learning a top down character controller (No rigid body) I've seen this question asked on Reddit and unity forums like a decade ago, but I don't really understand their answers.
My problem right now is that I can't find out a way to tell mecanim if the player is moving towards the direction they're facing. For example, if the player is moving left and aiming to the left, the moveForward animation should be played. If the player is moving left but aiming to the right, the moveBackwards animation should be played.
This is the first time i'm posting a question. I am sorry if my formatting is wrong.
Here is the code I have, I would greatly appreciate any help.
public class CharacterMovement : MonoBehaviour
{
private Vector3 velocity;
private Vector3 PlayerMoveInput;
Animator animationS;
[SerializeField] CharacterController characterrController;
[SerializeField] private float MoveSpeed;
[SerializeField] private float JumpHeight;
[SerializeField] private float Gravity = -9.81f;
// Start is called before the first frame update
void Start()
{
animationS = GetComponent<Animator>();
}
// Update is called once per frame
void Update()
{
PlayerMoveInput = new Vector3(Input.GetAxis("Horizontal"), 0f, Input.GetAxis("Vertical")); //collects the input for the player
MovePlayer();
PlayerRotation();
}
private void MovePlayer()
{
if (characterrController.isGrounded)
{
velocity.y = -1f;
}
if (Input.GetKeyDown(KeyCode.Space) && characterrController.isGrounded)
{
velocity.y = JumpHeight;
}
///
else
{
velocity.y -= Gravity * -2f * Time.deltaTime;
}
Vector3 MoveVector = transform.TransformDirection(PlayerMoveInput);
characterrController.Move(MoveSpeed * Time.deltaTime * MoveVector);
characterrController.Move(velocity * Time.deltaTime);
float velocityX = Vector3.Dot(PlayerMoveInput, transform.forward);
float velocityZ = Vector3.Dot(PlayerMoveInput, transform.right);
animationS.SetFloat("velocityX", velocityZ, 0.1f, Time.deltaTime);
animationS.SetFloat("velocityZ", velocityX, 0.1f, Time.deltaTime);
}
void PlayerRotation()
{
Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition);
RaycastHit hit;
Debug.DrawRay(ray.origin, ray.direction, Color.yellow);
if (Physics.Raycast(ray, out hit))
{
Vector3 targetPosition = new Vector3(hit.point.x, transform.position.y, hit.point.z);
Quaternion rotation = Quaternion.LookRotation(targetPosition - transform.position);
transform.rotation = Quaternion.Lerp(transform.rotation, rotation, Time.deltaTime * 10.0f);
}
}
}
This isn't the exact implementation that would be used with your code but should give you a good start.
// get the angle between where the player is pointing, and where he is moving
float angle = Vector3.Angle(MoveVector, targetPosition - transform.position);
if (angle < 90f){
//move forward animation
} else {
//move back animation
}
I haven't tested this code but the idea is that if the angle between the direction the player is facing and the direction the player is moving is less than 90 degrees, then he is moving forward. Also the variables used here: MoveVector and targetPosition are private so you will need to fix that issue before this method can be implemented.
With mechanim, its very common to have 4 separate float value params in your animator. Two are used for movement info, two are for look direction info. This lets you use blend trees based on those functions that handle alot of the pain, all you need to do is update the values in the animator with the correct ones on each update/lateupdate depending on what you are doing.
This is how my animator params are layed out as well as the locomotion blend tree, I have another on a separate layer just for the head that uses Head left right, and head updown params to, you know, control the head.
And here is the code (or at least some of it) that sets the values on the animator component
EDIT:
Sorry, forgot to mention that the function i call "LerpFloat" on the anim variable is an extention method i defined myself for the animator component, all it does is gets the float value, lerps it, then sets the float value back. It just uses Mathf.Lerp.
Thank you both for your answers, the problem was the settings of the animation clips.....
Where you tick
Root transform Rotation
Root transform position (Y)
Root transform position (XZ)
I had ticked all of them, where just the root transform rotation was needed.
I will need to be more careful with the animator and animation clips in the future to avoid headaches.

How do I remove the glide in 3D unity movement?

The Unity 3D movement has this built in feature where it glides when you stop while holding the key. How do I remove this glide?
Fair warning, I haven't tried anything yet. I am very new to Unity and assume its most likely just some button I need to click so please help.
Here's the code that I'm using:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class movement : MonoBehaviour
{
float moveSpeed;
float walkSpeed = 3;
float sprintSpeed = 6;
public CharacterController controller;
public float gravity = -9.81f;
public Transform groundCheck;
public float groundDistance = 8.4f;
public LayerMask groundMask;
Vector3 velocity;
bool isGrounded;
// Start is called before the first frame update
void Start()
{
}
// Update is called once per frame
void Update()
{
if (Input.GetKey(KeyCode.LeftShift))
{
moveSpeed = sprintSpeed;
}
else
{
moveSpeed = walkSpeed;
}
isGrounded = Physics.CheckSphere(groundCheck.position, groundDistance, groundMask);
if (isGrounded && velocity.y < 0)
{
velocity.y = -2f;
}
float x = Input.GetAxis("Horizontal");
float z = Input.GetAxis("Vertical");
Vector3 move = transform.right * x + transform.forward * z;
controller.Move(move * moveSpeed * Time.deltaTime);
velocity.y += gravity * Time.deltaTime;
controller.Move(velocity * Time.deltaTime);
}
}
I'm more than happy to answer your question but I can't leave out this tidbit. I can't stress this enough. If you are new to Unity, you should avoid posting questions like this as much as possible. I'm not discouraging you from asking questions, but since you're new, really give it a go before you post anything. You'll learn soooo much more by removing variables and lines of code to figure out a solution on your own instead of getting the answer right away.
Regardless I believe the answer is in the last two lines of your code:
velocity.y += gravity * Time.deltaTime;
controller.Move(velocity * Time.deltaTime);
Let's think about this line by line. Dissect each line and try to really understand what they mean. As you run through your code, you are receiving three main inputs from the user:
if (Input.GetKey(KeyCode.LeftShift))
float x = Input.GetAxis("Horizontal");
float z = Input.GetAxis("Vertical");
if (Input.GetKey(KeyCode.LeftShift)) is just a check to see if your user wants to run or not. The other two are receiving what is likely mouse movement input. These are primarily used to define the users movement. We know this because they are directly tied to these next two lines:
Vector3 move = transform.right * x + transform.forward * z;
controller.Move(move * moveSpeed * Time.deltaTime);
Now when you look closer at this, it tells us that if the user isn't moving the mouse, then the Vector3 move will be 0. That means that our player will stop moving and there should be no reason they glide. However, take a look at the next two.
velocity.y += gravity * Time.deltaTime;
controller.Move(velocity * Time.deltaTime);
These are not necessarily driven by user input. This means that considering they are tied to your controller and the other move calls aren't running without user input, these two lines must be the culprits for your glide problem.
So remove these and you should be good. However, I must make it clear, asking questions is good, but breaking the code to learn more about it is soo much better. As a challenge I think you should look into what drives velocity.y... or maybe isGrounded... and that will tell you more about why the glide is happening!
Hope this helps!
Check the "Gravity" field on your "Horizontal" and "Vertical" axes in the input manager (Edit > Project Settings, then select the Input category). If not 0, it will smooth out keyboard inputs.

How to make a player only move in 1 direction (Unity)

I am trying to create a game in Unity where the player can only move in the direction it is facing, but the following code allows the player to move in all 4 directions.
(This is for a 3D project)
Any help would be appreciated! Thanks!
public class PlayerController : MonoBehaviour {
public float speed;
private Rigidbody rb;
void Start() {
rb = GetComponent<Rigidbody>();
}
void FixedUpdate() {
float moveHorizontal = Input.GetAxis("Horizontal");
float moveVertical = Input.GetAxis("Vertical");
Vector3 movement = new Vector3(moveHorizontal, 0.0f, moveVertical);
rb.AddForce(movement * speed);
}
}
So I didn't get what you wanted: first you said you wanted it to move only forward, then the only thing you have to do is get when he pressed a key and move forward while is not unpressed.
If you wanted to say that it can move all directions BUT only one at a time, the you will have to put the same code but with some changes:
First of all to make it move forward you have to get the forward of the transform, otherwise it will move in the same direction if you rotate it (you don't want that, no?).
Vector3 moveDirection = (transform.forward * Input.GetAxis("Vertical") + transform.right * Input.GetAxis("Horizontal")).normalized;
moveDirection.y = 0;
rb.velocity = moveDirection;
Then, to make that it ONLY moves to one direction at a time, you have to put the priority of the greatest axis number and if it's equal then you should think if you want to move forward or right (with it's axis value).
From the code you posted, I'm not sure where you are storing the player's facing-direction. However, I presume that it is stored as a Quaternion. If you have a player rotation quaternion called playerRotation, then you could do this (warning - untested):
Vector3 input = new Vector3(Input.GetAxis("Horizontal"), 0, Input.GetAxis("Vertical"));
Vector3 normal = playerRotation * Vector3.forward;
Vector3 movement = Vector3.Dot(normal, input) * input;
If the game is first-person, then you can take a shortcut and just use Camera.current.transform.forward instead of the normal vector.
This will project the input direction onto the normal with the player's facing direction so that your movement force can only be in that direction.

Unity Mouselook from any Player Orientation

I am working on a first person game where the character is able to latch on to any flat surface and walk on it (kind of like moon boots). I am having issues with the camera since most implementations of mouse look I have been able to find depend on the player being oriented straight up and down in the world, so as soon as my "flipping" animation (the player orienting to the new surface) has finished, the camera will instantly flip back to straight up and down as defined by the world. What I need is a way to implement mouse look so that it does not reset the rotation after my animation. I am currently using:
transform.Rotate(-Input.getAxis("Mouse Y") turnSpeed Time.deltaTime, Input.getAxis("Mouse X") turnSpeed Time.deltaTime, 0);
in order to perform my mouse look rotations while avoiding the standard method, but this is clearly incorrect since I get some weird rotations when turning the camera horizontally. Is there a better way to implement mouse look so that it works correctly regardless of the orientation of the player?
You can plance camera inside player Transform and add this script to player:
public class MouseLook : MonoBehaviour {
[SerializeField]
Camera Camera;
[Range(10f,50f)]
public float Speed = 30;
void Update () {
transform.rotation = Quaternion.AngleAxis(Input.GetAxis("Mouse X") * speed * Time.deltaTime, transform.rotation * Vector3.up)*transform.rotation;
Camera.transform.rotation = Quaternion.AngleAxis(-Input.GetAxis("Mouse Y") * Speed * Time.deltaTime, Camera.transform.rotation * Vector3.right)* Camera.transform.rotation;
}
}
You add reference to Camera in inspector. You will have to check not to rotate above +/- 90 degrees on camera to avoid over rotating to back. If you will have issue with that I will help you.

How to add friction to a Wheel of Fortune in Unity3D

First of all I am new to unity as will as C#. I want to create a Wheel of Fortune for my game using C# where you can rotate it with the mouse. I wrote this code in C# - it works well, the only problem is I don’t know how to add friction to it so that if I release the mouse button it still continues rotating and slows down until it is fully stopped (Just like a realistic Fortune Wheel).
using UnityEngine;
using System.Collections;
public class RotateCSharp : MonoBehaviour
{
public int speed;
public float lerpSpeed;
float xDeg;
float yDeg;
Quaternion fromRotation;
Quaternion toRotation;
void Update()
{
if (Input.GetMouseButton(0))
{
xDeg -= Input.GetAxis("Mouse X") * speed;
fromRotation = transform.rotation;
toRotation = Quaternion.Euler (yDeg,xDeg,0);
transform.rotation = Quaternion.Lerp(fromRotation, toRotation, Time.deltaTime * lerpSpeed);
}
}
}
How can I accomplish this?
Full Disclosure--I don't do Unity, and not much C#, but this is mostly math.
Friction from sliding or rolling is almost exactly constant deceleration. Deceleration is negative acceleration, and acceleration is (velocity change)/(elapsed time).
Figure out how long you want the wheel to spin at a the maximum initial angular speed you'll allow, and that speed/time ratio is your deceleration rate. That's a constant for the wheel.
Now, in the code either before or after adjusting the angular position, decrease the speed by the deceleration rate * time interval, being careful not to overshoot 0:
if (speed > 0)
speed = Math.max(0, speed - decelRate*Time.deltaTime);
else
speed = Math.min(0, speed + decelRate*Time.deltaTime);
That assumes that decelRate is a positive value, and will handle spins in either direction.
You may have to adjust the syntax a bit, but that's the basic idea.
I've put together a behaviour that applies friction on a hinge joint. Here it is:
using UnityEngine;
public class JointFriction : MonoBehaviour {
[Tooltip("mulitiplier for the angular velocity for the torque to apply.")]
public float Friction = 0.4f;
private HingeJoint _hinge;
private Rigidbody _thisBody;
private Rigidbody _connectedBody;
private Vector3 _axis; //local space
// Use this for initialization
void Start () {
_hinge = GetComponent<HingeJoint>();
_connectedBody = _hinge.connectedBody;
_axis = _hinge.axis;
_thisBody = GetComponent<Rigidbody>();
}
// Update is called once per frame
void FixedUpdate () {
var angularV = _hinge.velocity;
//Debug.Log("angularV " + angularV);
var worldAxis = transform.TransformVector(_axis);
var worldTorque = Friction * angularV * worldAxis;
_thisBody.AddTorque(-worldTorque);
_connectedBody.AddTorque(worldTorque);
}
}
You can apply this as a separate behaviour to the object with the hinge joint, or you can take bits of it and incorporate them into your behaviour.
This code assumes the hinge joint has a connected rigidbody that it is rubbing against, if that is not the case, simply remove the lines that refer to the _connectedRigidbody.