How to Instantiate the hit explosion at the impact point?

I want my hit explosions to appear where the enemy was hit. Is that possible? I tried some solutions online, and even copying and pasting the explanation in:

But it still gives an error, and, to be sincere, I would like to understand HOW it is supposed to work, how is it calculating the impact point?

Also, why doesn’t ParticleCollisionEvent.intersection allows me to get the vector3 point of impact directly? ( not sure if I managed to state this clearly…)

Any help would be appreciated! :smiley:

1 Like

Hi,

According to the API, ParticleCollisionEvent.intersection does return a Vector3. However, without seeing your code, it is impossible for me to tell why it did not work in your case. If possible, please share more information on what you did and see. Also the error message is relevant because there are thousands of different types.

It does not matter if your code is “wrong”. Share it anyway.

Thank you for the reply!

Ok, so first thing I tried was this: (in my Enemy script, attached to my enemy)

[SerializeField] GameObject enemyHitVFX;

private void OnParticleCollision(GameObject other)
{
    GameObject vfx = Instantiate(enemyHitVFX, ParticleCollisionEvent.intersection, Quaternion.identity);
}

but that gives this error:
Assets\Scripts\Enemy.cs(27,51): error CS0120: An object reference is required for the non-static field, method, or property ‘ParticleCollisionEvent.intersection’

I sincerely would like to understand what is happening, and (if there is a simple way to fix it) how to correct it.


Assuming there is no simple way to get that, I tried this code then, copying the parts from the API I mentioned above:

public ParticleSystem part;
public List<ParticleCollisionEvent> collisionEvents;

void Start()
{
part = GetComponent();
collisionEvents = new List();

}

private void OnParticleCollision(GameObject other)
{
    int numCollisionEvents = part.GetCollisionEvents(other, collisionEvents);
    int i = 0;

    while (i < numCollisionEvents)
    {
        Vector3 pos = collisionEvents[i].intersection;
        GameObject vfx = Instantiate(enemyHitVFX, pos, Quaternion.identity);
        i++;
    }

}

And as soon as I hit the object, I get this error:

ArgumentNullException: Value cannot be null.
Parameter name: ps
UnityEngine.ParticlePhysicsExtensions.GetCollisionEvents (UnityEngine.ParticleSystem ps, UnityEngine.GameObject go, System.Collections.Generic.List`1[T] collisionEvents) (at :0)
Enemy.OnParticleCollision (UnityEngine.GameObject other) (at Assets/Scripts/Enemy.cs:26)


Thank you again for your help, and I hope all this makes it clearer now!

Thank you. The code was helpful. :slight_smile:

Assuming you copied and pasted ParticleCollisionEvent.intersection from the API, let me explain how to interpret the information there. This is the relevant part:

image

public Vector3 intersection; means that this variable/property is part of an object. I can see that because there is no static keyword. With the static keyword, it would be part of the class.

If something is part of a class, we can access it directly via the classname, for example, Mathf.Infinity. Look it up in the API.

In other games, why did you create a variable of type ParticleSystem instead of simply executing ParticleSystem.GetCollisionEvents?

If you know the answer, you know how to apply ParticleCollisionEvent.intersection correctly. It works the same way as Rigidbody.velocity and GameObject.transform.


To which line does the ArgumentNullException refer? And to which particle system does part refer? Whose collision events do you want to check?

I have not yet used ParticleCollisionEvent.intersection or ParticleSystem.GetCollisionEvents yet.

I still have a hard time with the terminology, but your examples helped me to start to understand their differences.


What I am trying to do is: Instantiate an explosion exactly where my laser hits an enemy. I would like for this only to happen when hitting an enemy, and not the terrain, or when the lifetime of the particle ends.

That way I tried editing this “enemy” script, in the same process that was teached in this class. But I did not want to have the explosion aways centered at the enemy target, but instead exactly where the enemy was hit. ( I intend to have big enemies…)

I thought that there was a simple line that could refer to “The point where the particle of the other game object hit this game object” ( and at first I thought that ParticleCollisionEvent.intersection was a Vector3 that refered to that impact point).

So, I don’t want to know how many particles hit or anything, I just wanted a way to pinpoint the location where the laser hit the target, and instantiate an explosion there.


The last error refers to this line:

int numCollisionEvents = part.GetCollisionEvents(other, collisionEvents);

And, with your last question, I think I am starting to understand why this is not working… Although I’m not sure how to fix it yet, hehe.

I guess “part” refers to NO particle system at the moment :man_facepalming: I wanted it to refer to the particle system of another gameobject (The player’s ship), but that is described in another script (PlayerControls linked to the player ship).

Thank you so much for the help so far. I think this may be beyond what I am ready to do at this point, and maybe there are other partes of the next classes that will help me get back here. I thought it would be simpler ( I am pretty confident that I would make it work if the particle was a bullet (gameobject) instead, since I could then simply instantiate the explosion at the transform.position of the bullet.

In your previous post, I read this:

private void OnParticleCollision(GameObject other)
{
    GameObject vfx = Instantiate(enemyHitVFX, ParticleCollisionEvent.intersection, Quaternion.identity);
}

Apart from the mistake I described in my previous answer, this is basically the code you need to instantiate the game object at the position where the laser hit the enemy.


Yes, that’s what you probably wanted to do. The relevant data is assigned to other in the OnParticleCollision. other is of type GameObject, so do what you would do with other variables of type GameObject. Don’t get confused by names.

I think this may be beyond what I am ready to do at this point

I hope “beyond what I am ready” does not refer to your knowledge because I am sure that you already have the relevant knowledge. Unless you have other plans, I would suggest to solve these two problems because they will teach you two fundamental things about programming: how to interpret the API and referencing. The problems you decribed are the most common problems (if you ignore the names). You will very likely encounter similar issues in the future. :slight_smile:

I have spent the last 2 hours trying to figure this out, and to be sincere, I’m feeling lost, frustrated and dumb.

As I said, I do not think I have the required knowledge to tackle this. If i do, I need some more information like in wich lecture it was referenced…

I tried filling the missing vector3 required in “GameObject vfx = Instantiate” with

other.ParticleCollisionEvent.intersection
ParticleCollisionEvent.[this, other].intersection
other.GetComponet().ParticleCollisionEvent.intersection,
other.GetComponentInChildren().ParticleCollisionEvent.intersection,
other.GetComponentInChildren(ParticleCollisionEvent.intersection)
other.GetComponentInChildren().emission.ParticleCollisionEvent.intersection,
other.GetComponentInChildren().GetParticles.ParticleCollisionEvent.intersection,

the best I managed to do was:
other.GetComponentInChildren().transform.position ,
and that just made the hitVFX appear on the laser emmiter, not at the particle impact position…


I read a lot of examples, re-read the API a hundred times, and I have no idea what I’m doing. I simply do not know how to reference it. (Should I put it into ? into( )? into { } ? State the object’s name and a dot after it? I need to reference only the gameObject emmiting the particle, or the object being hit as well? I need to set something public on another script?

All examples on the internet seem to create an array and I have not even a clue to why would I need an array. I don’t want the number of particles that hit, I just want the position… The variable “i” that I posted in my script was simply copied and pasted from the API’s example, as well as the list declaration in the beginning of the code…

I feel as if I’m being asked to write words when I’m still learning the letters.

You already helped me a lot, and I appreciate that, but if I am to solve this I need more… I’m sorry.

In the other games I made in this course the collision did not use the point of impact, just the fact that the collision had happened (we changed colors, disabled meshes…). The script for playing a particle effect was attached to the object containing the ParticleSystem, so it was easier to reference it as well, and we just needed the play / stop commands, not a waay to get th data from the particles themselves…

Maybe I accidentally confused you. :frowning:

What happened to this solution?

private void OnParticleCollision(GameObject other)
{
    int numCollisionEvents = part.GetCollisionEvents(other, collisionEvents);
    int i = 0;

    while (i < numCollisionEvents)
    {
        Vector3 pos = collisionEvents[i].intersection;
        GameObject vfx = Instantiate(enemyHitVFX, pos, Quaternion.identity);
        i++;
    }
}

I do not see why this should not be working, apart from the NullReferenceException error referring to part. In this case, you applied the intersection code correctly, and you assigned it to a variable named pos, which made your code readable.

Given there are no other problems with this code, use this code and answer two questions:

  • To which game object is this code assigned?
  • To which game object is the ParticleSystem attached that is triggering this OnParticleCollision method?

Which game object is assigned to other? Unfortunately, the description in the API is relatively confusing, so a Debug.Log to get the name of the other game object might help. If you don’t know what I mean:

Debug.Log("other.name: " + other.name);

You know best where the relevant particle system is. If it is on the other game object, you look for it on the other game object like this:

ParticleSystem part = other.GetComponent<ParticleSystem>();

And then your code should look like this based on the information on GetCollisionEvents:

Remove ParticleSystem part from the top of your code and the line with part from Start().

private void OnParticleCollision(GameObject other)
{
    ParticleSystem part = other.GetComponent<ParticleSystem>();

    // I did not test the code, so I do not know which of the two lines is correct.
    // If the second line does not work, remove it and use the first one.
    // int numCollisionEvents = part.GetCollisionEvents(part, this.gameObject, collisionEvents);
    int numCollisionEvents = part.GetCollisionEvents(this.gameObject, collisionEvents);
    
    int i = 0;
    while (i < numCollisionEvents)
    {
        Vector3 pos = collisionEvents[i].intersection;
        GameObject vfx = Instantiate(enemyHitVFX, pos, Quaternion.identity);
        i++;
    }
}
And here is the version without the while-loop:
private void OnParticleCollision(GameObject other)
{
    ParticleSystem part = other.GetComponent<ParticleSystem>();   
    int numCollisionEvents = part.GetCollisionEvents(this.gameObject, collisionEvents);
    
    foreach (ParticleCollisionEvent collisionEvent in collisionEvents)
    {
        Vector3 pos = collisionEvent.intersection;
        GameObject vfx = Instantiate(enemyHitVFX, pos, Quaternion.identity);
    }
}

I hope this helped. Don’t worry if you were not able to figure this out yourself. It is great that you pondered on this problem and did not give up after two minutes. That’s more important than finding the actual solution because you also learn a lot just by trying to solve the problem. :slight_smile:

1 Like

ezgif.com-gif-maker (1)
It worked perfectly with this code, using your last suggestion (I used this one because I feel I could understand it better than the others.

This is my complete code, with comments, of what I am using so far (I hope it will be able to help others that may struggle with the same concepts). I intend to create variables for different weapons in the future, and setting points for deaths of the big, harder enemies.

// Start is called before the first frame update
void Start()
{
    scoreBoard = FindObjectOfType<ScoreBoard>(); // See ScoreBoard lecture

    Color usualColor = GetComponent<MeshRenderer>().material.color; // setting the color for the gameobject

    
    collisionEvents = new List<ParticleCollisionEvent>(); // creating the collision list

    Rigidbody rb = gameObject.AddComponent<Rigidbody>();// this makes the enemies use all the colliders from their children gameObjects
    rb.useGravity = false; // We do not want the enemies falling... 

    parentGameobject = GameObject.FindWithTag("VFXParentTag"); // For organizing purposes

}

// Update is called once per frame
void Update()
{
    
}

}

public class Enemy : MonoBehaviour // this scrip is attached to the enemy, in the “Enemy” script
{

[SerializeField] GameObject deathVFX; // the visual Explosion when the enemy dies.
[SerializeField] GameObject enemyHitVFX; // the visual explosion when the enemy is hit
GameObject parentGameobject; // declaring a parent to hold all the explosions that are going to be created.

[SerializeField] int enemyHealth = 30;// enemies start with 30 health, others can be easily be changed in the field

ScoreBoard scoreBoard; // Score board variable - See Score Board Lecture for more
   
Color usualColor; // Marking the color of the enemy as a variable
float redderModifier = 0f; // new variable so the enemy changes color to a reddish tone after being hit


public List<ParticleCollisionEvent> collisionEvents; // creating a list to store the collision events


private void OnParticleCollision(GameObject other)
{
   

    ParticleSystem part = other.GetComponent<ParticleSystem>(); // *** important! Making a variable to acess the particle system of the emmiting object, in this case, the lasers from my player ship.
    int numCollisionEvents = part.GetCollisionEvents(this.gameObject, collisionEvents); 

    foreach (ParticleCollisionEvent collisionEvent in collisionEvents) //  for each collision, do the following:
    {
        Vector3 pos = collisionEvent.intersection; // the point of intersection between the particle and the enemy
        GameObject vfx = Instantiate(enemyHitVFX, pos, Quaternion.identity); // creating the explosion effect.
        vfx.transform.parent = parentGameobject.transform; // this makes the new gameobjects children to my "VFX Parent" gameObject in my Hierarchy, for organizarion purposes
    }
    ProcessHit();
}


private void ProcessHit()
{
    if (enemyHealth >= 0) // that means, if the enemy is not dead yet...
    {
        scoreBoard.ModifyScore(10); // raise score by 10 by each hit
        enemyHealth -= 10; // reduces the enemy's Health by 10
        redderModifier = redderModifier + 0.1f; // increases this variable so the enemy gets redder and redder each time, by the next code:
        GetComponent<MeshRenderer>().material.color = new Color
       (usualColor.r + redderModifier, usualColor.g - redderModifier, usualColor.b - redderModifier, usualColor.a); // this code makes the enemy becomes the same color as it always was, with this extra redderModifier...

    }
    else // if the enemy is dead...
    {
     KillEnemyAndIncreaseScore();
    }
    
}

private void KillEnemyAndIncreaseScore()
{
    scoreBoard.ModifyScore(100); // Raise points by 100 (deaths are more valuable than hits
    GameObject vfx = Instantiate(deathVFX, transform.position, Quaternion.identity); // Death explosion, centered on the enemy.
    vfx.transform.parent = parentGameobject.transform; // organizing the explosions inside a parent gameObject
    Destroy(this.gameObject); // removes the enemy from the game
}

Thank you so much for helping me throught this. I believe my main mistake is that I did not think that

ParticleSystem part = other.GetComponent();

could be written inside

private void OnParticleCollision(GameObject other)

My attempt was to try to write this outside of this method, and that made “other” to be unidentifiable.

I will still try to understand why I needed a list to do this, but that can wait for now, and the important thing is that it is working as intended :smiley:

I am not a native english speaker, so in my frustration I may have misinterpreted your help at some moments - again, thank you very much for this process, and for helping me understand more of what was happening and not just simply pasting the answer - I really intend to learn everything I can to improve in my future projects :slight_smile:

2 Likes

Good job. :slight_smile:

I hope you agree with me that you almost found this solution yourself. Just a little detail was missing. Regarding the while-loop, I found it odd that somebody uses it with a List. It is more common to iterate over a List with a foreach- or a for-loop. That’s why I changed that part of the code, not because the while-loop was wrong.

My attempt was to try to write this outside of this method, and that made “other” to be unidentifiable.

That’s why I formatted “other” like this: other. In C#, there is something called scope. A scope is defined by curly brackets. The parameter of a method (here: GameObject other) always refers to the method code block below. The parameter is basically a special local variable, which does not exist outside the method code block. It is fine that you made this mistake because you learnt something important about C#. Next time, you will do it right. :slight_smile:

I will still try to understand why I needed a list to do this

The API is our friend. :wink:

C# is a relatively simple programming language. There are a few rules, and that’s it. The rest are names. Many, many different names.

If you focus on the syntax and then on the types, you will at least know what the program does, for example, it calls a method, it assigns a value to a variable, and so on. In the end, a program is nothing but a list of instructions that get executed: Do A, then B, then C, then D, and so on. And we programmers express our ideas with this limited “vocabulary”.

If you are interested in a free C# course, I can recommend Bob Tabor’s.

It might be that I did not express myself clearly. I think I should have emphasised that my answer #4 referred to your question why ParticleCollisionEvent.intersection did not work in your code. And I should have mentioned that you used it correctly here: Vector3 pos = collisionEvents[i].intersection;. I can totally see why you were confused, so thanks for having been so patient and polite despite your frustration.

Enjoy the remainder of the course! :slight_smile:

1 Like

This topic was automatically closed 24 hours after the last reply. New replies are no longer allowed.

Privacy & Terms