Anthropomorphic environment?

I’ve been wondering whether I should make the game’s environment more anthropomorphic, i.e. add faces (and possibly some capability for action) to ‘alive’ parts of the environment.  This might act as an extra nudge towards the game’s conservationist message and provides a more immediate feedback loop for the environmental message.

As an example, all trees would have a face.   Maybe they’re always asleep to start with?  Nearby noises wake them and they then look at activity.  Innocuous things mean they eventually go back to sleep.  Worrying things like fires get them worried.  Catching on fire causes them to make terrified faces (and maybe little blowing motions)?  Having the fire put out causes them to make a relieved (re-leaved? 😛 ) face.

A non-obvious ‘live’ thing could also be fire!  An evil cheeky face on each one — with the fire graphic switched from the semi-real it is now to a cartoon style?  Even less sure about this part.  Guess I’ll have to see (a) whether can come up with a suitable look and (b) how it feels?  I’ve mostly finished the fire’s code now so don’t really want to change it from spreading in the natural-sort of way it does.

Anyway, I haven’t fully decided yet.  As always, all thoughts welcome!

p.s. Attached picture was while looking after kids.  Whaddya think?  Trees might be a bit ‘red neck’ with their big foliage-like moustaches? :-{D

Fire, part 6 and GameCamp 8

Continuing from yesterday’s post.

Decided to circumvent problem of explosions not hitting scenery for now by making the rocket itself start fires.  Worked well — fire spread up tree and onto the floor (as predicted due to the layer situation).  I’ll address with the Component idea I had (rather than splitting to another layer).  That’ll also allow things to be partially inflammable without arbitrary splitting models if needed later.  Done — made a Flammable component.  Yep, that works well.  Here’s a video of current state:

 

Performance is pretty heavy so far — it’ll need tuning to get a sensible number of fires — probably tweak the range values.

One oddity is smoke looking orange.  Obviously it’s caused by my sunlight being that colour since it’s low.  However, the smoke is in shadow so it looks wrong to be orange all the way down.  Will it accept shadows?  Hmm… apparently not (yet) — dev says too heavy performance.  I’ll have to investigate that another time.  Perhaps an interim kludge will be to tweak the smoke colour so it looks more right?  Think that’s also another time since I need to get going to GameCamp!  (Already late due to no. 1 son waking in the night and my consequent oversleeping!)

A new build sees the players dying when they join in the PlayerSetupArena!?  Great.  I’ll debug that on the train on the way in.

(from train)

Yep, the WorldBounds changes I’d made a few weeks back weren’t tuned for other arenas and were killing players at their spawn points!  Fixed in all arenas in build.

Aaaand, in case I forget later (due to being in a pub), I’m going to set this to auto-publish later today!  (what could possibly go wrong?! :-O )

p.s. I enabled Twitter sharing for this blog yesterday so people will actually know what rubbish I write.  OK, they’ll have more evidence 😛

Edit: (on train back from GameCamp 8) Had a lovely time despite only arriving at 3pm! Had a good play testing session with lots of good feedback. In fact, given how I’m trying to be more open and forthcoming, perhaps I ought to copy it all to this blog later! My favourite was one games professor (@drdavient) suggesting an actual viable solution to a common complaint regarding the control system! We’ll have to see how it feels (perhaps as a selectable option per player?)

Fire, part 5

Continuing from last time, I decided to sink an hour clearing the blog post backlog (all those previous fire posts were stuck in draft!  Hopefully there aren’t too many inconsistencies in timings, etc!  I know it’s mostly for my benefit but it’d be nice if someone else can read it and it make some sort of sense!)

Tomorrow I’m going along to GameCamp 8.  I intend to take SnwScf to do some play-testing.  Obviously I’ll need a good & recent build.  Sensible plan is to do the build first but, since the last good build, the only integrated change is the AI, I’m going to push forward with fire sinc, while the AI needs feedback, I’d like more about the fire.  (and adding an AI player doesn’t have good UX yet — it’s a shortcut key still!  I’m not sure what’s a good way to do this yet — perhaps I’ll ask people there! 🙂 )

Right, so next up: integrate into SnwScf!  That consists of several steps:

  1. Basic presence and visualization
  2. Fire starting from Carrockets and Mines (and perhaps some other game-start situations to lend a feeling of time pressure in certain arenas?  Man!  That feels like those old games where the screen started filling-in if you stayed alive!)
  3. Damaging the players!
  4. Extinguishing: burning things really ought to indicate when they’ve been exhausted.

(4) is kind of a back-and-forth relationship so I think it makes sense to use another Component — Burnable which the fire will check for.  This might augment the layer requirement since in SnwScf, inflamable and in-inflamable things (*1) must reside in the same layers (for other physics reasons).  So, that’ll be getting a few things burnable in the dev project and having them burn-out.  First though, let’s get the fire into SnwScf and see how it works!

*1: Is it just me that finds “inflamable” ridiculous.  Perhaps it’s from “inflame”?  Yeah, I could look this up but NOT NOW!!  😉

Well, importing worked OK.  Had to do some fix-up work in my own Assertion library (since that nicely took precedence).  It’s not spreading since I have the wrong layers.  Fixed.  Now need to notify Snowmen and injure them!  Then add damaging the scene!

While I was committing, I realized I had a Fire sound already in the project waiting!  Great — I’ll need that integrating … but in SnwScf since I use Master Audio (with a home-brewed solution to having everything be dynamic) in SnwScf and it’d likely be a pain to set it up for the dev project :-\

Ha, I also noticed the DryGrass texture I have.  Wouldn’t it be cool if the grass might burn and turn brown afterwards?  Yeah anyway!

Hmm, that’s odd — the fire isn’t spreading on a single block in SnwScf despite the same values (I checked).  It’s detecting (doing the rays right, etc) but considers all points found “too close to another fire”.

Argh!  I just realized — Physics layer interactions!  They wouldn’t have been brought over by the package export/import so things are interacting wrongly!  Let’s fix that!  Layer “Fire” only interacts with layers it needs to trigger, i.e. those it spreads-to or notifies.  I guess I could have done this in code with Physics.IgnoreLayerCollision().  Oooh, but I can assert correctness-of-settings for future blunders with Physics.GetIgnoreLayerCollision() which gets values from code *AND* inspector!  For each layer, check Physics.GetIgnoreLayerCollision() is != presence in the FireManager masks.

Done but took longer than I’d have liked.  Revealed some bugs in my layer checking code though so overall = hopefully worth it.

TODO: Resolve fire not spreading enough

I’m getting fewer “too close to another fire” oddities but I am still getting some.  I’ll have to punt on those for now.  It’s time to trigger fire from explosions!  Neatly IIRC both the Carrocket and Mine use the same explosion prefab atm so that simplifies things.

TFW code makes sense

Don’t you love it when an integration is easy (*1) through an accretion of good code, util libraries, etc.  Starting a Fire from an Explosion was as simple as:

if (startsFires && null != FireManager.singleton && FireManager.singleton.layersToSpreadTo.layerMaskContainsLayer(other.layer)) {
    LogR.log("Fire", logOptions, "{0} starting fire at {1} on {2}", this, pos, other);
    FireManager.singleton.spawnFireAtOn(pos, other.transform);
}

*1: I haven’t tested it when I wrote this :-\

Hmm… floor that isn’t snow, walls and other things are in ScenaryStatic physics layer which I’d intended to set fire to.  Perhaps move flamable things into a different layer?

Hmm… (yes, all my musings start that way) Explosion.OnTriggerEnter() is being called for WallUnderneath and Snowman but not for the static and dynamic trees.  Both dynamic trees and Explosion have a Rigidbody (a common cause of lack of OnTriggerEnter() calls).  I guess I need to investigate what’s different between (a) WallUnderneath and Snowman vs. (b) Trees.

Maybe a job for tomorrow morning.


Here’s a couple of things I noticed while I was working earlier but didn’t want to disturb the ‘prose’ flow.

TODO: Resolve RandomGrabBag un-use

TODO: Hmm, just realized I’ve kind-of made my use of the RandomGrabBag redundany since I’m basically spreading all ways each time.  I should either:

  1. remove it (cheaper but more deterministic fire spreading) or
  2. reduce the number of directions the fire might spread in a given spreadTo() and call only once
  3. do spread to all but split the calls between multiple calls to spreadTo() — requires a RandomGrabBag per Fire = not terrible.  The multiple calls would have random time between them (over a curve distribution defined by an AnimationCurve — it’s waaay too widely useful for that name!).

Think (3) would look most realistic = it spreads all ways but might wait a bit to catch in a certain direction.

Next big steps after fire and more interactive environs: more fun! (modes)

Just reviewing the site About page — it’s a bit out of date but does remind me that a large part of the fun was expected to be the different game modes.  Just shooting (thwacking with wet fish, firing carrots, etc) at each other repeatedly is a bit dull.  I need to finish the Treasure Hunt mode (which is mostly done) and get on to Tag, etc.  That’ll probably help the pacing issues as well.

Fire, part 4

Continuing from last time.

Did a little more investigating on the ANMS fire/cloud not working and realized it wasn’t displaying when the Particle Playground System had a non-1.0 Time effect specified!  (lots of trial and error)

I’m still not seeing normal mapping but I’m now wondering if, given it works when not Emit()ing from script, it’s related to something about Emit() and how it generates the particles — something that interacts badly with the ANMS.  I guess this could be a question to ask the PP author.  Done (and linked on ANMS thread).

Next day, I was investigating the problem and realized the API call I was using mandated supplying a colour:

Emit()(int quantity, Vector3 randomPositionMin, Vector3 randomPositionMax, Vector3 randomVelocityMin, Vector3 randomVelocityMax, Color32 giveColor)

Switched it from the default “white” to black and got shadowing but still lacked yellow, red and orange.

However I *am using* the PP “Rendering (Source) | Color” section to specify a “Lifetime Color” (which ranges from yellow, through orange, red and black to transparent).  I’d hazard that it’s not applying when I’m using this Emit() variant.

Checking the source, it looks like I should have “COLORSOURCEC colorSource” set to “COLORSOURCEC.LifetimeColor”.  Ahha!  Changed the “Color Source” field 2 above and bingo!  It’s perfect.
Fed back on threads.

Here’s how it looks now.
Screenshot 2016-05-17 23.50.38.png
Sadly it looks better static than moving.  When moving, the smoke looks great but the flames flicker too fast.  The original flames looked much better.  Partly it’s the shader (Additive vs. normal), partly it’s the change in spritesheet size.  I guess I could double-up on flames (and/or cut-down on smoke) but this feels like a kludge.  Really, I need that timeScale value to work.  Fed back on PP forum thread to ask.

Out of time tonight.

Just accidentally discovered Alt-Escape minimizes a window in Windows.

Also just accidentally discovered that in the Unity AnimationCurve window, pressing Return allows you to enter an absolute value for a key!

Spent another night fiddling with texture sheets and animation curves in the pursuit of great fire!
It looks good standalone but now looks less good scattered by the script.  Allow it more items or scale these up?  Let’s try the latter.  Yeah, bit better and even feel can halve the number of particles again.  Here it is before that scaling.  (Ignore the fake snowman model — I needed to ensure the fire was the right scale.  Also probably best to ignore the odd music — is it just me that ends up, after screen-capture, realizing you can hear typing sounds and that it’s time to tap the free audio? 😀

Fire, part 3

Welcome back!  Time to finish the fire in the test project and get it into SnwScf and being dangerous!

 

Phone tangents

First, the bad news — I’ve been letting these posts back-up a bit.  I have 2 from during the week that need finishing, proofing and posting.  Today’s Saturday again, but a really late start due to No. 1 Son getting sick this morning.  …well, that and getting lost setting up my new phone!!11!!1! 😉  Upgrade time and I went for the SGS7.  I’m justifying it as a good dev/test device for VR until I splurge on a Vive (probably) — finally time to build that Google Cardboard @DevilQube kindly gave me about a year ago that I haven’t been able to use due to my SGS5 having a dead accelerometer after the Lollipop upgrade!  (known issue)  As always, I’m quite pleased with the deal I managed to find … but it took 45 minutes on the phone with my phone provider to get it.  I always look at TCO for phone contracts and this time, it worked out cheaper to go SIM-only (which they discounted) and buy the phone outright.  Nicely, they discounted the SGS7Edge to the SGS7 price so all-in-all, it was an excellent deal!  Anyway, more on the new phone once Samsung’s Smart Switch‘s “Transfer from Galaxy device” eventually moves past 99% “Searching <old phone name> for content to transfer…” — it’s been stuck there for about 30 minutes!  Nope, still not finished — time to cancel.  Think I’ll retry without the 64GB SD card in the old phone!

 

Status

So, since last time, I’ve refactored Fire out from FireSpreader — the latter now isn’t a MonoBehaviour, just a Serializable and a field in Fire — better separation of concerns while maintaining ease of settings (not that I’m using it much since most settings are in the FireManager — still, it’s handy to have during dev).  Regardless, both suffered from 2 problems: (1) Instantiate() finding no ‘transform.parent’ in Awake() or OnEnable() and (2) the Collider causing OnTriggerEnter() to fire as soon as spawned — sometimes at the origin (which might be the cause for the odd fire at the origin?).  The obvious answer to (1) is doing it later and to (2) is having it initially disabled.  Easy.  So when to enable them?  Either: (1) an explicit call or (2) after an automated delay.  Since I want this to potentially work without explicit code (e.g. during dev), (2) is the obvious answer — a 0.1 second disabled period on first spawning won’t adversely affect the experience (and is even slightly more realistic since a recently spawned fire might not catch on nearby things).

Next hitch is that, since the Collider is initially disabled, fires started during the disabled period no longer detect each other so I’m getting lots of overlaps = redundancy (went from 40 fires in my test setup to 100).  I’d been thinking to throttle fire spreading more anyway so let’s do that now.

Actually, a better way might be to avoid the problem by doing both (1) and (2) above — code and delayed.  The latter only if the prior isn’t done.  Done.  Works but I’m still getting 63 fire instances.  63 particles systems seems like kind’of a lot.

 

Fluidity

Spent a chunk of time seeing whether I could get Fluidity to do the fire instead.  Obviously it’d likely be a high-end option but wow it looks good!

Sadly it seems too inefficient to use a single simulation for the whole arena and I don’t see an obvious/easy way to tie multiple proximal fires into a single simulation.  Perhaps there’s a better way to do it — asked question on the forum.  Right back to medium ‘end’ approach 😉

 

Physics.*Cast* indicates overlaps with ‘distance = 0’ etc!

Ahha, resolved the ‘spawned at the origin’ problem!  It was caused by my SphereCastAll() overlapping a Collider at the start of the sweep.  The documentation explains that this causes the hitInfo to have distance 0 (which I now check for) along with direction negated and position zeroed!  It’s obviously one of those hacks (like most of UNIX source using -1 to indicate failure) where the semantics of a function were re-used for efficiency but, without reading the documentation, it’s not obvious that it’s there!

 

Particle Playground

Switched to Emit() on Particle Playground.  I’ve previously done work with emitting straight from Shuriken so I know I can do that if this doesn’t work but PP adds nice noise disturbances to its particles so I’d prefer it.  Mostly working.

To improve the efficiency, implemented a ‘timeBetweenEmits’ concept — throttles particles nicely!

Comparing Advanced Normal Mapped Shaders and Particle Shaders, Vol 1.  After much demo trying and deliberating, I decided to buy the more ambitious (and expensive) ANMS in the hope that the dev will live up to his forum thread promises.  Posted request for his textures though.

Particle Playground integration proving tricky.  I keep running into the problem that, after changing a Particle Playground system a bit, it ceases working properly and switches to flickering the particles (they appear and disappear).  I’ve see it once with the PP default Torch and now I’m seeing it with the Torch adapted with ANMS.  Sure, I’d believe I’d made a mistake but (a) it works fine when not firing from a script and (b) an initial ‘puff’ of particles works in the first-ish frame.  This makes me worry it might be something in PP that’s broken.

Running binary2text on both and diff’ing in Vim:

  • the sourceTransforms are different.
  • Lots of m_RotationOrder stuff.
  • UV setup, max particle size and sort mode correctly different.

Added a “FireManager.doSpreading” bool to disable it for a bit.  Now I don’t even get the flitterring particles.  Hmm…  Let’s sleep on it.

 

Fire, part 2

Continuing from last time.

Bug?

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 🙂 )

A dilemma… Or burn it all!

I’m facing a hard question at the moment:  What to work on next.  It’s always tricky.  The AI is sufficient for play-testing but needs proper trials to reveal the most important areas to improve it next.  There are many areas I’d like to improve it but for showing the game, AI isn’t the most important.  For that, full experience is most important.

Maybe this arena is exhausted?  By that I mean a given environment prompts certain ideas, reveals certain failings, etc.

Hmm, no — I still haven’t done what I’d originally intended with the trees — set fire to them and have them fall down! (A nice destructible environment adds to the fun, I feel.)

So, enough wondering for now.  Fire time!

So, how to implement spreading?  Want lots and efficient.  A GameObject per fire centre might kill things.  Lowest is a single ParticleSystem (perhaps a “Particle Playground” or “PA Particle Field” one for the turbulence?)  Use one-off sphere-cast to detect areas to spread to.  Of course we actually want things affected by the fire so this might be ‘too efficient’.  Also it would be nice to have parts to burn-out — ideally from exhausting fuel which we’ll need to specify per thing affected.  Let’s call YAGNI and do simplest implementation in a new project and test performance.  Then bring into SnwScf actual and see how it performs there.

First version simply has a trigger and spawns on anything entering the trigger. This worked but needed limiting to not overlay itself (since new instances generated new OnTriggerEnter() calls). Also, since triggers give a Collider (not a Collision), I needed to ray-cast to find the actual point on the Collider. I used the obvious and cast from own centre to Collider’s Transform centre but then realized the fire would only spread towards the centre of the subject!

Next switched to a random point from cardinal directions above (picture 8 points at cardinal points around point but car down to surface from above).
To make it more organic, I limited it to one of these points and, after listening to more of this episode of Game Developer’s Radio on juiciness where they discuss good randomness, I decided to implement the Grab Bag Random facility Devon talked about. I’ve put the source up since it’s kinda handy if you want this sort of randomness.

This worked but looked OK on the fence I was testing it on but looked like a game of snake on a flat surface!

Needed to switch to all three points! This fills a surface but would look less good on other shapes and doesn’t handle going down.

Flame-spread-around

Switched to a 3D filling pattern — works well!

Flame-spread-3D.gif

Or, setting spread range to the radius produces this rather dense result!

Screenshot 2016-05-09 00.43.40.png

And, replacing the sphere with some simple Particle Playground flames

 

Of course the prefab I quickly pasted in isn’t a good final version but it shows the idea. The final one will need to build incrementally (so the flames don’t seem to suddenly jump) and generally look better (include some dark multiply particles for contrast or ideally use a volumetric shader).

Anyway, that’s it for Sunday.