How to jump to a point using CharacterController? - c#

I have a script to jump, but it jumps depending on the value of the JumpFactor constant. I would like the jump to happen using the JumpToPositionX (float target) method. I think I need to somehow calculate the JumpFactor depending on the value of the target in the JumpToPositionX method. I found an article on Wikipedia, but I don't even know where to start. I have not found examples of how to do this with the CharacterController.
public class ExampleClass : MonoBehaviour
{
const float Speed = 6f;
const float JumpFactor = 14f;
const float Gravity = 20f;
private Vector3 moveDirection = Vector3.zero;
CharacterController controller;
void Start()
{
controller = GetComponent<CharacterController>();
}
void Update()
{
if (controller.isGrounded)
{
moveDirection = new Vector3(1f, 0f, 0f); // Move X Direction
moveDirection *= Speed;
if (Input.GetButton("Jump"))
moveDirection.y = JumpFactor;
}
moveDirection.y -= Gravity * Time.deltaTime;
controller.Move(moveDirection * Time.deltaTime);
}
// Method for jump, calling from other script.
void JumpToPositionX(float target)
{
}
}

Theory
I assume you want to use the ballistic physic.
Let's see what formula we can use
Where :
V : Velocity
a : Acceleration
P : Position
t : time since start
0 : Initial
is known it's (0f,-9.81f) or (0f, Gravity) in your case.
is known too, it's your initial position before the jump.
is the end target position.
Resolution
You have to find the end time and the initial Velocity . So here's the position equation:
We can simplify the process by looking only for the x axis because there's no acceleration.
is simplified to :
Let's isolate the from the x position formula :
Now you have the total time of the jump .
You just need to find the initial y velocity by using the y position formula :
Isolate the Vyo :
So now you have all informations for computing the jump : Initial and end position, Total time, Initial velocity and acceleration.
Implementation
Here's an example of the MonoBehaviour you can create :
public class JumpBehaviour : MonoBehaviour
{
public CharacterController CharacterController;
public Transform TargetPosition;
private bool jumpStarted = false;
private float totalTime = 0f;
private float t = 0f;
private Vector2 V0;
private Vector2 initialPosition;
private bool jumpKeyDown;
private const float xSpeed = 6f;
private const float gravity = -9.81f;
void Update()
{
jumpKeyDown = Input.GetKeyDown(KeyCode.Space);
}
void FixedUpdate()
{
if (jumpStarted)
{
UpdateJumpPosition(Time.fixedDeltaTime);
}
else if (jumpKeyDown )
{
StartJump();
}
}
private void UpdateJumpPosition(float deltaTime)
{
t += deltaTime;
if(t > totalTime)
{
//End of jump
transform.position = TargetPosition.position;
jumpStarted = false;
return;
}
Vector2 newPosition = new Vector2(0f, gravity) * t * t + V0 * t + initialPosition;
CharacterController.Move(newPosition);
}
private void StartJump()
{
jumpStarted = true;
initialPosition = transform.position;
// Absolute because xSpeed could be positive or negative. We dont want negative time
float delta = TargetPosition.position.x - transform.position.x;
totalTime = Mathf.Abs(delta / xSpeed);
t = 0f;
float yInitialSpeed = (TargetPosition.position.y - transform.position.y - gravity * totalTime * totalTime) / totalTime;
// Using Sign to set the direction of horizontal movement
V0 = new Vector2(xSpeed * Mathf.Sign(delta), yInitialSpeed);
}
}
And this is the result
Conclusion
This is just an quick example to figure out how to compute the initial value of the jump and movement in 2D.
As derHugo said on comment you should not use transform.position direct attribution with Physic.
For 3D it's the same idea but you should compute the speed x,z from target direction and speed magnitude.
Vector3 delta = TargetPosition - transform.position;
delta.y = 0f;
float direction = delta.normalized;
Vector3 initialSpeed = direction * movementSpeed;

Related

How to make an FPS Camera using Netcode for gameobjects and FPS Starter Assets?

All I know is that every time a new client joins, all the player set their main camera to that ones.
I use the starter assets but I changed few things to solve this and do the networking:
using Unity.Netcode;
using UnityEngine;
#if ENABLE_INPUT_SYSTEM && STARTER_ASSETS_PACKAGES_CHECKED
using UnityEngine.InputSystem;
#endif
namespace StarterAssets
{
[RequireComponent(typeof(CharacterController))]
#if ENABLE_INPUT_SYSTEM && STARTER_ASSETS_PACKAGES_CHECKED
[RequireComponent(typeof(PlayerInput))]
#endif
public class FirstPersonController : NetworkBehaviour
{
[Header("Player")]
[Tooltip("Move speed of the character in m/s")]
public float MoveSpeed = 4.0f;
[Tooltip("Sprint speed of the character in m/s")]
public float SprintSpeed = 6.0f;
[Tooltip("Rotation speed of the character")]
public float RotationSpeed = 1.0f;
[Tooltip("Acceleration and deceleration")]
public float SpeedChangeRate = 10.0f;
[Space(10)]
[Tooltip("The height the player can jump")]
public float JumpHeight = 1.2f;
[Tooltip("The character uses its own gravity value. The engine default is -9.81f")]
public float Gravity = -15.0f;
[Space(10)]
[Tooltip("Time required to pass before being able to jump again. Set to 0f to instantly jump again")]
public float JumpTimeout = 0.1f;
[Tooltip("Time required to pass before entering the fall state. Useful for walking down stairs")]
public float FallTimeout = 0.15f;
[Header("Player Grounded")]
[Tooltip("If the character is grounded or not. Not part of the CharacterController built in grounded check")]
public bool Grounded = true;
[Tooltip("Useful for rough ground")]
public float GroundedOffset = -0.14f;
[Tooltip("The radius of the grounded check. Should match the radius of the CharacterController")]
public float GroundedRadius = 0.5f;
[Tooltip("What layers the character uses as ground")]
public LayerMask GroundLayers;
[Header("Cinemachine")]
[Tooltip("The follow target set in the Cinemachine Virtual Camera that the camera will follow")]
public GameObject CinemachineCameraTarget;
[Tooltip("How far in degrees can you move the camera up")]
public float TopClamp = 90.0f;
[Tooltip("How far in degrees can you move the camera down")]
public float BottomClamp = -90.0f;
// cinemachine
private float _cinemachineTargetPitch;
// player
private float _speed;
private float _rotationVelocity;
private float _verticalVelocity;
private float _terminalVelocity = 53.0f;
// timeout deltatime
private float _jumpTimeoutDelta;
private float _fallTimeoutDelta;
#if ENABLE_INPUT_SYSTEM && STARTER_ASSETS_PACKAGES_CHECKED
private PlayerInput _playerInput;
#endif
private CharacterController _controller;
private StarterAssetsInputs _input;
[SerializeField] private GameObject _mainCamera;
private const float _threshold = 0.01f;
private bool IsCurrentDeviceMouse
{
get
{
#if ENABLE_INPUT_SYSTEM && STARTER_ASSETS_PACKAGES_CHECKED
return _playerInput.currentControlScheme == "KeyboardMouse";
#else
return false;
#endif
}
}
public override void OnNetworkSpawn()
{
// get a reference to our main camera
if (_mainCamera == null)
{
foreach (Transform child in gameObject.transform)
{
if (child.name == "PlayerCameraRoot")
{
foreach (Transform granchild in child.transform)
{
if (granchild.tag == "MainCamera")
_mainCamera = granchild.gameObject;
}
}
}
}
}
private void Start()
{
if(!IsOwner) return;
_controller = GetComponent<CharacterController>();
_input = GetComponent<StarterAssetsInputs>();
#if ENABLE_INPUT_SYSTEM && STARTER_ASSETS_PACKAGES_CHECKED
_playerInput = GetComponent<PlayerInput>();
#else
Debug.LogError( "Starter Assets package is missing dependencies. Please use Tools/Starter Assets/Reinstall Dependencies to fix it");
#endif
// reset our timeouts on start
_jumpTimeoutDelta = JumpTimeout;
_fallTimeoutDelta = FallTimeout;
Cursor.visible = false;
}
private void Update()
{
if(!IsOwner) return;
JumpAndGravity();
GroundedCheck();
Move();
}
private void LateUpdate()
{
CameraRotation();
}
private void GroundedCheck()
{
// set sphere position, with offset
Vector3 spherePosition = new Vector3(transform.position.x, transform.position.y - GroundedOffset, transform.position.z);
Grounded = Physics.CheckSphere(spherePosition, GroundedRadius, GroundLayers, QueryTriggerInteraction.Ignore);
}
private void CameraRotation()
{
// if there is an input
if (_input.look.sqrMagnitude >= _threshold)
{
//Don't multiply mouse input by Time.deltaTime
float deltaTimeMultiplier = IsCurrentDeviceMouse ? 1.0f : Time.deltaTime;
_cinemachineTargetPitch += _input.look.y * RotationSpeed * deltaTimeMultiplier;
_rotationVelocity = _input.look.x * RotationSpeed * deltaTimeMultiplier;
// clamp our pitch rotation
_cinemachineTargetPitch = ClampAngle(_cinemachineTargetPitch, BottomClamp, TopClamp);
// Update Cinemachine camera target pitch
CinemachineCameraTarget.transform.localRotation = Quaternion.Euler(_cinemachineTargetPitch, 0.0f, 0.0f);
// rotate the player left and right
transform.Rotate(Vector3.up * _rotationVelocity);
}
}
private void Move()
{
// set target speed based on move speed, sprint speed and if sprint is pressed
float targetSpeed = _input.sprint ? SprintSpeed : MoveSpeed;
// a simplistic acceleration and deceleration designed to be easy to remove, replace, or iterate upon
// note: Vector2's == operator uses approximation so is not floating point error prone, and is cheaper than magnitude
// if there is no input, set the target speed to 0
if (_input.move == Vector2.zero) targetSpeed = 0.0f;
// a reference to the players current horizontal velocity
float currentHorizontalSpeed = new Vector3(_controller.velocity.x, 0.0f, _controller.velocity.z).magnitude;
float speedOffset = 0.1f;
float inputMagnitude = _input.analogMovement ? _input.move.magnitude : 1f;
// accelerate or decelerate to target speed
if (currentHorizontalSpeed < targetSpeed - speedOffset || currentHorizontalSpeed > targetSpeed + speedOffset)
{
// creates curved result rather than a linear one giving a more organic speed change
// note T in Lerp is clamped, so we don't need to clamp our speed
_speed = Mathf.Lerp(currentHorizontalSpeed, targetSpeed * inputMagnitude, Time.deltaTime * SpeedChangeRate);
// round speed to 3 decimal places
_speed = Mathf.Round(_speed * 1000f) / 1000f;
}
else
{
_speed = targetSpeed;
}
// normalise input direction
Vector3 inputDirection = new Vector3(_input.move.x, 0.0f, _input.move.y).normalized;
// note: Vector2's != operator uses approximation so is not floating point error prone, and is cheaper than magnitude
// if there is a move input rotate player when the player is moving
if (_input.move != Vector2.zero)
{
// move
inputDirection = transform.right * _input.move.x + transform.forward * _input.move.y;
}
// move the player
_controller.Move(inputDirection.normalized * (_speed * Time.deltaTime) + new Vector3(0.0f, _verticalVelocity, 0.0f) * Time.deltaTime);
}
private void JumpAndGravity()
{
if (Grounded)
{
// reset the fall timeout timer
_fallTimeoutDelta = FallTimeout;
// stop our velocity dropping infinitely when grounded
if (_verticalVelocity < 0.0f)
{
_verticalVelocity = -2f;
}
// Jump
if (_input.jump && _jumpTimeoutDelta <= 0.0f)
{
// the square root of H * -2 * G = how much velocity needed to reach desired height
_verticalVelocity = Mathf.Sqrt(JumpHeight * -2f * Gravity);
}
// jump timeout
if (_jumpTimeoutDelta >= 0.0f)
{
_jumpTimeoutDelta -= Time.deltaTime;
}
}
else
{
// reset the jump timeout timer
_jumpTimeoutDelta = JumpTimeout;
// fall timeout
if (_fallTimeoutDelta >= 0.0f)
{
_fallTimeoutDelta -= Time.deltaTime;
}
// if we are not grounded, do not jump
_input.jump = false;
}
// apply gravity over time if under terminal (multiply by delta time twice to linearly speed up over time)
if (_verticalVelocity < _terminalVelocity)
{
_verticalVelocity += Gravity * Time.deltaTime;
}
}
private static float ClampAngle(float lfAngle, float lfMin, float lfMax)
{
if (lfAngle < -360f) lfAngle += 360f;
if (lfAngle > 360f) lfAngle -= 360f;
return Mathf.Clamp(lfAngle, lfMin, lfMax);
}
private void OnDrawGizmosSelected()
{
Color transparentGreen = new Color(0.0f, 1.0f, 0.0f, 0.35f);
Color transparentRed = new Color(1.0f, 0.0f, 0.0f, 0.35f);
if (Grounded) Gizmos.color = transparentGreen;
else Gizmos.color = transparentRed;
// when selected, draw a gizmo in the position of, and matching radius of, the grounded collider
Gizmos.DrawSphere(new Vector3(transform.position.x, transform.position.y - GroundedOffset, transform.position.z), GroundedRadius);
}
}
}
And this is my Prefab for the player:
enter image description here
My Network Manager:
enter image description here
Note: I don't know if it matters. It probably does not but I am raycasting with the cameras.
I've had some experience with a 2d game but it should also work for 3d fps make a empty gameobject then use that as a parent for the camera then using a new script you can disable it if IsOwner is false (make sure your using Unity.Netcode and instead of a MonoBehaviour you use a NetworkBehaviour otherwise you cant use IsOwner)

Unity need character to rotate to face movement direction on planetoid

we are working on a student project and we chose to do a Mario Galaxy style platformer with planetoids and gravity (kind of a big mistake for my first coding project but I cannot back out of it now) but I am having a hard time to get the character to face it's movement direction without absolutely spazzing out.
I have only been coding for around 2 months so please excuse me being useless at trying to figure this out.
This is the code I use for movement for the character
using System.Collections.Generic;
using UnityEngine;
public class SC_RigidbodyWalker : MonoBehaviour
{
public float speed = 5.0f;
public bool canJump = true;
public float jumpHeight = 2.0f;
public Camera playerCamera;
public float lookSpeed = 2.0f;
public float lookXLimit = 60.0f;
bool grounded = false;
Rigidbody r;
Vector2 rotation = Vector2.zero;
float maxVelocityChange = 10.0f;
void Awake()
{
r = GetComponent<Rigidbody>();
r.freezeRotation = true;
r.useGravity = false;
r.collisionDetectionMode = CollisionDetectionMode.ContinuousDynamic;
rotation.y = transform.eulerAngles.y;
Cursor.lockState = CursorLockMode.Locked;
Cursor.visible = false;
}
void Update()
{
}
void FixedUpdate()
{
if (grounded)
{
// Calculate how fast we should be moving
Vector3 forwardDir = Vector3.Cross(transform.up, -playerCamera.transform.right).normalized;
Vector3 rightDir = Vector3.Cross(transform.up, playerCamera.transform.forward).normalized;
Vector3 targetVelocity = (forwardDir * Input.GetAxis("Vertical") + rightDir * Input.GetAxis("Horizontal")) * speed;
Vector3 velocity = transform.InverseTransformDirection(r.velocity);
velocity.y = 0;
velocity = transform.TransformDirection(velocity);
Vector3 velocityChange = transform.InverseTransformDirection(targetVelocity - velocity);
velocityChange.x = Mathf.Clamp(velocityChange.x, -maxVelocityChange, maxVelocityChange);
velocityChange.z = Mathf.Clamp(velocityChange.z, -maxVelocityChange, maxVelocityChange);
velocityChange.y = 0;
velocityChange = transform.TransformDirection(velocityChange);
r.AddForce(velocityChange, ForceMode.VelocityChange);
if (Input.GetButton("Jump") && canJump)
{
r.AddForce(transform.up * jumpHeight, ForceMode.VelocityChange);
}
}
grounded = false;
}
void OnCollisionStay()
{
grounded = true;
}
}
And here are the code for the gravity functions
using System.Collections.Generic;
using UnityEngine;
public class SC_PlanetGravity : MonoBehaviour
{
public Transform planet;
public bool alignToPlanet = true;
float gravityConstant = 9.8f;
Rigidbody r;
void Start()
{
r = GetComponent<Rigidbody>();
}
void FixedUpdate()
{
Vector3 toCenter = planet.position - transform.position;
toCenter.Normalize();
r.AddForce(toCenter * gravityConstant, ForceMode.Acceleration);
if (alignToPlanet)
{
Quaternion q = Quaternion.FromToRotation(transform.up, -toCenter);
q = q * transform.rotation;
transform.rotation = Quaternion.Slerp(transform.rotation, q, 1);
}
}
}
I can propose an alternative approach which I believe will simplify the problem, should you choose. If the root of your character is positioned at the center of the planetoid, then all movement can be handled as a rotation on root and you won't be fighting the inertia of the character or needing to orient it to the planetoid. Jumping could be handled by adding another child below the root that can slide up and down. Hope this helps!
I managed to get it working by having a new script added to the player avatar.
Here is the code, it's basically a copy paste of a part of the RigidBodyWalker script with some more added parts. It's not perfect but it works like I wanted it to.
using System.Collections.Generic;
using UnityEngine;
public class InputRotation : MonoBehaviour
{
public Camera playerCamera;
public float speed;
public float rotationSpeed;
// Start is called before the first frame update
void Start()
{
}
// Update is called once per frame
void Update()
{
//float horizontalInput = Input.GetAxis("Horizontal");
//float verticalInput = Input.GetAxis("Vertical");
}
private void FixedUpdate()
{
Vector3 forwardDir = Vector3.Cross(transform.up, -playerCamera.transform.right).normalized;
Vector3 rightDir = Vector3.Cross(transform.up, playerCamera.transform.forward).normalized;
Vector3 targetVelocity = (forwardDir * Input.GetAxis("Vertical") + rightDir * Input.GetAxis("Horizontal")) * speed;
if(targetVelocity != Vector3.zero)
{
Quaternion toRotation = Quaternion.LookRotation(targetVelocity, transform.up);
transform.rotation = Quaternion.RotateTowards(transform.rotation, toRotation, rotationSpeed * Time.deltaTime);
}
}
}

Unity limit rigidbody velocity in specific direction with addforce?

I am creating a third person player movement script. The movement adds force to the rigidbody relative to the direction of the camera. I want to have a max speed limit in the forward direction (cForward), and a separate max speed limit for the horizontal/right direction (cRight). Normally I would be fine with setting the velocity directly, however this screws up gravity for the player. Is there any way to achieve this by using addforce OR is there a way to get gravity working properly when setting velocity directly? Here is what I have so far(some of my other attempts at a solution are commented out):
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class PlayerMovement : MonoBehaviour
{
public Camera mainCamera;
public float horizontalWalkSpeed = 500.0f;
public float verticalWalkSpeed = 500.0f;
public float horizontalSprintSpeed = 1000.0f;
public float verticalSprintSpeed = 1000.0f;
public bool isSprinting = false;
public bool cannotMove = false;
public float accelerationSpeed = 0.2f;
private Rigidbody pRigidBody;
private Vector2 currentInputVector;
private Vector2 smoothInputVelocity;
// Start is called before the first frame update
void Start()
{
pRigidBody = GetComponent<Rigidbody>();
}
// Update is called once per frame
void Update()
{
HandleInput();
}
private void FixedUpdate()
{
Vector3 cForward = mainCamera.transform.forward;
Vector3 cRight = mainCamera.transform.right;
cForward.y = 0.0f;
cRight.y = 0.0f;
cForward.Normalize();
cRight.Normalize();
if (!cannotMove && !isSprinting)
{
Vector3 vForce = cForward * currentInputVector.y * verticalWalkSpeed;
Vector3 hForce = cRight * currentInputVector.x * horizontalWalkSpeed;
//Vector3 gravity = Vector3.up * -9.8f;
Vector3 force = vForce + hForce;
//float verSpeed = Vector3.Dot(pRigidBody.velocity, cForward);
//float horSpeed = Vector3.Dot(pRigidBody.velocity, cRight);
//if (verSpeed >= 0 && verSpeed <= verticalWalkSpeed)
//{
// pRigidBody.AddForce(vForce, ForceMode.VelocityChange);
//}
//if(horSpeed < horizontalWalkSpeed)
//{
// pRigidBody.AddForce(hForce, ForceMode.VelocityChange);
//}
//float velocityInDirection = Vector3.Dot(pRigidBody.velocity, cForward);
//if(velocityInDirection > verticalWalkSpeed)
//{
// pRigidBody.AddForce(-vForce, ForceMode.VelocityChange);
//}
//pRigidBody.velocity = force;
pRigidBody.AddForce(force, ForceMode.VelocityChange);
}
else if (!cannotMove && isSprinting)
{
pRigidBody.velocity = cForward * currentInputVector.y * verticalSprintSpeed * Time.fixedDeltaTime + cRight * currentInputVector.x * horizontalSprintSpeed * Time.fixedDeltaTime;
}
}
private void HandleInput()
{
isSprinting = Input.GetButton("Sprint");
Vector2 input = new Vector2(Input.GetAxis("Horizontal"), Input.GetAxis("Vertical"));
currentInputVector = Vector2.SmoothDamp(currentInputVector, input, ref smoothInputVelocity, accelerationSpeed);
}
}
I think this thread may help --https://answers.unity.com/questions/9985/limiting-rigidbody-velocity.html.
Basically, there are two methods.
Add force in the opposite direction and increase with the extent to which the object exceeds the limit. This requires additional calculations and tests.
simply normalize the velocity value when it exceeds the limit.

Adding a clamp to the rotation of a 2D object in Unity2D

I have been working for a long time trying to get a clamp implemented on my 2D object in Unity2D. I have struggled with this issue for weeks now. I have a rocketship that moves upwards on it's own, and I'm trying to make it where you can't go further than 40 degrees rotating. Some details to note is that the ship rotates(x-axis of course) and moves toward the mouse(x-axis, doesn't go further up more then the current velocity) as a way to avoid obstacles which are coming soon. Here's my code for the ship:
public class MiniGameRocketShipController : MonoBehaviour
{
public Vector2 velocity = new Vector2(0f, 1500f);
private Vector2 directionX;
private Vector2 directionY;
public Rigidbody2D rb;
public new Camera camera;
public float moveSpeed = 100f;
public float rotation;
private void Start()
{
rotation = rb.rotation;
}
private void Update()
{
rb.AddForce(velocity * Time.deltaTime);
rb.velocity = new Vector2(directionX.x * moveSpeed, directionY.y * moveSpeed);
FaceMouse();
}
void FaceMouse()
{
if (rotation > 180)
{
rotation = 360;
}
rotation = Mathf.Clamp(rotation, -40f, 40f);
rb.rotation = rotation;
Vector3 mousePosition = Input.mousePosition;
mousePosition = camera.ScreenToWorldPoint(mousePosition);
directionX = (mousePosition - transform.position).normalized;
directionY = transform.position.normalized;
transform.up = directionX;
}
}
You can use Vector2.SignedAngle and Mathf.Clamp to do this. Explanation in comments:
// world direction to clamp to as the origin
Vector2 clampOriginDirection = Vector2.up;
// what local direction should we compare to the clamp origin
Vector2 clampLocalDirection = Vector2.up;
// how far can the local direction stray from the origin
float maxAbsoluteDeltaDegrees = 40f;
void ClampDirection2D()
{
// world direction of the clamped local
// could be `... = transform.up;` if you are ok hardcoding comparing local up
Vector2 currentWorldDirection = transform.TransformDirection(clampLocalDirection);
// how far is it from the origin?
float curSignedAngle = Vector2.SignedAngle(clampOriginDirection, currentWorldDirection);
// clamp that angular distance
float targetSignedAngle = Mathf.Clamp(curSignedAngle, -maxAbsoluteDeltaDegrees,
maxAbsoluteDeltaDegrees);
// How far is that from the current signed angle?
float targetDelta = targetSignedAngle - curSignedAngle;
// apply that delta to the rigidbody's rotation:
rb.rotation += targetDelta;
}

Rotate 2D Sprite with Virtual Joystick

I am trying to rotate a GameObject along z-axis using Joystick, but its rotating y-axis. I might missed some math calculation. Any Help??
Reference Image
void FixedUpdate()
{
// get input from joystick
// get input from joystick
rightJoystickInput = rightJoystick.GetInputDirection();
float xMovementRightJoystick = rightJoystickInput.x; // The horizontal movement from joystick 02
float zMovementRightJoystick = rightJoystickInput.y; // The vertical movement from joystick 02
// if there is only input from the right joystick
if (rightJoystickInput != Vector3.zero)
{
// calculate the player's direction based on angle
float tempAngle = Mathf.Atan2(zMovementRightJoystick, xMovementRightJoystick);
xMovementRightJoystick *= Mathf.Abs(Mathf.Cos(tempAngle));
zMovementRightJoystick *= Mathf.Abs(Mathf.Sin(tempAngle));
// rotate the player to face the direction of input
Vector3 temp = transform.position;
temp.x += xMovementRightJoystick;
temp.z += zMovementRightJoystick;
Vector3 lookDirection = temp - transform.position;
if (lookDirection != Vector3.zero)
{
rotationTarget.localRotation = Quaternion.Slerp(rotationTarget.localRotation, Quaternion.LookRotation(lookDirection) * Quaternion.Euler(0, 45f, 0), rotationSpeed * Time.deltaTime);
}
}
}
You don't need most of the code in your question and this is really simple.
1.Find the angle with Mathf.Atan2 then multiple it with Mathf.Rad2Deg.
2.Use Quaternion.Euler(new Vector3(0, 0, angle)) to get the rotation then apply it to the Object.
This should be one in the Update function not FixedUpdate because FixedUpdate is used to move Rigidbody Objects.
public Transform rotationTarget;
public bool flipRot = true;
void Update()
{
rightJoystickInput = rightJoystick.GetInputDirection();
float horizontal = rightJoystickInput.x;
float vertical = rightJoystickInput.y;
float angle = Mathf.Atan2(horizontal, vertical) * Mathf.Rad2Deg;
angle = flipRot ? -angle : angle;
rotationTarget.rotation = Quaternion.Euler(new Vector3(0, 0, angle));
}
If using Rigidbody2D then use Rigidbody2D.MoveRotation in the FixedUpdate function. The rest of the code stays the-same.
public Rigidbody2D rg2d;
public bool flipRot = true;
void FixedUpdate()
{
rightJoystickInput = rightJoystick.GetInputDirection();
float horizontal = rightJoystickInput.x;
float vertical = rightJoystickInput.y;
float angle = Mathf.Atan2(horizontal, vertical) * Mathf.Rad2Deg;
angle = flipRot ? -angle : angle;
rg2d.MoveRotation(angle);
}
EDIT:
But only the problem is when i leave joystick its rotation is setting
to 0 instantly which looks too odd. How can i fix it?
You have to detect when you release the joystick in OnPointerUp then slowly lerp the joystick thump back to the Zero position. You also have to lerp the current target object angle to zero or to its default value and this should be done in a coroutine function. When OnPointerDown is called, stop the current coroutine function. Prevent the code in FixedUpdate from running when finger is released so that it won't interfere with the coroutine function.
For the sake of completeness, below is the combination of a Joystick code and the Rigidbody answer above:
public class VirtualJoystickController : MonoBehaviour,
IDragHandler, IPointerUpHandler, IPointerDownHandler
{
private Image bgImg;
private Image joystickImg;
public float mas_distance = 7f;
void Start()
{
bgImg = GameObject.Find("JoystickBGImage").GetComponent<Image>(); // the joysticks background
joystickImg = GameObject.Find("Joystickthumb").GetComponent<Image>(); // the joystick object to use
}
private Vector3 _inputDirection = Vector3.zero;
//the movementDirection
public Vector3 joystickInputDirection
{
set
{
//Change only if value is different from old one
if (_inputDirection != value)
{
_inputDirection = value;
Debug.Log("Dir: " + _inputDirection);
}
}
get
{
return _inputDirection;
}
}
public void OnDrag(PointerEventData eventData)
{
dragJoyStick(eventData);
}
void dragJoyStick(PointerEventData eventData)
{
Vector3 tempDir = Vector3.zero;
Vector2 pos = Vector2.zero;
if (RectTransformUtility.ScreenPointToLocalPointInRectangle
(bgImg.rectTransform,
eventData.position,
eventData.pressEventCamera,
out pos))
{
pos.x = (pos.x / bgImg.rectTransform.sizeDelta.x);
pos.y = (pos.y / bgImg.rectTransform.sizeDelta.y);
float x = (bgImg.rectTransform.pivot.x == 1) ? pos.x * 2 + 1 : pos.x * 2 - 1;
float y = (bgImg.rectTransform.pivot.y == 1) ? pos.y * 2 + 1 : pos.y * 2 - 1;
tempDir = new Vector3(x, y, 0);
if (tempDir.magnitude > 1)
{
tempDir = tempDir.normalized;
}
joystickImg.rectTransform.anchoredPosition = new Vector3(
tempDir.x * (bgImg.rectTransform.sizeDelta.x / mas_distance),
tempDir.y * (bgImg.rectTransform.sizeDelta.y / mas_distance));
joystickInputDirection = tempDir;
}
}
public void OnPointerDown(PointerEventData eventData)
{
released = false;
//Stop current coroutine
if (retCoroutine != null)
StopCoroutine(retCoroutine);
if (eventData.pointerCurrentRaycast.gameObject == bgImg.gameObject ||
eventData.pointerCurrentRaycast.gameObject == joystickImg.gameObject)
{
OnDrag(eventData);
}
}
public void OnPointerUp(PointerEventData eventData)
{
released = true;
//Stop current coroutine then start a new one
if (retCoroutine != null)
StopCoroutine(retCoroutine);
retCoroutine = StartCoroutine(SlowReturn(returnTime));
}
IEnumerator SlowReturn(float duration)
{
RectTransform thumbstickTransform = joystickImg.rectTransform;
Vector3 toPosition = Vector3.zero;
float counter = 0;
//Get the current position of the object to be moved
Vector2 currentThumb = thumbstickTransform.anchoredPosition;
while (counter < duration)
{
counter += Time.deltaTime;
//Slowly returns thumbstick
Vector2 tempThumbStickVal = Vector2.Lerp(currentThumb, toPosition, counter / duration);
joystickInputDirection = tempThumbStickVal;
thumbstickTransform.anchoredPosition = tempThumbStickVal;
//Slowly returns the target Object to original pos
float tempTargetObjAngle = Mathf.Lerp(angle, originalAngle, counter / duration);
rg2d.MoveRotation(tempTargetObjAngle);
yield return null;
}
}
public float returnTime = 1.0f;
public Rigidbody2D rg2d;
public bool flipRot = true;
const float originalAngle = 0;
bool released = true;
float angle;
Coroutine retCoroutine;
void FixedUpdate()
{
if (released)
return;
float horizontal = joystickInputDirection.x;
float vertical = joystickInputDirection.y;
angle = Mathf.Atan2(horizontal, vertical) * Mathf.Rad2Deg;
angle = flipRot ? -angle : angle;
rg2d.MoveRotation(angle);
}
}
your function calculates a point where you want to look at:
Vector3 temp = transform.position;
temp.x += xMovementRightJoystick;
temp.z += zMovementRightJoystick;
Vector3 lookDirection = temp - transform.position;
this point is on the XZ plane, that is why the car rotates on Y axis
if you want to rotate on Z axis, calculate a point on XY plane like this:
Vector3 temp = transform.position;
temp.x += xMovementRightJoystick;
temp.y += zMovementRightJoystick;
Vector3 lookDirection = temp - transform.position;
PS: i don't know why you multiply with Quaternion.Euler(0, 45f, 0) - that is a constant angle on Y axis, it just means that each lookDirection will be rotated for 45 degrees - i would have to see your scene to know why you need this...

Categories