Forum begins after the advertisement:
[Part 3.5] After jumping and falling, damage is not counted.
Home › Forums › Video Game Tutorial Series › Creating a Metroidvania in Unity › [Part 3.5] After jumping and falling, damage is not counted.
- This topic has 28 replies, 4 voices, and was last updated 1 week ago by
Da.
-
AuthorPosts
-
February 9, 2025 at 9:31 am #17227::
i guess you could also attach and configure a boxcollider2d in the coroutine (for the duration of however many frames you want), instead of creating a overlapbox every frame. and then at the end of the coroutine you destroy(or remove from player gameobject(?)) the boxcollider2d. this would eliminate the problem of having multiple collisions on the same enemy with a single attack, however if an enemy is exactly on the edge of the spawned boxcollider2d and moves in and out it might trigger twice. to fix that, you could do a list that collects collisions, and on adding a new collision you check if the list already has an object with the same enemy gameobject as its triggersource, if it doesnt, add to list and call the deal damage function. i am a bit to lazy to figure out a code for this right now, its 3 am for me xD sorry
February 9, 2025 at 11:11 am #17228::No problem, Lukas! It sounds like you’re trying to manage enemy collisions in a Unity game, and you’re considering using a
BoxCollider2D
to simplify your collision detection for a temporary attack. Here’s how you can implement the idea you have in mind.Step 1: Create a Coroutine to Manage the BoxCollider2D
You can create a coroutine that will enable a
BoxCollider2D
, check for collisions, and then disable or destroy it after a certain duration.Step 2: Use a List to Track Collisions
You can maintain a list of enemies that have already been hit to prevent applying damage multiple times.
Sample Code
Here’s an example of how you might implement this:
using System.Collections; using System.Collections.Generic; using UnityEngine; public class PlayerAttack : MonoBehaviour { public BoxCollider2D attackCollider; // Assign this in the Inspector public float attackDuration = 0.5f; // Duration the collider is active private List hitEnemies = new List(); private void Start() { // Make sure the collider is disabled at the start attackCollider.enabled = false; } public void PerformAttack() { StartCoroutine(AttackCoroutine()); } private IEnumerator AttackCoroutine() { // Enable the collider attackCollider.enabled = true; // Wait for the specified duration yield return new WaitForSeconds(attackDuration); // Disable the collider attackCollider.enabled = false; // Clear the list for future attacks hitEnemies.Clear(); } private void OnTriggerEnter2D(Collider2D collision) { // Check if the collided object is an enemy if (collision.CompareTag("Enemy")) { // Check if we have already hit this enemy if (!hitEnemies.Contains(collision.gameObject)) { // Add the enemy to the list and deal damage hitEnemies.Add(collision.gameObject); DealDamage(collision.gameObject); } } } private void DealDamage(GameObject enemy) { // Implement your damage logic here Debug.Log($"Dealt damage to {enemy.name}"); } }
Explanation:
- BoxCollider2D: The collider is assigned in the Inspector and is disabled by default.
- PerformAttack: This method starts the coroutine to handle the attack.
- AttackCoroutine: This coroutine enables the collider, waits for the specified duration, and then disables it.
- OnTriggerEnter2D: This method checks for collisions with enemies. If an enemy collides and is not already in the
hitEnemies
list, it adds the enemy and calls theDealDamage
method. - DealDamage: This method contains the logic to apply damage to the enemy.
Notes:
- Make sure that your enemy GameObjects have the tag “Enemy” set in the Inspector.
- Adjust the
attackDuration
as needed to fit your game. - You may want to implement additional logic to handle what happens when an enemy takes damage or is defeated.
Feel free to modify this code to suit your specific gameplay mechanics! If you have any further questions or need clarification, just let me know.
February 9, 2025 at 1:13 pm #17237::Not quite. Let’s call the attack method itself “Attack” and the attack animation “Attack_1”. In Attack, when the button is pressed and the attack silence time has passed (you can attack), a hit is registered. But the problem is that registration only happens at this moment. I want the registration to continue for some time after pressing the button (without having to press the button again), regardless of Attack_1 (I’m happy with the animation playback, it doesn’t matter here in principle, as I think it doesn’t matter. You can set it up later). For example, take circular attacks or sustained attacks (for example, in dark souls). They use the same logic, that is, after pressing the key, hits are registered for a while. About the array of colliders. This is a separate issue (or maybe a common one with the previous one). Let’s say we’ve made the “Hit” method work for a while after pressing a key. Now we need to record all the colliders that fall under our area of attack. This does not pose any problems if the method itself is instantaneous (only at the moment when the attack is pressed). When it should last for a certain amount of time, the question arises how to remember all the colliders that were hit at these points in time. This point is still unclear to me, although the previous post suggested to me that we should introduce a common array of colliders and write there from a buffer (another array of colliders), everything that got into it at some point in time. After that, there is also a problem of how to eliminate repetitions so that there is no double damage (that is, to exclude repeated hits of identical colliders).
If I understand you correctly, you want your attacks to be active for multiple frames right? Something like this:
Where you want the attack to be active for multiple frames?
The code I shared above works like that, but it is missing an array that records all hit enemies (so that enemies who are hit don’t get hit again) for each attack.
I will be doing a stream tomorrow. I can address this on the stream.
February 9, 2025 at 10:55 pm #17244::Yes, that’s it. I saw this message late, but I’ve already figured it out (not without the help of your posts). I used a list, entered all the colliders that were hit within 0.2 seconds (I used enumeration, a cycle of five times 0.04 seconds) and, so that there would be no repeated damage, I gave invulnerability to the hit enemy (for a few seconds).
has upvoted this post. February 10, 2025 at 1:48 pm #17248::Yes, that’s it. I saw this message late, but I’ve already figured it out (not without the help of your posts). I used a list, entered all the colliders that were hit within 0.2 seconds (I used enumeration, a cycle of five times 0.04 seconds) and, so that there would be no repeated damage, I gave invulnerability to the hit enemy (for a few seconds).
That’s great to hear.
If you don’t mind, I’ll really appreciate it if you can share your code here.
February 14, 2025 at 4:19 pm #17253::Code using attack frames and invulnerability. Character:
private IEnumerator Attack() { TimeSinceAttack += Time.deltaTime; if (Input.GetButtonDown("Attack") && TimeSinceAttack >= TimeBeetwenAttack) { //attack = true; TimeSinceAttack = 0f; anim.SetTrigger("Attacking"); for (int i = 0; i < 5; i++) { Hit(SideAttackTransform, SideAttackArea); yield return new WaitForSeconds(0.08f); } /*yield return new WaitForSeconds(0.15f); Hit(SideAttackTransform, SideAttackArea); //attack = false; */ } } private void Hit(Transform _attackTransform, Vector2 _attackArea) { Collider2D[] objectsToHit; objectsToHit = Physics2D.OverlapBoxAll(_attackTransform.position, _attackArea, 0, attackableLayer); //Debug.Log($"Attack Position: {_attackTransform.position}, Area: {_attackArea}"); for (int i = 0; i < objectsToHit.Length; i++) { if (objectsToHit[i].GetComponent<Enemy_1>() != null && !Enemy_1.xz.invincible) { objectsToHit[i].GetComponent<Enemy>().EnemyHit(damage, (transform.position - objectsToHit[i].transform.position).normalized, 100); objectsToHit[i].GetComponent<Enemy_1>().Invincible_1(); } } if (objectsToHit.Length > 0) { Debug.Log("Hit!"); } else if (objectsToHit.Length <= 0) { Debug.Log("No Hit!"); } }
Enemy:
[SerializeField] public bool invincible = false;public IEnumerator Invincible() { invincible = true; yield return new WaitForSeconds(0.4f); invincible = false; } public void Invincible_1() { StartCoroutine(Invincible()); }
has upvoted this post. -
AuthorPosts
- You must be logged in to reply to this topic.