01. 補足コンテンツ
オンラインゲームの仕組み
インターネットの仕組み
オンラインゲームを作り始める前に、まずインターネットが大まかにどう動いているのかを理解しておこう。
クライアント/サーバー構造
現代のインターネットのほとんどは「クライアント/サーバー構造」と呼ばれる仕組みで動いている。この構造では、「クライアント」と「サーバー」という2つの存在が互いにメッセージをやり取りする。
クライアントとは、ユーザーが直接触れるデバイスのことだ。普段はパソコンやスマートフォンのブラウザがこれにあたる。オンライン決済アプリやLINEのチャットアプリもクライアントと言える。要するに、毎日使っているアプリケーションのことだ。
サーバーとは、データセンターなどにある遠隔地のコンピューターで、すべてのクライアントからのリクエストを受け取り、レスポンス(応答)を返す役割を持つ。例えば、QRコードで支払いをするとき、クライアントは支払い情報をサーバーに送り、サーバーがその金額を購入先の店に送金する、という流れになる。
ステートレスな接続
多くのクライアント/サーバー構造では、通信は必ずクライアントからのリクエストで始まる。サーバーはリクエストが来るまでじっと待っている。この仕組みでは、サーバー側からクライアントに連絡することはない。なぜなら、クライアントが接続してくるまで、サーバーはそのクライアントが誰なのか分からないからだ。このようなサービスは「ステートレス(状態を持たない)」と呼ばれる。サーバーが個々のクライアントの状態を記憶していないからだ。サーバーはリクエストを処理し、レスポンスを返したら、それで終わり。
こう考えてみるといい。Googleは世界中から毎秒何千もの検索クエリを処理している。どのパソコンが何を検索したかをいちいち記録するのは不可能だ。だからGoogleは検索を実行し、結果を返したら、そのクライアントのことはもう忘れてしまう。
リクエストとレスポンス
クライアントからサーバーへ送られるメッセージを「リクエスト」と呼ぶ。サーバーから返ってくる答えを「レスポンス」と呼ぶ。ステートレスな接続では、レスポンスが送られた時点で接続は切断され、次のリクエストのためにはまた新しく接続をやり直す必要がある。
IPアドレスとポート
IPアドレス
コンピューター同士がつながるためには、識別子が必要になる。IPアドレスとは、インターネット上のすべてのコンピューターを識別するための一意の番号のことだ。コンピューターの「電話番号」のようなものだと考えるといい。IPv4とIPv6の2種類があるが、ここでは主にIPv4アドレスを扱う。IPv4は次のような形をしている。
XXX.XXX.XXX.XXX
これは0〜255の数字4組をピリオドで区切ったもので、合計で
256 x 256 x 256 x 256 = 4,294,967,296
通りのアドレスが存在する。ただし、そのすべてが使えるわけではない。インターネット上では使用できない、特別な用途のアドレスがいくつかある。
127.XXX.XXX.XXX- これは「ループバック」アドレスと呼ばれる。127から始まるアドレスは、すべて自分自身のコンピューターを指す。つまり127.0.0.0に接続しようとすると、今使っているそのコンピューターにつながることになる。192.168.XXX.XXX- これは「内部使用」アドレスと呼ばれる。同じネットワーク内にある他のコンピューターに接続するために使われるが、インターネットからは見えない。インターネットのクライアントにはなれるが、インターネットのサーバーにはなれない。ただし、同じネットワーク内であればサーバーとしても機能する。
ポート
ポートとは、同じコンピューター内にある接続用の「差し込み口」のようなものだ。1台のコンピューターが持つIPアドレスは1つだけでも、同時に複数の接続を持つことができる。それぞれの接続は異なるポートを使う。ある接続ではウェブページを見ていて、別の接続ではLINEでメッセージを送っている、というようなことが同時に起きる。各ポートは1〜65535の番号で識別されるが、いくつかの番号は特定のサービス用に予約されている。
- ポート80 : ウェブサービス(HTTP)
- ポート443 : 暗号化されたウェブサービス(HTTPS)
- ポート23 : メールサービス(SMTP)
- ポート137、138、139、445 : Windowsのファイル共有サービス(Samba)
自分のパソコンが今どんな接続を使っているかは、netstat コマンドですぐに確認できる。ポート番号は、接続中の各IPアドレスの後ろにあるコロン(:)の後の数字として表示される。
リアルタイムゲーム vs 非同期ゲーム
ゲームのプレイスタイルによって、サーバーへの接続方法は大きく2つに分けられる。
リアルタイムゲーム
このカテゴリーに入るのは、FPS(ファーストパーソン・シューティングゲーム)やMMOなどだ。サーバーへの速く安定した接続が必要になる。サーバー側のレスポンスも速くなければならない。レスポンスが遅れると、ゲームの結果が大きく変わってしまうこともあるからだ。そのため、こうしたゲームは通常サーバーへの常時接続を使い、素早く送受信・処理できるようバイナリ形式の小さなデータパケットを使う。
非同期ゲーム
非同期ゲームとは、多少の遅延(ラグ)が問題にならないゲームのことだ。リクエストを送ってから1〜2秒の遅れがあっても、ゲームプレイに支障は出ない。トレーディングカードゲームやアイテム収集ゲームなどがこのカテゴリーに入る。速度が重要でないため、必要なときだけデータを送るステートレスな接続の方が好まれる。データは扱いやすく読みやすいJSONのテキスト形式で送られることが多い。
| リアルタイム | 非同期 | |
| ゲームの種類の例 | FPS、MMO、MOBA | トレーディングカードゲーム、アイテム収集ゲーム、ガチャゲーム |
| 接続の持続時間 | 常時接続のみ | 一時的な接続 |
| 送られるデータの種類 | バイナリデータ | 主にテキストデータ |
| 接続の状態 | ステートフルな接続(サーバーが接続中のクライアントを把握している) | ステートレスな接続(クライアントが接続のたびに自分自身を識別させる必要がある) |
| プログラミング言語 | C++ / Rust | Node.js (JavaScript) / PHP |
APIとは何か
APIとは「アプリケーション・プログラミング・インターフェース」の略だ。簡単に言うと、インターネット上に存在する関数やメソッドのことだ。オンラインゲームなどのアプリケーションは、コードの一部をオンラインで実行するためにAPIを使う。例えば、トレーディングカードゲームで新しいカードを手に入れるためにガチャを引きたいとする。ガチャは1日に1回しか回せないので、サーバー側で確認する必要がある。そこでAPIを使ってサーバーに「ガチャを回す」というリクエストを送り、条件を満たしていればサーバーが必要な処理を実行し、その結果をレスポンスとしてクライアントに返す。
この科目の目的
この科目では、PHPとデータベース(MySQL)を使ってオンラインゲームのAPIを作る方法を学ぶ。サーバー上でデータを保存・検索する方法を学び、ユーザーとその所持アイテムを管理できるようにする。そして、PHPを使ってAPI(クラウド上の関数)を作り、サーバーにログインして自分のインベントリを確認できるようにする。
最後に、Unityを使ってそのAPIにアクセスし、自分たちで作ったサーバーに接続してゲームをプレイし、結果をテストしていく。
SQL文の練習
テーブル作成
1. 学生のデータベースを作ろうとし、以下のデータを管理したい:
- 学生ID(数値)
- お名前(テキスト)
- 生年月日(日付)
SQL文
CREATE TABLE students (id INT, name TEXT, birthday DATE);
2. プレーヤーのログイン情報を管理するテーブルを作成し、以下のデータを管理したい:
- プレーヤーID(数値)
- ユーザー名(テキスト - 255文字まで)
- パスワード(テキスト - 255文字まで)
- 最後のログイン(日時)
SQL文
CREATE TABLE players (id INT, name VARCHAR(255), password VARCHAR(255), last_login DATETIME);
データを挿入
3. 学生のテーブルに2つの学生を追加してください:
- 大原太郎
- 生年月日:2006年5月17日
- ID:1
- 盛岡一郎
- 生年月日:2006年3月29日
- ID:2
- 石割さくら
- 生年月日:2004年6月4日
- ID:3
SQL文
INSERT INTO students SET id=1, name="大原太郎", birthday="2006-5-17";
INSERT INTO students SET id=2, name="盛岡一郎", birthday="2006-3-29";
INSERT INTO students SET id=3, name="石割さくら", birthday="2004-6-4";
または
INSERT INTO students(id, name, birthday) VALUES
(1,'大原太郎','2006-05-15'),
(2,'盛岡一郎','2006-03-29'),
(3,'石割さくら','2004-6-4');
4. プレーヤーテーブルに1つのプレーヤーを追加してください。ユーザー名とパスワードは適当で、最後のログインは今日の朝10時にしてください。
SQL文
INSERT INTO players SET id="1", name="test_player", password="qwerty", last_login='2026-09-02 10:00:00';
または
INSERT INTO players (id, name, password, last_login) VALUES
('1', 'test_player', 'qwerty', '2026-09-02 10:00:00');
データを更新
5. 大原太郎(ID 1)の生年月日を訂正し、2006年5月25日に直してください。
SQL文
UPDATE students SET birthday='2006-5-25' WHERE id=1
6. プレーヤーのパスワードを「pass1234」に更新し、最後のログインは、今日の午後3時30分にしてください。
SQL文
UPDATE players SET password="pass1234", last_login="2026-09-02 15:40:00" WHERE id=1;
データを削除
7. 盛岡一郎(ID2)が学校を辞めたので、削除してください
SQL文
DELETE FROM students WHERE id=2;
データの検索
8. 2006年生まれの学生を求めてください(2006年1月1日~2026年12月31日)
SQL文
SELECT * FROM students WHERE birthday >= "2006-01-01" AND birthday <= "2026-12-31";
9. プレーヤーの名前だけを表示してください。
SQL文
SELECT name FROM players;
テーブル削除
10. 上記の練習で作った2つのテーブルを削除してください
SQL文
DROP TABLE players, students;
リレーション
空のデータベースで以下のSQL文を実行してください:
アイテムのテーブル
CREATE TABLE `items` (
`id` INT UNSIGNED NOT NULL AUTO_INCREMENT ,
`name` TINYTEXT NOT NULL ,
`cost` SMALLINT NOT NULL , PRIMARY KEY (`id`));
INSERT INTO `items` (`id`, `name`, `cost`) VALUES
(1, 'ポーション', '100'),
(2, '魔法のマント', '500'),
(3, '銀の剣', '600');
プレーヤーのテーブル
CREATE TABLE `players` (
`id` INT UNSIGNED NOT NULL AUTO_INCREMENT ,
`name` TEXT NOT NULL , PRIMARY KEY (`id`));
INSERT INTO `players` (`id`, `name`) VALUES
(1, 'アンパンマン'),
(2, 'メロンパンナ');
各プレーヤーが持っているアイテムの関係テーブル
CREATE TABLE `player_items` (
`id` INT NOT NULL AUTO_INCREMENT ,
`player_id` INT NOT NULL ,
`item_id` INT NOT NULL ,
`count` INT NOT NULL , PRIMARY KEY (`id`));
INSERT INTO `player_items` (`id`, `player_id`, `item_id`, `count`) VALUES
(1, 1, 1, 2),
(2, 1, 3, 1),
(3, 2, 1, 5),
(4, 2, 2, 1);
この場合は:
11. player_itemsとplayersのテーブルを id で連携し、すべてを表示してください
SQL文
SELECT *
FROM player_items, players
WHERE player_id = players.id;
12. 上記のSQL文を編集し、items の連携を追加してください(つまり、各プレーヤーのアイテムを表示)
SQL文
SELECT *
FROM player_items, players, items
WHERE player_id = players.id AND item_id = items.id;
13. 上記のSQL文を編集し、id を隠し、この順番でカラムを表示してください:
プレーヤー名|アイテム名|数
SQL文
SELECT players.name, items.name, player_items.count
FROM player_items, players, items
WHERE player_id = players.id AND item_id = items.id;
「PHP」と「C/C++/C#」の違い
インタープリタ言語とコンパイル言語
コンパイル言語
C、C++、C#は「コンパイル言語」と呼ばれる。まずテキストファイル(.c/.cpp/.cs)を作る。そのファイルを、実行できるファイル(Windowsなら.exe)に変換する。
このテキストファイルを実行ファイルに変換する作業を「ビルド」または「コンパイル」と呼ぶ。この過程で、文字で書かれた命令が、コンピュータが理解できる2進数(バイナリ)のコードに変換される。さらに、もっと速く動くように、コードが「最適化」されることも多い。
例えば、とても短いforループでも、コードの中を何度も戻らないように、同じ命令を繰り返す形に「展開」されることがある(下の表を参照)。
| オリジナル | 展開 |
|
|
最適化されたコードでは、比較(i < 4)が要らなくなり、使われない変数iも消え、forループの終わりで前に戻る動作もなくなる。だからコードが速く動く。
このような理由で、コンパイル言語は次に紹介する言語と比べて動作が速い。
インタープリタ言語
インタープリタ言語には、Lua、Python、JavaScriptなどがある。これらの言語はバイナリにコンパイルされず、「インタープリタ」というものを使う。インタープリタとは、コードを1行ずつ、実行するその瞬間にバイナリへ変換する、特別なソフトのことである。
インタープリタ言語の大きな利点は、コンパイルが要らないことである。実行ファイルを作り直さなくても、コードを少し変えるだけで反映できる。ただし欠点もある。1行ずつしか実行しないので、最適化ができない(インタープリタは、この先にどんなコードが来るか分からないので、最適化のしようがない)。
インタープリタ言語のもう一つの利点は、その環境用のインタープリタさえあれば、Windowsでも、Linuxでも、MacOSでも、そのままほぼどこでも動くことである。
PHP
HPはインタープリタ言語である。つまり、コンパイルは不要である。PHPのインタープリタは、たいていWebサーバーのプラグインとして動く。今回の授業では、XAMPP に Apache(Webサーバー)とPHPが最初から入っているので、そのまま使うことができる。
拡張子が .php のページを開くと、Apache が PHP インタープリタを動かし、その結果をHTMLに変換して、普通のWebページとして表示してくれる。
PHPは1回だけ動く(状態は保存されない)
PHPは、動的なWebページを作るために生まれた言語である。動的なWebページとは、内容が変わっていくページのことである。例えば、X(旧Twitter)を更新するたびに、みんなが新しい投稿をするので、新しい投稿一覧が表示される。同じページを読み込んでいるのに、中身は動的に変わっているわけである。
PHPはそのような仕組みで作られているので、1ページにつき1回しか動かない。Web接続は「状態を持たない(ステートレス)」ことを思い出してほしい(接続する→内容を受け取る→切断する、という流れである)。だから、ページをユーザーに返した後もPHPのプログラムを動かし続ける意味がないのである。
これは、普通のプログラムとの大きな違いである。私たちは、プログラムが動いている間ずっと変数が生き続けるのに慣れている。しかしPHPは、ページを返した時点で終了してしまうので、ページを再読み込みするたびにデータは何も残らない。ページを読み込むたびに、まったく新しいプログラムが、新しい変数と内部状態を持って始まるのである。
呼び出しの間でデータを保持したい場合は、いくつかの仕組み(クッキーやセッションなど)を使うことができる。ただし、これらの仕組みは今回の授業の範囲外である。興味のある人は、教科書の113~119ページを見てください。
とはいえ、呼び出しの間で変数を渡す方法は必要になる。UnityとPHPを使ったゲームを作るときに、GETデータとPOSTデータを使って、PHPスクリプトの実行と実行の間でサーバーにデータを覚えさせる方法を学んでいく。
JSONとPHP
JSONのファイル形式について
JSONとは、テキストファイルの中にデータを保存し、整理するための方法である。ウェブアプリケーションでよく使われていて、クライアントとサーバーの間でデータをやりとりするときによく使われる。
JSONファイルの中では、辞書(連想配列)と添字付き配列を組み合わせてデータを保存する。JSONでは辞書のことを「オブジェクト」と呼ぶので、注意してほしい。
オブジェクト(Object)
オブジェクトとは、キーと値のペアをコンマで区切って集めたもので、下のように中かっこ({ })の中に書く。
{
"key 1" : "value 1",
"key 2" : "value 2",
...
"key n" : "value n"
}
ここで「キー」は必ず文字列(引用符で囲んで書く)であり、「値」は次のいずれかの種類になる。
|
文字列(引用符で囲む) |
|
| 整数 |
|
| 浮動小数点数(小数) |
|
| 真偽値(true / false) |
|
| ほかのオブジェクトや配列 |
|
配列(Array)
配列とは、順番に並んだ値の集まりで、ふつうは同じ種類の値が入る。配列は角かっこ([ ])を使って定義する。
[
"Apple",
"Banana",
"Grape"
]
JSONでは、同じ配列の中に違う種類の値を混ぜることもできる。少し変に見えるかもしれないが、これも可能である。
[
"Apple",
25,
false
]
例
JSONファイルは必ず、中かっこ(オブジェクトとして)か、角かっこ(配列として)のどちらかから始まる。この授業では、わかりやすいのでオブジェクトから始める形を使っていく。
セーブデータを扱うJSONを考えてみよう。日付、XP、HP、そしてクリアしたステージのリストを保存したいとする。すると、下のようになる。
{
"save_date" : "2026-07-14",
"save_time" : "11:45",
"XP" : 54796
"HP" : 97.15
"stage_cleared" : [true, true, false, false, true, false]
}
JSONとPHPのデータ変換
JSONはウェブの場面でよく使われるため、PHPには次の2つの関数が用意されている。ひとつはPHPの連想配列をJSONのテキストに変換する関数、もうひとつはその逆の変換をする関数である。
| PHPの連想配列からJSONテキストへ |
|
| JSONテキストからPHPの連想配列へ |
|
練習
ここまで見てきたことを使って、簡単な練習をしてみよう。
- このJSONファイルをダウンロードし、自分のパソコンのXAMPPウェブサーバー(C:\xampp\htdocs)の中に置いてください。
- PHPの命令
file_get_contentsを使って、このJSONファイルを読み込んでください(教科書80ページ)。 save_dateとsave_timeを、今の日付と時刻に変更してください(教科書40ページ)。- 3つ目のステージ(インデックス2)をクリア済みにしてください(trueにする)。
- もう一度JSONのテキストに変換してください。
- ブラウザに表示されるように、そのJSONテキストを出力してください。
test_json.php
<?php
// JSONを読み込む(テキスト)
$json = file_get_contents("save_slot.json");
// PHPの連想配列へ変換
$data = json_decode($json, true);
// 現在の日時にする
$data["save_date"] = date("Y-m-d");
$data["save_time"] = date("H:i:s");
// 3つ目のステージをクリアにする
$data["stage_cleared"][2] = true;
// またJSONへ
$json = json_encode($data);
// 表示(返す)
print($json);
?>
JSONだと明示して返す
初期設定では、Apache(ウェブサーバー)は結果をHTMLファイルだと思い込んでいて、ブラウザにも「HTMLファイルが来る」と伝えてしまう。これは「ヘッダー」というものを使って行われていて、その中でデータの種類が指定されている。これはMIMEタイプと呼ばれるもので、一覧はリンク先で見ることができる。
今回は、結果がHTMLファイルではなくJSONファイルであることをブラウザに伝えたい。これは、PHPの header() という命令を使うことでできる。
// JSONを返すべき
header("Content-Type: application/json; charset=utf-8");
先ほどの練習でヘッダーを変えてみて、結果がどう変わるか確認してください。
GETとPOSTのデータ送信
クライアントからサーバーへのデータ送信
ここまでは、サーバー上にコンテンツを置いて、それをウェブブラウザで取得する方法を見てきた。しかし、逆にウェブブラウザからサーバーへデータを送る必要がある場合もある。
ログインページを考えてみよう。ユーザー名とパスワードを入力すると、そのデータをサーバーに送信しなければならない。サーバーはデータベースを確認して、ユーザー名とパスワードが一致すればブラウザにログインを認識させる。
ウェブ開発では、クライアントからサーバーへデータを送る方法が2つある。GETとPOSTだ
GET
GETは短い文字列や値を送るときに使う。GETの値をクライアントからサーバーへ渡すには、以下のようにURLの末尾に書く。
http://www.mywebsite.co.jp/mypage.php?変数A=値A;変数B=値B ...
URLの末尾に「?」を付けて、その後がGETの値であることを示す。そして、各値のペアを「;」(セミコロン)で区切って渡す。
例えば、Googleで「大原学園」を検索したいとする。Googleにアクセスして検索語を入力してもいいが、単純に次のようにすることもできる
https://www.google.com/search?q=大原学園
ここでは、「q」(query、つまり検索)という1つの値として「大原学園」をGoogleに渡している。
PHPでは、$_GETという特別なグローバル連想配列を通してGETパラメータを取得できる。以下の演習をやってみよう。
<?php
// get_example.phpで保存してください
// "name"のパラメータが設定されている?
if (!isset($_GET["name"]))
{
print("お名前を教えてください");
}
else
{
$name = $_GET["name"];
print("こんにちは、" . $name . "さん。");
}
?>
ここでisset()は、そのGET変数が存在するかどうかをチェックしている。では、ブラウザを開いて次を試してみてください。
http://localhost/get_example.php?name=一郎
見ての通り、変数「name」が正しく読み込まれて表示されている。今度はURLに2つ目のパラメータ「age」(年齢)を追加してみよう。
<?php
// get_example.phpで保存してください
// "name"のパラメータが設定されている?年齢も?
if (!isset($_GET["name"]) || !isset($_GET["age"]))
{
print("お名前と年齢を教えてください");
}
else
{
$name = $_GET["name"];
print("こんにちは、" . $name . "さん。");
$beerOk = $_GET["age"] >= 20;
if ($beerOk)
print("ビールはいかがでしょうか?");
else
print("コーラを飲みませんか?");
}
?>
そして次を試してみてください。
http://localhost/get_example.php?name=一郎&age=19
ちょっとした遊びとして、今度はパラメータを何も渡さずに試してみよう。
http://localhost/get_example.php
どうなるか見てみてください。
POST
POSTもブラウザにデータを送るもう一つの方法だ。画像やテキストファイルのような、文字列ではない情報を送るときによく使われる(例えば、Instagramの投稿に画像を添付するとき、それはPOSTで送信されている)。POSTは小さなパラメータを送るのにも使われるが、URLには表示されないので、ユーザーが直接それを編集できないという特徴がある。
POSTのデータはURLに表示されないため、サーバーへデータを送るには特別な方法が必要になる。ブラウザがPOSTでサーバーへデータを送れるように「HTMLフォーム」を作る必要がある。まずは、先ほどの例と同じ2つのパラメータを持つ、シンプルなHTMLフォームを作ってみよう。
<!-- post_form.html として保存してください -->
<meta charset="utf-8">
<form action="get_example.php" method="get">
<input type="text" name="name" value="一郎">
<input type="text" name="age" value="19">
<input type="submit">
</form>
これはフォームのごくシンプルな例だ。1行ずつ見ていこう。
| コード | 説明 |
|
文字コードを設定している。漢字やかなを使うためUTF-8を使用している |
|
フォームの開始。フォームが送信されると、値はGETモードでget_example.php に送られる |
|
デフォルト値は「一郎」と「19」 |
|
送信ボタンを表示する。このボタンを押すと値が |
|
フォームの終了 |
ブラウザで http://localhost/post_form.html を開いて、送信ボタンを押してみてください。
見ての通り、これは同じパラメータをURL経由(GETモード)で渡しているだけだ。今度はこれをPOSTに切り替えてみよう。まず、フォームの中の
<form action="get_example.php" method="get">
を
<form action="post_example.php" method="post">
に置き換えてください。
次にPHPスクリプトを編集して、$_GET の代わりに $_POST から読み込むようにする必要がある。get_example.php をコピーして post_example.php という名前に変更し、次のようにしよう。
<?php
// post_example.phpで保存してください
// "name"のパラメータが設定されている?年齢も?
if (!isset($_POST["name"]) || !isset($_POST["age"]))
{
print("お名前と年齢を教えてください");
}
else
{
$name = $_POST["name"];
print("こんにちは、" . $name . "さん。");
$beerOk = $_POST["age"] >= 20;
if ($beerOk)
print("ビールはいかがでしょうか?");
else
print("コーラを飲みませんか?");
}
?>
そしたらフォームを再読み込みして、もう一度試してみてください。見ての通り、今度はURLにパラメータが表示されていない。
ブラウザだけじゃない!
ここまではブラウザを使ってサーバーへデータを送るテストをしてきた。これはとてもよくある使い方だけど、それだけではない。どんなアプリケーションでも、GETとPOSTを使ってウェブサーバーにデータを送ることができる。Unityの授業では、UnityWebRequest クラスを使って自分のUnityゲームからPHPサーバーへデータを送る方法を後で学ぶ。