7/24/2014 – Creature AI Part 2

Status

The Creature AI upgrade stretch goal is now 100% complete. In addition to the behaviors I blogged about earlier we now have companions, stealth and behavior voting.

Companion AI

You can tame animals with their favorite food. If a creature sees you with their favorite food equipped they’ll wait and watch you. If you manage to approach without scaring them – timid creatures will bolt if you do anything but crouch and move slowly – then you can tame them with a context skill. They’ll eat the food and follow you around from that point on.

If any hostiles attack you, they’ll attack the hostiles on your behalf unless they are Timid and spook. If they don’t have hostile capabilities then they’ll just follow you and look at things they find interesting. If the animal has at least average intelligence (the range is ‘Stupid, Average, Smart’) then you can instruct it to wait for you. But it will only wait as long as its short-term memory lasts.

The best part? None of this is hackey or kludgey – it’s all done using the Motile script, two WIScripts (Tameable / Tamed) and a basic Skill script.

Stealth

Creatures can now ‘hear’ IAudible objects within their hearing range and ‘see’ IVisible objects within their view distance and field of view. IAudible / IVisible objects can influence the effectiveness of the creature’s seeing and hearing by supplying multipliers for these ranges. So if a creature sees the Player (which is both an IAudible and an IVisible) and the player’s combined stealth skills return a 0.5 multiplier on any creature’s visible range, then the creature fails to see the player at any range greater than half its visual range. Separating the visual / audio makes it possible to mix and match stealth skills – Eg the Silent Stalker skill will decrease your audible range while the Hidden skill will decrease your visible range. And creatures who have bad eyesight can have great hearing & vice versa.

By design creatures can’t identify anything based solely on sound, but they can hear just about anything including the rustling of leaves and bushes. If they turn to look at the source of the sound, then they can identify what it is.

Here’s a top-down screenshot from Unity where I’ve turned on visible field of view / audible range for players & creatures:

It’s nothing fancy – there are some really amazingly sophisticated stealth engines out there – but it gets the job done. The only unanticipated downside of all this is that the stealth skills make it harder to train animals, since they have to see you holding the food and a narrowed field of view / reduced visible range makes it harder to get their attention. Not really sure how to fix that one.

Behavior Voting

To keep all these WIScripts (Timid, Aggressive, Hostile) from contradicting each other I’ve created an anonymous voting system. Each Creature has a CollectiveThought object – they can collectively vote on how to react to what they’ve seen. Whenever a Creature is informed that they’ve seen an item of interest, it loads it into its CollectiveThought and invokes its OnCollectiveThoughtStart action. Any WIScripts subscribed to that action can vote on how to react to it. It can also vote multiple times if it’s really serious. Here’s how the TImid script handles a CollectiveThought:

Code: Select all
namespace Frontiers.World
{
public class Timid : WIScript {

public Creature creature;

public void OnInitialized ( )
{
creature = worlditem.Get ( );
creature.OnCollectiveThoughtStart += OnCollectiveThoughtStart;

...
}

public void OnCollectiveThoughtStart ( )
{
IItemOfInterest itemOfInterest = creature.CurrentThought.CurrentItemOfInterest;
switch (itemOfInterest.IOIType) {
case ItemOfInterestType.Player:
default:
if (creature.IsFamiliarWith (Player.Local.ID)) {
creature.CurrentThought.Vote = ItemOfInterestReaction.IgnoreIt;
} else {
if (Player.Local.IsCrouching) {
creature.CurrentThought.Vote = ItemOfInterestReaction.FleeFromIt;
} else if (Player.Local.IsWalking) {
creature.CurrentThought.Vote = ItemOfInterestReaction.FleeFromIt;
creature.CurrentThought.Vote = ItemOfInterestReaction.FleeFromIt;
} else if (Player.Local.IsSprinting) {
creature.CurrentThought.Vote = ItemOfInterestReaction.FleeFromIt;
creature.CurrentThought.Vote = ItemOfInterestReaction.FleeFromIt;
creature.CurrentThought.Vote = ItemOfInterestReaction.FleeFromIt;
}
}
break;

case ItemOfInterestType.Scenery:
//scenery is things like rustling trees
creature.CurrentThought.Vote = ItemOfInterestReaction.WatchIt;
break;

case ItemOfInterestType.WorldItem:
if (creature.CurrentThought.CurrentItemOfInterest.worlditem.Is () && !creature.Den.BelongsToPack (itemOfInterest.worlditem)) {
creature.CurrentThought.Vote = ItemOfInterestReaction.FleeFromIt;
} else if (itemOfInterest.worlditem.HasAtLeastOne (creature.Props.ThreateningScripts)) {
creature.CurrentThought.Vote = ItemOfInterestReaction.FleeFromIt;
}
break;
}
}
...
}

Unless the creature is familiar with the player – this is something the Tamed script is able to modify – the Timid script votes to flee. If the player is acting aggressively – standing or sprinting – it votes to flee multiple times.

And if the creature sees another creature that doesn’t belong to its pack, it votes to flee. Or if it sees a WorldItem that has a script that it finds threatening (eg, Hostile or Fire) it flees. TL;DR – it flees.

But that doesn’t mean that the eventual result will be ‘FleeFromIt’ because other WIScripts are getting in on the voting as well. If (say) I had a Starving script for creatures that hadn’t eaten in a long, long time (I don’t plan to include this but it’s something modders could add) it might vote like this:

Code: Select all
namespace Frontiers.World
{
public class Starving : WIScript {

public Creature creature;

public FoodStuff ThingToEat;

public void OnInitialized ( )
{
creature = worlditem.Get ( );
creature.OnCollectiveThoughtStart += OnCollectiveThoughtStart;

...
}

public void OnCollectiveThoughtStart ( )
{
FoodStuff foodStuff = null;
IItemOfInterest itemOfInterest = creature.CurrentThought.CurrentItemOfInterest;
//can we eat it?
if (itemOfInterest.IOIType == ItemOfInterestType.WorldItem && itemOfInterest.worlditem.Is (out foodStuff)) {
ThingToEat = foodStuff;
//we can eat it! vote to eat it
creature.CurrentThought.Vote = ItemOfInterestReaction.EatIt;
if (Stacks.Can.Stack (foodStuff.StackName, creature.Props.FavoriteFood.StackName)) {
//it's our favorite food! oh my god we HAVE to eat this
creature.CurrentThought.Vote = ItemOfInterestReaction.EatIt;
}
} else if (ThingToEat != null) {
//we don't care WHAT this new thing is, we have a thing to eat
//ignore anything that's not the thing to eat
creature.CurrentThought.Vote = ItemOfInterestReaction.IgnoreIt;
creature.CurrentThought.Vote = ItemOfInterestReaction.IgnoreIt;
creature.CurrentThought.Vote = ItemOfInterestReaction.IgnoreIt;
creature.CurrentThought.Vote = ItemOfInterestReaction.IgnoreIt;
creature.CurrentThought.Vote = ItemOfInterestReaction.IgnoreIt;
}
}

...

}
}

If it sees its favorite food it’ll try to eat it – and if anything else comes up while it’s still trying to eat that food, it’ll spam the votes with ‘IgnoreIt’ to keep its Timid nature from winning out. And at no point did I have to coordinate with the Timid script to make sure we don’t contradict each other – the two scripts know nothing about one another.

Read more here: 7/24/2014 – Creature AI Part 2

7/21/2013 – Map Data

Status

I can’t wait to show off my beard in the next KS update. It has become truly absurd.

Every day is a little stream of victories, especially now that Hannah is on organizational duty full-time. (Apart from her day job.) All distractions from my life have been eliminated. It’s get up at first light and chip away until it’s dark – and it’s working.

The main quest is 1/3 playable. Locations built, characters & events scripted, the whole nine yards. I still want to tweak the hell out of it, and there’s plenty of really rough work in there – but it’s playable, dammit. Another 2/3 is ‘playable’ – not as solid, and there are some holes and shortcuts, but it’s getting there. You can go from place to place and make story stuff happen in the right order. And the final 1/3 isn’t playable but it is fully written, and many of the locations are built. The main quest is broken into three acts so that final 1/3 will probably be left out of the beta – it’s a natural stopping point. Stopping there will also make it easier to ensure that beta save files will be usable when the final game is released. (I still have bad memories from the Fallout 2 days when their patches would force me to start from scratch.) The final 1/3 is without a doubt my favorite part of the game and contains some of the biggest surprises so knowing that people will experience it after we’ve squashed all the bugs during beta playtesting helps me relax a bit.

A few days ago I finally managed to pull all the map data – splats, color overlays, region data, audio data – out of the inaccessible Resources folder and into the fully moddable external Mods/ChunkMaps folder. Hooray, fully modifiable world! I also created a couple of master PSD files that automate the slicing up and naming of map data, which again I’ll make available for modders to use. This step has ended up saving me far more time than I had expected. I was editing audio data chunk-by-chunk when needed and it was pretty laborious, but now I can edit whole regions with a single brush stroke and export them as little 64×64 pixel slices.

Biome:

Region:

Snowfall:

Map-Snowfall.png

Coastline / Tide:

Map-Coastline.png

Audio Map (Above ground):

Map-Audio.png

Along the way I finally figured out a way to create a usable world map without blowing right past our texture memory limit. FRONTIERS uses color overlays on our terrain and the plan has always been to use those images as map tiles. Actually, let me back up – the plan before discovering my texture memory limitations was to use the hand-drawn map for the world map, but once I realized that wasn’t feasible I decided to use the color overlays. They’re pretty detailed – 512×512 (1024×1024 in file, just in case I can find a way to free up more room for them) but there are a few problems with them. First, they don’t have a coastline, and without a coastline you can’t really tell where you are, especially when you’re zoomed in.

Map-Overlay.png

My first thought was to create a mask for the overlays to carve out a coastline, but texture memory was a problem here too – for the mask to look remotely acceptable it had to match the resolution of the overlay textures, so 512×512 per chunk. Even if I just used an alpha-only map that takes a big bite. The other problem is that even at 512×512 the overlay textures start to look pretty chunky when you zoom in far enough to navigate through a city or town. A detail overlay helped a bit but it was still pretty ugly. I decided to shelve the problem for a couple of months while I hammered out other things.

Then about a week ago Hannah suggested that I try to make the chunkiness work for me instead of against me. That gave me an idea. There are a couple of well-known techniques for making an image look like a painting – if you’ve played with Photoshop you’ve seen them in action – and I knew there are screen-space shaders that can do that sort of thing in real time. With an overlay like that big fat pixels don’t matter – they just get rounded off:

Map-Shader.png

Just to be clear, this isn’t a one-time filter that gets applied to the texture – it’s a camera shader, so the look is preserved no matter how far you zoom in or out. This style also lets me to use the 64×64 coastline textures as a mask, since the smooth rounded edges no longer look incongruent with the sharp overlay image.

Coastline.png

I can’t say I’m thrilled with the final look of the map – a hand-drawn version would have looked sooo much cooler – but it also doesn’t look like a smeary, unusable mess either. The last thing to work out is the label fading – as you zoom in, labels for larger locations fade out and smaller locations fade in. The code is done but it’s going to take a lot of variable tweaking to get the different fade levels feeling right. I’ll probably save that for the beta, though.

Read more here: 7/21/2013 – Map Data

7/14/2014 – Dungeon Cheat Sheet

Status

Creating dungeons can be complex because of the above ground / below ground transitions. A lot has to go right for everything to work in game – so much that I keep forgetting what steps to take and lose valuable time noodling with the system! So I created a cheat sheet. It explains how all the pieces are organized and what steps I have to take for a dungeon to build properly. I thought some of you might find it interesting:

CLICK HERE TO VIEW THE DUNGEON CHEAT SHEET

This won’t make a ton of sense on its own so here’s a screenshot of the hierarchy in the Unity editor:

Read more here: 7/14/2014 – Dungeon Cheat Sheet