Wasn’t quite sure where to post this question, figured this was close enough for a place to post, apologies if it isn’t.
Here’s some context before the question. I’m doing the Tilevania course. So after doing most of the lectures (some followed along, others did the challenges for) I felt good about myself and decided to make a second enemy that chases the player. Initial idea was that the enemy would have two states -
- state one: patrolling
- state two: chasing the player
If the enemy sees the player they’d enter an aggro state and run after them. If they’re out of their area of detection (Checked by a raycast), they’d keep running towards the player until either A - player is in their detection zone again and the enemy continues to chase or B - player has not been in their detection zone for a period of time. If B happens, the enemy should return to their patrol state.
A little bit of tinkering with current knowledge gained from here, watching/following a video or two on the subject there, and a lot of reading the API and even more trial and error attempts later… I almost did it.
If the enemy is placed in an area surrounded by walls, the code works pretty well!
hell-doggo bumps into the wall for a few and goes to his regular patrol route when he gets bored of headbanging. I’m okay with the current result
What is NOT working however, is when you try the same thing with a platform that has no walls surrounding it, well… This happens:
Hell-doggo has the urge to defy the laws of gravity, and also the laws of my spaghetti code.
Question is - what is going on, where does the code hiccup and how can I fix it? I’ll send the code, which is… I hope not too painfully long and spaghetti-ish for such a script. My best guess so far has been that the OnTriggerExit2D
method hasn’t been working right, but even after hours of testing and attempts of getting it to work I am still not sure how to fix it, neither am I sure if that really is the problem here.
Help would be greatly appreciated, and feel free to give me an earful about how weird the code is.
public class HellhoundMovement : MonoBehaviour
{
[Header("Components Outside GameObj")] //...mostly outside the gameObj
[SerializeField] CompositeCollider2D terrainCol;
[SerializeField] Transform player;
[SerializeField] Transform castPoint;
[Header("Agro Settings")]
[SerializeField] float aggroRange;
[SerializeField] float defaultAgroTime = 3f;
[SerializeField] float aggroTimeLeft;
[Header("Scripts and Components")]
Rigidbody2D enemyRB;
Animator enemyAnim;
[SerializeField] BoxCollider2D[] enemyWallDetectors;
EnemyUtil enemyUtil;
[Header("Movement Speed Settings")]
[SerializeField] public float defaultMoveSpeed;
[SerializeField] float currentMoveSpeed;
[SerializeField] float speedModif = 2;
[Header("gameObj checks")]
[SerializeField] public bool isFacingLeft = false;
[SerializeField] public bool enemyIsAlive = true;
[SerializeField] bool isAgro = false;
[SerializeField] bool isSearching = false;
private void Start()
{
enemyRB = GetComponent<Rigidbody2D>();
enemyAnim = GetComponent<Animator>();
enemyWallDetectors = GetComponents<BoxCollider2D>();
enemyUtil = GetComponent<EnemyUtil>();
currentMoveSpeed = defaultMoveSpeed;
aggroTimeLeft = defaultAgroTime;
enemyRB.constraints = RigidbodyConstraints2D.FreezeRotation | RigidbodyConstraints2D.FreezePositionY;
}
void Update()
{
if (CanSeePlayer(aggroRange))
{
ChasePlayer();
isAgro = true;
}
else if (isAgro)
{
isSearching = true;
if (isSearching)
{
StartAgroTimer();
}
}
else
{
StartPatrolling();
}
}
private void ChasePlayer()
{
enemyAnim.speed = 2f;
if (!enemyIsAlive)
{
enemyRB.constraints = RigidbodyConstraints2D.FreezePositionX;
return;
}
else if (transform.position.x < player.position.x)
{
enemyRB.velocity = new Vector2((currentMoveSpeed * speedModif), 0);
enemyUtil.FlipSpriteToPos();
isFacingLeft = false;
}
else
{
enemyRB.velocity = new Vector2(-(currentMoveSpeed * speedModif), 0);
enemyUtil.FlipSpriteToPos();
isFacingLeft = true;
}
}
private void StartPatrolling()
{
isAgro = false;
isSearching = false;
enemyAnim.speed = 1f;
if (!enemyIsAlive)
{
enemyRB.constraints = RigidbodyConstraints2D.FreezePositionX;
return;
}
else
{
enemyRB.constraints = RigidbodyConstraints2D.FreezePositionY | RigidbodyConstraints2D.FreezeRotation;
enemyRB.velocity = new Vector2((defaultMoveSpeed), 0);
enemyUtil.FlipSpriteToPos();
if (enemyRB.velocity.x > 0)
{
isFacingLeft = false;
}
else
{
isFacingLeft = true;
}
}
}
private void OnTriggerEnter2D(Collider2D collision)
{
if (enemyWallDetectors[1].IsTouchingLayers(LayerMask.GetMask("StaticTerrain")))
{
Debug.Log(collision);
CheckSpriteAfterCollision(collision);
}
}
private void OnTriggerStay2D(Collider2D collision)
{
if (enemyWallDetectors[1].IsTouchingLayers(LayerMask.GetMask("StaticTerrain")))
{
CheckSpriteAfterCollision(collision);
}
}
private void OnTriggerExit2D(Collider2D collision)
{
if (!enemyWallDetectors[0].IsTouchingLayers(LayerMask.GetMask("StaticTerrain", "OneWayPlatform")))
{
CheckSpriteAfterCollision(collision);
Debug.Log("Triggered with: " + collision.name);
}
}
private void CheckSpriteAfterCollision(Collider2D collision) //not sure how to call this method TBH. Suggestions are welcome
{
defaultMoveSpeed = -defaultMoveSpeed;
if (defaultMoveSpeed < 0)
{
isFacingLeft = true;
enemyUtil.FlipSprite();
}
if (defaultMoveSpeed > 0)
{
isFacingLeft = false;
enemyUtil.FlipSprite();
}
else { return; }
}
void StartAgroTimer()
{
if (aggroTimeLeft == defaultAgroTime || aggroTimeLeft > 0)
{
aggroTimeLeft -= Time.deltaTime;
Physics2D.IgnoreCollision(enemyWallDetectors[0], terrainCol);
}
if (aggroTimeLeft <= 0)
{
Physics2D.IgnoreCollision(enemyWallDetectors[0], terrainCol, false);
StartPatrolling();
}
}
bool CanSeePlayer(float distance)
{
bool val = false;
float castDist = distance;
if (isFacingLeft)
{
castDist = -distance;
}
else
{
castDist = distance;
}
Vector2 endPos = castPoint.position + Vector3.right * castDist;
RaycastHit2D hit = Physics2D.Linecast(transform.position, endPos, 1 << LayerMask.NameToLayer("Player") |
1 << LayerMask.NameToLayer("StaticTerrain"));
if (hit.collider != null)
{
if (hit.collider.gameObject.CompareTag("Player"))
{
val = true;
aggroTimeLeft = defaultAgroTime;
}
else
{
val = false;
}
Debug.DrawLine(castPoint.position, hit.point, Color.red);
}
else
{
Debug.DrawLine(castPoint.position, endPos, Color.blue);
}
if(isAgro)
{
ChasePlayer();
}
return val;
}
}