I’m building a 2D game that uses emitters to spawn various types of particles (some pickups, some hazards). Particle emitters are attached as components to GameObjects that are placed outside of main camera view (just to the right). I have scripts to instantiate collisions with player object that are working fine (for pickups and hazards).
Problem is that my particles are spawning on top of each other from time to time which is problematic. I.e. A fuel recharge pickup is spawning on top of a hazard and so a player can’t get to it.
Things I have tried…
-Increasing the value of the ‘Sorting Fudge’ variable in the renderer properties for the emitter (doesn’t seem to make any difference)
-Placing the emitters at the exact same location and changing my collision script so that one should ‘destroy’ another if they spawn on top of each other (could be doing something wrong here but doesn’t seem to work)
-Lots of time researching forums, searching Google and as yet haven’t found a solution
I don’t usually post on the forums but at this point I’m hoping that a greater mind than mine has run into this problem in the past and successfully solved it. Any suggestions would be welcome.
I would think you could use Physics2D functions OverlapArea, OverlapCircle, or OverlapPoint, to check if one collider is currently on top of another. Perhaps the emitter can check the chosen location with an overlap check using the size of the next object’s bounds, and choose a new location if it is overlapping.
1 Like
Thank you for your suggestion. I gave this a try and was able to detect a GameObject that I had placed in the scene that had a Box Collider 2D attached, but the script I wrote was unable to detect when a particle system object with collider turned on entered the “Overlap Area”. I’m certain it’s because I’m doing something wrong but sadly after quite a few hours am unable to figure out what. Here is the code I am using. Any suggestions would be appreciated…
using UnityEngine;
using System.Collections;
public class EmitterCollider : MonoBehaviour
{
Vector2 pointA;
Vector2 pointB;
Collider2D result;
void Start()
{
pointA = transform.position;
pointB = new Vector2(transform.position.x + 5, transform.position.y + 5);
}
void Update()
{
result = Physics2D.OverlapArea(pointA, pointB);
if (result == null)
{
return;
}
else
{
Debug.Log("Overlap Found");
}
}
}
Now, when you say “particle system object with collider” do you mean a gameobject with a particle system component and a collider2d component? Or do you mean a particle system with collision per-particle enabled?
Are your pickups prefabs or meshes being spawned by the particle system?
You may find your life is easier if you program your own emitter to handle spawning prefabs, which are much easier to interface with.
Thank you for being patient with me, and apologies for not being clear. Let me take another run at explaining…
In my game I have 3 basic elements…
- A player: Just your run of the mill game object with a Sprite Renderer, 2D Collider, Rigidbody2D etc.
- Hazards: Spawned by attaching a particle system component to a game object and emitting them from right to left
- Pickups: Same as #2
- #2 and #3 are just meshes being spawned by the particle system.
- #2 and #3 both have the ‘Collision’ property enabled in the Particle System options. Also have ‘send collision messages’ checked
- #2 and #3 do not have separate 2D collider components or rigidbodies attached to their GameObjects, they just have the ‘collision’ property active in their particle system properties
- #2 and #3 are both set to Simulation Space ‘Local’
- I have a separate script to perform collision detection between the hazards/pickups and the player that is driven mostly by the OnParticle Collision method. It does things like increment score, perform special effects on collision etc. and is working fine.
- The challenge that I am having that in some cases a ‘hazard’ spawns on top of a ‘pickup’ making the pickup unreachable by the player (the player would be destroyed by the hazard before being able to pickup the pickup)
I tried modifying the OnParticle Collision script that I had so that if a pickup collided with a hazard it would destroy the hazard (basically a poor man’s way of clearing the area). That didn’t work for some reason. Tried quite a few other things as well (mentioned a few in the initial post) and then went to the forums.
Please let me know if this answers your questions?
Yes, this answers my questions.
Currently I don’t believe particle-to-particle collision exists. “OnParticleCollision” will not be called between two particles.
I think by using a particle system to spawn your hazards and pickups, there’s an inherent weakness in that you can only spawn a mesh, and test for collision through OnParticleCollision.
I think the best approach would be to code your own emitter that spawns prefabs setup with colliders and scripts that handle the collisions for that object. You can have that emitter check the location before spawning a new one.
Here’s an example of a class that randomly spawns prefabs from a list, at a position that doesn’t overlap anything on the LayerMask.
using UnityEngine;
using System.Collections.Generic;
public class GameObjectSpawner : MonoBehaviour
{
// list of prefabs to spawn
public List<GameObject> spawnableObjects;
// the vertical position of the ground
public int groundLevel;
// objects on these layers will not get spawned on top of
public LayerMask doNotOverlapOnSpawn;
// called to spawn a new object
public void Spawn()
{
// pick next prefab randomly
int randomIndex = (int)(Random.value * (spawnableObjects.Count - 1));
GameObject prefabToSpawn = spawnableObjects[randomIndex];
// choose a position that doesnt overlap anything on the layermask
Vector3 randomPositionX = getValidSpawnLocation(prefabToSpawn);
// create the object at the new position
Instantiate(prefabToSpawn, randomPositionX, Quaternion.identity);
}
private Vector3 getValidSpawnLocation(GameObject go)
{
// get the spawning object's collider to use for size
Collider2D objectCollider = go.GetComponent<Collider2D>();
Vector3 newPosition = Vector3.zero;
bool validPosition = false;
do
{
// get a random position horizontally on screen
newPosition = Camera.main.ViewportToWorldPoint(Vector3.right * Random.value);
// set it to ground level height
newPosition.y = groundLevel;
// match this spawner's z value
newPosition.z = transform.z;
// get the corners of the object at the new position
Vector3 min = newPosition - objectCollider.bounds.extents;
Vector3 max = newPosition + objectCollider.bounds.extents;
// check that area for overlaps with the layermask
Collider2D[] overlapObjects = Physics2D.OverlapAreaAll(min, max, doNotOverlapOnSpawn);
// if it does not overlap any objects in the layermask
if(overlapObjects.Length == 0)
{
// break out of the do while loop
validPosition = true;
}
} while(!validPosition);
// return the valid position
return newPosition;
}
}
You could also use SpriteRenderer.bounds instead of Collider.bounds for sizing if your object does not have a collider.
1 Like
I feared that was the case, as I was unable to create a ‘particle to particle’ collision event through scripting despite many failed attempts. I was leaning heavily on the particle system to produce my hazards and pickups out of laziness, and got burnt for it. Thank you so much for your suggestion here. I will attempt to use the framework that you have generously provided to produce the desired results.
1 Like
Just keep in mind i did not test my code at all, so it’s entirely possible I overlooked something.
In fact, I just noticed a problem, and updated my previous post. The line choosing the random position from the Viewport wouldn’t have worked right. I believe I fixed it. (still haven’t tested the code though)
Let me know if you get errors or problems, or if you need help extending it.
Your code works beautifully. So nice to have access to the methods that I didn’t have access to when dealing with particles. Thanks so much for your help!
1 Like