Rigidbody addForce on moving and standing player is different - c#

I'm trying to make my player dash at where he's looking. The code below works but it gives 2 different result.
If the player dash and move at the same time, the player gets pushed a few units.
If the player dash only, the player gets pushed 4/5 times the one above.
How do I get result 1 for both of them whether they are moving or standing still.
private void Start() {
rb = GetComponent<Rigidbody>();
}
private void Update()
{
horizontalInput = Input.GetAxisRaw("Horizontal");
verticalInput = Input.GetAxisRaw("Vertical");
direction = new Vector3(horizontalInput, 0f, verticalInput).normalized;
if(Input.GetKeyDown(KeyCode.Space)){
isDashing = true;
}
}
private void FixedUpdate() {
Move();
if(isDashing){
Dash();
}
}
void Move()
{
if(direction.magnitude >= 0.1f)
{
float targetAngle = Mathf.Atan2(direction.x, direction.z) * Mathf.Rad2Deg + camera.eulerAngles.y;
float angle = Mathf.SmoothDampAngle(transform.eulerAngles.y, targetAngle, ref turnSmoothVelocity, 0.1f);
transform.rotation = Quaternion.Euler(0f, angle, 0f);
Vector3 moveDirection = Quaternion.Euler(0f, targetAngle, 0f) * Vector3.forward;
moveDirection.y = rb.velocity.y;
rb.velocity = moveDirection * currentSpeed * Time.deltaTime;
}
}
void Dash()
{
rb.AddForce(transform.forward * dashForce, ForceMode.Impulse);
isDashing = false;
}

Related

Unity AI character controller gravity

I am trying to add gravity to my "residents" of a town builder game. Basically the residents just walk around to a random location, wait 2 seconds then do it again. I am trying to add gravity to them so they don't float.
controller = GetComponent<CharacterController>();
moveDirection = Vector3.zero;
moveDirection.y -= gravity * Time.deltaTime;
controller.Move(moveDirection * Time.deltaTime);
These four lines are the gravity lines I tried added and very weird things happen such as the resident teleporting up if it touches anything, even the ground. What can I do? I basically just want the residents not to float above the ground and follow the slopes of the land like regular walking.
Below is the whole code if its needed to help solve my problem.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Resident : MonoBehaviour
{
private Vector3 location;
private Quaternion rotation;
private int speed;
private Vector3 moveDirection = Vector3.zero;
private bool canRotate = true;
Vector3 moveVector;
CharacterController controller;
// Start is called before the first frame update
void Start()
{
transform.rotation *= Quaternion.Euler(-90, 0, 0);
controller = GetComponent<CharacterController>();
speed = 5;
SetRandomPos();
StartCoroutine(ExampleCoroutine());
}
void Update()
{
int gravity = 20;
moveDirection = Vector3.zero;
//Check if cjharacter is grounded
moveDirection.y -= gravity * Time.deltaTime;
// Move the controller
controller.Move(moveDirection * Time.deltaTime);
float step = speed * Time.deltaTime;
transform.position = Vector3.MoveTowards(transform.position, location, step);
if (canRotate)
{
transform.LookAt(location);
transform.rotation *= Quaternion.Euler(-90, 90, 0);
canRotate = false;
}
if (transform.position == location)
{
StartCoroutine(ExampleCoroutine());
}
}
void SetRandomPos()
{
location = new Vector3(Random.Range(transform.position.x - 10f, transform.position.x + 10f), transform.position.y, Random.Range(transform.position.z - 10f, transform.position.z + 10f));
canRotate = true;
}
IEnumerator ExampleCoroutine()
{
//yield on a new YieldInstruction that waits for 5 seconds.
yield return new WaitForSeconds(2);
//After we have waited 5 seconds print the time again.
if (transform.position == location)
{
SetRandomPos();
}
}
}
I am not exactly clear what you intend to do, but saw a few issues with the code and tried to fix them. The snippet is untested, I just added what I believe to be the correction that should fix your issues.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
private Vector3 location;
private Coroutine changeMovement = null;
private float speed;
private Vector3 playerVelocity = Vector3.zero;
CharacterController controller;
// Start is called before the first frame update
void Start()
{
controller = GetComponent<CharacterController>();
speed = 5;
SetRandomPos();
}
void Update()
{
bool groundedPlayer = controller.isGrounded;
if (groundedPlayer && playerVelocity.y < 0)
{
playerVelocity.y = 0f;
}
Vector3 move = (transform.position - location).normalized;
controller.Move(move * Time.deltaTime * speed);
if (move != Vector3.zero)
{
gameObject.transform.forward = move;
}
// check if we are close to our goal, then change the goal
if (changeMovement == null && Vector3.Distance(transform.position,location) < 0.1f)
{
changeMovement = StartCoroutine(ExampleCoroutine());
}
playerVelocity.y += gravityValue * Time.deltaTime;
controller.Move(playerVelocity * Time.deltaTime);
}
void SetRandomPos()
{
location = new Vector3(Random.Range(transform.position.x - 10f, transform.position.x + 10f), transform.position.y, Random.Range(transform.position.z - 10f, transform.position.z + 10f));
}
IEnumerator ExampleCoroutine()
{
//yield on a new YieldInstruction that waits for 5 seconds.
yield return new WaitForSeconds(2);
SetRandomPos();
changeMovement = null;
}
Edit: I changed the code and used the CharacterController.Move as a base.

Stop my first person character controller going through the wall in Unity using C#?

As seen in the video here: https://i.gyazo.com/ad45ef9e231fd2f9ec6d4cf76889aece.mp4
My code:
MouseLook.cs:
using UnityEngine;
using System.Collections;
public class MouseLook : MonoBehaviour
{
public enum RotationAxes { MouseXAndY = 0, MouseX = 1, MouseY = 2 }
public RotationAxes axes = RotationAxes.MouseXAndY;
public float sensitivityX = 3F;
public float sensitivityY = 3F;
public Camera playerCamera;
public float minimumX = -360F;
public float maximumX = 360F;
public float minimumY = -60F;
public float maximumY = 60F;
private float rotationX = 0F;
private float rotationY = 0F;
private Quaternion originalRotation;
void Update()
{
if (axes == RotationAxes.MouseXAndY)
{
rotationX += Input.GetAxis("Mouse X") * sensitivityX;
rotationY += Input.GetAxis("Mouse Y") * sensitivityY;
rotationX = ClampAngle(rotationX, minimumX, maximumX);
rotationY = ClampAngle(rotationY, minimumY, maximumY);
Quaternion xQuaternion = Quaternion.AngleAxis(rotationX, Vector3.up);
Quaternion yQuaternion = Quaternion.AngleAxis(rotationY, -Vector3.right);
transform.localRotation = originalRotation * xQuaternion * yQuaternion;
}
if (axes == RotationAxes.MouseX)
{
rotationX += Input.GetAxis("Mouse X") * sensitivityX;
rotationX = ClampAngle(rotationX, minimumX, maximumX);
Quaternion xQuaternion = Quaternion.AngleAxis(rotationX, Vector3.up);
transform.localRotation = originalRotation * xQuaternion;
}
if (axes == RotationAxes.MouseY || playerCamera != null)
{
rotationY += Input.GetAxis("Mouse Y") * sensitivityY;
rotationY = ClampAngle(rotationY, minimumY, maximumY);
Quaternion yQuaternion = Quaternion.AngleAxis(-rotationY, Vector3.right);
if (playerCamera != null)
{
playerCamera.transform.localRotation = originalRotation * yQuaternion;
}
else
{
transform.localRotation = originalRotation * yQuaternion;
}
}
}
void Start()
{
/*
if (gameObject.GetComponent<Rigidbody>())
{
gameObject.GetComponent<Rigidbody>().freezeRotation = true;
}
*/
originalRotation = transform.localRotation;
}
public static float ClampAngle(float angle, float min, float max)
{
if (angle < -360F)
{
angle += 360F;
}
if (angle > 360F)
{
angle -= 360F;
}
return Mathf.Clamp(angle, min, max);
}
}
FirstPersonController.cs:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UIElements;
using Cursor = UnityEngine.Cursor;
public class FirstPersonController : MonoBehaviour
{
private float speed = 5;
private float jumpPower = 4;
Rigidbody rb;
CapsuleCollider col;
public GameObject crossHair;
bool isActive;
float HorizontalInput;
float VerticalInput;
void Start()
{
Cursor.visible = false;
Cursor.lockState = CursorLockMode.Locked;
rb = GetComponent<Rigidbody>();
col = GetComponent<CapsuleCollider>();
crossHair = GameObject.FindWithTag("CrossHair");
}
void Update()
{
HorizontalInput = Input.GetAxisRaw("Horizontal");
VerticalInput = Input.GetAxisRaw("Vertical");
if (Input.GetKeyDown("escape"))
{
Cursor.lockState = CursorLockMode.None;
}
if (Input.GetButtonDown("Sprint"))
{
speed = 15;
}
if (Input.GetButtonUp("Sprint"))
{
speed = 5;
}
if (Input.GetKeyDown(KeyCode.H))
{
isActive = !isActive;
}
if (isActive)
{
crossHair.SetActive(true);
}
else
{
crossHair.SetActive(false);
}
}
void FixedUpdate()
{
Vector3 xMovement = transform.right * speed * HorizontalInput * Time.deltaTime;
Vector3 zMovement = transform.forward * speed * VerticalInput * Time.deltaTime;
rb.velocity = new Vector3(HorizontalInput, 0, VerticalInput) * speed;
if (isGrounded() && Input.GetButtonDown("Jump"))
{
rb.AddForce(Vector3.up * jumpPower, ForceMode.Impulse);
}
}
private bool isGrounded()
{
return Physics.Raycast(transform.position, Vector3.down, col.bounds.extents.y + 0.1f);
}
}
Is there anything wrong I am doing in this code, if so how do I fix it?
Entire project can be downloaded here: https://github.com/Some-T/FirstPersonController-CSharp
Project has relevant colliders and rigidbodies set up!
Someone has advised me to use shapecast, but I believe that may incorrect? I can't see how that would work as my player does not have character controller component added to it?
Overall how do I stop my first person character controller going through the wall like in the initial video specified above?
Upon further research I have discovered the following:
The answer is to use:
https://docs.unity3d.com/ScriptReference/Rigidbody.AddForce.html as a quick fix.
But definitively for flawlessness use:
https://docs.unity3d.com/ScriptReference/Rigidbody-velocity.html
As to how in C# I am not so sure, but here is a screen shot I did in bolt asset.
Currently I have movmement working with velocity but it does not work properly, not sure as to why? So overall my question now is how do I get movement working using velocity? I added a line in FirstPersonController.cs that moves the character using velocity of rb.velocity = new Vector3(HorizontalInput, 0, VerticalInput) * speed; so my only question and issue now is my player does not move in the direction the camera on my player is facing so I am not sure how to fix this specific thing overall?
In your project code is different from one that you provided here.
Try to enable rigidbody`s rotation constraint - freeze X and Z rotation, leave only Y. When you rotate your capsule collider (as it works in your project), it can "climb" on a wall.
Do the isGrounded check when you move, and lock movement, if not grounded.
Try to increase Collider.contactOffset
If above does not helps, try to use Rigidbody.velocity instead of Rigidbody.MovePosition.
You can also reduce Rigidbody.drag
In general, pretty good technique is to use NavMesh for movement - in this way, you able to explicitly lock player from movement outside of navmesh surface. But, of course, it doesn`t fit to many gameplays.
Hope, it helps.
upd
You move your player like this:
void FixedUpdate()
{
Vector3 xMovement = transform.right * speed * HorizontalInput * Time.deltaTime;
Vector3 zMovement = transform.forward * speed * VerticalInput * Time.deltaTime;
}
Note, that you not even apply it to rigidbody;
But you get your input this way:
float HorizontalInput = Input.GetAxis("Horizontal");
float VerticalInput = Input.GetAxis("Vertical");
in Update, so input just stays inside Update, and does not applied at all. You declared your variables twice, once on class top, another in Update().

Unity: player acts weirdly when I release movement input

Good day everyone!
I was trying to make my player face the direction he's walking towards, but then weird things started happening. Whenever I now let go of my input keys, the player slowly gets sucked to his local z-axis. Sometimes he does this while standing up, sometimes he does this flipped.
Any help would be highly appreciated!
Thanks in advance!
I'll provide you with my script:
public class JackMovement3D : MonoBehaviour {
public float speed = 6.0F;
public float VerticalSpeed = 10f;
public float HorizontalSpeed = 60f;
public float rotationSpeed = 5f;
public float jumpSpeed = 8.0F;
public float gravity = 20.0F;
private Vector3 moveDirection = Vector3.zero;
private Animator animator;
private void Start()
{
animator = GetComponent<Animator>();
}
void Update()
{
CharacterController controller = GetComponent<CharacterController>();
// is the controller on the ground?
if (controller.isGrounded)
{
float horizontal = Input.GetAxis("Horizontal") * HorizontalSpeed;
float vertical = Input.GetAxis("Vertical") * VerticalSpeed;
//Feed moveDirection with input.
moveDirection = new Vector3(horizontal, 0, vertical);
moveDirection = transform.TransformDirection(moveDirection);
//Multiply it by speed.
moveDirection *= speed;
}
//Applying gravity to the controller
moveDirection.y -= gravity * Time.deltaTime;
animator.SetFloat("Blend", controller.velocity.magnitude);
//Look at walking direction
Quaternion newRotation = Quaternion.LookRotation(moveDirection);
transform.rotation = Quaternion.Slerp(transform.rotation, newRotation, rotationSpeed);
// CharacterController.Move to move the player in target direction
controller.Move(moveDirection * Time.deltaTime);
}
private void LateUpdate()
{
transform.localEulerAngles = new Vector3(0, transform.localEulerAngles.y, transform.localEulerAngles.z);
}
}
Not sure if this is the issue you describe but it might be related to
Quaternion.LookRotation(moveDirection);
since if there is no Input the moveDirection is a Vector3(0,0,0) and Quaternion.LookRotation
Returns identity if forward or upwards magnitude is zero.
so you could probably avoid that resetting the rotation to idendity by adding a check like
// if movement is 0 do nothing else
if(Mathf.Approximately(moveDirection.sqrMagnitude, 0)) return;
this should be before adding the gravity.
Actually not sure again but I guess the gravity should be added after
void Update()
{
CharacterController controller = GetComponent<CharacterController>();
// is the controller on the ground?
if (controller.isGrounded)
{
float horizontal = Input.GetAxis("Horizontal") * HorizontalSpeed;
float vertical = Input.GetAxis("Vertical") * VerticalSpeed;
//Feed moveDirection with input.
moveDirection = new Vector3(horizontal, 0, vertical);
moveDirection = transform.TransformDirection(moveDirection);
//Multiply it by speed.
moveDirection *= speed;
}
animator.SetFloat("Blend", controller.velocity.magnitude);
// if movement is 0 do nothing else
if(Mathf.Approximately(moveDirection.sqrMagnitude, 0))
{
return;
}
moveDirection.y -= gravity * Time.deltaTime;
//Look at walking direction
var newRotation = Quaternion.LookRotation(moveDirection);
transform.rotation = Quaternion.Slerp(transform.rotation, newRotation, rotationSpeed);
// CharacterController.Move to move the player in target direction
controller.Move(moveDirection * Time.deltaTime);
}

3rd person controller spinning the wrong way around

i've been trying to make a third person controller and its going ok. Right unltil i startet on the movement itself.
When the Y axis reaches anything above 360 it spins all the way from 360>300..150>50>5 instead of just 360>5 and i dont know how i could make it do what i want
Below is the two scripts that im using
Camera Rotation:
public bool LockMouse;
public Transform target;
public float Sens;
[Range(0,10)]
public float Distance = 2;
public Vector2 MinMaxPitch = new Vector2(-40, 85);
public float RotSmoothTime = 0.12f;
Vector3 rotationSmoothVelocity;
Vector3 currentRot;
float pitch;
float yaw;
private void Start()
{
if(LockMouse)
Cursor.lockState = CursorLockMode.Locked;
}
void LateUpdate()
{
yaw += Input.GetAxis("Mouse X") * Sens * Time.deltaTime;
pitch -= Input.GetAxis("Mouse Y") * Sens * Time.deltaTime;
pitch = Mathf.Clamp(pitch, MinMaxPitch.x, MinMaxPitch.y);
currentRot = Vector3.SmoothDamp(currentRot, new Vector3(pitch, yaw), ref rotationSmoothVelocity, RotSmoothTime);
transform.eulerAngles = currentRot;
transform.position = target.position - transform.forward * Distance;
}
Player Movement:
public float speed;
public float SmoothSpeed;
Transform camT;
Rigidbody rb;
Vector3 rot;
private Vector3 vel = new Vector3(0, 0, 0);
// Start is called before the first frame update
void Start()
{
camT = Camera.main.transform;
rb = GetComponent<Rigidbody>();
}
// Update is called once per frame
void Update()
{
rb.velocity = (transform.right * Input.GetAxis("Horizontal") * Time.deltaTime * speed) + (transform.forward * Input.GetAxis("Vertical") * Time.deltaTime * speed) + (transform.up * rb.velocity.y);
if(new Vector2(Input.GetAxis("Horizontal"), Input.GetAxis("Vertical")).magnitude > 0)
{
rot = new Vector3(0, camT.eulerAngles.y, 0);
Debug.Log(rot);
if(rot.y > 358)
{
transform.eulerAngles = new Vector3(rot.x, 3, rot.z);
}
if (rot.y < 2)
{
transform.eulerAngles = new Vector3(rot.x, 357, rot.z);
}
}
transform.eulerAngles = Vector3.SmoothDamp(transform.eulerAngles, rot, ref vel, SmoothSpeed);
}
Vector3.Smoothdamp doesn't understand that e.g., (x,359,z) can wrap around to (x,0,z).
Use Quaternions instead of vectors and use Quaternion.RotateTowards to calculate the rotation to use between the current rotation and rot:
public float speed;
public float smoothSpeed; // naming convention for fields is camelCase
Transform camT;
Rigidbody rb;
Quaternion rot;
private Vector3 vel = new Vector3(0, 0, 0);
// Start is called before the first frame update
void Start()
{
camT = Camera.main.transform;
rb = GetComponent<Rigidbody>();
}
// Update is called once per frame
void Update()
{
rb.velocity = (transform.right * Input.GetAxis("Horizontal") * Time.deltaTime * speed) + (transform.forward * Input.GetAxis("Vertical") * Time.deltaTime * speed) + (transform.up * rb.velocity.y);
if(new Vector2(Input.GetAxis("Horizontal"), Input.GetAxis("Vertical")).magnitude > 0)
{
rot = Quaternion.Euler(0f,camT.eulerAngles.y,0f);
}
transform.rotation = Quaternion.RotateTowards(transform.rotation, rot, Time.deltaTime * smoothSpeed);
}

Raycasting Unity/ gravity Error

I'm using the Charactercontroller component for the movement of my character.
The thing I'm trying to achieve is for the character to go when I'm clicking with the mouse. For that, I'm rotating where I click and then going forward to the point where the ray collider with my terrain.
The character does move and goes to the point I want.
The problem is when walking into a ramp, for example, the character keeps walking in the air and does not go down (gravity problem as far as I can understand although when I test putting the character in the air when the editor it does go down) it stops above when it supposed to go and starts spinning really fast.
Here's my movement script
private float gravity = 1f;
Animator anim;
CharacterController charController;
private float mvtSpeed = 3f;
private float distanceToPoint;
Vector3 playerMvt;
bool canMove = false;
CollisionFlags collisionFlags;
Ray ray;
RaycastHit hit;
Vector3 PlayerTarget;
float height;
private void Awake()
{
anim = GetComponent<Animator>();
charController = GetComponent<CharacterController>();
}
void MoveThePlayer()
{
if (Input.GetMouseButton(0))
{
ray = Camera.main.ScreenPointToRay(Input.mousePosition);
if (Physics.Raycast(ray, out hit)) {
if (hit.collider is TerrainCollider)
{
if (Vector3.Distance(transform.position, hit.point) >= 0.5f)
{
canMove = true;
anim.SetFloat("Walk", 1.0f);
PlayerTarget = hit.point;
}
}//terrain collider
}//raycast
}//mouse Down
if (canMove)
{
Vector3 targetTemp = new Vector3(PlayerTarget.x, transform.position.y, PlayerTarget.z);
transform.rotation = Quaternion.Slerp(transform.rotation, Quaternion.LookRotation(targetTemp - transform.position), 15.0f * Time.deltaTime);
playerMvt = transform.forward * mvtSpeed * Time.deltaTime;
if (Vector3.Distance(transform.position, PlayerTarget) <= 0.1f)
{
anim.SetFloat("Walk", 0f);
playerMvt.Set(0f, 0f, 0f);
}
}//canMove
}
private void Update()
{
MoveThePlayer();
charController.Move(playerMvt);
if (!charController.isGrounded)
{
playerMvt.y -= gravity * Time.deltaTime;
}
}
A few things.
canMove is never reset to false, it should probably reset in here
if (Vector3.Distance(transform.position, PlayerTarget) <= 0.1f)
{
anim.SetFloat("Walk", 0f);
playerMvt.Set(0f, 0f, 0f);
}
This code has no impact where its located right now:
if (!charController.isGrounded)
{
playerMvt.y -= gravity * Time.deltaTime;
}
You apply gravity to playerMvt.y after you have already moved the player, and the next frame you will re-assign playerMvt here:
playerMvt = transform.forward * mvtSpeed * Time.deltaTime;
So change it up to this
private void Update()
{
MoveThePlayer();
if (!charController.isGrounded)
{
playerMvt.y -= gravity * Time.deltaTime;
}
charController.Move(playerMvt);
}

Categories