【第7回】モンスター育成ゲーム用APIをPythonのFastAPIで作ってみたい…けど大丈夫かな?

「本ページはプロモーションが含まれています」

こんにちは。これまでの連載では、

  1. 第1回:モンスター育成ゲームAPIの概要/フレームワーク選定
  2. 第2回:Mermaidでシーケンス図を描き、モンスター入手フローを可視化
  3. 第3回:MermaidでDB設計を考え、SQLiteに落とし込む
  4. 第4回:FastAPI環境構築&フォルダ構成
  5. 第5回:ユーザー管理APIを実装(CRUD
  6. 第6回:モンスター管理APIを実装(モンスター種を登録・取得)

kunio-ud-zatta.hatenablog.com

ここまででユーザーもモンスター種も用意できました。次は「ガチャを引く」要素を作り、ランダムにモンスターを入手できるようにしてみます。 ソシャゲ的なワクワク感が加わり、一気に“ゲームらしさ”が増すはずです。

1. ガチャ実装の概要

1.1 抽選の流れ

  1. ユーザーが「ガチャを引く」エンドポイント(例:POST /gacha?user_id=xxx)にリクエス
  2. サーバーでモンスター種をレア度などの確率に応じてランダム抽選
  3. 当選したモンスターをuser_monstersテーブルに登録(所有モンスターとして追加)
  4. レスポンスに「どのモンスターが当たったか」を返す

1.2 レア度の確率管理

  • rarity("common" / "rare" / "legendary" など)と、その出現率を設定
  • 例: "common":70%, "rare":25%, "legendary":5%
  • もし「レア度の概念」を使わないなら、モンスター種すべてを一律の確率で抽選する方法もあり

2. DBのおさらい

第3回でMermaid ER図を描いたように、MONSTERSテーブルに各モンスター種が登録され、USER_MONSTERSテーブルでユーザーが所有する個体を管理します。

erDiagram

    USERS ||--o{ USER_MONSTERS : "owns"
    MONSTERS ||--o{ USER_MONSTERS : "instances of"

    USERS {
        int id PK
        string username
        datetime created_at
    }

    MONSTERS {
        int id PK
        string name
        string rarity
        int base_hp
        int base_attack
        int base_defense
    }

    USER_MONSTERS {
        int id PK
        int user_id FK
        int monster_id FK
        int level
        int exp
        datetime obtained_at
    }

ガチャではMONSTERSから1体をランダムに選出し、USER_MONSTERSにINSERTする形をとります。

3. ガチャAPIのサンプル実装

3.1 gacha.py (APIRouter)

# app/routers/gacha.py

import random
from fastapi import APIRouter, Depends, HTTPException, Query
from sqlalchemy.orm import Session

from app.database import get_db
from app.models import User, Monster, UserMonster
from pydantic import BaseModel
from typing import Optional

router = APIRouter()

# レア度ごとの確率 (例)
RARITY_PROB = {
    "common": 0.7,
    "rare": 0.25,
    "legendary": 0.05
}

class GachaResult(BaseModel):
    user_id: int
    monster_id: int
    monster_name: str
    monster_rarity: str
    message: str

@router.post("/", response_model=GachaResult)
def pull_gacha(
    user_id: int = Query(..., description="ガチャを引くユーザーID"),
    db: Session = Depends(get_db)
):
    """
    ユーザーがガチャを引いて、ランダムにモンスターを入手するエンドポイント。
    """

    # 1. ユーザーの存在確認
    user = db.query(User).filter(User.id == user_id).first()
    if not user:
        raise HTTPException(status_code=404, detail="User not found")

    # 2. レア度を抽選 (random.choicesで確率的に選択)
    rarities = list(RARITY_PROB.keys())   # ["common", "rare", "legendary"]
    probs = list(RARITY_PROB.values())   # [0.7, 0.25, 0.05]
    chosen_rarity = random.choices(rarities, probs)[0]

    # 3. 選ばれたレア度に該当するモンスター一覧を取得
    monsters = db.query(Monster).filter(Monster.rarity == chosen_rarity).all()
    if not monsters:
        raise HTTPException(status_code=500, detail="No monsters found for this rarity")

    # 4. 一覧の中からランダムで1体ピック
    chosen_monster = random.choice(monsters)

    # 5. user_monstersに所有記録を追加
    new_owned = UserMonster(
        user_id=user_id,
        monster_id=chosen_monster.id,
        level=1,
        exp=0
    )
    db.add(new_owned)
    db.commit()
    db.refresh(new_owned)

    # 6. 結果を返却
    return GachaResult(
        user_id=user_id,
        monster_id=chosen_monster.id,
        monster_name=chosen_monster.name,
        monster_rarity=chosen_monster.rarity,
        message=f"You got a {chosen_monster.name}!"
    )
  • レア度の確率は random.choices() を使って重み付き抽選を行う。
  • 取得したモンスターを UserMonster にINSERTし、ユーザーが所有する個体として記録。
  • データベースに一切モンスターが登録されていないレア度だと動かないため、初期データを仕込み。

モンスターマスター

3.2 main.py にルーターを追加

from app.routers import users, monsters, gacha

app.include_router(gacha.router, prefix="/gacha", tags=["gacha"])
  • これで POST /gacha?user_id=1 のように呼び出すとガチャが引けるようになるはず!
  • Swagger UIで、クエリパラメータ user_id に値を入れて Execute する

4. 動作確認 (簡易テスト項目書)

  1. 前提:
  2. ユーザー(usersテーブル)が存在
  3. モンスター種(monstersテーブル)にいくつか登録され、rarityが"common"/"rare"/"legendary"などで設定済み
  4. uvicorn app.main:app --reload でサーバー起動
  5. POST /gacha?user_id=1

スライムゲット

6回ほど実行してやっと。。。

ドラゴン

20回実行してやっと。。。

魔王様ゲット

5. 7回連載を終えて

これまで7回にわたって、Python+FastAPIでモンスター育成ゲームのバックエンドAPIを作る過程を紹介してきました。最初は「本当に形になるのかな…」と不安でしたが、一歩ずつ進めるうちに、ユーザー管理やモンスター管理、ガチャ機能など、ゲームらしい機能が揃ってきましたね。

全体のおさらい

  1. 第1回:ゲームの概要とFastAPI選定
  2. 第2回:Mermaidを使ってシーケンス図を描き、モンスター入手フローを可視化
  3. 第3回:ER図とSQLiteでDB設計
  4. 第4回:FastAPI環境構築とフォルダ構成
  5. 第5回:ユーザー管理APIを実装(CRUD処理)
  6. 第6回:モンスター管理APIを実装し、レア度やステータスをCRUD
  7. 第7回:ガチャ機能を作って、ユーザーがランダムにモンスターを手に入れる仕組みを実装

それぞれの記事で、Mermaidでの図解やSQLiteを使ったDB設計のポイント、FastAPIのルーター分割、Pydanticによるバリデーションなど、学んだ要素がたくさんあったと思います。連載を通して、設計から実装までの流れを体験できたかなと思います。 一応、GitHubに上げてます。

github.com

今後の展望

  • 認証・セキュリティ: トークンベースの認証やOAuthなどを導入して、ユーザーのログイン機能をしっかり作り込む。
  • バトル機能・レベルアップ: ユーザーが所有するモンスターで戦って経験値を稼ぎ、レベルアップや進化をする要素を追加。
  • UI・フロントエンド: ReactやVueなどで簡単な画面を用意すれば、さらにゲーム感がアップ。
  • 本番運用: Dockerやクラウドサービス(AWSGCP、Renderなど)にデプロイして、多人数で遊べるように整備。
  • この連載で作ったAPIは、あくまでも「最初の試作」とも言える内容なので、さらに機能を付け足して自分好みのゲームに仕上げていくとか?
  • コストを抑えて、DynamoDBで運用できるようにv2を作るのも良いかな

まとめ

いろいろな試行錯誤をしながらコードを書き、エラーに悩まされ、機能を少しずつ増やすうちに、モンスター育成ゲームAPIの大枠が形になったかな。。。

よくありそうな、開発の順で書いてみています。 超簡易設計 => 超簡易実装 => 簡易テストと、ゲームだろうがC#のソフトウェアだろうが、あまり流れは変わらない気がしますね。。

  • FastAPIのおかげで、APIのプロトタイプを素早く構築できた
  • Mermaidを使うと、フロー図やER図がテキストで管理できるため、仕様変更にも対応しやすかった
  • SQLiteはシンプルながら、CRUDやガチャ機能を実装するには十分な力を発揮

ありがとうございました。

次回は、全然違う、雑多なことでもしようかなと思います。