Model Graph

I've been busy updating the architecture of Glace to solve an issue I started to run in to. Basically, behaviors in the game had to know way too much about other behaviors in order to work. Whenever I added new features - or changed one, I'd have to go back and update way too much stuff. Classic.

Take the air dash component for example. It explains how the air dash behavior works, but it also defines when the behavior can be performed. Before it would execute, it would have to ask a bunch of other components if it was ok to do so. Am I climbing a ladder? Am I in the middle of an attack? Am I bouncing off a wall?

This means air dash needs to know about climbing ladders and attacking and yadda. And it gets real gross, real fast.

I took a step back and decided on a new way to move forward. That's mostly why I haven't had much new stuff to show lately. I've been "in the weeds" as they say. The Unity weeds.

So real quick - here's info on the solution I'm going with. The problem is I had the behavior and the conditions for that behavior all roled up into one booger. These should be two different boogers. Air dash should do air dash things and someone else should decide on whether to air dash at all.

One solution is the good old finite state machine. The nodes are your behaviors (air dash), and the transitions are your deciders on whether to do something.

No bueno for me as I needed multiple nodes active at once + I wanted to treat nodes as state in addition to behaviors.

I ended up with something I'm calling a model graph in my app. It's a graph of nodes, similar to a finite state machine, but allows multiple states active at once. Instead of transitions between two explicit nodes, nodes have independent conditions to decide whether to run. If a node doesn't have any conditions it just always runs.

There are two types of nodes. Activity Node and Value Node.

ActivityNodes are for any kind of behavior you need. You attach behaviors to them in the form of Activities. Activities have methods like OnStart, OnUpdate, OnStop. E.g. When a node's conditions pass, it starts, and all of its activities get a call to OnStart. When the conditions cease to pass, it calls stop, etc.

ValueNodes associate an arbitrary value (like whether the jump button is down) and allow other nodes to have conditions on it.

Dependencies between nodes are used to do a topology sort so they execute in the proper order. The graph ends up being directed and acyclic (a DAG).

So this let's me create a node called AirDashNode, give it an activity called AirDash (which replaces the need for an AirDashMonoBehavior), and then add conditions to the AirDashNode to decide when to run. The AirDash activity doesn't know anything about the conditions.

After you build the graph you just have to call update on it once per frame.

I plan to open source this later. I'll be talking about it more.

Sprite Numbers

In the new edition of Glace you’ll collect lots of goodies as you cruise about. The original game had blue gems (ding). The Made Again edition has those too, but also more stuff. I got to working on this part recently, and I call it the treasure component. Let’s talk about showing numbers in the UI.

sprite-number-1.gif

In the last post I talked on object pooling and why it’s neat. It limits the number of objects you create as the player derps around the level. This can lead to smoother play for the humans. Well, there’s sometimes a similar story around updating the text in your game’s UI.

You found a gem! There’s another one! And another! Great! But that’s not all you picked up - you’ve also got three new objects on the managed heap. Say what? I thought we were pooling our gems. What’s up with the three new guys?

Alas, these objects came up when we updated the UI to show the latest number of gems. Every time we use the player’s gem count (an integer) to set the UI Text component, a new string object is created. If you pick up 100 gems, that’s 100 string objects on the managed heap (because you updated your UI 100 times).

Is this a big deal? I don’t know. I guess it depends. These short strings are pretty small in memory at around 25 bytes each. You’re probably not going to run out of memory from these little guys. Instead, I think any issues are more likely to involve the number of objects you’re creating instead of their size.

Remember those Friday nights spent watching the disk defragmenter as it transformed your scattered hard drive into a tidy stack of princely goodness? Well Unity doesn’t do the tidying thing with its managed heap. If you’ve got a bunch of small objects on the heap, it can make it hard to find a spot for the bigger guys, which can slow things down.

Another bit is that Unity’s garbage collector is non-generational, which means it scans the entire heap every time it wants to reclaim resources. Each object on the heap takes time to scan, even if it’s super small. Generational GC’s can be smarter on which objects are eligible for collection.

But again, does this really matter? Is your game gonna have any noticeable problems if you put 500,000 strings on the heap? You’ll have to profile your game to really know. But if I’m honest with you, I pretty much just WANT it to matter. And just because something ain’t broke doesn’t mean you can’t take it apart and fix it. I just don’t like knowing I’m putting stuff on the heap when it’s not totally necessary. And in this case it’s not totally necessary.

Here’s the plan, see?

We need to toss the idea of using a Text component to show gem count in your UI. Actually just forget any method that needs a string. If you need a string to update it, then you have to create that string, and that’s what we need to avoid. Side note: there are multiple ways of doing this kind of thing that you can find by searching around. The following is just the method I like the most.

Right so no Text component. Instead you’ll just use game objects to show numbers in the UI. One game object per digit to be more specific. These objects can represent any digit 0 to 9. We can write a simple component to handle this for us.

public class SpriteDigit : MonoBehaviour
{
    public SpriteDigitSpriteSource SpriteDigitSpriteSource;
    public RectTransform RectTransform;
    public Image Image;

    public void Set(int digit)
    {
        var sprite = SpriteDigitSpriteSource.Get(digit);            
        Image.overrideSprite = sprite;
        RectTransform.sizeDelta = new Vector2(sprite.rect.width, sprite.rect.height);
    }
}
SmallSpriteDigit.PNG

The SpriteDigit component has a reference to a RectTransform and Image component that exist on the same game object. Also, since this is a UI thing, the game object lives within the hierarchy of a Canvas. The SpriteDigitSpriteSource is a scriptable object that provides the sprites for each digit. It looks like this.

[CreateAssetMenu(menuName = "UI/Common/Sprite Digit Sprite Source")]
public class SpriteDigitSpriteSource : ScriptableObject
{
    public Sprite[] Sprites = new Sprite[10];

    public Sprite Get(int index)
    {
        return Sprites[index];
    }
}

We instantiate one of these scriptable objects as an asset in our project and set it up with sprites for each digit. Then connect the asset to the Sprite Digit Sprite Source field on the SpriteDigit component.

SmallDigitSpriteSource.png

So now we’ve got a game object that can represent any digit 0 to 9. We can call Set(…) on the SpriteDigit component and it’ll show that digit. Again this only shows 0 to 9. It can’t show 10, or 10,000. For this to be useful we need more. Side note: you might want to sanitize the input on the Set(…) call by clamping it or modding by 10 or something. Otherwise bad stuff happens if you try to set a digit to 10.

To show the a bigger number we’ll need more of these SpriteDigit objects and we’ll need them to work together. For that you you’ll need another component to orchestrate them. For this we’ve got SpriteNumber.

public class SpriteNumber : MonoBehaviour
{
    public SpriteDigit[] SpriteDigits;

    public void SetNumber(int number)
    {
        int workingNumber = number;
        int i = 0;
        
        while (i < SpriteDigits.Length && workingNumber > 0)
        {
            int power = (int)Mathf.Pow(10, i);
            int mod = (int)Mathf.Pow(10, i + 1);
            int digit = (workingNumber % mod) / power;
            SpriteDigits[i].Set(digit);

            workingNumber -= digit * power;
            i++;
        }

        for (; i < SpriteDigits.Length; i++)
        {
            SpriteDigits[i].Set(0);
        }
    }
}

SpriteNumber has an ordered array of SpriteDigit objects and sets each of them so together they show the given number. You can put this component on a game object that parents the SpriteDigit game objects. Then you can add a horizontal layout group to align the children.

SpriteNumberHierarchy.PNG
SpriteNumberScene.PNG
SpriteNumberInspector.PNG

This is just one way to organize this kind of thing. Another option is to instantiate the SpriteDigits from a prefab as you need them. That’d be the job of the SpriteNumber component if you wanted to go that way. Also, in this example we set any unused digits to zero. So if you set the SpriteNumber to 25, it’d set up the digits to read 0025. You could just as easily hide the unused digits (just don’t hide the first one or the number 0 won’t show). That way the first two digits wouldn’t show and it’d just read 25.

That’s it. Now we can update our gem count without putting stuff on the heap. This method also opens up some cool per-digit animation possibilities. The future is bright.

Let me know if I messed something up or if you’re interested in sharing another strategy around this. Thanks for reading!

March Updates, Object Pools, AI, Hamsters

Hello humans. Here’s an update.

Recycling

I knew I needed to add this at some point so I went ahead and did it. This enables the game to recycle objects instead of constantly buying new ones and then throwing them away right after. This means better performance during game play. People often call this “object pooling” but I decided just now that I’ll call it recycling. So if an enemy is shooting a ton of bullets the game will only create enough as it needs to show them all on screen at once. As soon as a bullet isn’t needed anymore (it hits a wall or flies to far away), it will be hidden (not destroyed) and prepared to be shot out of the gun again.

AI System Update

I coded another enemy using the AI system I wrote the other month. This is the illustrious Whicket. They’re a tribe of sassy tree folk who live in the swamp. They wear tastefully carved masks. Well it’s more of a hollowed out log that they wear, over their whole body. Anyway those little red berry bullets are recycled. That little bit of spit that flies out the end of its blowgun is recycled. That’s cool right.

whicket-3.gif

I ended up making some big simplifications to the AI system that also make it more flexible. Pretty happy about it. Below is a shot of the colliders used to trigger different behaviors. The box in front is the “Ima shoot at you” box and the one behind is the “did I just hear something behind me box?”. These colliders are references by “ObjectDetected” scripts that are used to transition states. I call a state a “conduct” in code. State = behavior = conduct.

whicket-triggers.PNG

And here’s a shot of the prefab hierarchy for this little guy.

whicket-prefab.PNG

As you can see I’ve decided to be pretty liberal with my use of game objects as organizational tools. Some of this hierarchy is required by design, but other parts are strictly for the human (me) who’s trying to keep everything understandable (by me). The ShootAttackZone and InvestigateZone are game objects that host the colliders mentioned above.

The Conducts object and its children represent the brain of the enemy. The ConductController component, which is hosted in the Conducts object, makes sure the correct conduct object is enabled and all others are disabled. Here you can see the Wander, ShootAttack, and Stunned conducts. Let me grab a screenshot of the Wander object for you.

wander-object.PNG

There it is. So this ^ is a Conduct object. When this conduct becomes the active one, an animator trigger is set to start the walk animation, the “simple walk behavior” is started, and a conduct transition is enable called ChangeConductOnObjectDetected. You just give this transition the a collider, a layermask, and a target conduct to transition to. We add a separate transition that checks the InvestigateZone and that will target the Investigate conduct (that’s not on this object for now).

Anyway I plan on putting this code up on GitHub in case anyone is interested in it. I want to make a more detailed post about this later.

Sounds, Music, FMOD

Zach, the composer for Glace, is building up his refreshed versions of the music tracks. They sound great of course. You’ll definitely recognize the music. It hasn’t changed such that you’ll miss the original tracks. These are the same songs but with extra polish and some more variation.

We’re giving FMOD a shot. Haven’t integrated it into Unity yet but I’m looking forward to it. I also grabbed a really great sound effects pack that will fit in great with the game.

Other Things

The overworld and stages are on speaking terms now. You can exit a level at a gate stone and appear at a specific place on the overworld. You can go to a landmark on the overworld map and enter the stage for it and appear at the right place in the stage. I’m happy with how it’s turning out but lots more work to do. I’m working on a smarter camera and an improved background system with parallax features and what not.

I’m mostly just really excited to show you this game. I think you’re going to love it. More updates to come.

Organizing Scenes

Hello! The last week I’ve been working on transitioning gracefully between scenes in the Glace remake. I’m using Unity, so the word “Scene” here is a proper noun. I didn’t have much experience with this so got to learn some new stuff.

There are some common challenges regarding all this. One being that when you load a new scene, Unity tears down your current scene in some non deterministic fashion. Unity just starts disabling and destroying components and game objects seemingly at random. This is basically the same way Unity works at scene creation, so it’s not a new paradigm, but it’s easy for me to forget. Even after a component is up and running nicely, you still can’t assume that its dependencies will be around in the future.

Another issue is synchronizing services between scenes. I’m doing my best to minimize the state that sticks around between scene loads. I’m heeding the warnings of my predecessors about the dangers of having scenes that know anything about the scenes that came before them. Basically if you have a scene that requires other scenes to be loaded before it you will be sad and your turtle will be disappointed in you. So none of my services even stick around between scenes. They all get torn down and then recreated if they’re needed in the next scene. Any state I want to keep is handled by configuration assets AKA ScriptableObjects.

Let’s see what else? Well one of the things that a scene passes to the next scene (through those config assets) is at which spawn point the turtle should appear in the next scene. This way when you warp out of a stage you’ll appear where you expect on the overworld. This let’s me do some interesting linking between stages and the overworld too. At some point I’d like to experiment with using stages as a way to move around the overworld. For example on the overworld you would go into the entrance of a cave. You’d enter a spooky cave stage with turtles in it. You’d get through the cave stage and when you exit at the other end, you’d appear on the other side of the mountain on the overworld. On a beach. The only way you can get to this super secret turtle beach is through this turtle cave. Maybe you’d unlock a turtle gate stone at the beach so you can turtle back without having to turtle through the turtle again. Or turtle not, but I always turtle off fast turtle on all the games I turtle. Who knows. Ok more work to do. Thanks for reading.

Oh, here’s a gif just for fun of some lighting experiments:

Ooh look at the gate stones being activated. They act as spawn points.

Ooh look at the gate stones being activated. They act as spawn points.

Walking the Overworld

Just wanted to post a new GIF real quick. This shows a rough cut of some overworld tiles with Glace sized down to something closer to what he’ll be in the finished game. Lots of room for improvement but things are starting to come together. Next I’m adding some points of interest to the overworld that act as entry points into stages.

gate-overworld-2.gif

Overworld Perspective

Hi friends. I finished the gate stone UI that’s used to warp out of levels. This lead me to get started on the first bits of the new overworld. I’m excited to build this new part. My first major task was to decide on a camera perspective. I could do an overhead view or some isometric angle. I started by deciding how I’d like to see Glace move around in the overworld, That lead me to creating some animations, and before I knew it I’d decided on the following.

gate-overworld-1.gif

So I’m going with an isometric view. I went ahead and added support for eight directions of movement. Well, eight animated directions - you can move any direction with an analog stick. I just got this working a few minutes ago so things are looking a little unpolished, but it’s a good stopping point for today. The ground here is actually 3D - something I threw together to help with testing perspective, but I don’t plan on keeping it like this. It’ll be a 2D tile map I think. I also zoomed in quite a bit to make the GIF. Glace will be pretty small on the map in the actual game. I want to have lots of screen space for the world.

So in the Glace remake, you’ll explore and find stages on the overworld map. Some stages are locked or behind obstacles depending on where you are in the story. You can’t just run to the last stage of the game right away. We’ll still have the same “zones” from the original. That is the grassy, swamp, mountain, and lab. These are all connected in the overworld. I have a few extra game play mechanics planned that involve the overworld.

Originally I had planned on doing the overworld in a Glace sequel, but I feel like it can really boost the potential of the original, so here we are. If the Glace remake sells I can build on top of this for part 2.

What’s next? Fixing up the animations a bit and getting started on the overworld tile map! Thanks for reading.

The Gate Stone

Nothing too glamorous to show today. I’ve worked on the new gate stones a bit - mainly the little UI that pops up when you’re near them. Gate stones act like check points within a level. If you die you’ll fly back to the last one you touched. They also act as exit and entry points. You can’t see it yet, but when you activate them, a menu pops up so you can do a few things - notably exit the level and go to the overworld. When you come back to the same level, you’ll come in at the same gate stone.

gate-stone-ui-2.gif

The button is turning orange because I’m pressing it on the controller. I’m going to use this same kind of pop up UI for anything in the game that’s activatable. Things like doors and… other things.

Like most other things in the new Glace, gate stones are lit by a directional light and a normal map. What you see in the GIF is the combination of the two following textures.

Normal map
WarpStone.png

Also I hooked up Rewired today. Rewired is an incredibly well-designed input component. I’m blown away by its quality. Not an ad. But man I love it.

More gate stone work tomorrow!

New Moves

In the original Glace game from 2004, Glace could bounce if you held the up button after falling from some height. He’d also bounce off walls if you hit them fast enough. I thought this was generally pretty fun but I’m rethinking a few parts of it. One difference in the new game is that Glace won’t bounce off walls unless a button is held down. This should stop folks from accidentally triggering the horizontal bounce just by running fast. Some other major differences are the new air dash and ground slam that you can see in the animation.

move-tests-9.gif

In the new Glace game, players can jump in the air and then dash a short distance. It’s kind of like a double jump but you can fly in any direction instead of just jumping up. This started out as an experiment but it didn’t take long before I really fell in love with the extra mobility. The control of Glace just seems more lively now. It’ll need some more tweaking and testing but I’m excited to see what players think of it.

As you can maybe tell by the gratuitous screen shake, Glace can slam the ground if he dashes straight down. You can use this to damage and sometimes stun nearby enemies. I just added this in today and so far I love it. To shake the camera, I used the same shake/wobble logic that I showed in a previous post. I like the sine-wave shake better than the way I used to shake the screen. In the old Glace I just added random values to the camera position for a quarter second before returning it to its original position.

I swapped out the enemy alert indicator too. In this previous post you can see the monster flash orange before attacking. I decided to go with the tried-and-true exclamation point instead.

Another thing about air dashing is you can use it to bounce off horizontal walls quickly without having to get a long running start. Thanks for reading. Let me know if you have any thoughts.

Moving On From Physics

My physics/collision side quest didn’t go so great. I’m going to put the new component down for now. I could spend another week on it at least before getting it right - and there’s just so much more to get done for the new Glace. So it’s back to my previous physics component for now. All is not lost though. I learned a few new things a long the way.

I didn’t have a good intuitive grasp of the dot product operation until recently. Now I understand that it basically tells us how similar two vectors are. It turns out this is really useful. For two normalized vectors that point the same direction, the result is 1. If they point the opposite directions their result is -1. As two vectors become perpendicular, their result approaches zero.

We can use this to determine how an object should bounce off a surface given the object’s velocity and the normal of the surface. We can also use it to simulate the difficulty of walking up steep inclines or the ease of walking down a slope. Very cool.

I drew a few new tiles for the grassy meadows tile map this morning. These are roof shingles to add to the tops of some buildings. Here’s a quick screen grab from Photoshop. Cheers!

roof-tiles.png

Physics with Tommy Visic

Hi friends. I've been busy rewriting the physics component in Glace. The system I had before worked alright, but it was getting a little complicated as I added more features to the game. Basically I need something that uses less tricks and special states.

Glace doesn't use Unity's built-in physics engine. From what I've gathered, it's common for Unity platformers to forego the built-in engine in favor of a raycast-based solution. The reasoning here is even though the built-in physics engine is great, it's really difficult to make it behave as intended for a 2D platformer game. It just wasn't meant to do it.

So back to raycasting. That's how Glace handles physics. Before an object is moved in the world, its shape is cast out in the direction of travel. If the cast doesn't hit anything, the object moves to its destination. If the raycast hits something, well, it's complicated. At least for me.

My old system was cobbled together from various sources on the net, including the super cool Corgi Engine I grabbed from the Asset Store. The Corgi Engine is a great asset that I'd recommend to anyone who’s trying to get a handle on platformers for Unity. It uses a collision detection method a little similar to what I did in the original Glace. That is, when you move an object, you do it one dimension at a time. First on the x-axis, then on the y, with each dimension having its own ray cast. So for a vector (2, 3), you'd first raycast and move 2 along the x-axis, then raycast and move 3 along the y-axis. If youd been at the origin prior, now you'd be at (2, 3). That is, assuming you didn't hit something with those raycasts.

So why perform two separate raycasts instead of just a single cast along the movement vector? One reason: it can give you an idea of what to do with a collision. If you hit something on the x phase of the movement, you know you hit something moving horizontally. You don't have to do any other calculations to know this. Same goes for the y check. Folks will use this to determine whether or not an object is "grounded" (standing on a horizontal surface). If you’re moving an object down during the y phase and you collide with something, you know you hit the ground. If you hit something while moving up you know your character just bumped his head on the ceiling. You get all this without doing any complicated stuff. In the original Glace where I only did overlap tests (no raycasts), it was the ONLY way I knew how to tell if Glace should bounce left/right off a wall or up/down off the ground.

A drawback to the "x then y" method is that it doesn't handle non-right angle surfaces well. If you want slopes in your game, there are some extra tricks you need to manage. This is where things start getting weird and where, in my opinion, the method just breaks down. The Corgi Engine has a pretty nice way of handling these extra tricks, but for me its just too much state to worry about when combined with other things I’m doing. I want to find a different way.

jeff.jpg

I don't even know if I want slopes in the new Glace. Would it be cool? I mean yea. But would slopes actually make the game more fun to play? I guess I don't know. One thing's for sure though, it'd be nice to try it out.

Anyway, this is what I've been doing! I have a system that I'm pretty happy with. It casts the x and y as a single vector and uses the normals of hit surfaces to determine how to position the object post collision(s). I'll post details on it next!

Here’s a shot of the physics system handling an object that moved (500, 0) in a single frame. You can see it being redirected around until it ran out of juice.

Here’s a shot of the physics system handling an object that moved (500, 0) in a single frame. You can see it being redirected around until it ran out of juice.

Attack Decisions

Monsters can have different kinds of attacks and they decide what attack to use based on the circumstance. Attacks have some kind of “tell” so players who pay attention can know what’s coming.

The simplest enemy in the original game is the Squish. He walked back and forth with no player sensing ability at all. He’d detect walls and cliffs with rectangle intersect tests and flip direction if needed. If the player touches the Squish then the player receives damage. Actually if the player touched any of the enemies they’d receive damage. That was easy to do but I didn’t really like how it turned out in the end. It was easy to program pretty challenging AI because the enemy would just have to run around and the player would have to make sure never to touch them. Some enemies enemies would shoot stuff at the player too but that was just more touch-based damage.

In the new Glace damage will work a little differently. Enemies this time around have actual attacks, outside of just trying to occupy the same spot as the player. In fact Glace won’t be damaged by just touching an enemy. The enemy needs to perform some kind of attack and that attack has to connect for damage to happen. If you’ve played many games you can probably see why this is more interesting.

squish-slash.gif

It’s true that it takes more work for this kind of setup, but it’s worth it in my opinion. There were a few cases in the original game where it was downright annoying for enemies to damage you upon just a touch. Not only does it hurt you, but it totally slows your roll. Glace likes to move fast and that means there’s not a lot of time to react to an unexpected enemy moving within view. You’d hit the enemy and be stunned for a second (which is also a little annoying), and then you’d have to get back up to speed and get on with your business. Another pain point is when Glace jumps down onto a lower platform, he’ll sometimes fall on top of an enemy that was pretty much impossible to avoid. Cue the stun. No likey. With this new way of handling damage, Glace would jump down onto the platform and not be immediately damaged. The player would potentially have a chance to respond to the situation. The enemies would be just as surprised as you as they decide what kind of attack to deploy.

Come to think of it that stun mode that Glace enters after being damaged is super annoying. The way I look at it now is that taking control away from the player, even for a second, should be kind of a big deal. Maybe there’s a stun affect that certain enemies can inflict but it shouldn’t just be handed out all willy nilly. I think I’ll look into changing that for the new edition. Probably go with zero stun time and not have the player knocked back much or at all. Like I said there could be certain powerful attacks that would reserve those effects. Lots of work to do! Thanks for reading.

Climbables

Glace can climb things in the new game. When I thought on how to implement this I looked at a few others games. I saw some differences in how 2-D games handle climbing that I hadn’t thought about much until now. You maybe notice that you can see the side of Glace as he climbs up the rope. He’s facing one direction as he normally would while running around a stage doing non-climbing things. In some games when characters climb ladders, they show you their back. They face into the 3rd dimension. For example, like Mario in Donky Kong.

glace-rope-climb.gif
donkey-kong-climb.png

I decided to go with the climb-from-one-side method for two reasons. First, I want Glace to be able to do things that require a facing direction while he’s climbing. For example, I want players to be able to throw beads and know what direction they’ll fly. Second, I want Glace to climb some of walls. Since I animate him from the side when he’s climbing ropes n’ ladders, I can use the same animation for climbing walls. I can use the same code too.

Also Glace can change the direction he’s facing while climbing non-walls.

AI Architecture

I’ve been working on an AI pattern for the last couple of days. It’s been challenging for me to come up with a clean solution that doesn’t get super complicated

I want enemy actions/abilities to be decoupled from the decision-making logic that triggers them. I want to drop behaviors onto an enemy and then attach the conditions that trigger those behaviors. So far I haven’t found a process I’m really happy with, but I think I at least have a “good enough” system in place.

Here’s the solution I landed on. It’s your basic state machine but with an added event layer on top. The players here are conducts, transitions, and reporters.

A conduct is an ability that gets attached to an enemy. It’s the finite state of the state machine. It’s what the enemy is currently doing - it’s behavior or goal. The granularity of these behaviors can be whatever. They can be as small as “jump” or as large as “chase the player while opening doors and shooting bottle rockets.” Currently I have a wander conduct, a pursue conduct, a shoot conduct, and a dodge conduct.

Transitions are attached to a conduct and determine when and how a new conduct is triggered (when the state machine changes state). It’s what needs to happen for an enemy to decide it should stop wandering and start shooting. Conducts can have multiple transitions. Again, transitions can be simple or complex.

Reporters are components attached to the enemy that send events to the state machine. As I’m writing this I’m wondering if I really need them. Reporters observe stuff happening in the world and report events to the state machine of the enemy they’re attached to. The rationale here is I wanted a reusable way to add “senses” to enemies. For example one monster would have a reporter that would check the player’s position every quarter second and if within a certain range would cast a ray to check for line of sight. If the player is “sighted”, the reporter would send a PlayerSighted event to the state machine, which might affect a transition object. A transition might be waiting to activate on this event. Then for a more difficult enemy, we could attach a different reporter that has a greater detection range and doesn’t bother with the line of sight test. It would send the PlayerSighted event even if the player was on the other side of a wall.

The reason I’m not sure if the reporter is needed now that I think more on it is it’s really just a decoupling of the trigger. A reporter decides when to send an event. A trigger decides what event to listen to and what conduct to switch to. I could nix the reporter and just have different transition for the two PlayerSighted cases mentioned previously. One would do the ray cast and one wouldn’t. However, having the detection logic in the reporter is pretty nice. Oh ok - I remember now why I added the reporters. Sorry guys this is just a stream of consciousness at this point. Like I said there are different kinds of transitions. Some will transition to the target conduct as soon as they get an event of a certain type, others will transition if they don’t get an event of a certain type within a time limit. The use case here is I want enemies to stop chasing Glace if they lose sight of him for a while. So the enemy has a reporter that sends player sightings to the state machine. The wander/idle conduct has a transition that listens for this event and upon receiving moves to the pursue player conduct. The pursue player conduct has a transition that will go back to wander/idle if it doesn’t get a player sighted event for like three seconds.

I’m excited to get some enemies fully coded in! Thanks for reading.

glace-peng.png

Doors & Wobble Physics

Wobble

What is science? Is this science? Today I added a little wobble effect. I plan to use it for a few different cases in the game. For example, some items you don’t pick up but instead you have to bust them open with a weapon. Then they’ll sprout pick-ups. I use the wobble effect to try to say to the player “yes I know you touched it, but that’s not going to do anything helpful - try something else.”

wobble.gif
door.gif

Opening Doors

There are doors in the new edition of Glace, and I use the wobble effect to give doors more of a doory feel. They budge a little bit if you hit them with a weapon. Designers call this “door affordance” or doorfordance for short. Most doors in Glace can be smashed open with weapons or a particular fast-moving player. Other doors will need a key of sorts. I’ll use these for some more interesting variation in the stages. Enemies can’t see through them. I’m thinking Glace will get a power that will let him bash doors open and stun any nearby foes. Oooh doors.

3D Doors in 2D World

Speaking of doors, check it out - they’re 3D. Doing it this way ended up being easier than using animated sprites. I think it looks nicer too. I’m pretty sure this is how Dead Cells does their doors and probably lots of other games too. I still use a 2D collider to handle collisions with the door and to block the player while it’s shut. That’s the thin green outline in the shot below. You can also see how the two tile maps are layered.

day96.jpg

96 days left!

I didn’t get the whole day to dedicate to the Glace project but made some good progress anyway. I made a little tear-off day counter thing to keep my schedule visible. I really, really want to have this game releasable in 96 days. Lots of work to do still!

Collisions & Fast Movers

I’ve been experimenting with some new bead attack modes. One thing Glace can do now is make beads zip back to him super fast in a straight line. This causes beads to slash through the air in a line between Glace and wherever the beads was when the attack was triggered. Since the beads move so fast in this case, I can’t rely on overlap tests between colliders. I had to go ahead and add ray cast collision tests for this. I use Physics2D.CircleCastNonAlloc(…) in this case.

A bead slash attack cuts through an enemy. The bead moves so fast that ray casts are needed to detect the collisions.

A bead slash attack cuts through an enemy. The bead moves so fast that ray casts are needed to detect the collisions.

Ray casts are used to detect collisions between Glace and the environment as well. In the original game, I only used overlap tests, which would cause Glace to move through walls if he was moving too fast. I had to put a bunch of hacks in place to reduce the chances of this, like minimum time steps, which ended up being a pretty bad idea in practice. It’s nice to know we won’t have to worry about this now.

I also imported UniRx today and put it to work. I was so happy to see Rx extensions for Unity!.