Get 10% off orders above 50% with SEPT2025.
Forum begins after the advertisement:
[Part 6] Zombie won’t chase player
Home › Forums › Video Game Tutorial Series › Creating a Metroidvania in Unity › [Part 6] Zombie won’t chase player
- This topic has 9 replies, 3 voices, and was last updated 25 minutes ago by
Tyler Fruge.
-
AuthorPosts
-
September 15, 2025 at 10:27 pm #18836::
my zombie keeps flipping but not chasing me.
Video: https://imgur.com/a/ejXEA1I
code:
using System.Collections; using System.Collections.Generic; using UnityEngine; public class Cadaver : Enemy { [SerializeField] private float flipWaitTime; [SerializeField] private float ledgeCheckX; [SerializeField] private float ledgeCheckY; [SerializeField] LayerMask whatIsGround; float timer; // Start is called before the first frame update protected override void Start() { base.Start(); rb.gravityScale = 12f; } protected override void UpdateEnemyStates() { switch (currentEnemyState) { case EnemyStates.Cadaver_Idle: Vector3 _ledgeCheckStart = transform.localScale.x > 0 ? new Vector3(ledgeCheckX, 0) : new Vector3(-ledgeCheckX, 0); Vector2 _wallCheckDir = transform.localScale.x > 0 ? transform.right : -transform.right; if (!Physics2D.Raycast(transform.position + _ledgeCheckStart, Vector2.down, ledgeCheckY, whatIsGround) || Physics2D.Raycast(transform.position, _wallCheckDir, ledgeCheckX, whatIsGround)) { ChangeState(EnemyStates.Cadaver_Flip); } if (transform.localScale.x > 0) { rb.velocity = new Vector2(speed, rb.velocity.y); } else { rb.velocity = new Vector2(-speed, rb.velocity.y); } break; case EnemyStates.Cadaver_Flip: timer += Time.deltaTime; if(timer > flipWaitTime) { timer = 0; transform.localScale = new Vector2(transform.localScale.x * -1, transform.localScale.y); ChangeState(EnemyStates.Cadaver_Idle); } break; } } }
September 16, 2025 at 3:45 pm #18837::@Alp can you see anything in the code here that is causing the enemy to flip but not move / chase the player?
using System.Collections; using System.Collections.Generic; using UnityEngine; public class Cadaver : Enemy { [SerializeField] private float flipWaitTime; [SerializeField] private float ledgeCheckX; [SerializeField] private float ledgeCheckY; [SerializeField] LayerMask whatIsGround; float timer; // Start is called before the first frame update protected override void Start() { base.Start(); rb.gravityScale = 12f; } protected override void UpdateEnemyStates() { switch (currentEnemyState) { case EnemyStates.Cadaver_Idle: Vector3 _ledgeCheckStart = transform.localScale.x > 0 ? new Vector3(ledgeCheckX, 0) : new Vector3(-ledgeCheckX, 0); Vector2 _wallCheckDir = transform.localScale.x > 0 ? transform.right : -transform.right; if (!Physics2D.Raycast(transform.position + _ledgeCheckStart, Vector2.down, ledgeCheckY, whatIsGround) || Physics2D.Raycast(transform.position, _wallCheckDir, ledgeCheckX, whatIsGround)) { ChangeState(EnemyStates.Cadaver_Flip); } if (transform.localScale.x > 0) { rb.velocity = new Vector2(speed, rb.velocity.y); } else { rb.velocity = new Vector2(-speed, rb.velocity.y); } break; case EnemyStates.Cadaver_Flip: timer += Time.deltaTime; if(timer > flipWaitTime) { timer = 0; transform.localScale = new Vector2(transform.localScale.x * -1, transform.localScale.y); ChangeState(EnemyStates.Cadaver_Idle); } break; } } }
September 16, 2025 at 3:55 pm #18838::It looks like your enemy, the
Cadaver
, is currently set up to flip but may not be moving towards the player effectively. Here are a few points to consider that could help you resolve the issue:-
Movement Logic: In the
Cadaver_Idle
state, the enemy is applying a velocity based on its scale (direction). However, if the enemy is not continuously checking for the player’s position and changing its state to actively chase the player, it will remain idle after flipping. You might want to add a condition to check if the player is within a certain range and switch to a chasing state. -
Chasing Logic: Ensure that you have a state for chasing the player (e.g.,
EnemyStates.Cadaver_Chase
) and implement the logic to move towards the player’s position. You might need to add a new case in the switch statement for chasing. -
Speed Variable: Make sure that the
speed
variable is defined and assigned a value. Ifspeed
is not initialized or set to zero, that would explain why the enemy appears to flip but not move. -
Raycasting Logic: The raycasting logic checks for walls and ledges. Make sure that the conditions under which the enemy flips are appropriate. If the enemy is constantly flipping due to hitting walls or ledges, it may not get a chance to move towards the player.
-
Player Detection: Implement a method to detect the player (e.g., using a collider trigger or distance checks). If the player is within a certain range, you could switch to the chasing state.
Here’s a basic example of what you can add:
case EnemyStates.Cadaver_Chase: Vector2 direction = (player.position - transform.position).normalized; // Assuming you have a reference to the player rb.velocity = new Vector2(direction.x * speed, rb.velocity.y); if (Mathf.Abs(direction.x) < 0.1f) // Close enough to stop chasing { ChangeState(EnemyStates.Cadaver_Idle); } break;
You will need to make sure that you have a reference to the player's position (for example, through a public
Transform player
variable) and adjust the states accordingly.By implementing these suggestions, you should be able to get the
Cadaver
to chase the player after it flips.September 16, 2025 at 4:21 pm #18842::@tylerfruge can you share your Enemy code as well, and a recording of your zombie’s Rigidbody component when the game is active. Specifically, make sure to open up the Info tab (all the way at the bottom in the screenshot below) when you are recording. I want to check whether the rigidbody’s velocity gets updated.
View post on imgur.com
September 16, 2025 at 9:18 pm #18843::rigid body info:
View post on imgur.com
enemy code:
using System.Collections; using System.Collections.Generic; using Unity.VisualScripting.Antlr3.Runtime.Tree; using UnityEngine; public class Enemy : MonoBehaviour { [SerializeField] protected float health; [SerializeField] protected float recoilLength; [SerializeField] protected float recoilFactor; [SerializeField] protected bool isRecoiling = false; [SerializeField] protected float speed; [SerializeField] protected float damage; protected float recoilTimer; protected Rigidbody2D rb; protected enum EnemyStates { //Cadaver Cadaver_Idle, Cadaver_Flip } protected EnemyStates currentEnemyState; // Start is called before the first frame update protected virtual void Start() { rb = GetComponent<Rigidbody2D>(); } // Update is called once per frame protected virtual void Update() { UpdateEnemyStates(); if (health <= 0) { Destroy(gameObject); } if (isRecoiling) { if (recoilTimer < recoilLength) { recoilTimer += Time.deltaTime; } else { isRecoiling = false; recoilTimer = 0; } } } public virtual void EnemyHit(float _damageDone, Vector2 _hitDirection, float _hitForce) { health -= _damageDone; if (!isRecoiling) { rb.AddForce(-_hitForce * recoilFactor * _hitDirection); isRecoiling = true; } } protected void OnCollisionStay2D(Collision2D _other) { if (_other.gameObject.CompareTag("Player") && !PlayerController.Instance.pState.invincible) { Attack(); PlayerController.Instance.HitStopTime(0, 5, 0.5f); } } protected virtual void UpdateEnemyStates() { } protected void ChangeState(EnemyStates _newState) { currentEnemyState = _newState; } protected virtual void Attack() { PlayerController.Instance.TakeDamage(damage); } }
September 17, 2025 at 3:37 pm #18844::Why is it that your
Cadaver
script does not have any settable attributes on it? Because it inheritsEnemy
, so there should be some settings like Health, Speed, etc.Can you turn on Debug Mode on your Inspector, and show me what you see on the Cadaver component when the game is playing?
How to turn on Debug Mode: https://discussions.unity.com/t/turn-on-debug-mode/242854/2
September 17, 2025 at 8:40 pm #18845::oh it DOES have health, speed and damage, i just have to click on the cadaver a few times for it to show.
do you mean like this?
View post on imgur.com
September 18, 2025 at 12:20 am #18846::Can you double check if your platform is correctly marked as ground, or whether your
_ledgeCheckStart
is calculated correctly? YourCadaver
never changes state out ofCadaver_Flip
, which is why it is unable to move, as the movement code is inCadaver_Idle
.It is likely because when your cadaver switches state to Idle, the highlighted code below immediately fires and changes the state back to
Cadaver_Flip
:protected override void UpdateEnemyStates() { switch (currentEnemyState) { case EnemyStates.Cadaver_Idle: Vector3 _ledgeCheckStart = transform.localScale.x > 0 ? new Vector3(ledgeCheckX, 0) : new Vector3(-ledgeCheckX, 0); Vector2 _wallCheckDir = transform.localScale.x > 0 ? transform.right : -transform.right; if (!Physics2D.Raycast(transform.position + _ledgeCheckStart, Vector2.down, ledgeCheckY, whatIsGround) || Physics2D.Raycast(transform.position, _wallCheckDir, ledgeCheckX, whatIsGround)) { ChangeState(EnemyStates.Cadaver_Flip); } if (transform.localScale.x > 0) { rb.velocity = new Vector2(speed, rb.velocity.y); } else { rb.velocity = new Vector2(-speed, rb.velocity.y); } break; case EnemyStates.Cadaver_Flip: timer += Time.deltaTime; if(timer > flipWaitTime) { timer = 0; transform.localScale = new Vector2(transform.localScale.x * -1, transform.localScale.y); ChangeState(EnemyStates.Cadaver_Idle); } break; } }
September 18, 2025 at 3:33 am #18847::yes, the platform is labeled as ground, and i typed the code in just like it was in the video
September 18, 2025 at 3:52 pm #18852::Add the highlighted portion here and show me what is in your Console when the enemy is flipping:
protected override void UpdateEnemyStates() { switch (currentEnemyState) { case EnemyStates.Cadaver_Idle: Vector3 _ledgeCheckStart = transform.localScale.x > 0 ? new Vector3(ledgeCheckX, 0) : new Vector3(-ledgeCheckX, 0); Vector2 _wallCheckDir = transform.localScale.x > 0 ? transform.right : -transform.right; if (!Physics2D.Raycast(transform.position + _ledgeCheckStart, Vector2.down, ledgeCheckY, whatIsGround) || Physics2D.Raycast(transform.position, _wallCheckDir, ledgeCheckX, whatIsGround)) { print("Detected a ledge."); ChangeState(EnemyStates.Cadaver_Flip); } if (transform.localScale.x > 0) { rb.velocity = new Vector2(speed, rb.velocity.y); } else { rb.velocity = new Vector2(-speed, rb.velocity.y); } break; case EnemyStates.Cadaver_Flip: timer += Time.deltaTime; if(timer > flipWaitTime) { timer = 0; transform.localScale = new Vector2(transform.localScale.x * -1, transform.localScale.y); ChangeState(EnemyStates.Cadaver_Idle); } break; } }
September 18, 2025 at 9:34 pm #18853 -
-
AuthorPosts
- You must be logged in to reply to this topic.
Advertisement below: