Using GOAP for a Jam
As you can tell from the title, I implemented a GOAP AI system for a Game Jam. Extremely overkill, I know. I'll go through all the decisions I made to result in this cool little game, and things I learned along the way.
The Jam
This particular Jam is limited to a 64x64px canvas and has a pool of 10 optional themes to pick from, letting you develop the game for about two weeks. A really cool limitation, in my opinion!
The idea
First day of the jam, I saw the themes and I couldn't come up with many ideas. The best I could manage is to use these three themes: "Player is replaced by AI", "Undo" and "Just one button". The gist of it was that the player character goes about their day doing stuff commanded by its AI, and the player could undo the last action using the one button. The player character then cannot use that action until after choosing another one out of the action pool. Simple is not what I would describe this as, but it's one of those ideas that sound easy, but are not in reality.
The execution
GOAP
I first started researching GOAP systems and how to implement them. I found some youtube videos and a cool open source project made in Godot that served as a base for my project, although I ended up changing and adding a lot of stuff. It's a very complicated system, but I found that once the planner and agent are set up, they'll stay the same no matter the complexity of the systems you add onto it through goals and actions. If you stop yourself of revising the code every time a hard problem arises, that is.
The gist of GOAP is, you have a set of Goals like Eat, Sleep, Attack, GetResource, and a set of Actions like Move, PickItem, UseItem, LookForFood, and the Planner will decide which Actions satisfy the current Goal, and calculate the best plan to achive it.
A good way of implementing a GOAP system is to think of a flow like this:
I encourage everyone to read more about it, specially since it's not mutually exclusive with other AI systems. You can easily setup a Finite State Machine in your player character where the states are decided upon using GOAP Actions, like MoveAction triggers the Move state and gives a target position to move to.
Art
At this point, I had a barebones GOAP system with a single Goal and Action, and I started doing tests with placeholder art (Godot's icon *cough, cough), but I wanted a more polished look. I remembered that I had bought some asset packs from Oryx a while back, and the Wee Fantasy Pack fit the aesthetic and pixel size, almost perfectly. I say this because a psycho decided to make this asset pack be in multiples of 10px instead of 8px! Which is slightly infuriating. But not to worry, I'll add a 2px border around the tiles so that everything seems to line up...
Camera
After realizing the art was 10x10px, I decided to line everything up so that it seems perfect for the screen. So I not only added a border around the viewport to hide the excess pixels, I also decided to make the camera snap to 6x6 tile screens, so that this excess wouldn't show while the player character runs around. In other games, the camera follows the player freely, but I have had a lot of issues snapping the camera to individual tiles in previous projects, so I decided to snap it to the whole 64x64px viewport.
I ran into some issues when dealing with negative coordinates, sometimes skipping sections of the map. With this and a problem with the navigation system I decided all maps would start at (0, 0) and drew all tiles into the positive coordinates.
Navigation
Speaking of the navigation system, or lackthereof, was a big issue. As I now had a snapped camera system, it didn't feel right for the player to be walking in straight lines from A to B. I first started by snapping the position of the player to the tiles, but that didn't work so well with my current setup. Later, I decided to remove the physics movement and opted for modifying the position of the sprite in increments, which worked better in my case. Then, I used Godot's implementation of AStar to draw up paths between points. And this was the second pain point that made me change how I made the levels entirely, since the grid had to be set up and if I made it offset from (0, 0) the math gets more complicated, although not impossible. And so I made all levels as I said before.
As you can see in the video, this system is working with my GOAP system already, and that will be a trend for any new mechanic I add. It's all about GOAP! And, to end this section, the navigation system automatically detects certain tiles and makes them "solid" which the system avoids. So essentially I threw away any physics as I didn't need it for this project.
Characters
Quick tangent here. I actually made it before the navigation system, but it's less significant overall. Basically, the tileset I'm using has all characters in one large sheet, and it aligns all the frames of animation into columns. This way I could set up a selector for the character by specifying a height offset, and animate it by changing columns. I had dreamt of making the player character be selectable and change attack patterns depending on the character, but it was too much for the jam. Luckily, this system allowed me to create a single enemy scene and randomly selects a type, as you'll see more of later.
Inventory
So at the end of the last video, I was left thinking that I wanted to make an inventory system next. I had the eat action alreaddy, but I wanted to add an intermediary in case I wanted to hunt for food instead of picking up an apple, or to buy the food. I ultimately didn't do those things but I went ahead with the system anyways.
It's pretty simple. I have an array of 10 items maximum (configurable), and these are just strings of their item type. To initialize the inventory, I populated it with as many "empty" strings as capacity, so I ended up with an array full of "empty" strings. You can already see it in some output consoles of the previous videos. From there, I added functionality to store an item (checking for empty slots) and to retrieve an item (leaving an empty slot behind). It could also be locked, needing a key to unlock it (which I never used), for maybe adding inventories to locked chests or similar. It would even work for a locked door with inventory size of 1, accepting the key and opening itself. Again, just ideas.
Later, when I added complex actions related to the inventory, I made a use item action, which retrieved the item from the inventory and passed its attributes to the action for it to apply modifiers onto the player, like decrease hunger when using a food item. Also, I made a whole system for when the player runs out of space in its inventory, they would either use the most useful item in that moment, freeing up a space, or throw away the worst item (checking if it's actually worse than the new item).
And all of this is all fine, but this simple inventory brought more problems. Like if I'm storing only a string, how do I know the item's attributes... So I made a database that would define base attributes for all items and entities. You only have to search for the key relating to the item's type, and you get its data back. The idea (also not used) is to make any item have a base value, and if an NPC is selling it, they can add an overhead to this value, to make a profit. Worth noting that with this system, entities also have value and so they could be set up to be sold... And before you think of slavery, there are chickens in this, ok? But yes, that would also be compatible. Specially when I made it possible for entities to be food, and to prevent the player eating itself, I check if the food type is different than the player type, thus preventing canibalism (at least temporarily).
State Indicators
I wanted to showcase the GOAP system to the player, so I took the liberty to procrastinate and add icons on top of their head to accomplish that. Generally, each action has its own icon and it shows for the duration in which it's active. But what happens with actions that resolve instantly? They didn't get to show their icons. So additionally to this, I delayed the execution of each action from the Agent, so that they would at least take some seconds to resolve. This added some needed breathing room to let the player see what the character is doing at any given moment.
It's tiny so you'll get a better look in the video of this next section.
Cards
At this point, I had 3 days left and no game. No input required from the player, and no core game loop either. So I though of implementing a card system. You have three cards to choose from, you select one and place an object in the screen. Easy enough. I decided to work with Godot's containers, but those restrict the position and rotation of children too much. So I looked for an alternative, and found a useful custom container type made by someone in the community. The problem was that it didn't work particularly well, so I had to tweak some parts and finally I had a working card container. I set up three on the main UI, gave them different colors and this is the result:
I continued working on this system as it now was the core of the game, letting the player character interact with the objects the player adds to the world, and adding some randomization so that the player has options. This would lead me to create actions to always try to grab nearby items, and to flee from or fight monsters, giving the player a semblance of control over the player character's movements.
This also marks the point where I forego completely the "Undo" and "Just One Button" themes, as it was too late to implement anything for them, and were already replaced by this card system.
Levels
Finally, the last step of development (after tearing my hair out implementing the final Goals and Actions), level design and creation. With everything I made to this point, I could've implemented a lot of cool features here. For example, Having a really long level with a shelter at the beginning. If the player got tired, it would go back to it every time, and could've resulted in an interesting mechanic. As with everything, I had less than three hours to finish, and didn't get to implement more than this.

The End
I had to export the project and post it with minutes to spare, so I didn't have much time to solve any issues. Which unfotunately make the game unplayable at random. The issue is that sometimes when changing levels the navigation system freezes and the player has no other alternative than to reload the game, sending them back to level 1. And it could happen at any point during the transitions.
Other than that, I'm incredibly satisfied with what I could accomplish in these past two weeks, and can't wait to apply some of these systems into my other projects.
Hope this was interesting!
Files
Get GOAPventurer
GOAPventurer
a game controlled by a GOAP AI system, find out how to help the player through the wilderness
Status | Released |
Author | GameDev_byHobby |
Genre | Adventure, Card Game, Simulation |
Tags | 2D, Fantasy, Pixel Art |
Accessibility | One button |
Leave a comment
Log in with itch.io to leave a comment.