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!.