メリークリスマス！クリスマスツリーを飾ってサンタさんをお出迎え！pythonで簡単なゲーム作ってみた！


# メリークリスマス！
起動すると下のような画面が表示されます。ちなみに画像なので少し分かりづらいですが、周りにあるまばらな点はイルミネーションのつもりです。

<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3682615/42858ca9-7864-04e0-456b-8bb0705d99ca.png" width=500>

クリスマスツリーの中に少し大きめの白丸が4つあるのですが、ここをクリックすると飾り付けできます。
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3682615/56e1062a-26fd-5008-f2e2-8f7ee7826bb6.png" width=500>

全て飾り付けが終わると雪が降り始めて、サンタさんがやってくるという仕様です。
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3682615/f5dd4736-8d36-63ec-fe2b-88b02cbeda97.png" width=500>

# 実装

<details>
<summary>コードまとめ</summary>

```python
import pygame
import random
import sys
import math

# 初期設定
pygame.init()
screen = pygame.display.set_mode((800, 600))
pygame.display.set_caption("クリスマスツリー")
clock = pygame.time.Clock()

# 色の定義
GREEN = (34, 139, 34)
BROWN = (139, 69, 19)
WHITE = (255, 255, 255)
ORANGE = (255, 165, 0)
YELLOW = (255, 255, 0)
BLUE = (0, 0, 255)
RED = (255, 0, 0)

# イルミネーションの位置
lights = [(random.randint(300, 500), random.randint(150, 500)) for _ in range(25)]

# 飾り付けすべき位置
decor_positions = [(360, 235), (445, 300), (320, 380), (480, 480)]
placed_decorations = []

# 状態
game_over = False

# サンタの画像をロード
santa_image = pygame.image.load("santa_claus.png")
santa_image = pygame.transform.scale(santa_image, (450, 450))  # サンタ画像のサイズを調整
santa_position = (175, 150)  # サンタ画像の表示位置

# デコレーションの画像をロードしてリストに追加
decor_image_paths = [
    "decorations/christmas_oranament_red.png", 
    "decorations/christmas_oranament_orange.png", 
    "decorations/christmas_oranament_blue.png", 
    "decorations/christmas_oranament_green.png"
    ]

def load_decorations():
    decorations = []
    for path in decor_image_paths:
        img = pygame.image.load(path)
        img = pygame.transform.scale(img, (60, 80))  # 画像サイズを調整
        decorations.append(img)
    return decorations

decorations_images = load_decorations()

# 星型を描く関数
def draw_star(surface, color, center, outer_radius, inner_radius, num_points):
    points = []
    angle = math.pi / num_points  # 1つの頂点間の角度
    start_angle = -math.pi / 2    # 90度上方向からスタート

    for i in range(num_points * 2):
        # 外側と内側の頂点を交互に計算
        radius = outer_radius if i % 2 == 0 else inner_radius
        theta = start_angle + i * angle
        x = center[0] + radius * math.cos(theta)
        y = center[1] + radius * math.sin(theta)
        points.append((x, y))

    # 星型を描画
    pygame.draw.polygon(surface, color, points)
    

def draw_tree():
    # 背景
    screen.fill((0, 0, 0))
    
    # 幹
    pygame.draw.rect(screen, BROWN, (360, 400, 80, 200))
    
    # ツリーの3つの三角形
    pygame.draw.polygon(screen, GREEN, [(400, 100), (300, 300), (500, 300)])
    pygame.draw.polygon(screen, GREEN, [(400, 200), (270, 400), (530, 400)])
    pygame.draw.polygon(screen, GREEN, [(400, 300), (250, 500), (550, 500)])
    
    # 星
    draw_star(screen, YELLOW, (400, 95), 30, 10, 5)  # 星型を描画

def illuminate_lights():
    for x, y in lights:
        color = random.choice([ORANGE, BLUE, WHITE, YELLOW])
        pygame.draw.circle(screen, color, (x, y), 5)

def draw_decorations():
    for x, y in decor_positions:
        pygame.draw.circle(screen, WHITE, (x, y), 10, 1)  # 枠線だけの円で飾り付け位置を表示
    for (x, y), img in zip(placed_decorations, decorations_images):
        screen.blit(img, (x-30, y-10))  # デコレーション画像を描画する位置を調整

        # 枠線を削除する
        pygame.draw.circle(screen, WHITE, (x, y), 10, 0)

# 雪の粒を初期化
snowflakes = [{"x": random.randint(0, 800), "y": random.randint(-600, 0), "speed": random.uniform(1, 3)} for _ in range(100)]

def update_and_draw_snowflakes():
    global snowflakes
    for flake in snowflakes:
        flake["y"] += flake["speed"]
        if flake["y"] > 600:  # 画面下に到達したら、上から再生成
            flake["x"] = random.randint(0, 800)
            flake["y"] = random.randint(-50, -10)
            flake["speed"] = random.uniform(3, 5)
        pygame.draw.circle(screen, WHITE, (int(flake["x"]), int(flake["y"])), 3)


# メインループの変更
while True:
    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            pygame.quit()
            sys.exit()
        if event.type == pygame.MOUSEBUTTONDOWN and not game_over:
            mouse_x, mouse_y = event.pos
            for pos in decor_positions:
                if pos not in placed_decorations and (mouse_x - pos[0])**2 + (mouse_y - pos[1])**2 <= 100:
                    placed_decorations.append(pos)
                    break
            if len(placed_decorations) == len(decor_positions):
                game_over = True

    if game_over:
        screen.fill((0, 0, 0))  # 背景を黒にする
        update_and_draw_snowflakes()  # 雪を描画
        # サンタ画像を描画
        screen.blit(santa_image, santa_position)
    else:
        draw_tree()
        illuminate_lights()
        draw_decorations()
    
    pygame.display.flip()
    clock.tick(30)
```
</details>


## 初期設定と基本構造
Pygameライブラリを初期化、画面設定、フレームレート管理をしています。

```python
pygame.init()
screen = pygame.display.set_mode((800, 600))
pygame.display.set_caption("クリスマスツリー")
clock = pygame.time.Clock()
```


## 色と装飾の準備
色の定義、イルミネーションの位置、装飾の位置、配置済み装飾リストの準備をしています。

```python
GREEN = (34, 139, 34)
BROWN = (139, 69, 19)
WHITE = (255, 255, 255)
ORANGE = (255, 165, 0)
YELLOW = (255, 255, 0)
BLUE = (0, 0, 255)
RED = (255, 0, 0)

lights = [(random.randint(300, 500), random.randint(150, 500)) for _ in range(25)]
decor_positions = [(360, 235), (445, 300), (320, 380), (480, 480)]
placed_decorations = []
```


## ツリーと星の描画
背景、ツリーの幹、ツリーの葉、星お描画しています。

```python
def draw_tree():
    screen.fill((0, 0, 0))
    pygame.draw.rect(screen, BROWN, (360, 400, 80, 200))
    pygame.draw.polygon(screen, GREEN, [(400, 100), (300, 300), (500, 300)])
    pygame.draw.polygon(screen, GREEN, [(400, 200), (270, 400), (530, 400)])
    pygame.draw.polygon(screen, GREEN, [(400, 300), (250, 500), (550, 500)])
    draw_star(screen, YELLOW, (400, 95), 30, 10, 5)
```


## 動的要素の描画
### イルミネーション
ランダムな色のライトを小さな円形で描画しています。

```python
def illuminate_lights():
    for x, y in lights:
        color = random.choice([ORANGE, BLUE, WHITE, YELLOW])
        pygame.draw.circle(screen, color, (x, y), 5)
```

### 雪の描画
雪を動的に描画し、画面下部に到達した雪を再利用して無限ループを作成しています。

```python
snowflakes = [{"x": random.randint(0, 800), "y": random.randint(-600, 0), "speed": random.uniform(1, 3)} for _ in range(100)]

def update_and_draw_snowflakes():
    for flake in snowflakes:
        flake["y"] += flake["speed"]
        if flake["y"] > 600:
            flake["x"] = random.randint(0, 800)
            flake["y"] = random.randint(-50, -10)
            flake["speed"] = random.uniform(3, 5)
        pygame.draw.circle(screen, WHITE, (int(flake["x"]), int(flake["y"])), 3)
```


## ユーザーインタラクティブとメインループ
### ユーザーインタラクティブ
ユーザーのクリックを検知し、装飾位置から近い場所にデコレーションを追加します。

```python
for event in pygame.event.get():
    if event.type == pygame.MOUSEBUTTONDOWN and not game_over:
        mouse_x, mouse_y = event.pos
        for pos in decor_positions:
            if pos not in placed_decorations and (mouse_x - pos[0])**2 + (mouse_y - pos[1])**2 <= 100:
                placed_decorations.append(pos)
                break
        if len(placed_decorations) == len(decor_positions):
            game_over = True
```


### メインループ
ゲームの状態に応じてツリーや装飾を描画し、ゲームオーバー後はサンタと雪を表示します。

```python
while True:
    if game_over:
        screen.fill((0, 0, 0))
        update_and_draw_snowflakes()
        screen.blit(santa_image, santa_position)
    else:
        draw_tree()
        illuminate_lights()
        draw_decorations()
    
    pygame.display.flip()
    clock.tick(30)
```


## サンタの追加とゲーム終了
サンタクロースの画像をロードし、サイズを調整してゲーム終了時に表示します。
全ての装飾が配置された時点で、game_overフラグが有効になり、サンタ画像と雪が表示されます。

```python
santa_image = pygame.image.load("santa_claus.png")
santa_image = pygame.transform.scale(santa_image, (450, 450))
santa_position = (175, 150)
```


# まとめ
今回は複雑な工程を含んでいないので、簡単にかわいい感じのゲームを実装できました。
興味があれば皆さんも実装してみてください。