Thanks, BPR.
The past few days I spent refactoring my game code and refactoring my BGHelper module. It’s basically a one-stop-shop for everything I’ve been working on, and while it’s not as full-featured as my BGHelper module used to be, it’s a lot easier to use and has some nifty features.
I’ve finished (?) refactoring my Sprites module to act like a Spritemap in other engines. Rather than the cumbersome “set variables and then use a function on the sprite object” method I used to have, a Spritemap is just an instance of a class. You create a new map, define animations, and then play them:
sprite = obj.children['Sprite']
obj['sprite_map'] = Sprites.SpriteMapMesh(sprite)
obj['sprite_map'].add('Run', ['PlayerRun', 0, 1, 2, 3], 15) # Creates a new animation named "Run" with the actual animation of ['PlayerRun', 0, 1, 2, 3], and an FPS of 15.
# . . .
obj['sprite_map'].play('Run') # Plays that running animation.
I also added some other nice features to the module, like the ability for animations to only play once before having to be manually restarted, and the ability to check to see which animation is playing currently. It’s pretty interesting. Kinda awkward porting the code over, but interesting.
EDIT - UPDATE:
I am currently refactoring the game’s core code. Instead of using functions for everything, I’ve started to use classes and instances. This was mainly to have code completion through PyCharm, but it also is very, very useful as it allows me to fully exploit inheritance between objects and sharing code. For example, both “advanced” NPCs and the player can share code, and they can also share functions (gravity handling, jumping, or dying, for example). It’s a lot cleaner than the past way my code worked (in which the “class” instance was stored in a variable, and then the class’s methods were called through that variable, like “obj[‘character’].HandleGravity()”).
I am ALSO refactoring the enemies’ code. There are only two enemies currently, but they used to run functions, and each basically had their own code bases (even though they did a lot of the same things). To solve this, I decided to implement a system called Behaviors.
Basically, each instance of the Character class (including enemies) can have one or more Behaviors running on it at any given time. These Behaviors could range from things like following the target to escaping from it, shooting at a target, dodging attacks, blocking, and so on. This allows for basically the maximum amount of code re-use between enemies (as the enemies will look different, but perform the same in the code).
All I have to do to set up new enemies is to define their unique properties (how much health they have, how much Scrap they drop when defeated, how much attack power they have, etc), define any new Behaviors if I need to, and add them to the character. This should make it a ton easier to define new enemies as well as new behaviors, as well as troubleshoot when problems arise.
Each Behavior is an instance of a class. This allows me to have tweakable variables that are easy to edit. For example:
self.add_behavior(self.Behaviors.Follow(min_dist = 5, max_dist = 10, max_spd = 10))
… Adds a new following behavior that makes the following character stay at a distance of between 5 and 10 units away and have a maximum move speed of 10 units a second. There’s also other variables in there that have default values I didn’t tweak. The handle_behaviors() function in the character’s class handles carrying out these behaviors. I can also remove behaviors later if I need to.
I recently thought of having a weight value for behaviors, as well, to influence either which ones take precedent over others, the chance that the behavior is executed, or the degree to which the behaviors happen. I haven’t decided exactly, but I think it could be useful to add in (for example, weighting Escape more than Follow could make a more skittish enemy that runs away from you a lot more than follows you around?). It feels like if executed correctly, this could kind of be pretty organic…
I’ve also come up with the idea of Random and Sequence Behaviors. These should be useful for unpredictable and bosses respectively. A Random Behavior will execute one of a set of behaviors randomly (i.e. choose between escaping, shooting, and blocking). A Sequence Behavior will execute a series of Behaviors in a row, like a boss would (i.e. shoot, shoot, wait, follow, jump, wait, and repeat).
One thing I’ve yet to approach are enemies that physically behave like the player, with parts that can be equipped, for example. It’s definitely something to consider.
I think I’m getting / have some cool ideas for gameplay mechanics - the combat might even be a bit out of the ordinary… :o