PygameとWebSocketでオンラインゲーム

## はじめに
つい最近、ハッカソンに参加した際、初めてオンライン対戦ゲームに挑戦したのでその時の記録を残します。Waffleのゲーム開発部は部員を常時募集しております～🤝

## 使用技術

* [pygame](https://www.pygame.org/wiki/about):pythonのゲーム開発特化のライブラリ
* [Tornado](https://github.com/tornadoweb/tornado):WebSocket通信をサポートしているPythonのウェブフレームワーク
* [websockets](https://websockets.readthedocs.io/en/stable/intro/index.html):ウェブブラウザとウェブサーバー間で双方向にリアルタイムの通信をするためのプロトコル
* [nginx](https://nginx.org/):高性能なHTTPサーバーおよびリバースプロキシサーバー

```
#使用したバージョン pip installしてください
pygame==2.6.1
tornado==6.4.1
websockets==13.1
```

## 実装
私自身の環境はWSLです。

# 1. Tornadoサーバーの設定
server.pyなどファイルを作成して以下を記入します。
```python
import tornado.ioloop
import tornado.web
import tornado.websocket

class WebSocketHandler(tornado.websocket.WebSocketHandler):
    def open(self):
        #接続が開始された後の処理を書く
        #プレイヤーの初期状態、ルーム分けetc

    def on_message(self, message):
        #プレイヤーからメッセージを受信したときの処理
        #JSONなどを使ってクライアントからデータを受け取ったり、
        #プレイヤーの状態を更新したり、
        #更新された状態を他のプレイヤーに送信したり

    def on_close(self):
        #接続が終了された後の処理

def make_App():
    return tornado.web.Application([
        #/websocketにアクセスしたときにWebSocketHandlerを使用
        (r"/websocket", WebSocketHandler)
    ])

#サーバーの起動
if __name__ == "__main__":
    app = make_app()
    app.listen(8080)
    print("Server started on http://localhost:8888")
    tornado.ioloop.IOLoop.instance().start()#イベントループを開始
```

# 2. nginxの設定
1.nginxをインストール
```
sudo apt update
sudo apt install nginx
```
2.設定ファイルの作成
/etc/nginx/sites-available/の下にdefault(違う名前でもいい)ファイルを作り、
```
sudo nano /etc/nginx/sites-available/default
```
そのまま以下のコードを記入します。
```
server {
    #サーバーがポート80でリッスンすることを指定
    listen 80;
    #localhostまたは、サーバーのパブリックIPやドメイン名
    server_name localhost;

    #ルートURL（/）に対するリクエストを処理するための設定を定義
    location / {
        proxy_pass http://127.0.0.1:3001;
        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection "upgrade";
        proxy_set_header Host $host;
        proxy_cache_bypass $http_upgrade;
    }
}
```
リバースプロキシとして機能します。今回は、クライアントからのリクエストをローカルホストのポート3001でリッスンしているサーバーに転送してます。
そして、設定を保存出来たら再起動します。
```
sudo nginx -t　#sucsessなど出てきたらok
sudo systemctl restart nginx
```

:::note info
今回はpygameを利用して、ローカルのexeファイルなどから実行することを想定しているので、EC2など実際のオンラインサーバーで動かすにはEC2上にserver.pyとnginxの設定を置きます。
:::

# 3. pygameでゲームを作る
game.pyなどファイルを作ってゲームを作っていきます。
```python
import pygame
from pygame.locals import *
import websockets

class Main:
    async def main(self):
        
        # ゲームの初期化
        pygame.init()

        # ゲーム画面の設定
        screen = pygame.display.set_mode((800, 600))
        pygame.display.set_caption("ゲームタイトル")

        #オンラインの時は"localhost:8080"をパブリックIPにする
        uri = "ws://localhost:8080/websocket"
        async with websockets.connect(uri) as websocket:
        
            # ゲームループ
            while True:
            #ゲーム内処理を記述

        # 画面を更新
        pygame.display.flip()

        # ゲームの終了
        pygame.quit()

# asyncio.run()を使用して非同期関数を実行
if __name__ == "__main__":
    main_instance = Main()
    asyncio.run(main_instance.main())
```

ここまで出来たら、
まずサーバーを起動して
```
python server.py
```
そのまま別のターミナルを開いてゲームを起動します。
2人以上でプレイするとき、ターミナルを2つ以上新しく開いて起動します
```
python game.py
```

記事ではゲーム周りは詳しく書いていないのですが、pygameはたくさん資料があるので誰でもゲームを作りやすいと思います！

https://github.com/kurumayusamugariko/toetore

# さいごに
EC2を使うことが初めてだったので、私はハッカソン中そこに苦戦してました。nginxの辺りなどもっと理解するために、またゲーム開発できたらなって思っています。
Waffleアドベントカレンダー実現していただけて嬉しいです。ありがとうございます！