Fire, part 2

Continuing from last time.


Getting some odd results from positions saying a Collider that is no-where near the world-origin (global:0,0,0) is being hit at the world-origin?!   This is a pain since it causes a random spurious fire to appear at the world-origin.  I can special-case it in the code but I shouldn’t have to.  What’s going on?!

Distribution thoughts

Trying to ensure my fires are appropriately distributed, I want to check candidate positions aren’t too near an existing fire.  I’m using Physics.CheckSphere() like so:

Physics.CheckSphere(pos, fireRadius * fireCheckFactor, fireManager.fireLayer, QueryTriggerInteraction.Collide);

However that means the sphere is checking whether this sphere overlaps any other fire’s (trigger) Collider.  Those Colliders are intentionally larger than the visualization to represent the fire’s area of influence (for damaging and spreading).  However the fire should spread below that area to indicate it growing in intensity.  Hmm… writing this, I’ve just thought of the same thing you probably did — which I didn’t when I was writing the code!  If you allow fire instances closer than their Colliders, you’ll have overlapping Colliders which is wasteful.  Better would be growing the visualization to represent the intensity growth.  Heck, perhaps grow both visualization and Collider size (since a bigger fire has greater area of effect).  Of course, growing the Collider when initial placement of spread fires was based on the original size means they’ll overlap once grown but the total number will be much less.  What’d be totally efficient is spotting geometrically neatly shaped contiguous blocks of fires and combining them into single Colldiers + representations.  That’s a future optimization if necessary for now.

OK well that probably makes redundant the code I had otherwise been using which I’ll include here just for interest.

 int numFound = Physics.OverlapSphereNonAlloc(pos, fireRadius * fireCheckFactor, resultantColliders, fireManager.fireLayer, QueryTriggerInteraction.Collide);
 if (0 >= numFound)
   return false;

 // The above says that the other's Collider touched fireRadius * fireCheckFactor sphere from pos. Now check exactly how far away.
 var minRangeSqr = fireRadius * fireCheckFactor;
 minRangeSqr *= minRangeSqr;

 // return true if any of those within fireRadius * fireCheckFactor
 for (int i = numFound - 1; i >= 0; i--) {
   var rangeSqr = (resultantColliders[i].transform.position - pos).sqrMagnitude;
   if (rangeSqr < minRangeSqr)
     return true;

 return false; // none close


Right, I’ll commit this then switch to increasing the size to backfill areas… I guess it’d be more realistic to only grow the fire with the original mechanism after the fire has ‘grown’ this other way!  That makes things even more efficient — I like!

(Surface) normal fire

Fire might need to consider surface normals to inform how to position/orient.  Perhaps fire should simply always be aligned to surface normal?  This would probably be best if the particle system is appropriately designed (initial velocity along local ‘up’ and a world-up force (or negative gravity influence)). E.g. if this is a vertical face, orienting with surface normal sticking out sideways, the fire would seem to be billowing from the side rather than appearing partially within the object. …although maybe that way is good for some situations (e.g. translucent objects — Colliders are used to position but a fence or tree has huge holes despite being fundamentally blocking)?

Addendum to the ‘growing’ idea from the last section — maybe it should only grow orthogonally to the surface normal?  (i.e. it’ll spread sideways on flat areas and up-down-left-and-right on walls.)  Of course, there’s risk here about overlapping edges but that’s probably ignorable if we don’t use silly values since the fire could conceivably be billowing from the sides or something.  The nice part is that’s all free if I align the prefab to the surface normal.

Doing the 2 things above…

Added FireVisualization interface so can abstract particle effect or initial mesh visualization scaling.  Using an AnimationCurve to map input (arbitrary scale value ranging from 0 (initial fire size) to 1 (final fire size)) to output (mesh transform local scale).

Next, do scaling by time in FireSpreader (or possibly a separate class) and populate to FireVizulation).

Optimization dilemma

I started writing InvokeRepeating() call in FireSpreader and realized this might become unreasonably inefficient very quickly.  Rather than having all FireSpreaders doing the serialization operation (or creating a Coroutine each), it might be better if they all register with the FireManager which does one for loop for all of them.

This is one of those “premature optimization is the root of all evil” (Donald Knuth) situations.  As game developers, we have to balance this risk with our experience that you’ll always want things that are called frequently to have been coded to be more efficient (where “frequently” often means every frame when you’re trying to hit 60+ FPS).  The resolution of this problem lies in the detail of “things” — things that make it into the final build will want to be faster.  However if you optimize all “things”, some of them might be cut.  When one is first starting coding, it’s also difficult to know what code is fast or slow.  Hence the recommendation to code cleanly first, profile, then optimize (rinse and repeat).

Given I’m not absolutely sure that scaling the fire will work out looking good, I’m gonna let this be mediocre optimization and almost-maximum readability for now.  (I say “almost-maximum” since for optimization patterns I find readable due to familiarity, I’ll use them rather than less optimal more generally readable ones.)

What to do if there is no parent?  Well that’s easy — how can fire burn if there’s nothing for it to burn?  No parent = no fire!  (i.e. it shouldn’t be allowed 🙂 )


One thought on “Fire, part 2

  1. Pingback: Fire, part 3 | Snwscf Dev Blog

Leave a Reply

Fill in your details below or click an icon to log in: Logo

You are commenting using your account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s