0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

Tシャツ欲しさに Amazon Q Developer CLI で インベーダーゲームを作ってみた

Last updated at Posted at 2025-06-19

はじめに

遅ればせながら 『Amazon Q CLi Developer で ゲームを作ろう』 のキャンペーンに応募すべく、ゲームを作成しました。
Amazon Q Developer CLI はリリース当初からお世話になっている推しAIツールので、このTシャツは絶対に欲しい。

動作環境

  • Windows 11
  • WSL2
    • ubuntu
  • Amazon Q Developer CLI

セッティング

丁度、Claude4へのモデル変更が可能になったアップデートが入っていたので、せっかくなので最新のClaudeのモデルの性能も確認すべく、モデルの変更を実施。

/modelでモデル変更の選択肢が現れ、変更が可能に。
もちろん claude4-sonnetを選択。
毎度ですがターミナルに表示されるAAにはロマンがありますね...

スクリーンショット 2025-06-10 031525.png

q chat --model claude-4-sonnetで起動時のモデルをclaude4に変更できるので、マストで変更しておきます。

スクリーンショット 2025-06-10 031438.png

今回作ったもの

インベーダーゲーム

ベタですがレトロゲームをサクッと作ってみたかったのでインベーダーゲームにしました。

実装

Claude4のパワーもあり、3,4回のやり取りで完成しました。

一発目の出力でほぼほぼインベーダーゲームとしての機能はできあがりました。

  • 自機・敵機の存在
  • 自弾・敵弾の当たり判定
  • 衝突判定からの消滅
  • 左右への移動・全身

スクリーンショット 2025-06-10 033241.png

しかしながら自機も敵機も完全に豆腐で色気が無さすぎるので、もう少しグラフィカルに、かつゲーム性を持たせるためスコア表示機能の追加を依頼。

スクリーンショット 2025-06-10 033748.png

追加で以下の機能も実装しました。

  • 残機の追加
  • ステージの概念(3面クリアで終了)
  • クリアタイムの記録
  • GAME OVER 機能
  • 敵弾が命中した際の点滅エフェクト
  • 自弾が命中した際の爆発エフェクト

開始時は敵機の数が多いのでなかなかの弾幕が降り注ぎます。

image.png

舐めてると意外と簡単にやられてしまいますね。

image.png

3面倒しきるとクリアです!

スクリーンショット 2025-06-10 035827.png

まとめ

普段はAWSリソースのパラメータ取得やエラー調査ばかり依頼しているAmazon Q Developer CLIですが、Claude4にアップグレードしたこともあり、これくらいのゲームなら余裕で作ってくれますね。

リリース当初から頻繁なアップデートによりどんどん使いやすくなっていくAmazon Q Developer CLI 君に今後も期待しています!!

作成してくれたコード

良かったら遊んでみてください!

kuraken_space_invaders.py
import pygame
import random
import sys

# Initialize Pygame
pygame.init()

# Screen settings
SCREEN_WIDTH = 800
SCREEN_HEIGHT = 600
screen = pygame.display.set_mode((SCREEN_WIDTH, SCREEN_HEIGHT))
pygame.display.set_caption("Space Invaders - Advanced")

# Colors
WHITE = (255, 255, 255)
BLACK = (0, 0, 0)
GREEN = (0, 255, 0)
RED = (255, 0, 0)
BLUE = (0, 0, 255)
YELLOW = (255, 255, 0)
ORANGE = (255, 165, 0)
GRAY = (128, 128, 128)

# Font
font = pygame.font.Font(None, 36)
small_font = pygame.font.Font(None, 24)

# Player class
class Player(pygame.sprite.Sprite):
    def __init__(self):
        super().__init__()
        self.image = pygame.Surface([50, 30], pygame.SRCALPHA)
        self.original_image = pygame.Surface([50, 30], pygame.SRCALPHA)
        self.draw_fighter()
        self.rect = self.image.get_rect()
        self.rect.x = SCREEN_WIDTH // 2
        self.rect.y = SCREEN_HEIGHT - 50
        self.speed = 5
        self.lives = 3
        self.invincible = False
        self.invincible_timer = 0
        self.blink_timer = 0

    def draw_fighter(self):
        """Draw a fighter jet shape"""
        # Clear both surfaces
        self.image.fill((0, 0, 0, 0))
        self.original_image.fill((0, 0, 0, 0))
        
        # Main body
        pygame.draw.polygon(self.image, GREEN, [
            (25, 0),   # nose
            (15, 20),  # left wing back
            (20, 25),  # left body
            (30, 25),  # right body
            (35, 20),  # right wing back
            (25, 0)    # back to nose
        ])
        # Wings
        pygame.draw.polygon(self.image, GRAY, [
            (10, 15), (15, 20), (20, 25), (15, 25)
        ])
        pygame.draw.polygon(self.image, GRAY, [
            (40, 15), (35, 20), (30, 25), (35, 25)
        ])
        # Cockpit
        pygame.draw.circle(self.image, BLUE, (25, 10), 3)
        
        # Copy to original image
        self.original_image.blit(self.image, (0, 0))

    def take_damage(self):
        """Handle taking damage"""
        if not self.invincible:
            self.lives -= 1
            self.invincible = True
            self.invincible_timer = 120  # 2 seconds at 60 FPS
            return True
        return False

    def update(self):
        keys = pygame.key.get_pressed()
        if keys[pygame.K_LEFT] and self.rect.left > 0:
            self.rect.x -= self.speed
        if keys[pygame.K_RIGHT] and self.rect.right < SCREEN_WIDTH:
            self.rect.x += self.speed
        
        # Handle invincibility and blinking
        if self.invincible:
            self.invincible_timer -= 1
            self.blink_timer += 1
            
            # Blink every 8 frames
            if self.blink_timer % 8 < 4:
                # Make transparent (blink off)
                self.image.fill((0, 0, 0, 0))
            else:
                # Show normal image (blink on)
                self.image.fill((0, 0, 0, 0))
                self.image.blit(self.original_image, (0, 0))
            
            # End invincibility
            if self.invincible_timer <= 0:
                self.invincible = False
                self.blink_timer = 0
                # Restore normal image
                self.image.fill((0, 0, 0, 0))
                self.image.blit(self.original_image, (0, 0))

# Enemy class
class Enemy(pygame.sprite.Sprite):
    def __init__(self, x, y):
        super().__init__()
        self.image = pygame.Surface([30, 30], pygame.SRCALPHA)
        self.draw_ufo()
        self.rect = self.image.get_rect()
        self.rect.x = x
        self.rect.y = y
        self.speed_x = 2
        self.speed_y = 20
        self.direction = 1
        self.shoot_timer = random.randint(60, 300)

    def draw_ufo(self):
        """Draw a UFO shape"""
        # Main body (ellipse)
        pygame.draw.ellipse(self.image, GRAY, (5, 10, 20, 10))
        # Top dome
        pygame.draw.ellipse(self.image, RED, (8, 5, 14, 12))
        # Bottom lights
        pygame.draw.circle(self.image, YELLOW, (10, 15), 2)
        pygame.draw.circle(self.image, YELLOW, (15, 15), 2)
        pygame.draw.circle(self.image, YELLOW, (20, 15), 2)

    def update(self):
        # Move horizontally
        self.rect.x += self.speed_x * self.direction
        
        # Check if this enemy hits the screen edge
        if self.rect.right >= SCREEN_WIDTH or self.rect.left <= 0:
            # Move all enemies down and reverse their direction
            for enemy in enemies:
                enemy.rect.y += enemy.speed_y
                enemy.direction *= -1
            # Move back from the edge to prevent getting stuck
            if self.rect.right >= SCREEN_WIDTH:
                self.rect.right = SCREEN_WIDTH
            else:
                self.rect.left = 0
        
        # Shoot randomly
        self.shoot_timer -= 1
        if self.shoot_timer <= 0:
            self.shoot_timer = random.randint(300, 600)
            enemy_bullet = EnemyBullet(self.rect.centerx, self.rect.bottom)
            enemy_bullets.add(enemy_bullet)
            all_sprites.add(enemy_bullet)

# Player bullet class
class Bullet(pygame.sprite.Sprite):
    def __init__(self, x, y):
        super().__init__()
        self.image = pygame.Surface([4, 10])
        self.image.fill(WHITE)
        self.rect = self.image.get_rect()
        self.rect.x = x
        self.rect.y = y
        self.speed = 7

    def update(self):
        self.rect.y -= self.speed
        if self.rect.bottom < 0:
            self.kill()

# Enemy bullet class
class EnemyBullet(pygame.sprite.Sprite):
    def __init__(self, x, y):
        super().__init__()
        self.image = pygame.Surface([4, 10])
        self.image.fill(RED)
        self.rect = self.image.get_rect()
        self.rect.x = x
        self.rect.y = y
        self.speed = 3

    def update(self):
        self.rect.y += self.speed
        if self.rect.top > SCREEN_HEIGHT:
            self.kill()

# Explosion effect class
class Explosion(pygame.sprite.Sprite):
    def __init__(self, x, y, score=0):
        super().__init__()
        self.images = []
        # Create explosion animation frames
        for size in [20, 25, 30, 25, 20, 15, 10]:
            img = pygame.Surface([size, size], pygame.SRCALPHA)
            pygame.draw.circle(img, ORANGE, (size//2, size//2), size//2)
            self.images.append(img)
        
        self.index = 0
        self.image = self.images[self.index]
        self.rect = self.image.get_rect()
        self.rect.center = (x, y)
        self.counter = 0
        
        # Score display
        self.score = score
        self.score_counter = 30  # frames to show score
        self.score_y_offset = 0

    def update(self):
        self.counter += 1
        if self.counter >= 4:  # Update animation every 4 frames
            self.counter = 0
            self.index += 1
            if self.index >= len(self.images):
                self.kill()
            else:
                self.image = self.images[self.index]
                
        # Update floating score position
        if self.score > 0:
            self.score_y_offset -= 1
            self.score_counter -= 1
            if self.score_counter <= 0:
                self.score = 0

# Game state
class GameState:
    def __init__(self):
        self.score = 0
        self.level = 1
        self.game_over = False
        self.victory = False
        self.level_clear = False
        self.start_time = pygame.time.get_ticks()
        self.level_start_time = pygame.time.get_ticks()
        self.clear_time = 0

def draw_text(text, x, y, color=WHITE):
    """Draw text on screen"""
    text_surface = font.render(text, True, color)
    screen.blit(text_surface, (x, y))

def game_over_screen():
    """Display game over screen"""
    screen.fill(BLACK)
    draw_text("GAME OVER", SCREEN_WIDTH//2 - 100, SCREEN_HEIGHT//2 - 50, RED)
    draw_text(f"Final Score: {game_state.score}", SCREEN_WIDTH//2 - 120, SCREEN_HEIGHT//2)
    draw_text("Press R to restart or Q to quit", SCREEN_WIDTH//2 - 180, SCREEN_HEIGHT//2 + 50)
    pygame.display.flip()

def victory_screen():
    """Display victory screen"""
    screen.fill(BLACK)
    draw_text("VICTORY!", SCREEN_WIDTH//2 - 80, SCREEN_HEIGHT//2 - 50, GREEN)
    draw_text(f"Final Score: {game_state.score}", SCREEN_WIDTH//2 - 120, SCREEN_HEIGHT//2)
    draw_text("Press R to restart or Q to quit", SCREEN_WIDTH//2 - 180, SCREEN_HEIGHT//2 + 50)
    pygame.display.flip()

def level_clear_screen():
    """Display level clear screen"""
    screen.fill(BLACK)
    draw_text("LEVEL CLEAR!", SCREEN_WIDTH//2 - 100, SCREEN_HEIGHT//2 - 50, GREEN)
    clear_time = (game_state.clear_time - game_state.level_start_time) / 1000  # Convert to seconds
    draw_text(f"Clear Time: {clear_time:.2f} seconds", SCREEN_WIDTH//2 - 150, SCREEN_HEIGHT//2)
    draw_text(f"Score: {game_state.score}", SCREEN_WIDTH//2 - 80, SCREEN_HEIGHT//2 + 50)
    draw_text("Press SPACE to continue", SCREEN_WIDTH//2 - 150, SCREEN_HEIGHT//2 + 100)
    pygame.display.flip()

def create_enemies():
    """Create a new wave of enemies"""
    for row in range(5):
        for column in range(8):
            enemy = Enemy(80 + column * 80, 50 + row * 50)
            enemies.add(enemy)
            all_sprites.add(enemy)
    # Reset level start time when creating new enemies
    game_state.level_start_time = pygame.time.get_ticks()

# Create sprite groups
all_sprites = pygame.sprite.Group()
enemies = pygame.sprite.Group()
bullets = pygame.sprite.Group()
enemy_bullets = pygame.sprite.Group()
explosions = pygame.sprite.Group()

# Create player
player = Player()
all_sprites.add(player)

# Create game state
game_state = GameState()

# Create initial enemies
create_enemies()

# Game loop
running = True
clock = pygame.time.Clock()

while running:
    # Event handling
    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            running = False
        elif event.type == pygame.KEYDOWN:
            if game_state.game_over or game_state.victory:
                if event.key == pygame.K_r:
                    # Restart game
                    game_state = GameState()
                    player.lives = 3
                    player.rect.x = SCREEN_WIDTH // 2
                    player.rect.y = SCREEN_HEIGHT - 50
                    # Clear all sprites except player
                    for sprite in all_sprites:
                        if sprite != player:
                            sprite.kill()
                    enemies.empty()
                    bullets.empty()
                    enemy_bullets.empty()
                    explosions.empty()
                    create_enemies()
                elif event.key == pygame.K_q:
                    running = False
            elif game_state.level_clear:
                if event.key == pygame.K_SPACE:
                    # Continue to next level
                    game_state.level_clear = False
                    game_state.level += 1
                    create_enemies()
                    # Increase difficulty
                    for enemy in enemies:
                        enemy.speed_x += 0.5
            else:
                if event.key == pygame.K_SPACE:
                    bullet = Bullet(player.rect.centerx - 2, player.rect.top)
                    bullets.add(bullet)
                    all_sprites.add(bullet)

    if not game_state.game_over and not game_state.victory and not game_state.level_clear:
        # Update
        all_sprites.update()
        explosions.update()

        # Collision detection - player bullets hit enemies
        hits = pygame.sprite.groupcollide(enemies, bullets, True, True)
        for enemy in hits:
            # Create explosion effect
            explosion = Explosion(enemy.rect.centerx, enemy.rect.centery, 10)
            explosions.add(explosion)
            all_sprites.add(explosion)
            game_state.score += 10

        # Collision detection - enemy bullets hit player
        player_hits = pygame.sprite.spritecollide(player, enemy_bullets, True)
        if player_hits and player.take_damage():
            if player.lives <= 0:
                game_state.game_over = True

        # Check if enemies reach player
        if pygame.sprite.spritecollide(player, enemies, False):
            game_state.game_over = True

        # Check if all enemies are destroyed
        if len(enemies) == 0:
            game_state.clear_time = pygame.time.get_ticks()
            game_state.level_clear = True

        # Check victory condition (for demo purposes, victory at level 3)
        if game_state.level > 3:
            game_state.victory = True

    # Draw
    if game_state.game_over:
        game_over_screen()
    elif game_state.victory:
        victory_screen()
    elif game_state.level_clear:
        level_clear_screen()
    else:
        screen.fill(BLACK)
        all_sprites.draw(screen)
        explosions.draw(screen)
        
        # Draw floating scores from explosions
        for explosion in explosions:
            if explosion.score > 0:
                score_text = small_font.render(f"+{explosion.score}", True, YELLOW)
                screen.blit(score_text, (explosion.rect.centerx - 10, 
                                       explosion.rect.centery - 20 + explosion.score_y_offset))
        
        # Draw UI
        draw_text(f"Score: {game_state.score}", 10, 10)
        draw_text(f"Lives: {player.lives}", 10, 50)
        draw_text(f"Level: {game_state.level}", 10, 90)
        
        # Draw elapsed time
        elapsed_time = (pygame.time.get_ticks() - game_state.level_start_time) / 1000
        draw_text(f"Time: {elapsed_time:.2f}", SCREEN_WIDTH - 150, 10)
        
        pygame.display.flip()

    # Control game speed
    clock.tick(60)

# Quit game
pygame.quit()
sys.exit()

``` 
</details>
0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?