Unity need character to rotate to face movement direction on planetoid - c#

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

Related

Character rotation relative with camera forward?

I spent some time already and still cant figure it out by myself.
The problem is i can"t rotate character forward to move the way i pointing my camera.
To move camera i use cinemachine with Orbital Transposer and normal Composer.
My Movement script to let you know with what I work :
public class PlayerMovement : MonoBehaviour
{
public float m_Impulse = 1f;
public bool grounded = false;
public float groundCheckDistance;
private float bufferCheckDistance = 0.02f;
public float airMultiplier = 0.2f;
float horizontalInput;
float verticalInput;
public float groundDrag;
Rigidbody m_Rigidbody;
Vector3 moveDirection;
public float m_Speed = 5f;
public float m_runSpeed = 7f;
void Start()
{
m_Rigidbody = GetComponent<Rigidbody>();
m_Rigidbody.freezeRotation = true;
}
void FixedUpdate()
{
horizontalInput = Input.GetAxis("Horizontal");
verticalInput = Input.GetAxis("Vertical");
// calculate movement direction
moveDirection = orientation.forward * verticalInput + orientation.right * horizontalInput;
if (Input.GetKey(KeyCode.Space) & grounded)
{
m_Rigidbody.AddForce(Vector3.up * m_Impulse * 2);
}
if (Input.GetKey(KeyCode.LeftShift))
{
m_Rigidbody.AddForce(moveDirection * m_runSpeed, ForceMode.Force);
}
else
{
m_Rigidbody.AddForce(moveDirection * m_Speed, ForceMode.Force);
}
}
void Update()
{
groundCheckDistance = (GetComponent<CapsuleCollider>().height / 2) + bufferCheckDistance;
RaycastHit hit;
if (Physics.Raycast(transform.position, -transform.up, out hit, groundCheckDistance))
{
grounded = true;
}
else
{
grounded = false;
}
if (grounded)
{
m_Rigidbody.drag = groundDrag;
}
else if (!grounded)
{
moveDirection *= airMultiplier;
m_Rigidbody.drag = 2;
}
}
Hope you know how to make it works, Thank You in advance.
P.S. My camera script was left with Cursor.LockMode only cuz i messed something up rly bad and my camera was bugged.

Unity player movement on moving planet

I am making a game involving orbital physics. I was successfully able to implement this with a slightly modified version of Brackeys gravity tutorial https://youtu.be/Ouu3D_VHx9o, this is the code:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class gravity : MonoBehaviour
{
public GameObject self;
public Rigidbody rb;
public Vector3 initialVelocity;
const float G = 66.74f;
public static List<gravity> Attractors;
public bool isAttractable;
private void Awake()
{
rb.AddForce(initialVelocity);
}
private void FixedUpdate()
{
//planets
if (isAttractable == false)
{
foreach (gravity attractor in Attractors)
{
if (attractor != this)
Attract(attractor);
}
}
//players, spaceships, astroids, ect
if (isAttractable == true)
{
foreach (gravity attractor in Attractors)
{
if (attractor != this)
Attract(attractor);
}
}
}
void OnEnable()
{
if( isAttractable == false)
{
if (Attractors == null)
Attractors = new List<gravity>();
Attractors.Add(this);
}
}
void OnDisable()
{
if (isAttractable == false)
{
Attractors.Remove(this);
}
}
void Attract(gravity objToAttract)
{
Rigidbody rbToAttract = objToAttract.rb;
Vector3 direction = -1 * (rb.position - rbToAttract.position);
Vector3 Force = direction.normalized * (G * ((rb.mass * rbToAttract.mass) / direction.sqrMagnitude));
rb.AddForce(Force);
}
public GameObject GetClosestPlanet()
{
GameObject close = null;
float minDist = Mathf.Infinity;
foreach (gravity attracor in Attractors)
{
float dist = Vector3.Distance(attracor.transform.position, transform.position);
if (dist < minDist)
{
close = attracor.transform.gameObject;
minDist = dist;
}
}
return close;
}
}
Then for player movement I used (and modified) Sebastian Lagues tutorial https://youtu.be/TicipSVT-T8,
this resulted in this code for the player controller:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class playerController : MonoBehaviour
{
public float mouseSensitivityX = 250f;
public float mouseSensitivityY = 250f;
Transform cameraT;
float verticalLookRot;
private Rigidbody rb;
Vector3 moveAmount;
Vector3 smootgMoveVelocity;
public float moveSpeed = 15;
public float jumpForce = 220;
public LayerMask groundedMask;
public bool grounded;
public GameObject currentPlanet;
private gravity playerGravity;
private void Awake()
{
rb = GetComponent<Rigidbody>();
playerGravity = GetComponent<gravity>();
Cursor.lockState = CursorLockMode.Locked;
cameraT = Camera.main.transform;
}
void Update()
{
currentPlanet = playerGravity.GetClosestPlanet();
//camera
transform.Rotate(Vector3.up * Input.GetAxis("Mouse X") * Time.deltaTime * mouseSensitivityX);
verticalLookRot += Input.GetAxis("Mouse Y") * Time.deltaTime * mouseSensitivityY;
verticalLookRot = Mathf.Clamp(verticalLookRot, -60, 60);
cameraT.localEulerAngles = Vector3.left * verticalLookRot;
//move input
Vector3 moveDir = new Vector3(Input.GetAxisRaw("Horizontal"), 0, Input.GetAxisRaw("Vertical")).normalized;
Vector3 targetMoveAmount = moveDir * moveSpeed;
moveAmount = Vector3.SmoothDamp(targetMoveAmount, targetMoveAmount, ref smootgMoveVelocity, .15f);
//level on planet
if(currentPlanet != null)
{
transform.rotation = Quaternion.FromToRotation(transform.up, (transform.position - currentPlanet.transform.position).normalized) * transform.rotation;
}
//jump
if (Input.GetButtonDown("Jump"))
{ if(grounded)
{
rb.AddForce(transform.up * jumpForce);
print("u jumped");
}
}
}
private void FixedUpdate()
{
//move
rb.MovePosition(rb.position + transform.TransformDirection(moveAmount) * Time.fixedDeltaTime);
//check if on ground
Ray ray = new Ray(transform.position, -transform.up);
RaycastHit hit;
grounded = Physics.Raycast(ray, out hit, transform.localScale.y + 1.1f, groundedMask);
}
}
Now for the issue, this systems works fine when the planet the player is walking on is stationary. As in there are no other attracting bodys in the system and the planet has no initial velocity. However if the planet is moving the player will bounce up and down uncontrollably and will not be able to walk a certain distance away from the planets farthest point from its direction of movement. Here is a recording of this: https://youtu.be/noMekosb7CU
Does anyone know what is causing the bouncing and walking restrictions and how I can fix it?
Some notes on suggested solutions that haven't worked:
-set the planet as the players parent object, same results
-increase players mass, same results
-set the players velocity to += the planets velocity, same results or player goes into infinity
For me it seems to be working "correctly".
Looking like your player is attracted correctly and when the planet moves, your player is quickly moving towards the planet.
I think you could temporarily assign the player as a child gameobject to the planet he's walking on and he should probably move correctly along the planet coordinates and not on global coordinates. (If it works, you could just always assign the player as a child gameObject to every new planet that he visits)

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.

How to let camera's vertical rotation move the camera up and down?

I have a player in the game, which I can move using the keyboard and rotate only on the horizontal axis using the mouse. That means, I can aim only horizontally and I can not aim it up and down.
I have the Main Camera and another VM Camera from Cinemachine. The current state of the game is like this:
On the horizontal axis, I rotate the player, but on the vertical axis I only want the player's camera/FOV to be moved up and down.
My movement script attached to the player is:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class PlayerMovement : MonoBehaviour
{
public CharacterController characterController;
public float speed = 35f;
public Animator animator;
// camera and rotation
public Transform cameraHolder;
public float mouseSensitivity = 2f;
public float upLimit = 50;
public float downLimit = -50;
// gravity
private float gravity = 9.87f;
private float verticalSpeed = 0;
void Update()
{
Move();
Rotate();
}
public void Rotate()
{
float horizontalRotation = Input.GetAxis("Mouse X");
float verticalRotation = Input.GetAxis("Mouse Y");
transform.Rotate(0, horizontalRotation * mouseSensitivity, 0);
cameraHolder.Rotate(-verticalRotation * mouseSensitivity, 0, 0);
Vector3 currentRotation = cameraHolder.localEulerAngles;
if (currentRotation.x > 180) currentRotation.x -= 360;
currentRotation.x = Mathf.Clamp(currentRotation.x, upLimit, downLimit);
cameraHolder.localRotation = Quaternion.Euler(currentRotation);
}
private void Move()
{
float horizontalMove = Input.GetAxis("Horizontal");
float verticalMove = Input.GetAxis("Vertical");
if (characterController.isGrounded) verticalSpeed = 0;
else verticalSpeed -= gravity * Time.deltaTime;
Vector3 gravityMove = new Vector3(0, verticalSpeed, 0);
Vector3 move = transform.forward * verticalMove + transform.right * horizontalMove;
characterController.Move(speed * Time.deltaTime * move + gravityMove * Time.deltaTime);
}
}
This is the code i use, it works for me, it's super easy to implement, and stops moving the camera when you aren't focusing the game, is the script from one of Brackey's tutorials modified for this purpose:
using UnityEngine;
public class CameraController : MonoBehaviour
{
public PlayerController player;
public float sensitivity = 150f;
public float clampAngle = 85f;
public bool look = true;
private float verticalRotation;
private float horizontalRotation;
private void Start()
{
verticalRotation = transform.localEulerAngles.x;
horizontalRotation = player.transform.eulerAngles.y;
// Defines the state of the cursor
Cursor.lockState = CursorLockMode.Locked;
Cursor.visible = false;
}
private void Update()
{
// Looks around if the user is in the window
if (look)
{
Look();
}
Debug.DrawRay(transform.position, transform.forward * 2, Color.red);
// If the player presses ESC while in the game, it unlocks the cursor
if (look && Input.GetKeyDown(KeyCode.Escape))
{
look = false;
Cursor.lockState = CursorLockMode.None;
Cursor.visible = true;
}
else if (Input.GetMouseButtonDown(0) && !look)
{
look = true;
Cursor.lockState = CursorLockMode.Locked;
Cursor.visible = false;
}
}
private void Look()
{
float _mouseVertical = -Input.GetAxis("Mouse Y");
float _mouseHorizontal = Input.GetAxis("Mouse X");
verticalRotation += _mouseVertical * sensitivity * Time.deltaTime;
horizontalRotation += _mouseHorizontal * sensitivity * Time.deltaTime;
verticalRotation = Mathf.Clamp(verticalRotation, -clampAngle, clampAngle);
transform.localRotation = Quaternion.Euler(verticalRotation, 0f, 0f);
player.transform.rotation = Quaternion.Euler(0f, horizontalRotation, 0f);
}
}

How can I add a jump to the player controller?

I want to be able to jump by pressing on space key inside a place like home or space station and also outside.
This is the player controller script attached to a capsule :
It was working fine until I added the jump part. At the top I added this 3 lines :
public float jumpSpeed = 8.0F;
public float gravity = 20.0F;
private Vector3 moveDirection = Vector3.zero;
Then in the Update added :
if (controller.isGrounded && Input.GetButton("Jump"))
{
moveDirection.y = jumpSpeed;
}
moveDirection.y -= gravity * Time.deltaTime;
controller.Move(moveDirection * Time.deltaTime);
But I don't have the variable controller.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class PlayerController : MonoBehaviour
{
public float speed = 10.0F;
public float jumpSpeed = 8.0F;
public float gravity = 20.0F;
private Vector3 moveDirection = Vector3.zero;
// Start is called before the first frame update
void Start()
{
Cursor.lockState = CursorLockMode.Locked;
}
// Update is called once per frame
void Update()
{
float translation = Input.GetAxis("Vertical") * speed;
float straffe = Input.GetAxis("Horizontal") * speed;
translation *= Time.deltaTime;
straffe *= Time.deltaTime;
transform.Translate(straffe, 0, translation);
if (controller.isGrounded && Input.GetButton("Jump"))
{
moveDirection.y = jumpSpeed;
}
moveDirection.y -= gravity * Time.deltaTime;
controller.Move(moveDirection * Time.deltaTime);
if (Input.GetKeyDown("escape"))
{
Cursor.lockState = CursorLockMode.Locked;
}
}
}
And this is the cam mouse look script attached to the main camera that is child of the capsule :
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class CamMouseLook : MonoBehaviour
{
public float sensitivity = 5.0f;
public float smoothing = 2.0f;
Vector2 mouseLook;
Vector2 smoothV;
GameObject character;
// Start is called before the first frame update
void Start()
{
character = this.transform.parent.gameObject;
}
// Update is called once per frame
void Update()
{
var md = new Vector2(Input.GetAxisRaw("Mouse X"), Input.GetAxisRaw("Mouse Y"));
md = Vector2.Scale(md, new Vector2(sensitivity * smoothing, sensitivity * smoothing));
smoothV.x = Mathf.Lerp(smoothV.x, md.x, 1f / smoothing);
smoothV.y = Mathf.Lerp(smoothV.y, md.y, 1f / smoothing);
mouseLook += smoothV;
mouseLook.y = Mathf.Clamp(mouseLook.y, -90f, 90f);
transform.localRotation = Quaternion.AngleAxis(-mouseLook.y, Vector3.right);
character.transform.localRotation = Quaternion.AngleAxis(mouseLook.x, character.transform.up);
}
}
Have you thought about applying a force?
public Rigidbody myphysic;
public float forceofjump = 100f; // or minor or more....
void Awake()
{
myphysic= transform.GetComponent<Rigidbody>();
}
void FixedUpdate()
{
if (controller.isGrounded && Input.GetButton("Jump"))
{
myphysic.AddForce( transform.up * forceofjump, ForceMode.Impulse);
}
}
Vector3.zero is wrong beacouse set all movement vector to zero! so = no actions!
or, exemple:
Vector3 moveDirection = new Vector3(0, 10, 0);
what does it mean:
new Vector3(X=stop, Y=10 of force, Z=stop);

Categories