And I attach this rudimentary, but nicely commented script to it.
Which - if you can’t be bothered to read it, simply:
Creates a bunch of random positions on a mesh surface.
Then assigns those positions to a particle list and then
Assigns that list of particles to the system.
here is that code
using UnityEngine;
using System.Collections;
using UnityEngine.UI;
using System.Text;
public class PlaceParticles : MonoBehaviour
{
public bool debugThisMess = true;
public ParticleSystem tree;
public Transform ObjectToPopulateWithParticles;
private StringBuilder _sb = new StringBuilder(256);
// perhaps this is getting called out of sequence?
void Start()
{
// should be 1000
int partNum = tree.particleCount;
// make a list, of the same length
ParticleSystem.Particle[] ParticleList = new ParticleSystem.Particle[partNum];
tree.GetParticles(ParticleList);
if (debugThisMess)
{
_sb.Append("placing " + partNum + " particles as trees");
DisplayError(ref _sb);
}
for (int i = 0; i < partNum; ++i)
{
//for sanity we check the positions created by our helper methods.
// "5" is the tree sprite height offset we want, ignore it.
//
// GenerateRandomLocationInArea and ShootRayReturnParticlePosition
// just find a place in a block and shoot a ray down,
// nothing fancy, just puts it on the floor.
Vector3 newPlace = ItemSpawner.ShootRayReturnParticlePosition(ItemSpawner.GenerateRandomLocationInArea(ObjectToPopulateWithParticles)) + (Vector3.up * 5); // magic number erk!
// we should have a valid position
// (since this is a "tested as working" method used for non-particle placement)
if (debugThisMess)
{
_sb.Append("placing particle " + i + " at " + newPlace);
DisplayError(ref _sb);
}
// actually assign the location to the particle in the list
ParticleList[i].position = newPlace;
}
//actually assign the particles, with new postions, back to the system
tree.SetParticles(ParticleList, partNum);
//tree.gravityModifier = 0; in case your trees sink...
}
///<summary>
/// Ugly helper to spit out stuff to screen and log
///</summary>
///<param name="sb"></param>
private void DisplayError(ref StringBuilder sb)
{
Debug.Log(_sb);
// toScreen code elided
_sb.Remove(0, _sb.Length);
}
}
looks like
This works perfectly in the editor.
Giving me thousands of trees for a couple of draw calls…
but if I try to do this in the webplayer I get a 0 result for my test.
and all the particles stay in the default position (since I don’t actually set them in that case)
Do I need to wait one frame for the particle system to populate? or something?
I’ll give that a try. I thought that was what Pre-warming was. Plus I told it to jam 1000 right at the start…
Hardcoding the particles to 1000 and jamming them into the system didn’t work
And it’s not just the webplayer, it does this on anything /outside/ the editor, where it works fine…
I also tried with and without prewarming to no avail
so. I was right. it was some sort of undocumented (as far as I can find) delay in the particle creation.
using UnityEngine;
using System.Collections;
public class PlaceParticles : MonoBehaviour
{
public ParticleSystem tree; // plural. like sheep.
public Transform ObjectToPopulateWithParticles;
// a flatish square mesh is a good idea since our helper will take a
// random point sample in the negative Y on a pre-defined square.
// You can write better helpers...
// we will just start a loop until we have our particles ready to reposition
void Start()
{
StartRepeatingCheckForParticleCount();
}
void CreateTreesFromParticles()
{
// should be 1000 in my case...
int partNum = tree.particleCount;
if (partNum == 0)
return;
// make a list, of the same length
ParticleSystem.Particle[] ParticleList = new ParticleSystem.Particle[partNum];
tree.GetParticles(ParticleList);
for (int i = 0; i < ParticleList.Length; ++i)
{
// "5" is the tree sprite height offset we want, ignore it. change it for your tree height
// GenerateRandomLocationInArea and ShootRayReturnParticlePosition
// just find a place in a block and shoot a ray down,
// nothing fancy, just puts it on the floor.
Vector3 newPlace = ItemSpawner.ShootRayReturnParticlePosition(ItemSpawner.GenerateRandomLocationInArea(ObjectToPopulateWithParticles)) + (Vector3.up * 5); // magic number erk!
// actually assign the location to the particle in the list
ParticleList[i].position = newPlace;
}
//actually assign the particles, with new positions, back to the system
tree.SetParticles(ParticleList, partNum);
CancelInvoke("CreateTreesFromParticles");
}
void StartRepeatingCheckForParticleCount()
{
InvokeRepeating("CreateTreesFromParticles", 0, 0.02F);
}
}
EDIT: I removed all the old debug code
PROS: fixes the issue. well okay - works around it. which I can live with. Is incredibly cheap.
Does not require additional batching.
Is fire and forget (well maybe not, not sure what will happen after 100000 counts…)
CONS: the shadows are never going to be perfect without more work.
needs a fast dynamic collider system adding and near frustum intersection checking for the camera to seem like “real trees”.
an enterprising person could put a fast spacial grid of colliders in the same places and get some extremely cheap trees this way. see below I provide a worked example of this
I need to figure out a way to fix the shadows being offset but it seems doable; now we have an “only shadows” option more-so than ever
Enjoy.
This does have the unfortunate side-effect of shooting rays onto existent buildings etc…
So you might want to add a few checks to the ray casting
EDIT: I simply added a similar conditional wait to my main item spawner and check for a GameManager.TreeParticlesCreated flag that I set upon completion.