Making an AI-Assisted Game in 14 Days: ChatGPT (Part 2/4)

Everyone’s Frenemy

Michael Hsieh
17 min readFeb 5, 2024
Photo by Maximalfocus on Unsplash

Chatting with GPT

I wanted to make a game from scratch, in a couple days, with free AI tools. You can read the details in my previous post. I decided on a mini game concept, tossing chibis onto a bed. It would be an iPhone app using a built-in software library.

First up, ChatGPT-3.5 (hence ChatGPT). Tasked to help me code.

It was helpful…and also frustrating.

Day 1

Before I began, I was already aware ChatGPT loves to lie. So when I pulled up, I approached with caution.

I also figured it can’t do anything too complex — it’s not magic. It required detailed input, so I decided to move in stages, and break each stage into distinct chunks.

The first stage would be like a “skeleton” of game mechanics, not worrying about how anything looks. I’d set up a game scene with the software library SpriteKit, show a basic shape, and setup physics.

My basic game “skeleton” was as follows:

  1. Scene setup
  2. Square boxes to represent chibi sprites
  3. Rectangle box to represent a bed sprite
  4. User taps a box to select it, and box changes color to indicate selection
  5. User swipes a box to represent tossing a chibi into the air
  6. Collision with a bed, and box changes another color to indicate collision
  7. Game in landscape and full screen

Normally I’d have to spend time reading documentation and finding code to get started, but I had ChatGPT to do that now. I predicted this “startup” time would be the biggest time-saver I could get out of ChatGPT.

Sometimes in coding, when learning something new, you’ll pull your hair out trying to understand basic concepts and then throwing together some boilerplate. By the time you’re done wrestling with errors and concepts, likely motivation has leaked out of you. ChatGPT can eliminate a lot of this.

I asked ChatGPT, first setup a game scene.

It started out well enough:

ChatGPT:

import SpriteKit

class GameScene: SKScene {
override func didMove(to view: SKView) {
// Your SpriteKit scene setup code goes here
backgroundColor = .blue
}
}
import SwiftUI
import SpriteKit

struct GameView: UIViewRepresentable {
class Coordinator: NSObject {
// Add any SpriteKit-specific setup code here if needed
}

func makeUIView(context: Context) -> SKView {
let skView = SKView()
skView.showsFPS = true
skView.showsNodeCount = true
skView.showsPhysics = true

if let scene = GameScene(size: skView.bounds.size) {
scene.scaleMode = .aspectFill
skView.presentScene(scene)
}

return skView
}

func updateUIView(_ uiView: SKView, context: Context) {
// Update any SpriteKit-specific content here if needed
}
}

struct ContentView: View {
var body: some View {
VStack {
Text("Your SwiftUI Content Here")
GameView()
.frame(width: 300, height: 300)
}
}
}

struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView()
}
}

A bunch of boilerplate, done. Nice.

However, a couple doubts were creeping in already: The code seemed a little too overly complex, but maybe it was just me?

Whatever, I thought. The AI is giving me the average output trained on data from tons of developers’ game code, so it knows best.

Problem 1

I tried to paste in the code and received 2 errors:

Type 'GameView' does not conform to protocol 'UIViewRepresentable'
Initializer for conditional binding must have Optional type, not 'GameScene'

Fine. I told ChatGPT. It rewrote the code.

Fixed 1 error, still had 1 left:

Type 'GameView' does not conform to protocol 'UIViewRepresentable', Do you want to add protocol stubs?

It told me to remove the makeCoordinater method. That didn’t work. I put it back:

Argument passed to call that takes no arguments

ChatGPT said sorry, makeCoordinator should have no arguments, and then proceeded to give code which did not work.

Initializer for conditional binding must have Optional type, not 'GameScene'

I scratched my head and recalled some knowledge on iOS development, which is obviously not doable if you don’t have such coding experience.

After some back-and-forth which got nowhere, I scrolled through my history, and after a while found one of its given code was working.

import SwiftUI
import SpriteKit

struct GameView: UIViewRepresentable {
class Coordinator: NSObject {
var scene: SKScene?

// Add any SpriteKit-specific setup code here if needed

// Example: Add a method to handle events or updates from SwiftUI
// func handleEventFromSwiftUI() {
// // Handle the event
// }
}

func makeCoordinator() -> Coordinator {
Coordinator()
}

func makeUIView(context: Context) -> SKView {
let skView = SKView()
skView.showsFPS = true
skView.showsNodeCount = true

let scene = SKScene(size: skView.bounds.size)
scene.backgroundColor = .white

// Create Red Ball
let ball = SKSpriteNode(color: .red, size: CGSize(width: 50, height: 50))
ball.position = CGPoint(x: scene.size.width / 2, y: scene.size.height / 2)
scene.addChild(ball)

skView.presentScene(scene)

// Save the scene to the coordinator
context.coordinator.scene = scene

return skView
}

func updateUIView(_ uiView: SKView, context: Context) {
// Update any SpriteKit-specific content here if needed

// Example: Call a method on the coordinator
// context.coordinator.handleEventFromSwiftUI()
}
}

struct ContentView: View {
var body: some View {
VStack {
Text("Your SwiftUI Content Here")
GameView()
.frame(width: 300, height: 300)
}
}
}

struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView()
}
}

Breathed a sigh of relief and kept on going.

…Little did I know. This starter code would come back to bite me later.

Problem 2

The result was just a blank screen so far— not too impressive.

Game scene. With a white background but you can see “nodes” and “fps” count from the scene in the bottom-right corner.

I next asked ChatGPT for a red square box, a blue rectangle box, and a flicking motion to launch the square. The boxes would stand in for 1 chibi sprite and 1 bed, respectively, but have similar physics.

And then, disaster struck.

The next error was far more insidious. I couldn’t see any sprites on the screen! No boxes, no basic shape at all.

Try this yourself. Can you see the problem with this code? (Besides ChatGPT calling a box a ball)

import SwiftUI
import SpriteKit

struct GameView: UIViewRepresentable {
func makeUIView(context: Context) -> SKView {
let skView = SKView()
skView.showsFPS = true
skView.showsNodeCount = true
skView.showsPhysics = true

if let scene = GameScene(size: skView.bounds.size) {
scene.scaleMode = .aspectFill
skView.presentScene(scene)
}

return skView
}

func updateUIView(_ uiView: SKView, context: Context) {
// Update any SpriteKit-specific content here if needed
}
}

struct ContentView: View {
var body: some View {
VStack {
Text("Your SwiftUI Content Here")
GameView()
.frame(width: 300, height: 300)
}
}
}

struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView()
}
}
import SpriteKit

class GameScene: SKScene {
private var ball: SKSpriteNode!
private var box: SKSpriteNode!
private var ground: SKSpriteNode!

override func didMove(to view: SKView) {
backgroundColor = .white

// Create Ball
ball = SKSpriteNode(color: .red, size: CGSize(width: 50, height: 50))
ball.position = CGPoint(x: frame.midX, y: frame.midY + 100)
ball.physicsBody = SKPhysicsBody(circleOfRadius: ball.size.width / 2)
ball.physicsBody?.affectedByGravity = true
ball.physicsBody?.allowsRotation = true
ball.physicsBody?.friction = 0.2
ball.physicsBody?.restitution = 0.8
addChild(ball)

// Create Box
box = SKSpriteNode(color: .blue, size: CGSize(width: 100, height: 100))
box.position = CGPoint(x: frame.midX + 200, y: frame.midY + 100)
box.physicsBody = SKPhysicsBody(rectangleOf: box.size)
box.physicsBody?.isDynamic = false
addChild(box)

// Create Ground
ground = SKSpriteNode(color: .green, size: CGSize(width: frame.width, height: 20))
ground.position = CGPoint(x: frame.midX, y: ground.size.height / 2)
ground.physicsBody = SKPhysicsBody(rectangleOf: ground.size)
ground.physicsBody?.isDynamic = false
addChild(ground)

// Set Up Gesture Recognizer
let flickGesture = UIPanGestureRecognizer(target: self, action: #selector(handleFlick))
view.addGestureRecognizer(flickGesture)

// Set Up Physics Contact Delegate
physicsWorld.contactDelegate = self
}

@objc func handleFlick(sender: UIPanGestureRecognizer) {
if sender.state == .ended {
let velocity = sender.velocity(in: view)
let force = CGVector(dx: velocity.x / 10, dy: velocity.y / 10)
ball.physicsBody?.applyForce(force)
}
}

// Physics Contact Delegate Method
func didBegin(_ contact: SKPhysicsContact) {
let collision = contact.bodyA.categoryBitMask | contact.bodyB.categoryBitMask

if collision == PhysicsCategory.Ball | PhysicsCategory.Ground {
// Ball hit the ground
ball.physicsBody?.velocity.dy *= -0.5 // Bounce effect
} else if collision == PhysicsCategory.Ball | PhysicsCategory.Box {
// Ball hit the box
ball.physicsBody?.velocity.dy *= -0.5 // Bounce effect
}
}
}

// Physics Categories
struct PhysicsCategory {
static let Ball: UInt32 = 0b1
static let Ground: UInt32 = 0b10
static let Box: UInt32 = 0b100
}

extension GameScene: SKPhysicsContactDelegate {
func didEnd(_ contact: SKPhysicsContact) {
// Additional contact handling, if needed
}
}

…Yeah. I didn’t know either.

After asking ChatGPT, it suggested to check sprites might not have been correctly positioned and added on the screen. That’s pretty reasonable, and what I was thinking myself.

This didn’t seem to be the problem.

Following coding tradition, I decided to tackle possible problems one at a time. I then proceeded to go on a wild goose chase.

I said, let’s start over. Only give me a red box on the screen. I can’t possibly mess that up!

Nothing, blank screen.

I check if sprites have been added properly and positioned properly. Seems ok, I said. Oh, it’s not the sprite? Maybe it’s a problem with the rendering, ChatGPT said.

It gives me code to properly set up the scene and View.

Remember the boilerplate code your eyes probably glazed over earlier? It added a possible source of problems, and this produced some pain.

ChatGPT can be very wordy even for a simple problem. In some cases, it has not yet learned to give short, readable solutions based on your skill level when you first ask it a question.

It added an extra cognitive weight to my debugging process: Though it technically worked, the extra words and programming concepts were mentally getting in my way as I tried to isolate what my problem was.

Was the problem in the sprites? Was it in the physics? Or was it in the initial scene setup, given by ChatGPT itself?

Well, to find out, I’d have to learn how the code worked by asking ChatGPT. This can put you in a rabbit hole good for learning and gaining a deeper understanding. However, it’s not ideal for hitting goals and making an app quickly. I can see how in the future this wordiness can become a distraction over time if you build off its code, and the code is not simplified.

Anyways. I paste it in. Nope, doesn’t work.

Maybe you have experienced this yourself. ChatGPT will confidently give you suggestions, even wrong ones, and you will try them out. You will paste code over, and over, and over.

ChatGPT seemed to get stuck in an endless loop, at one point suggesting a solution I had already tried.

To be clear, I think this is a limitation of ChatGPT-3.5 being an offline AI model. It cannot search up-to-date answers online, unlike ChatGPT-4.0. Later I asked this same question to 4.0, and it got a more up-to-date answer, which I’ll get to shortly.

From the perspective of someone using the older version of ChatGPT, though, this is unexpected and maddening.

I think this showed one of AI’s current limitations:

If it doesn’t have a good solution, it will not tell you so. It will repeatedly attempt to give you answers, confidently.

You end up being led astray as you repeatedly attempt to solve your problem using the given solutions, while never getting closer to the real cause.

Do you know how I eventually solved this problem?

Google search.

A little disappointing, I know.

The problem with the above code, from my understanding at the time, was this little line here:

let scene = SKScene(size: skView.bounds.size)

The bounds size was zero. This gave the whole scene zero height and width.

Another line also affected visibility, causing sprite cropping problems:

scene.scaleMode = .aspectFill

ChatGPT did not catch this. I needed to find an online tutorial, went to Hacking with Swift, and, behold:


// A sample SwiftUI creating a GameScene and sizing it
// at 300x400 points
struct ContentView: View {
var scene: SKScene {
let scene = GameScene()
scene.size = CGSize(width: 300, height: 400)
scene.scaleMode = .fill
return scene
}

var body: some View {
SpriteView(scene: scene)
.frame(width: 300, height: 400)
.ignoresSafeArea()
}
}

Paste in the code. A couple things stood out to me immediately.

  • Defined scene size to match frame size. The scene scale mode is fill. Putting in the code, I could see sprites immediately.
  • It uses a SpriteView. ChatGPT did not mention this solution, but in my opinion it’s easier to read and understand. One line vs. ChatGPT’s 10 lines. No need to worry about errors in the previous code.

I threw the boilerplate away.

(Side note: This was the same solution ChatGPT-4.0 suggested after I wrote this. Unfortunately, back then I was using 3.5.)

Day 2–4

I then spent an agonizingly long time attempting to get my game to take up the whole width and height of the screen, then rotate to landscape mode.

Scene (in gray) does not fill the whole screen.
An attempt.
Another attempt. I’ll spare you the details, but this dragged on for way too long.

The problem was, I couldn’t articulate exactly why my scene was not taking up the whole width and height of my screen. It was because I lacked knowledge and context of what I wanted to do, and why.

ChatGPT is most useful if you already have a good grasp of the context or background of why you face problems in the first place.

This to me was true on multiple levels.

If you have experience in a domain, like terms and best practices in a job field, then it’s easier to find the context behind your problems. The same goes at a lower level, for example in the app developer field with specific software libraries.

If you know basic iOS development, then you will have an easier time understanding how your iPhone app is working with code. If you know how SpriteKit works, then you will have an easier time understanding how a SpriteKit game scene is sized relative to the screen.

I think people who already have experience in job fields will get the biggest productivity boost compared to people starting out, because they know the overall landscape of that field.

However, if you don’t know these things, you can take wrong approaches and focus on wrong solutions. You may ask ChatGPT to help you, but in the end it cannot help you when you already have a flawed mental model of what is going on.

I think you may have to quickly realize when that happens, and then use ChatGPT to build up a basic understanding of what your problem is, say by defining any terms related to whatever you’re dealing with, and how concepts relate to each other.

Like in my case, some of the problem was game domain knowledge. One approach to making a scrolling game is to have a large scene, extending past the entire phone or device, and then limiting the in-game camera to only show areas up to the boundaries of the scene.

ChatGPT’s answer might make sense here, because you can set a very large size for the scene that exceeds your device height and width. In my case, I had a small game scene and didn’t think about extending it or a camera at all. I lacked that pre-requisite knowledge to ask good questions.

Eventually, I decided to skip the screen size and landscape problems for the moment, and move on.

I don’t want to give the impression I’m ragging on ChatGPT. In fact, once I sorted that out the next few days were pretty nice.

After some wrestling, I had been forced to start over with just showing a ball.

I next switched the ball into a box, representing a chibi, and asked for flicking the box.

And I asked how to detect collision with a bed. Then re-introduced a shape to represent my bed. The bed I just defined as another box, but made it blue and rectangular.

The game concept was to toss a chibi onto a bed, so I wanted my first box to detect collision on the second box. If it collided, it would turn green.

This would represent detecting a chibi landing on a bed.

Because I wanted more than one chibi, I had 2 boxes and 1 bed next. I gave the 2 boxes different colors. 1 yellow and 1 red.

ChatGPT wrote some code and then helpfully informed me about collision bitmasks, which are used to separate physics category for objects. This seems specific to SpriteKit and is very confusing to look at for the untrained eye.

Cool. Figuring this out would’ve slowed me down.

The result:

The red and yellow boxes represent 2 chibis. The blue rectangular box represents a bed.

A lot of other great responses stood out.

I wanted code for a swipe gesture which tossed the square up in the air, kind of like the Angry Birds game. I would select a particular square and then flick to launch.

At first I tried to get a launching action on tapping my square. It gave me a solution using UITapGestureRecognizer. I got an error, but when I clarified I just wanted to tap with SpriteKit, it gave me a working solution.

After that, it suggested a way to toss a chibi in an arc shape. It then introduced the impulse method, which I didn’t know about, and gave me values for tossing the sprite.

It then helped me set up a camera to follow a square representing my chibi sprite.

Day 5–7

Later on, I wanted to switch my camera to the nearest box after the box I’d selected landed on a bed. This was to let a user select the next chibi to toss onto a bed. ChatGPT made a function to automatically calculate the distance to the nearest box. This was great because I didn’t even need to think about the math behind this — it just worked.

When I ran into a bug when the sprite wasn’t being detected at rest, it gave me a solution using velocity vector magnitude. This was crazy because I’m fairly sure I couldn’t have come up with this solution myself without a lot of trial and error.

Wouldn’t be able to math this at the top of my head.

Problem 3

There was one major problem that did come up.

I wanted the boxes to change color depending on whether a box was selected, was hitting the bed, or had left the bed. Selected boxes would be purple, hitting the bed would be green, and not hitting it would revert to its original color.

I think maybe I could have worded this more carefully. It could also be possible that ChatGPT didn’t have enough training data to help in my specific case, which was to have a box change color depending on three different states.

Since a box would represent a chibi later, it would have the states of selected, landing on bed, or no longer on bed.

As it hummed along, ChatGPT introduced code with a subtle logic bug built in.

Me introducing ChatGPT to the error made.

It had added a line, related to isBoxOnBed, to make both boxes detect when one had landed on a bed. This caused both to turn the wrong color, green.

If I didn’t color-code the boxes, I might not have spotted this early and proceeded with using code that didn’t work quite as I intended it to.

As for why it did this, I can only guess. Maybe ChatGPT isn’t optimized for coding the way some specialized coding AI assistants are? I did notice when I introduced a second box for example, it failed to automatically classify it in the box category for physics detection. A human usually wouldn’t make such a mistake.

I also saw previously it had tried to assign colors and boxes into separate lists, when putting both box sprite and its associated color together in the same data structure, i.e. a class, might have been more readable and reduce potential errors.

Result

The basic mechanics got done.

At the end I had a box which changed color depending on whether it was selected, hit the other box, or off the box.

If a box lands on the bed (blue rectangle), the box turns green. Glorious.

Friend and Enemy?

ChatGPT, at least the free version, is kind of like having another developer working with you. It’s really smart code-wise but very forgetful and a slacker . It’s very much your friend in that it will boost your ability to write code fast, but also your enemy because it can lead you into wrong paths and spend quite a bit of time forcing you to clarify what exactly you want to accomplish.

The problem is, doing that forces you to wrestle with more ambiguity, because you’re also constantly evaluating if what ChatGPT says is accurate.

If it is, then the problem could be in how you phrased your question or request. Then you have to think about what’s a good question that even a computer can understand. This can be helpful for your own thought process as a programmer, but it does take time.

And if it’s not accurate, you have to dig around the code itself. Sometimes it’s just a single ChatGPT-introduced line that’s causing an error, like the one where my boxes were not changing color correctly after leaving the box representing a bed.

If the problem you’re facing is common, then it’s very likely ChatGPT is accurate. For the most part I could trust what it said on certain problems. But when problems were more customized for my particular app, then it started to wander off and attempt to give me solutions that didn’t quite work.

This got me wondering about the future, because I’m optimistic AI in general will improve. I think over time, we will find that

People will focus more and more on having good higher-level concepts, and deal less with low-level concepts that AI can handle.

Like instead of worrying about how to toss a a chibi onto a bed, the AI could handle all of that by writing code. Then you can spend time thinking of potentially more interesting, high-level concepts. Like what would make this game more appealing in the first place? Instead of thinking about mechanics, you spend your brain power on something else.

Maybe move up in abstraction, into thinking about how your assets, user interaction, and physics will affect how users feel when they use your app. Maybe swap assets, like visual and audio, and easily tweak the AI code to give the player a more relaxed experience. Maybe have more of a soft toss instead of a hard launch. Maybe have more of a bouncy effect when there’s a collision. Considering if perhaps the game is too easy, or too simple to be engaging, and to introduce new features to fix that. Things like that.

Next

Now that I had the mechanics done, it was time to improve the visuals a little bit. I needed to replace my basic shapes of boxes with some images. That meant I could use my next tool, an AI image generator. Read about it here.

--

--

Michael Hsieh

California-based Mobile App Developer. Software Engineer. I like sharing what I've learned with others.