1
\$\begingroup\$

I've only been using Unity for a very short time and got stuck at one point. I explain.

It is a 2D platform game. I want that when hitting an enemy with a sword, right at the point where the collision has taken place, place a particle effect, but since I have never done it, I have many doubts. In the player I am using Physics2D.OverlapCircleAll which detects the collision with the enemy and inflicts damage, but I also want to place a contact effect (animation of contact with the particle system) right at that point of collision, although I wouldn't even know where or how to put it.

This is the method that registers the collision with an enemy:

void Hit()
     {
         Collider2D[] collided_objects = Physics2D.OverlapCircleAll(attack_controller.position, attack_radius, is_an_enemy);


       
  foreach (Collider2D collider in collided_objects)
         {


            collider.transform.GetComponent<Enemy>().enemy_damage(attack_damage, attack_power);

    
         }

  slash_weak_effect.Play();

     }

But the problem is that the effect always appears in the same place and it looks very ugly. My null knowledge does not manage to reach further. I have investigated on the net and I have not found anything, that's why I turn to you.

Where and how can I place this effect to make it realistic? In other words, I need something that places the particles at the exact point where the character's sword touches the enemy's collider.

Sorry if the question may be stupid for experts

\$\endgroup\$

2 Answers 2

1
\$\begingroup\$

You can simply set the position of an object via Transform.position:

slash_weak_effect.transform.position = xxx;
slash_weak_effect.Play();

Given that multiple enemies are hit at once, playing the same slash_weak_effect may not work. Multiple slash_weak_effects need to be played. We can create a clone of the gameObject by Object.Instantiate.

void Hit()
{
    Collider2D[] collided_objects = Physics2D.OverlapCircleAll(attack_controller.position, attack_radius, is_an_enemy);
    foreach (Collider2D collider in collided_objects)
    {
        collider.transform.GetComponent<Enemy>().enemy_damage(attack_damage, attack_power);
        var clone_of_slash_weak_effect = Instantiate(slash_weak_effect, collider.transform.position, Quaternion.identity);
        clone_of_slash_weak_effect.GetComponent<ParticleSystem>().Play();
    }
}

The second parameter of Instantiate specifies the position of the new object, and we can pass in the position of the hit collider.

And set Stop Action to Destroy in ParticleSystem component to have it self-destruct after playback is complete.

enter image description here

PS: While writing this answer I found another answer submitted which purported to show how to find exactly where the collision occurred. Maybe it's because of some ambiguity in the question itself, or maybe I'm reading it wrong. But I found that the original sample code did not set the position / create multiple effect clones. So I decided to leave this answer here.

But the problem is that the effect always appears in the same place and it looks very ugly.

the same place on the enemy? the same place on the screen? I think it's the latter, the other answer thinks it's the former. Anyway, hope at least one of the two answers will help you. :D

\$\endgroup\$
4
  • \$\begingroup\$ It's great mangata. It works perfect. Philipp I'm also saving your solution in case I need it. You guys are great, thanks for the help. \$\endgroup\$
    – PlayerWet
    Commented Feb 28, 2023 at 11:25
  • \$\begingroup\$ It seems there is an error. The Y position is always the same (-2,2), even if you place the OverlapCircleAll higher or lower. Why is this happening? The X position works perfect.. Thank you. \$\endgroup\$
    – PlayerWet
    Commented Feb 28, 2023 at 12:20
  • 1
    \$\begingroup\$ @PlayerWet This is what the other answer addresses. Here we just set the position of the effect to be the same as the collider. The position of OverlapCircleAll does not affect the position of the effect. You can find the midpoint of OverlapCircleAll and the collider, this will give a more accurate (still rough) result, you can also try following another answer to find the exact collision point. \$\endgroup\$
    – Mangata
    Commented Feb 28, 2023 at 12:26
  • \$\begingroup\$ Thank you so much. I'm going to try to turn the issue around. I will also try Philipp's solution to see if between the two solutions I can finish it. You are awesome. \$\endgroup\$
    – PlayerWet
    Commented Feb 28, 2023 at 12:48
2
\$\begingroup\$

You can't do that that easily with Physics2D.OverlapCircle, because it only tells you which colliders were found, but not where exactly the overlap happens.

If you want more information about collisions, then you need to use rigidbody collisions. These work very different from how you are doing collision detection right now.

  1. The sword has to be a separate GameObject with a Rigidbody2D and a Collider2D. Note that this means that you can visualize the character and the sword as two separate sprites, but you don't have to. When you prefer to animate character and sword together as one sprite, then the sword can also be an invisible game-object with an animation that matches that of the sprite animation as good as possible.
  2. The sword has to physically move to attack. I would recommend to do that with the animation system. When you don't know the animation system yet (you should really learn it - it's a very powerful and useful system that can be used for much more than just humanoids), you might need to animate it with code for now. Or, to get you going, you could provisorically just parent it statically to the player to act as a "lightsaber" that does damage when it touches stuff.
  3. The enemies need to have Rigidbody2Ds as well, because otherwise you won't get any rigidbody collision events.
  4. You need a script that is either on the sword or on the enemies and implements the method OnCollisionEnter2D(Collision2D collision)

This method gets executed whenever the object collides with another object with a rigidbody2D. The method has access to the collision object of type Collision2D. This class provides a lot of useful information about the collision event. Among that information is a method GetContact(int) that returns an object which contains a Vector2 of the exact point where the two colliders touched. Note that in some situations two colliders can collide at multiple points simultaneously, which is why this method requires an index. But you always have at least one contact point, so when you are not interested in handling this edge-case too accurately for now, you can just use GetContact(0).

Here is an example script that assumes that the script is on the weapon and that anything that can be hit by it has a component "Enemy":

[RequireComponent(typeof(Rigidbody2D))]
class Hurtbox : MonoBehaviour {
    
    void OnCollisionEnter2D(Collision2D collisionInfo) {
        if (collisionInfo.gameObject.TryGetComponent<Enemy>(out Enemy enemy)) {
            Vector2 impactPoint = collisionInfo.GetContact(0).point;
            Debug.Log($"The { name } hit the { enemy.name } at { impactPoint }");
        }
    }
}
\$\endgroup\$

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .