So, I Made a Game: carboldie.gr
Table of Contents
- So, I Made a Game: carboldie.gr
- Table of Contents
- The Idea
- Getting Started with AI
- Building the Game
- Setting Up
- Game Logic
- Visual Stuff
- Shoutout to Kanella D. for designing all these 8-bit elements from scratch.
- Diving into the Code
- Canvas Setup
- Game Loop
- Player Movement
- Adding Complexity
- Power-ups
- Difficulty Scaling
- Particle Effects
- Oops! Mistakes I Made
- Cool Code Bits
- What I Learned
- Future Ideas
- Wrapping Up
- Shoutout to Kanella D. for designing all these 8-bit elements from scratch.
The Idea
So, I made a game. It's nothing fancy, just a simple browser game featuring CarBoldie, our company mascot at instacar. I'm not a game developer - or any developer at all - by any stretch, but I thought it'd be fun to try. Plus, who doesn't like 8-bit games, right?
Getting Started with AI
I decided to use Claude AI to help me out. Here's how our conversation started:
I would like you to create a simple, HTML5-based arcade game inspired by classic arcade games like catch game. The game should feature my company's mascot, CarBoldie. The key requirements are: CarBoldie Mascot: CarBoldie, my company's mascot, should be the main playable character. Difficulty Progression: As the player progresses through the game, the difficulty should increase. This could be achieved by bad blocks move faster, introduce new spinning obstacles, and increase in number the bad blocks. Responsive Design: Ensure the game is optimized for both desktop and mobile devices, providing a seamless experience across platforms. HTML5 Implementation: Develop the game using HTML5, CSS, and JavaScript, making it easy to deploy on my website. Simplicity and Accessibility: Keep the gameplay simple and intuitive, with one-button controls, so it's easy for players of all skill levels to pick up and play. Replayability: Consider adding features like high scores and leaderboards to encourage players to keep coming back and trying to beat their best score. Provide a step by step guide on how to implement it. Use simple and clear guidelines because I am a complete beginner to coding. Provide the code and step by step guide on how to deploy it. I use macbook and vscode. Help me create the code and also create a step by step guide explaining to me in detail what you are doing
- Set up the HTML
- Create the player object
- Implement player movement
- Create falling objects
- Implement collision detection
- Add scoring system
- Implement game over conditions
Response: Let's start with setting up the basic HTML structure and canvas. Here's a simple HTML file to get you started:"
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>CarBoldie Game</title>
<style>
canvas {
border: 1px solid black;
}
</style>
</head>
<body>
<canvas id="gameCanvas" width="400" height="600"></canvas>
<script src="game.js"></script>
</body>
</html>
This was a great starting point, but I realized I wanted to add more elements to make the game feel more complete. So, I asked Claude to help me expand on this.
Building the Game
Setting Up
I asked Claude to help me create a more comprehensive HTML structure with start and game over screens. Here's what we came up with:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>CarBoldie Game</title>
<link rel="stylesheet" href="style.css">
</head>
<body>
<div id="gameContainer">
<div id="startScreen">
<h1>CARBOLDIE GAME</h1>
<div id="gifContainer">
<img id="startGif" src="images/carboldie.gif" alt="CarBoldie">
</div>
<button id="startButton">START GAME</button>
</div>
<div id="gameOverScreen" style="display: none;">
<h1>Game <span class="over-text">Over</span></h1>
<img id="trophy" src="images/trophy.png" alt="Trophy">
<p>Your Score: <span id="finalScore"></span></p>
<button id="tryAgainButton">Play Again</button>
</div>
<canvas id="gameCanvas"></canvas>
</div>
<script src="game.js"></script>
</body>
</html>
When I asked Claude about this structure, it explained:
"This HTML structure provides a more complete game experience. The gameContainer
div holds all the game elements. We have a startScreen
with a title, a GIF of CarBoldie, and a start button. There's also a gameOverScreen
that's initially hidden, which shows the player's score and a 'Play Again' button. The canvas is where the main game will be rendered. This setup allows for a more polished user experience with clear game states."
Game Logic
The game logic was the trickiest part. I had to ask Claude multiple times about different aspects. Here's an example of one of our exchanges:
Me: "How do I handle object creation and collision detection?"
Claude: "For object creation and collision detection, you'll want to create functions that handle these tasks. Here's a basic example of how you might structure this:"
const objects = [];
function createObject() {
const object = {
x: Math.random() * (canvas.width - 30),
y: 0,
width: 30,
height: 30,
speed: Math.random() * 2 + 1,
type: Math.random() < 0.7 ? 'good' : 'bad'
};
objects.push(object);
}
function checkCollision(player, object) {
return (
player.x < object.x + object.width &&
player.x + player.width > object.x &&
player.y < object.y + object.height &&
player.y + player.height > object.y
);
}
function updateObjects() {
for (let i = objects.length - 1; i >= 0; i--) {
const obj = objects[i];
obj.y += obj.speed;
if (checkCollision(player, obj)) {
if (obj.type === 'good') {
score++;
} else {
gameOver();
}
objects.splice(i, 1);
} else if (obj.y > canvas.height) {
objects.splice(i, 1);
}
}
}
Claude explained: "The createObject
function generates new objects with random positions and types. The checkCollision
function uses a simple rectangle collision detection algorithm. The updateObjects
function moves all objects, checks for collisions with the player, and removes objects that are either collected or off-screen."
This was super helpful, but I realized I needed to modify it to fit my specific game needs. For example, I needed different types of objects, not just 'good' and 'bad'. I later added ‘special blocks’ such as the magnet and the shield!
Visual Stuff
For the visuals, I wanted to use images instead of simple shapes. When I asked Claude about this, it suggested using the Image
object in JavaScript:
const playerImage = new Image();
playerImage.src = 'images/carboldie.png';
const goodObjectImage = new Image();
goodObjectImage.src = 'images/good_object.png';
const badObjectImage = new Image();
badObjectImage.src = 'images/bad_object.png';
function drawPlayer() {
ctx.drawImage(playerImage, player.x, player.y, player.width, player.height);
}
function drawObjects() {
objects.forEach(obj => {
const img = obj.type === 'good' ? goodObjectImage : badObjectImage;
ctx.drawImage(img, obj.x, obj.y, obj.width, obj.height);
});
}
Claude explained: "These functions use the drawImage
method of the canvas context to render images instead of shapes. The Image
objects are created and their src
properties are set to the appropriate image files. The drawPlayer
and drawObjects
functions then use these images to render the game elements."
Shoutout to Kanella D. for designing all these 8-bit elements from scratch.
Diving into the Code
Let's take a closer look at some of the key code blocks and what they do.
Canvas Setup
const canvas = document.getElementById('gameCanvas');
const ctx = canvas.getContext('2d');
// Set game dimensions
const GAME_WIDTH = 400;
const GAME_HEIGHT = 700;
let scale;
function resizeCanvas() {
const containerWidth = gameContainer.clientWidth;
const containerHeight = gameContainer.clientHeight;
if (window.innerWidth <= 767) { // Mobile view
canvas.width = containerWidth;
canvas.height = containerHeight;
canvas.style.width = `${containerWidth}px`;
canvas.style.height = `${containerHeight}px`;
scale = Math.max(containerWidth / GAME_WIDTH, containerHeight / GAME_HEIGHT);
} else { // Desktop view
scale = Math.min(containerWidth / GAME_WIDTH, containerHeight / GAME_HEIGHT);
canvas.width = GAME_WIDTH;
canvas.height = GAME_HEIGHT;
canvas.style.width = `${GAME_WIDTH * scale}px`;
canvas.style.height = `${GAME_HEIGHT * scale}px`;
}
// Adjust game elements based on new scale
player.x = (player.x / GAME_WIDTH) * canvas.width;
player.y = canvas.height - 80; // Adjust player position
}
When I asked Claude about making the game responsive, it provided this code and explained:
"This resizeCanvas
function ensures that the game looks good on both desktop and mobile devices. It calculates a scale factor based on the screen size and adjusts the canvas and game elements accordingly. For mobile devices (width <= 767px), it fills the entire screen. For desktop, it maintains the game's aspect ratio while fitting within the available space."
Game Loop
function update() {
if (gameState !== 'playing') return;
ctx.clearRect(0, 0, canvas.width, canvas.height);
updateBackground();
updatePlayer();
updateObjects();
createObjects();
checkCollisions();
updateScore();
requestAnimationFrame(update);
}
I asked Claude about how to structure the main game loop, and it provided this code, explaining:
"The update
function is the heart of the game loop. It clears the canvas, updates all game elements, checks for collisions, updates the score, and then requests the next animation frame. This creates a smooth, continuous game experience. The requestAnimationFrame
method is used instead of setInterval
for better performance and smoother animations."
Player Movement
function updatePlayer() {
if (keys['ArrowLeft'] && player.x > 0) {
player.x -= player.speed;
createParticle(player.x + player.width, player.y + player.height / 2, 'right');
}
if (keys['ArrowRight'] && player.x < canvas.width - player.width) {
player.x += player.speed;
createParticle(player.x, player.y + player.height / 2, 'left');
}
player.x = Math.max(0, Math.min(canvas.width - player.width, player.x));
}
When I asked about implementing smooth player movement, Claude provided this code and explained:
"This function handles player movement based on keyboard input. It checks if the left or right arrow keys are pressed (stored in the keys
object) and moves the player accordingly. The Math.max
and Math.min
functions ensure the player stays within the canvas boundaries. The createParticle
function adds a visual effect when the player moves."
Adding Complexity
Once I had the basic game working, I wanted to make it more interesting. I asked Claude for ideas on how to add complexity to the game.
Power-ups
Adding power-ups was an afterthought, but it really made the game more fun. I asked Claude how to implement a shield power-up, and it suggested something like this:
const player = {
// ... other properties ...
isShielded: false,
shieldTimer: 0
};
function updatePlayer() {
// ... other update logic ...
if (player.isShielded) {
player.shieldTimer--;
if (player.shieldTimer <= 0) {
player.isShielded = false;
}
}
}
function checkCollision(player, object) {
if (checkCollisionGeometry(player, object)) {
if (object.type === 'shield') {
player.isShielded = true;
player.shieldTimer = 300; // 5 seconds at 60 fps
return false; // Don't count as collision, just pick up the shield
}
if (object.type === 'bad' && player.isShielded) {
return false; // Shield protects from bad objects
}
return true;
}
return false;
}
Claude explained: "This code adds a shield power-up to the game. The player object now has isShielded
and shieldTimer
properties. When the player collects a shield, isShielded
is set to true and the timer starts. The shield protects the player from bad objects for a limited time. The updatePlayer
function decrements the shield timer and disables the shield when it reaches zero."
I thought this was cool, but I wanted to add more types of power-ups. So, I asked Claude about implementing a magnet power-up that attracts good objects. Here's what we came up with:
const player = {
// ... other properties ...
hasMagnet: false,
magnetTimer: 0,
magnetRange: 150
};
function updatePlayer() {
// ... other update logic ...
if (player.hasMagnet) {
player.magnetTimer--;
if (player.magnetTimer <= 0) {
player.hasMagnet = false;
} else {
attractObjects();
}
}
}
function attractObjects() {
objects.forEach(obj => {
if (obj.type === 'good' && !obj.avoid) {
const dx = player.x - obj.x;
const dy = player.y - obj.y;
const distance = Math.sqrt(dx * dx + dy * dy);
if (distance < player.magnetRange) {
const angle = Math.atan2(dy, dx);
obj.x += Math.cos(angle) * 5;
obj.y += Math.sin(angle) * 5;
}
}
});
}
Claude explained: "This code implements a magnet power-up. When active, it attracts good objects within a certain range towards the player. The attractObjects
function calculates the distance and angle between the player and each good object, then moves the objects towards the player if they're within range."
Difficulty Scaling
To keep the game challenging, I asked Claude about implementing difficulty scaling. Here's what we came up with:
let gameSpeed = 2;
let lastSpeedIncrease = 0;
let difficultyMultiplier = 1;
function updateGameDifficulty() {
if (Date.now() - lastSpeedIncrease > 15000) {
gameSpeed += 0.2;
difficultyMultiplier += 0.15;
lastSpeedIncrease = Date.now();
}
}
function createObject() {
// ... existing object creation logic ...
object.speed *= gameSpeed;
if (Math.random() < 0.1 * difficultyMultiplier) {
object.isFast = true;
object.speed *= 1.5;
}
}
Claude explained: "This code gradually increases the game's difficulty over time. Every 15 seconds, the gameSpeed
and difficultyMultiplier
increase. The createObject
function uses these values to make objects fall faster and increase the chance of creating 'fast' objects as the game progresses."
Particle Effects
To add some visual flair, I asked Claude about implementing a simple particle system. Here's what we came up with:
const particles = [];
function createParticle(x, y, color) {
return {
x: x,
y: y,
size: Math.random() * 3 + 2,
speedX: Math.random() * 4 - 2,
speedY: Math.random() * 4 - 2,
color: color,
life: 30
};
}
function createExplosion(x, y, color, particleCount) {
for (let i = 0; i < particleCount; i++) {
particles.push(createParticle(x, y, color));
}
}
function updateParticles() {
for (let i = particles.length - 1; i >= 0; i--) {
const p = particles[i];
p.x += p.speedX;
p.y += p.speedY;
p.life--;
if (p.life <= 0) {
particles.splice(i, 1);
} else {
ctx.fillStyle = p.color;
ctx.globalAlpha = p.life / 30;
ctx.beginPath();
ctx.arc(p.x, p.y, p.size, 0, Math.PI * 2);
ctx.fill();
ctx.globalAlpha = 1;
}
}
}
Claude explained: "This particle system creates visual effects for explosions and player movement. The createParticle
function generates individual particles with random properties. The createExplosion
function creates multiple particles at once for a burst effect. The updateParticles
function moves and draws all particles, removing them when their lifespan is over."
Oops! Mistakes I Made
Oh boy, did I make mistakes. Here are a few big ones:
- Forgetting to Clear the Canvas: At first, my objects left trails across the screen. Claude pointed out that I needed to clear the canvas before each draw:
function update() {
ctx.clearRect(0, 0, canvas.width, canvas.height);
// ... rest of update function
}
- Memory Leak with Particles: I added a particle system for explosions, but forgot to remove old particles. The game started lagging after a while. Claude helped me fix it:
function updateParticles() {
for (let i = particles.length - 1; i >= 0; i--) {
const p = particles[i];
p.life--;
if (p.life <= 0) {
particles.splice(i, 1);
} else {
// Update and draw particle
}
}
}
- Not Handling Screen Resizes: The game looked weird on different screen sizes. Claude suggested adding an event listener for resize events:
window.addEventListener('resize', function() {
resizeCanvas();
});
function resizeCanvas() {
// ... resize logic ...
// Adjust game elements based on new size
player.x = (player.x / GAME_WIDTH) * canvas.width;
player.y = canvas.height - 80;
}
Oh man, this screwed me multiple times!
Cool Code Bits
Here are some parts of the code that I think are pretty neat:
Weighted Random Object Creation:
function createObject() {
const totalWeight = objectTypes.reduce((sum, type) => sum + type.weight, 0);
let randomWeight = Math.random() * totalWeight;
let selectedType;
const badObjectBias = Math.min((gameSpeed - 2) * 0.1, 0.5);
const powerUpBoost = 0.05;
for (const type of objectTypes) {
let adjustedWeight = type.weight;
if (type.avoid) {
adjustedWeight *= (1 + badObjectBias);
} else if (type.powerUp) {
adjustedWeight *= (1 + powerUpBoost);
}
if (randomWeight < adjustedWeight) {
selectedType = type;
break;
}
randomWeight -= adjustedWeight;
}
// ... create object based on selectedType
}
This function uses a weighted random selection to choose which type of object to create. It adjusts the weights based on the current game speed, making bad objects more common as the game progresses.
What I Learned
- AI is Pretty Cool: Claude was super helpful. It's like having a patient teacher who's always ready to explain things.
- Start Simple, Then Add Complexity: I started with a basic game and gradually added features. This made the process less overwhelming. Keep iterating!
- Test Early, Test Often: I caught a lot of bugs by playing the game frequently during development. Live server addon in vscode helped a lot!
- Don't Be Afraid to Ask: When I was stuck, I asked Claude for help. Sometimes it took a few tries to get the right answer, but persistence paid off.
- Performance Matters: I learned the importance of optimizing for performance, especially with things like particle systems and collision detection.
- Responsive Design is Tricky: Making the game work well on both desktop and mobile was challenging but important.
Future Ideas
I'm pretty happy with how the game turned out, but there's always room for improvement. Here are some ideas I'm considering for future updates:
- Leaderboard: Implement a server-side leaderboard to track high scores.
- Power-up Combos: Allow power-ups to combine for interesting effects.
- Boss Battles: Add occasional boss encounters for extra challenge.
- Customizable Characters: Let players choose different characters or customize CarBoldie.
- Sound Effects and Music: Add audio to enhance the gaming experience.
What else should I add?
Wrapping Up
So, that's how I made a game with the help of AI. It's not perfect, but it works, and I learned a ton in the process. If you're thinking about trying something similar, go for it! You might surprise yourself with what you can create.
Remember, the key is to start simple and build up gradually. Don't be afraid to make mistakes - they're all part of the learning process. And don't hesitate to ask for help, whether it's from AI assistants like Claude or from other developers.
This project showed me that with a bit of creativity and persistence, you can create something fun and engaging, even if you're not an expert game developer.
As always,
Keep iterating and stay curious!