Raycasting Unity/ gravity Error - c#

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

Related

How to Rotate 2D Sprite Towards Moving Direction?

I have been searching all day and reading forums but I can't find a way that works. I'm making a top-down view horror game. When my player walks normally he can look around with the cursor in any direction, but when he wants to run he switches to "tank" controls and rotates toward the running direction. I need something like this.
My player movement script so far:
public float walkSpeed;
public float runSpeed;
public float turnSpeed;
private Rigidbody2D rb;
public Camera cam;
private Vector2 moveDirection;
void Start()
{
rb = GetComponent<Rigidbody2D>();
}
void Update()
{
ProcessInput();
}
void FixedUpdate()
{
Move();
}
private void ProcessInput()
{
float moveX = Input.GetAxisRaw("Horizontal");
float moveY = Input.GetAxisRaw("Vertical");
moveDirection = new Vector2(moveX, moveY).normalized;
}
void Move()
{
if (Input.GetKey(KeyCode.LeftShift))
{
//Looking toward movement direction should be applied here
} else {
rb.velocity = new Vector2(moveDirection.x * walkSpeed, moveDirection.y * walkSpeed);
Vector3 mousePosition = cam.ScreenToWorldPoint(Input.mousePosition);
Vector2 direction = mousePosition - transform.position;
float angle = Vector2.SignedAngle(Vector2.right, direction) - 90f;
Vector3 targetRotation = new Vector3(0, 0, angle);
transform.rotation = Quaternion.RotateTowards(transform.rotation, Quaternion.Euler(targetRotation), turnSpeed * Time.deltaTime);
}
}
Any help is greatly appreciated! Thanks in advance!
This problem can be solved by substituting moveDirection instead of the mouse look direction, here it is enough to define a hypothetical variable called direction to detect the correct direction in each of these conditions.
var direction = new Vector2();
var currentSpeed = walkSpeed;
if (Input.GetKey(KeyCode.LeftShift))
{
direction = moveDirection;
currentSpeed = runSpeed;
} else {
direction = cam.ScreenToWorldPoint(Input.mousePosition) - transform.position;
}
var angle = Vector2.SignedAngle(Vector2.right, direction) - 90f;
var targetRotation = new Vector3(0, 0, angle);
var lookTo = Quaternion.Euler(targetRotation);
rb.velocity = new Vector2(moveDirection.x, moveDirection.y) * runSpeed *Time.deltaTime;
transform.rotation = Quaternion.RotateTowards(transform.rotation, lookTo , turnSpeed * Time.deltaTime);

Rigidbody addForce on moving and standing player is different

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

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

3d Unity Player Object moves in the same direction

I have a problem where the player moves in the direction but the animation of the charcter stays the same. So if i click "w" key the player runs forward the animation works. But when i click "s" for backwards the character does not rotate around to the direction it just moves/slides back with the character facing forward and no animation. Please help!!
public class Player : MonoBehaviour {
private Animator anim;
private CharacterController controller;
public float speed = 600.0f;
public float turnSpeed = 400.0f;
private Vector3 moveDirection = Vector3.zero;
public float gravity = 20.0f;
private Vector3 curLoc;
void Start () {
controller = GetComponent <CharacterController>();
anim = gameObject.GetComponentInChildren<Animator>();
}
void Update (){
if (Input.GetKey ("w")) {
anim.SetInteger ("AnimationPar", 1);
} else {
anim.SetInteger ("AnimationPar", 0);
}
if (Input.GetKey("s"))
{
anim.SetInteger("Condition", 1);
}
else
{
anim.SetInteger("Condition", 0);
}
if (controller.isGrounded){
moveDirection = transform.forward * Input.GetAxis("Vertical") * speed;
}
float turn = Input.GetAxis("Horizontal");
transform.Rotate(0, turn * turnSpeed * Time.deltaTime, 0);
controller.Move(moveDirection * Time.deltaTime);
moveDirection.y -= gravity * Time.deltaTime;
if (Input.GetKey(KeyCode.S))
{
}
}
}
You need to show Animator, how animations are playing and transitioning. Just open animator when game is playing and check if animation is playing where "Condition" parameter is passes. Check the animation transitions too. Also, I'd suggest you to change value "AnimationPar" to 0 when "s" is pressed.
if (Input.GetKey("s"))
{
anim.SetInteger ("AnimationPar", 0);
anim.SetInteger("Condition", 1);
}

How can I move the object back to it's start position if the raycast is not hitting?

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class OnMouseOverEvent : MonoBehaviour
{
public float duration;
public string tag;
public Vector3 startPos;
public Vector3 endPos;
public float distancetoMove = 1f;
public float lerpTime = 5;
private float currentLerpTime = 0;
private void Start()
{
startPos = transform.position;
endPos = transform.position - Vector3.forward * distancetoMove;
}
private void Update()
{
Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition);
RaycastHit hit;
if (Physics.Raycast(ray, out hit, 100))
{
if (hit.transform.tag == tag)
{
currentLerpTime += Time.deltaTime;
if(currentLerpTime >= lerpTime)
{
currentLerpTime = lerpTime;
}
float Perc = currentLerpTime / lerpTime;
transform.position = Vector3.Lerp(startPos, endPos, Perc);
}
else
{
transform.position = Vector3.Lerp(endPos, startPos, Perc);
}
}
}
}
If it's hitting the object is moving smooth slowly forward but I want that while the raycast keep hitting the object it will move forward and if got to the distance keep it stay there as long as the raycast hitting it.
But once moving the mouse and the raycast is not hitting the object either in the middle of the movement or when it got to the distance move the object back to it's start position.
So when moving the mouse out of the object area it will start moving either forward to endPos or backward to startPos.
Another way to do it: (Without Raycast)
using UnityEngine;
public class Move : MonoBehaviour
{
public float speed = 5f;
public float distancetoMove = 1f;
public bool goForward;
public Vector3 startPos;
public Vector3 endPos;
private void Start()
{
startPos = transform.position;
endPos = transform.position - Vector3.forward * distancetoMove;
}
void Update()
{
if (goForward)
{
transform.position = Vector3.MoveTowards(transform.position, endPos, speed * Time.deltaTime);
}
else
{
transform.position = Vector3.MoveTowards(transform.position, startPos, speed * Time.deltaTime);
}
}
private void OnMouseOver()
{
goForward = true;
}
private void OnMouseExit()
{
goForward = false;
}
}
you have to declare Perc outsdide if if you want to use ot inside else
to solve your problem
change:
inside if (hit.transform.tag == tag)
transform.position = Vector3.Lerp(start, endPos, Perc);
to this:
transform.position = Vector3.Lerp(transform.position, endPos, Perc);
and inside else
else{
transform.position = Vector3.Lerp(endPos, startPos, Perc);
}
to this:
else{
transform.position = Vector3.Lerp(transform.position, startPos, Perc);
}
your code will become like this:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class OnMouseOverEvent : MonoBehaviour
{
public float duration;
public string tag;
public Vector3 startPos;
public Vector3 endPos;
public float distancetoMove = 1f;
public float lerpTime = 5;
float Perc;
private float currentLerpTime = 0;
private void Start()
{
startPos = transform.position;
endPos = transform.position - Vector3.forward * distancetoMove;
}
private void Update()
{
Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition);
RaycastHit hit;
if (Physics.Raycast(ray, out hit, 100))
{
if (hit.transform.tag == tag)
{
currentLerpTime += Time.deltaTime;
if (currentLerpTime >= lerpTime)
{
currentLerpTime = lerpTime;
}
Perc = currentLerpTime / lerpTime;
transform.position = Vector3.Lerp(transform.position, endPos, Perc);
}
else
{
transform.position = Vector3.Lerp(transform.position, startPos, Perc);
}
}
}
}
Interpolant (Perc) not updating properly
Your object currently moves in the update by a Lerp, which the interpolant is Perc.
The problem is that your code for updating your Perc by lerp timer is only done when the raycast hits this object.
You can update your Perc by lerp-timer before/after you done the movement process, like so:
private float Perc;
private void Update()
{
GetPercByLerpTime(); // Update before it moves
Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition);
RaycastHit hit;
if (Physics.Raycast(ray, out hit, 100))
{
if (hit.transform.tag == tag)
{
transform.position = Vector3.Lerp(startPos, endPos, Perc);
}
else
{
transform.position = Vector3.Lerp(endPos, startPos, Perc);
}
}
// Or you can update your 'Perc' here.
}
private void UpdatePercByLerpTime(){
currentLerpTime += Time.deltaTime;
if(currentLerpTime >= lerpTime)
{
currentLerpTime = lerpTime;
}
Perc = currentLerpTime / lerpTime;
}
Note: You might notice that currentLerpTime will keep incrementing until it reaches lerpTime, and it will stay as the same value as lerpTime.
You might want to implement something that either decreases or resets currentLerpTime.
No movement occurs if raycast hits nothing.
Your currently code only moves the object if the raycast hits something. (Though it does move in the correct direction depending if it hit this object)
What you probably want is to move the object back to the start position if the raycast hit nothing or the raycast hit something else.
So your final result would look like this:
private float Perc;
private void Update() {
UpdatePercByLerpTime();
if (MouseRaycastHitThisObject()) {
transform.position = Vector3.Lerp(startPos, endPos, Perc);
} else {
transform.position = Vector3.Lerp(endPos, startPos, Perc);
}
// Or you can update your 'Perc' here.
}
// True if the raycastfrom the mouse position hits this object
private bool MouseRaycastHitThisObject(){
Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition);
RaycastHit hit;
if (Physics.Raycast(ray, out hit, 100)) {
return hit.transform.tag == tag;
}
// Raycast did not hit anything; Player's mouse is not hovering over anything.
return false;
}
private void UpdatePercByLerpTime(){
currentLerpTime += Time.deltaTime;
if(currentLerpTime >= lerpTime) {
currentLerpTime = lerpTime;
}
Perc = currentLerpTime / lerpTime;
}

Categories