【python】pygameでスネーク風のゲームを作ってみた

## 背景
pygameでスネーク作りたい。

## スネークとは？

知らない方以外といそう。こちらをご参考にしてください。
https://www.coolmathgames.com/ja/blog/スネーク・ザ・ゲームの歴史

## ソースコード

```py main.py
import pygame
import sys
import random

# 初期化
pygame.init()

# 画面サイズの設定
width = 640
height = 480
screen = pygame.display.set_mode((width, height))

# 色の設定
white = (255, 255, 255)
green = (0, 255, 0)
red = (255, 0, 0)
black = (0, 0, 0)

# ゲームの状態
game_over = False
game_clear = False
game_start = False

clock = pygame.time.Clock()
snake_speed = 10

# スネークと食べ物の設定
snake_pos = [100, 50]
snake_body = [[100, 50], [90, 50], [80, 50]]
food_pos = [random.randrange(1, (width//10)) * 10, random.randrange(1, (height//10)) * 10]
food_spawn = True

direction = 'RIGHT'
change_to = direction

# スコアの初期化
score = 0

# クリア条件のスコア. これを超えたらクリア
clear_score = 10

# フォントの設定
font = pygame.font.SysFont('arial', 35)

def show_score(choice, color, font, size):
    score_font = pygame.font.SysFont(font, size)
    score_surface = score_font.render('Score : ' + str(score), True, color)
    score_rect = score_surface.get_rect()
    if choice == 1:
        score_rect.midtop = (width / 10, 15)
    else:
        score_rect.midtop = (width / 2, height / 1.25)
    screen.blit(score_surface, score_rect)

# タイトル画面
def show_title():
    title_font = pygame.font.SysFont('arial', 40)
    title_surface = title_font.render('Click to start Snake Game!', True, red)
    title_rect = title_surface.get_rect()
    title_rect.midtop = (width / 2, height / 4)
    screen.blit(title_surface, title_rect)
    pygame.display.flip()

# ゲームの更新とロジック
while True:
    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            pygame.quit()
            sys.exit()
        elif event.type == pygame.MOUSEBUTTONDOWN:
            if game_over or game_clear:
                # ゲームをリセット
                snake_pos = [100, 50]
                snake_body = [[100, 50], [90, 50], [80, 50]]
                direction = 'RIGHT'
                change_to = direction
                score = 0
                game_over = False
                game_clear = False
                food_spawn = True
            if not game_start:
                game_start = True
        elif event.type == pygame.KEYDOWN:
            if event.key == pygame.K_RIGHT and direction != 'LEFT':
                change_to = 'RIGHT'
            if event.key == pygame.K_LEFT and direction != 'RIGHT':
                change_to = 'LEFT'
            if event.key == pygame.K_UP and direction != 'DOWN':
                change_to = 'UP'
            if event.key == pygame.K_DOWN and direction != 'UP':
                change_to = 'DOWN'

    # 方向の確認
    if change_to == 'RIGHT' and direction != 'LEFT':
        direction = 'RIGHT'
    if change_to == 'LEFT' and direction != 'RIGHT':
        direction = 'LEFT'
    if change_to == 'UP' and direction != 'DOWN':
        direction = 'UP'
    if change_to == 'DOWN' and direction != 'UP':
        direction = 'DOWN'

    # スネークの移動
    if direction == 'RIGHT':
        snake_pos[0] += 10
    if direction == 'LEFT':
        snake_pos[0] -= 10
    if direction == 'UP':
        snake_pos[1] -= 10
    if direction == 'DOWN':
        snake_pos[1] += 10

    # スネークの体の成長
    snake_body.insert(0, list(snake_pos))
    if snake_pos[0] == food_pos[0] and snake_pos[1] == food_pos[1]:
        score += 1
        food_spawn = False
    else: snake_body.pop()

    # フードの再生成
    if not food_spawn:
        food_pos = [random.randrange(1, (width//10)) * 10, random.randrange(1, (height//10)) * 10]
    food_spawn = True

    # ゲームオーバー条件
    if snake_pos[0] < 0 or snake_pos[0] > width-10 or snake_pos[1] < 0 or snake_pos[1] > height-10:
        game_over = True
    for block in snake_body[1:]:
        if snake_pos == block:
            game_over = True

    # ゲームクリア条件
    if score > clear_score:
        game_clear = True

    # 画面の描画
    screen.fill(black)
    for pos in snake_body:
        pygame.draw.rect(screen, green, pygame.Rect(pos[0], pos[1], 10, 10))
    pygame.draw.rect(screen, red, pygame.Rect(food_pos[0], food_pos[1], 10, 10))

    # タイトル画面の表示
    if not game_start:
        show_title()

    # ゲームオーバー画面の表示
    if game_over:
        gameover_font = pygame.font.SysFont('arial', 50)
        gameover_surf = gameover_font.render('Game Over...', True, red)
        gameover_rect = gameover_surf.get_rect()
        gameover_rect.midtop = (width / 2, height / 3)
        screen.blit(gameover_surf, gameover_rect)
        show_score(0, red, 'arial', 25)

    # ゲームクリア画面の表示
    if game_clear:
        gameclear_font = pygame.font.SysFont('arial', 50)
        gameclear_surf = gameclear_font.render('Game Clear!', True, green)
        gameclear_rect = gameclear_surf.get_rect()
        gameclear_rect.midtop = (width / 2, height / 3)
        screen.blit(gameclear_surf, gameclear_rect)
        show_score(0, green, 'arial', 25)

    if not game_over and not game_clear and game_start:
        show_score(1, white, 'arial', 25)

    pygame.display.flip()
    clock.tick(snake_speed)


```

## 実行

```sh
$ python main.py
```

## 説明
- スタート画面
クリックでゲーム開始です。

![スクリーンショット 2024-07-09 11.54.13.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3327773/9ccc0ae5-7cca-ce31-dec1-f40d06eabc08.png)


- インゲーム画面
矢印キーでスネークを操作。
赤い四角を取得するとスコアが+1しスネークが成長します。

![スクリーンショット 2024-07-09 11.42.36.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3327773/e476ee02-fb0c-bec7-17c9-6e0c7b516172.png)

- ゲームオーバー条件
スネークが自身にぶつかったり、画面の端にぶつかるとゲームオーバーになります。
その後、画面をクリックすると再スタート出来ます。

![スクリーンショット 2024-07-09 11.43.10.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3327773/60e91742-a3cd-cad1-a9de-6a10287b3003.png)


- ゲームクリア条件
一定のスコア（ここでは10）を超えるとゲームクリアになります。
その後クリックで再スタート出来ます。

![スクリーンショット 2024-07-09 11.45.02.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3327773/713cd39e-6ae7-600f-85fb-f76f676de885.png)


## まとめ

できた！