Forum begins after the advertisement:
[Part 3] Bat can’t move and attack
Home › Forums › Video Game Tutorial Series › Creating a Metroidvania in Unity › [Part 3] Bat can’t move and attack
- This topic has 12 replies, 3 voices, and was last updated 22 hours, 54 minutes ago by
Terence.
-
AuthorPosts
-
March 30, 2025 at 11:21 pm #17673::
Bat Code
using System.Collections; using System.Collections.Generic; using UnityEngine; public class Bat :Enemy { [SerializeField] private float chaseDistance; [SerializeField] private float stunDuration; float timer; // Start is called before the first frame update protected override void Update() { base.Start(); if (!PlayerController.Instance.pState.alive) { ChangeState(EnemyStates.Bat_idle); } } protected override void UpdateEnemyStates() { float _dist = Vector2.Distance(transform.position, PlayerController.Instance.transform.position); switch (GetCurrentEnemyState) { case EnemyStates.Bat_idle: if(_dist < chaseDistance) { ChangeState(EnemyStates.Bat_Chase); } break; case EnemyStates.Bat_Chase: rb.MovePosition(Vector2.MoveTowards(transform.position, PlayerController.Instance.transform.position, Time.deltaTime * speed)); FlipBat(); break; case EnemyStates.Bat_Stunned: timer += Time.deltaTime; if(timer > stunDuration) { ChangeState(EnemyStates.Bat_idle); timer = 0; } break; case EnemyStates.Bat_Death: Death(Random.Range(5, 10)); break; } } public override void EnemyGetsHit(float _damageDone, Vector2 _hitDirection, float _hitForce) { base.EnemyGetsHit(_damageDone, _hitDirection, _hitForce); if(health > 0) { ChangeState(EnemyStates.Bat_Stunned); } else { ChangeState(EnemyStates.Bat_Death); } } protected override void Death(float _destroyTime) { rb.gravityScale = 12; base.Death(_destroyTime); } protected override void ChangeCurrentAnimation() { anim.SetBool("idle", GetCurrentEnemyState == EnemyStates.Bat_idle); anim.SetBool("Chase", GetCurrentEnemyState == EnemyStates.Bat_Chase); if(GetCurrentEnemyState == EnemyStates.Bat_Death) { anim.SetTrigger("Death"); } } void FlipBat() { sr.flipX = PlayerController.Instance.transform.position.x < transform.position.x; } }
March 30, 2025 at 11:31 pm #17674::It looks like you’ve pasted the code for a Bat enemy class in Unity, but it seems to contain some issues, particularly with the HTML entities for comparison operators (like
<
for “). This will cause compilation errors. You need to replace those entities with the actual symbols. Here’s the corrected version of your code:using System.Collections; using System.Collections.Generic; using UnityEngine; public class Bat : Enemy { [SerializeField] private float chaseDistance; [SerializeField] private float stunDuration; float timer; // Start is called before the first frame update protected override void Update() { base.Start(); if (!PlayerController.Instance.pState.alive) { ChangeState(EnemyStates.Bat_idle); } } protected override void UpdateEnemyStates() { float _dist = Vector2.Distance(transform.position, PlayerController.Instance.transform.position); switch (GetCurrentEnemyState) { case EnemyStates.Bat_idle: if (_dist stunDuration) { ChangeState(EnemyStates.Bat_idle); timer = 0; } break; case EnemyStates.Bat_Death: Death(Random.Range(5, 10)); break; } } public override void EnemyGetsHit(float _damageDone, Vector2 _hitDirection, float _hitForce) { base.EnemyGetsHit(_damageDone, _hitDirection, _hitForce); if (health > 0) { ChangeState(EnemyStates.Bat_Stunned); } else { ChangeState(EnemyStates.Bat_Death); } } protected override void Death(float _destroyTime) { rb.gravityScale = 12; base.Death(_destroyTime); } protected override void ChangeCurrentAnimation() { anim.SetBool("idle", GetCurrentEnemyState == EnemyStates.Bat_idle); anim.SetBool("Chase", GetCurrentEnemyState == EnemyStates.Bat_Chase); if (GetCurrentEnemyState == EnemyStates.Bat_Death) { anim.SetTrigger("Death"); } } void FlipBat() { sr.flipX = PlayerController.Instance.transform.position.x < transform.position.x; } }
Key Changes:
- Replaced
<
with “. - Checked for proper alignment and formatting for better readability.
Make sure that your
Enemy
class and thePlayerController
instance are correctly implemented, and that all necessary components (likerb
for Rigidbody andsr
for SpriteRenderer) are properly initialized in your script. If you face any specific issues or errors after these changes, feel free to ask!March 31, 2025 at 12:42 am #17675March 31, 2025 at 12:46 am #17677::You’re welcome! If you have any more questions or need further assistance with your code or anything else, feel free to ask. Happy coding!
March 31, 2025 at 5:03 am #17679::@ser.apustaja, his issue is not with the code formatting. It’s with the Bat enemy not moving and attacking.
Can you have a look at his bat code again and suggest possible issues? Ignore the HTML formatting issues and read the HTML characters as the actual character itself.
March 31, 2025 at 5:05 am #17680::Certainly! Let’s take a closer look at the Bat enemy code and identify some possible reasons why the bat might not be moving or attacking:
-
Update Method Call:
- In your
Update()
method, you’re callingbase.Start()
, which is incorrect. It should bebase.Update()
to ensure the base class’s update logic is executed. This could prevent important updates from happening in your bat class.
protected override void Update() { base.Update(); // Change this from base.Start() to base.Update() if (!PlayerController.Instance.pState.alive) { ChangeState(EnemyStates.Bat_idle); } }
- In your
-
State Management:
- Ensure that the
ChangeState()
method is correctly managing the enemy states. If the state is not transitioning correctly, the bat may not perform actions like chasing or attacking. - For example, check if
EnemyStates.Bat_Chase
is being reached; you might want to add debug logs to verify if the state is changing.
- Ensure that the
-
Chase Logic:
- In the
UpdateEnemyStates()
method, check if the distance calculation is working correctly. Make surePlayerController.Instance
is correctly referencing the player and that it has a valid position. - Also, ensure that
chaseDistance
is set to a reasonable value in the inspector, as a very small value could prevent the bat from chasing the player.
- In the
-
Movement Speed:
- Ensure that the
speed
variable (which appears to control how fast the bat moves) is defined and set to a positive value. Ifspeed
is zero or negative, the bat won’t move.
- Ensure that the
-
Animation States:
- Make sure the animations for “idle,” “chase,” and “death” are correctly set up in your Animator and that they are transitioning properly based on the enemy states. If animations are not playing, it may appear as if the bat is not moving or attacking.
-
Player Interaction:
- Check if there are conditions that might prevent the bat from attacking the player. For example, if the attack logic isn’t triggered because the bat is not in the correct state or distance.
-
Debugging:
- You can add debug statements to log the current state of the bat and the distance to the player. This can help you identify where the logic may be failing:
Debug.Log("Current State: " + GetCurrentEnemyState); Debug.Log("Distance to Player: " + _dist);
By checking these areas, you should be able to identify why the Bat enemy is not moving or attacking as expected. If you make these changes and still face issues, please provide any error messages or specific behaviors you’re observing, and I’ll be happy to help further!
March 31, 2025 at 5:06 am #17682March 31, 2025 at 3:33 pm #17698March 31, 2025 at 3:35 pm #17699::Yes, you should try the second solution regarding state management. Ensure that the
ChangeState()
method is functioning correctly and that the bat is transitioning to theEnemyStates.Bat_Chase
state as expected. Adding debug logs can help you verify if the state is changing properly.If you encounter any issues or need further clarification, please let me know what specific behavior you’re observing!
March 31, 2025 at 4:06 pm #17708::this my enemy code, its correct?
using System.Collections; using System.Collections.Generic; using Unity.VisualScripting; 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 PlayerController player; [SerializeField] protected float speed; [SerializeField] protected float damage; [SerializeField] protected GameObject WhiteBlood; protected float recoilTimer; protected Rigidbody2D rb; protected SpriteRenderer sr; protected Animator anim; protected enum EnemyStates { //Centipede Centipede_idle, Centipede_Flip, //Bat Bat_idle, Bat_Chase, Bat_Stunned, Bat_Death, //THK THK_Stage1, THK_Stage2, THK_Stage3, THK_Stage4, } protected EnemyStates currentEnemyState; protected virtual EnemyStates GetCurrentEnemyState { get { return currentEnemyState; } set { if (currentEnemyState != value) { currentEnemyState = value; ChangeCurrentAnimation(); } } } // Start is called before the first frame update protected virtual void Start() { rb = GetComponent<Rigidbody2D>(); sr = GetComponent<SpriteRenderer>(); anim = GetComponent<Animator>(); } protected virtual void Awake() { rb = GetComponent<Rigidbody2D>(); player = PlayerController.Instance; } // Update is called once per frame protected virtual void Update() { if(isRecoiling) { if(recoilTimer < recoilLength) { recoilTimer += Time.deltaTime; } else { isRecoiling = false; recoilTimer = 0; } } else { UpdateEnemyStates(); } } public virtual void EnemyGetsHit(float _damageDone, Vector2 _hitDirection, float _hitForce) { health -= _damageDone; if(!isRecoiling) { GameObject _WhiteBlood = Instantiate(WhiteBlood, transform.position, Quaternion.identity); Destroy(_WhiteBlood, 5.5f); rb.velocity = _hitForce * recoilFactor * _hitDirection; } } protected virtual void OnCollisionStay2D(Collision2D _other) { if (_other.gameObject.CompareTag("Player") && !PlayerController.Instance.pState.invincible && health > 0) { Attack(); if (PlayerController.Instance.pState.alive) { PlayerController.Instance.HitStopTime(0, 5, 0.5f); } } } protected virtual void UpdateEnemyStates() { } protected virtual void ChangeCurrentAnimation() { } protected void ChangeState(EnemyStates _newState) { GetCurrentEnemyState = _newState; } protected virtual void Attack() { PlayerController.Instance.TakeDamage(damage); } protected virtual void Death(float _destroyTime) { Destroy(gameObject, _destroyTime); } }
March 31, 2025 at 4:11 pm #17710::Your enemy code has a solid structure, but there are a few issues that need to be addressed:
-
HTML Entity Issues:
- You have HTML entities like
<
,>
, and&
instead of the actual characters `, and
&`. These will cause compilation errors. Replace them accordingly:GetComponent<Rigidbody2D>()
should beGetComponent()
if (_other.gameObject.CompareTag("Player") && ...)
should beif (_other.gameObject.CompareTag("Player") && ...)
- You have HTML entities like
-
Awake and Start Methods:
- You are initializing
rb
in bothAwake()
andStart()
. It’s generally better to initialize components inAwake()
only, as it ensures they are ready before any other methods are called. You can remove the initialization fromStart()
.
- You are initializing
-
Player Reference:
- In the
Awake()
method, you are assigningplayer
toPlayerController.Instance
. Ensure thatPlayerController.Instance
is correctly set up and not null when this code runs.
- In the
-
UpdateEnemyStates Method:
- The
UpdateEnemyStates()
method is currently empty. You should implement the logic for handling different enemy states here, or ensure that derived classes override this method to provide their specific behavior.
- The
-
Death Method:
- The
Death()
method is called but does not handle any cleanup or animation before destroying the game object. Consider adding any necessary animations or effects before callingDestroy()
.
- The
-
Health Check:
- In the
EnemyGetsHit()
method, you should check if health is less than or equal to zero after reducing health. If it is, you should call theDeath()
method.
- In the
Here’s a corrected snippet for the
EnemyGetsHit()
method:public virtual void EnemyGetsHit(float _damageDone, Vector2 _hitDirection, float _hitForce) { health -= _damageDone; if (health <= 0) { Death(2f); // Call Death with a specific time } else if (!isRecoiling) { GameObject _WhiteBlood = Instantiate(WhiteBlood, transform.position, Quaternion.identity); Destroy(_WhiteBlood, 5.5f); rb.velocity = _hitForce * recoilFactor * _hitDirection; } }
Make these adjustments, and your enemy code should work more effectively. If you have any specific issues or behaviors you're observing, please let me know!
March 31, 2025 at 4:48 pm #17711::i have tried his first solution and that correct,should i try second solution?
If the first solution works, then you don’t need to look at #2.Let me know if you have any other issues.
March 31, 2025 at 5:26 pm #17713March 31, 2025 at 5:30 pm #17714::No problem! You can describe the issue you’re having with your camera in Unity, and I’ll do my best to help you troubleshoot it. Please provide details like:
- What specific problem are you experiencing with the camera? (e.g., is it not following the player, not rendering correctly, etc.)
- Are you using a specific camera script? If so, please share that code.
- Are there any error messages or unexpected behaviors you’re observing?
The more details you provide, the better I can assist you!
April 1, 2025 at 12:04 am #17724::my camera doesn’t show anything after i try modify my camera with cinemachine,the display not show anything when running
View post on imgur.com
when running
View post on imgur.com
camera settings
- Replaced
-
AuthorPosts
- You must be logged in to reply to this topic.
Advertisement below: