Over the past few weeks, as we started building a deckbuilding/card system, I decided to try “vibe coding”. I’ve been wanting to jump more into the code side of Rads & Relics, but, despite having worked as a software engineer for my whole career, I’ve never really built a game before, let alone in Godot. So, I figured what the heck! Let’s give this vibe coding thing a try.
Motivation
One of the biggest challenges I’ve had so far while learning Godot is simply figuring out how to structure and architect my code. I understand that scenes / nodes can have scripts, but how should I organize them? Not knowing exactly where to begin, I thought this would be the perfect opportunity to see what all the hype for vibe coding is about. And hopefully learn some Godot along the way.
Full disclosure: I’ve been pretty skeptical of AI’s programming abilities in the past (it took me almost two years to try GitHub Copilot). However… the technology clearly keeps getting better and better, so I’ve realized that I’ve got to keep up with the times.
For my attempt at vibe coding I decided to use Cursor. The goal was to get the card UI working, which includes: rendering cards in their proper place, the ability to drag and drop cards, the ability to rearrange your hand, and the ability to play and discard cards.
Attempt #1
Looking back, it seems I really wanted to jump right in, because I basically asked Cursor to build the whole thing on top of our existing project with a single prompt:
The current battle is controlled by the player by clicking on party members and then selecting actions. Each character can move once and attack once.
I want to create a new battle scene with a new UI called
battle_cards
. In this version, the player has a deck of cards that they can use to “move” and “attack”. The player starts with 5 move cards and 5 attack cards. The cards are shuffled randomly and the player can only draw 5 cards each turn. When they use a move card they select the party member they want to move and the tile they are moving to. When they use an attack card they select the the party member they want to attack with and use that character’s attack. Each character has their own set of attacks.
Unfortunately this prompt did not work on the first try… At this point I had not created or modified any scenes or nodes in Godot, but luckily Cursor did manage to figure out how to do that. Sadly, when I tried to load the scene it created, nothing happened… I went back and forth a few times with cursor, asking it to try again and pasting error messages. Alas, I was not able to get this to work.
Attempt #2
Clearly asking Cursor to do everything at once wasn’t going to work. So this time I started by creating my own Card
scene with all the nodes I knew I was going to want (text, sprite, collision area and so on). I then created a Hand
scene and a simple script that just defined a basic set of cards:
var cards_in_hand: Array[Dictionary] = [
{
"name": "Attack",
"type": "Light Attack"
},
{
"name": "Move",
"type": "Movement"
},
{
"name": "Skill",
"type": "Skill"
},
]
With some simple scenes and this empty script, I proceeded to ask Cursor to simply render the cards in my hand, centered at the bottom of the screen. And voila, that worked!
This is when I really started vibing. I proceeded to go back and forth with Cursor, asking it to add bits of functionality at a time and fix errors and bugs as they came up.
I managed to slowly get things working
I even started getting fancy!
Unfortunately, this was not possible on vibes alone. After repeatedly asking Cursor to fix the same bugs over and over I had no choice but to dive into the code and fix things myself. That’s when my eyes really opened up. Even though I’ve never built a game before, I’ve seen a lot of code, and immediately I could tell there were some problems with the code Cursor was generating. There were unused variables, confusing state management, tight coupling between my classes, and more. It made me start to really distrust the code. It started to kill my vibe…
Lessons Learned
Ultimately, I decided to refactor just about all the code that I created during this experiment. Through a mix of: going back and forth with Cursor while vibing, jumping into the code to fix bugs, and refactoring everything, I learned a little bit of Godot! There are still many more things to learn, but vibe coding allowed me to get off the ground, get my hands dirty, and gain some confidence.
For now, I’m going to keep coding myself. But, if I decide to vibe code again, here are the lessons I learned:
- Make one small, specific request at a time. The smaller and more specific, the better.
- Don’t blindly trust the code. Just because it seems to work, doesn’t mean it actually works.
- Accept that you’ll have to jump in the code and fix it yourself sometimes. It’s inevitable.
State of the Game
- Progress this week
- Reworking enemy AI to support new MVC model
- Saving and loading game states
- Improvemnents to targeting using the new card UI
- Better card animations moving across zones
- Coming Up
- Pathfinding and world interaction
- Begin adding new enemy types and supporting systems