The bottom part of the player capsule collider is going inside the ground collider. I am using a raycast for detecting whether the plauer is grounded; an object (sphere) that is present on the bottom bound of the capsule collider is responsible for firing the raycast.
I think when the bottom part of the collider (including) the sphere is going inside the ground, my isGrounded
variable is getting set to false, and the player character is transitioning from the landing to falling animation instead of running, which is not the desired behavior of course. I don't know why this is happening. I have been trying to find the solution for 2 days now and I can't seem to find the solution or reason behind the problem
I am updating the size and center of the collider at runtime during different animations like jump, slide, and land (back to default size). I can provide code if needed.
I am also moving the object responsible for the ray according to the size of the box collider using a
collider.bounds
andextents
— will provide code if needed.
More details and code about how ground detection works
These two methods in the character control script are responsible for spawning and repositioning the object that fires ray on runtime. rayEmitter is a GameObject
variable where prefab is added and Emitter is another GameObject
variable. As you can tell RayEmitter object is spawning on the collider position.
public void IntialSetup_of_RayEmitter()
{
Emitter = Instantiate(RayEmitter, Vector3.zero, Quaternion.identity);
Emitter.transform.parent = transform;
Reposition_RayEmitter();
}
public void Reposition_RayEmitter()
{
float bottom = cCollider.bounds.center.y - cCollider.bounds.extents.y;
float side = cCollider.bounds.center.x - cCollider.bounds.extents.x;
float front = cCollider.bounds.center.z + cCollider.bounds.extents.z;
Emitter.transform.localPosition = new Vector3(side + 0.3f, bottom + 0.03f, front - 0.3f) - this.transform.position;
}
these two methods are responsible for recentering and resizing the capsule collider at runtime, here I am using a bool RepostionEmitter
as it turns true I am calling the reposition method Above on FixedUpdate, initially variable starts as false as it becomes true after updating size and center call reposition.
private void UpdateCenter()
{
if (!UpdateNow)
{
return;
}
else if (Vector3.SqrMagnitude(cCollider.center - targetCenter_C) > 0.01f)
{
cCollider.center = Vector3.Lerp(cCollider.center, targetCenter_C,
Time.deltaTime * CenterUpdate_Speed_C);
RepostionEmiter = true;
}
}
private void UpdateSize()
{
if (!UpdateNow)
{
return;
}
else
{
cCollider.height = Mathf.Lerp(cCollider.height, targetHeight,
Time.deltaTime * SizeUpdate_Speed_C);
RepostionEmiter = true;
}
}
private void FixedUpdate()
{
if (StartRun)
{
if (isGrounded())
{
Grounded = true;
}
else
{
Grounded = false;
}
}
ApplyGravity();
RepostionEmiter = false;
UpdateCenter();
UpdateSize();
if (RepostionEmiter)
{
Reposition_RayEmitter();
}
}
This bool method just a fire ray and detects the Grounded variable, I was also checking grounded or not using Rigidbody
velocity but I had to remove it due to Dashing mechanisms during the dash especially if performed after the jump or double jump means in the air I don't want any different kind of velocity like gravity, etc to affect the dashing and it's forward force character just need to move forward during dash i am using AddForce
for that, so you can imagine when I was doing that grounded variable was becoming true as i was messing with velocity during dash and due to that player was able to perform countless dashes in air and some other problems too.
public bool isGrounded()
{
//if (Mathf.Abs(RIGIDBODY.velocity.y) <= 0.01f)
//{
// return true;
//}
RaycastHit hit;
if (Physics.Raycast(Emitter.transform.position, Vector3.down, out hit, rayDistance))
{
if (hit.collider.CompareTag("Ground"))
{
//Debug.Log(hit.collider.gameObject.tag);
return true;
}
}
return false;
}
last thing and I think I should mention just in case, I am using something called a Statemachine pattern
to control the logic/behavior of the character For example; this scriptable object containing code is present on all the animation state that needs to check if player is grounded or not.
using UnityEngine;
namespace EndlessRunning
{
[CreateAssetMenu(fileName = "New Object", menuName = "ScriptableObject/Ability/GroundDector")]
public class GroundDector : ScriptableObjectData
{
public override void OnEnter(PlayerStateBase playerStateBase, Animator animator, AnimatorStateInfo stateInfo)
{
}
public override void OnUpdate(PlayerStateBase playerStateBase, Animator animator, AnimatorStateInfo stateInfo)
{
if (playerStateBase.characterControl.Grounded)
{
animator.SetBool(HashManger.Instance.DicMainParameters[AnimatorParameters.Grounded], true);
}
else
{
animator.SetBool(HashManger.Instance.DicMainParameters[AnimatorParameters.Grounded], false);
}
}
public override void OnExit(PlayerStateBase playerStateBase, Animator animator, AnimatorStateInfo stateInfo)
{
}
}
}
That is how different player movements are being handled Overall my scene does not have anything related to that just objects that are responsible for cameraControl
, and Spawning character
according to the selected character from character select scene These systems are completely separate, and decoupled from movement and has nothing to do with it.
Character Inspector