DSR Log #2

Posted by Astryl on April 28, 2016, 5:21 p.m.

I came extremely close to forgetting I had a blog to write. Fortunately, I got my phone to remind me.

I spent only half of the last week 'working'. The shift in seasons and the sudden dip in temperature always seem to nail me with a case of flu or worse. To top it off, as I'm recovering I suddenly get literal pile of laptops and PCs to fix.

Gotta fix them because I need the cash… but that basically knocked two days out of my week.

(Speaking of cash, I'm still waiting on the prize money for the Jam I won. Intel is taking their merry time. It's been almost exactly a month since they received my banking details. Have inquired further as to the state of the prizes, along with the other winners).

I've actually got a bit of organization planned for these blog posts. So let's get started.

I'm not normally a Sourcetree user, I find the command line to be far easier to quickly bring up and manipulate. But I'm finding the visual branches to be valuable tools for not only aiding with merges but also to see where I am in the project each morning.

And that, as it turns out, also helps me figure out what progress to log in this blog! All I have to do is read my week's git status output, or Sourcetree's representation of it, and I know what features I added.

I adopt the GIT approach of Commit per Feature. Whether it be something tiny, I add it as a commit. That way I can revert if something becomes too broken, potentially even remove something mid-stream then fast-forward back to Head.

So onward to the first big thing I worked on this week.

As pleased as I was with my rapid-fire audio work on the game, listening to the same two tracks and dozen sound effects gets really grating after you've tested the game for the 50th time in a row.

Not only that, but big resources make GM's save function as slow as molasses. I'm already at a 25 second save even after removing the audio, and it was nearly three times slower with the audio included.

I'll be going down the more sensible route later and loading them externally with an extension.

This covers a somewhat large cut of my git commits, but it was worth it. I finished up the State Machine AI system fully, and have re-implemented my "cannon fodder" enemy with the new system.

In addition to the way I described the system last week, I included another structure for entities, an "entity state" structure that has behavioral modifiers for things like movement speed, attentiveness, eyesight, hearing, etc, as these are all things I want my AI to take into account.

Somewhere in the middle of working with this I decided that I needed to get rid of the terribly movement I was using for enemies.

In the interests of saving time during the jam, I used GM's built in mp_potential_step function.

It works great, assuming that nothing is between it and the target, that nothing gets in the way, etc.

That basically means it doesn't work at all for what I want.

I wanted enemies to be able to at least semi-intelligently pathfind around obstacles to the player (Obviously only if they spotted the player, or 'heard' the player).

My first thought was to implement an A* solver. Fortunately, I actually read the manual, and (re)discovered the mp_grid_* functions.

I had a couple of wrinkles to iron out using this, notably stopping a path in progress properly, but it's made the enemies suddenly a lot more 'intelligent' than they were before.

To counteract their sudden newfound ability to actually track the player down, I implemented the concept of "eyesight" and "attention" in the states.

The ai_chase script basically keeps track of whether the entity can see the nearest player entity, and if it can't and is already chasing the player, it starts counting off the "attention" variable.

Once attention reaches zero, the state can return a new state (Such as RETURN which can make the entity path back to its former location, or one of the WANDER states I'm working on).

So its possible to outrun or hide from an enemy, and each enemy can have different levels of diligence in tracking down the player.

The concept of mixing states together has also led to a lot of suddenly easy scenarios to add to the AI. Low level enemies can have a fear response to a player, enemies can emanate a 'call' on seeing the player and have nearby entities come to their aid, nuanced behavior can be introduced via behavior counters, etc.

And this system leaves the main Step block looking a lot cleaner than it usually does in my games.

switch(state_current) {
    case ENTSTATE.IDLE: {
        state_current = ai_look_for_player(ENTSTATE.IDLE, ENTSTATE.CHASE);
        sprite_index = spr_enm_remain;
        image_speed = 0.05;
    } break;
    case ENTSTATE.CHASE: {        
        state_current = ai_chase_player_simple(ENTSTATE.CHASE, ENTSTATE.ATTACK);
        sprite_index = spr_enm_remain_walk;
        image_speed = 0.05;
    } break;
    case ENTSTATE.ATTACK: {
        state_current = ai_attack_player(ENTSTATE.ATTACK, ENTSTATE.COOLDOWN);
        sprite_index = spr_enm_remain_atk;
        image_speed = attack_sprite_speed;
        if(state_current == ENTSTATE.COOLDOWN) { 
            state_register_a = 10;
        }
    } break; 
    case ENTSTATE.COOLDOWN: {
        state_current = ai_action_cooldown(ENTSTATE.COOLDOWN, ENTSTATE.IDLE); // First major derp
        sprite_index = spr_enm_remain;
        image_speed = 0.05;
    } break;
    default: 
        state_current = ENTSTATE.IDLE;
        break;
}

I left, intact, one of my derp comments in there. I leave those to remind me whenever I do something stupid. In this case, I forgot the assignment of state_current in the COOLDOWN case, and spent literally five hours trying to track down what I thought was a horrible design screwup.

I should probably make a wrapper script, like "state_execute", that automatically manages reassignment of the current state.

As you can see, each of the state scripts takes two arguments at this point. The first is the action to return as 'default', the second is the one to return when the state needs to change. This is a bit limited, since the whole point of states is being able to segue into many potential states from a single state.

I'll probably handle this as a per-case scenario, where it makes sense. In my mind, the "look for player" script can only be in two states: Looking and Located.

No sense overcomplicating things until I need the complications.

Oh, and you might also notice that the state structure has a couple of "register" variables. Could've named them "counter", but register is what my mind jumped to first :P

These are basically just generalized counters used for things like the cooldown state.

register_a is set to the desired cooldown, which will vary per enemy, and per state-transition, and it's easier to set one variable than mess around with unique variables, loops and if statements per enemy.

Also included is some enemy-specific code that mostly handles animation changes. Kinda necessary, didn't want to complicate that either.

In preparation for the next item on the list, I implemented a system for saving/loading configuration data. In a bunch of older games I've made, I just used the usual GM INI file functions and stored things as plain text, which I think is fine for certain parameters that I believe should be human-editable (Resolution, scaling, fullscreen, etc).

But then I decided that with the relatively complex set of options I want available for input, I'd probably do a bit better by making an external option-editing tool for the game ala Spelunky. This also helps if the default graphical setup doesn't work for some reason or other.

So, to make the ini configs and make my internal code a bit clearer, I just save everything in a map (I'm using those a lot in this project), then just write that to a string, and load it when needed.

Currently the only two options that are tracked (Besided input, as mentioned in the next section), are what window scale the game is using, and whether fullscreen is to be used or not. It remembers your last setting.

Input is something I consider a core-mechanic to any game, and in this case I pretty much threw the game together in a panic and set default mappings of "Arrow keys and ZXCV".

That's all well and good for people who like that mapping… most don't. So I decided to get the potential nasty business of custom input mapping out of the way now rather than deal with it later.

Thinking ahead to a potential feature, I also decided to add support not just for a gamepad, but for up to four of the things.

This also led to players now keeping track of their player id and an input id.

The eventual plan is to maybe allow for chaotic four-player local play with gamepads or keyboard.

Implementing the remapping is (relatively) simple, and I just store the results in the config map with action keys + controller id. So something like this:

kbd_attack,
kbd_item,
kbd_left,
gp_action,
gp_attack
etc

Then, with a few custom input scripts, the game prefixes the action supplied to the script with whatever controller the calling player has mapped to them.

Gamepads are taken into special consideration; I'm applying the standard XB360 mapping with options to remap the face button actions.

I have yet to implement a nice way to change the mappings, but that'll be for later. Right now it's just a good thing that I have the system, and I won't have to go do a nightmare search-replace session later to add support for it.

And that's pretty much it. Quite a bit of progress for an incomplete week (Basically Friday, Monday and Tuesday. Saturday and Sunday are my "NOT DOING A DAMNED WORK RELATED THING" days. I'd go mad if I didn't have them).

My next big task for the new dev week is to implement status effects and their visual effects. There are a few of these and they all stand to interrupt players and enemies alike. That should be a bit more visual than the changes I've been working on recently, so fresh screenshots next week probably.

Well, I guess there is something I can show that I implemented a couple of weeks back that has really just been kinda sitting there…

Action context popups

Rudimentary camp menu that is going to see major redesigns for user-friendliness

Also on the list:

- Pause menu (With Equip/Item/Char submenus, as well as system menu)

- Leveling menu

- Spell system core framework (Need to determine the rules for learning and casting

spells)

- Spell Conversion (Special feature, will discuss at some future point)

That "other" game I was working on with the bone-animation is currently on indefinite hold due to a falling-out I had with the other guy over us not working on the 50% complete actually-interesting game we were making a while back (The one with the Team Rocket Hideout style puzzles and RPG elements).

I'd rather be working on that than a literal "Castlevania, but" game that we haven't got a clear direction for (Other game has fully mapped out areas, planned enemy sets, puzzle designed, traps, etc. It basically needs a decent bit of spritework, and a healthy dose of polish).

Comments

Astryl 8 years, 6 months ago

Quote:
Custom controls are a must you ask me, but do you intend to make the movement more fluid than it was in the jam demo? I'm just saying a little friction goes a long way :)

I've been tweaking player movement ever since the jam version; I've tried quite a few things, am still figuring out what I want the movement to feel like.

One of the features that's in the jam version but toned down a ton is movement acceleration; you aren't actually going full-speed at first, but I set the acceleration up pretty high because I wasn't sure if people would like the little bit of a wind-up to getting up to speed. Similarly, there was also a bit of momentum, but I removed that before the jam entry because it made positioning for attacks nearly impossible :P

What I do want to do is make the movement feel a bit more like the player is actually stepping on the ground below him if that makes any sense, same for enemies; will have to work on that.

Astryl 8 years, 6 months ago

Quote:
Just by tweaking it with speed/acceleration?

Or do you mean like movement speed decreases/increases depending on terrain?

Or perchance each step being with breaks and stuff depending on where on the step you are?

Could be done in more ways probably :P Slopes and wind and ice and swamp and agility and panic all added in.
Basically all of the above. Want to try a few variants of movement and settle on the one that feels most fun to play with.

Quote:
I shared this before on IRC and other places but can't help myself posting it anyway:
I think I'll try this out tomorrow, almost fits in with what I have in place already.

EDIT: Added it in, can see what you mean. Will probably use that code as the basis for my movement.