カード削除
カードを削除するには、PHPで新しいAPIを作る必要がある。このAPIは以下のことを行う。
- ユーザーIDを受け取る
- 削除したいカードIDのリストを受け取る(重複あり)
- リクエストに一致するカードごとに、カウントを1ずつ減らすSQLクエリを実行する
- カウントが0になったら、そのレコードをデータベースから削除する
SQLを使ってカードを削除する
まず、SQLでカウントを減らす方法を見てみよう。例えば、user_id = 1、card_id = 1のカードを2枚減らしたいとする。このユーザーはcard_id = 1のカードを2枚持っているので、これで全部なくなることになる。MySQLに「count」という項目から2を引くよう指示すれば、カウントを減らせる。以下のようにする
UPDATE user_cards SET count = count - 2 WHERE user_id = 1 and card_id = 1;
これは、user_id = 1かつcard_id = 1の行だけ、「count」という列から2を引くようMySQLに指示している。実行すると、以下のようになる。

カウントが0になったことが確認できる。次にやるべきことは、countが0以下になったレコードを全部削除することだけである。これはDELETEを使えば簡単にできる。
DELETE FROM user_cards WHERE count <= 0;
この2つの命令をPHPで使えば、プレーヤーからカードを削除できる。それでは、APIを設計していこう。
PHP スクリプト
始める前に、データベースを元に戻しておこう。phpMyAdmin で以下のSQLコマンドを実行してください。
REPLACE INTO `user_cards` (`id`, `user_id`, `card_id`, `count`) VALUES
(1, 1, 1, 2), (2, 1, 2, 1), (3, 1, 5, 1),(4, 1, 8, 2),
(5, 1, 6, 1), (6, 2, 3, 5), (7, 2, 6, 3),(8, 2, 12, 1);
では、スクリプトを設計していこう。delete_player_cards.php という新しいファイルを作ってください。このAPIでは、クライアント側からユーザーIDと、カードIDごとの削除枚数を送ってもらう。削除するカードの枚数は毎回変わるので、削除したいカードを以下のようなJSONオブジェクトとして送ってもらうことにする。
{
"user_id" : "1",
"cards" : [
{
"id" : "1",
"count" : 2
},
{
"id" : "8",
"count" : 1
},
]
}
この例では、ユーザーID「1」のカードID「1」を2枚、カードID「8」を1枚削除したいということになる。まだデータを送る仕組みがないので、最初はスクリプトの中に直接書き込んでおこう。Unityからデータを送る部分は、後でPOSTを使って作る。
<?php
// JSONを返すべき
header("Content-Type: application/json; charset=utf-8");
// 接続
$dbh = new PDO('mysql:host=localhost; dbname=card_game', "root", "", [PDO::ATTR_PERSISTENT => true]);
// テスト中にデータを固定
$data = '{
"user_id" : "1",
"cards" : [
{ "id" : "1", "count" : 2 },
{ "id" : "8", "count" : 1 }
]
}';
// JSONに変換
$json = json_decode($data, true);
// ユーザーIDと削除したいデータの分ける
$user_id = $json['user_id'];
$card_list = $json['cards'];
?>
次に、forループを使って、配列の中の削除リクエストを1つずつ処理しながらSQL文を組み立てていく。
// SQL文を構築
foreach ($card_list as $card)
{
$sql = "UPDATE user_cards SET count = count - " . $card['count']
. " WHERE user_id = " . $user_id
. " AND card_id = " . $card['id'];
// 確認しましょう
print($sql . "\n");
}
実行すると、以下のような結果になるはずである。

これはまさに、カードを削除するために必要なものである!SQL文を実行して、データベースからカードを削除するところまで作って、スクリプトを完成させよう。
// SQL文を構築
foreach ($card_list as $card)
{
$sql = "UPDATE user_cards SET count = count - " . $card['count']
. " WHERE user_id = " . $user_id
. " AND card_id = " . $card['id'];
$dbh->exec($sql);
}
// 最後に、countが0以下になったカードを削除
$dbh->exec("DELETE FROM user_cards WHERE count <= 0");
// 空のJSONを返す
print("{}")
コードを実行する前の状態を確認してみよう。

そして、実行した後はこうなる。

あとは、テスト用のJSONを実際のPOST入力に置き換えるだけである。以下が最終的なスクリプトになる。
<?php
// JSONを返すべき
header("Content-Type: application/json; charset=utf-8");
// 接続
$dbh = new PDO('mysql:host=localhost; dbname=card_game', "root", "", [PDO::ATTR_PERSISTENT => true]);
// POSTデータを取得
$data = $_POST['data'];
// JSONに変換
$json = json_decode($data, true);
// ユーザーIDと削除したいデータの分ける
$user_id = $json['user_id'];
$card_list = $json['cards'];
// SQL文を構築
foreach ($card_list as $card)
{
$sql = "UPDATE user_cards SET count = count - " . $card['count']
. " WHERE user_id = " . $user_id
. " AND card_id = " . $card['id'];
$dbh->exec($sql);
}
// 最後に、countが0以下になったカードを削除
$dbh->exec("DELETE FROM user_cards WHERE count <= 0");
// 空のJSONを返す
print("{}")
?>
Unityを使い、APIとの接続
次のステップは、Unity側を実装することである。PHPスクリプトが期待しているのと同じデータ構造を作る必要がある。APIRequest には、ユーザーIDと、削除したいカードIDのリストが必要になる。
// プレーヤーのカードを削除する
// userID:ユーザーID
// cards: 削除したい「カードID」→「数」のディクショナリー
public static async Awaitable DeleteCardsAsync(string userID, Dictionary<string, int> cards)
{
// JSONを構築
JArray list = new JArray();
foreach (var (id, count) in cards)
{
JObject card = new JObject();
card["id"] = id;
card["count"] = count;
list.Add(card);
}
JObject json = new JObject();
json["user_id"] = userID;
json["cards"] = list;
// POSTでデータを準備
var postData = new WWWForm();
postData.AddField("data", json.ToString());
// APIのURLへリクエストを送信
const string url = ServerAPI + "/delete_player_cards.php";
var webRequest = UnityWebRequest.Post(url, postData);
// 返事を待機し、終わり(何も返さない)
await webRequest.SendWebRequest();
}
最後のステップは、削除ボタンが押されたときに必要なカード情報を集められるよう、ゲームのUIを修正することである。リクエストには少し時間がかかることがあるので、非同期メソッドとして書く必要がある。
// 選択されたカードを削除
private void DeleteSelectedCards()
{
_ = DeleteCardsAsync();
}
// 選択されたカードを削除(非同期)
private async Awaitable DeleteCardsAsync()
{
// 削除ボタンを無効にする
deleteButton.interactable = false;
// 表示中のカードを巡り、選択されたものを処理
var cards = new Dictionary<string, int>();
foreach (Transform child in cardsList.transform)
{
// 選択されていない?
var card = child.GetComponent<CardDisplay>();
if (!card.IsSelected)
continue;
// 同じIDだったら、数える
cards.TryAdd(card.CardID, 0);
cards[card.CardID]++;
}
// カードがなければ、終わり
if (cards.Count == 0) return;
// サーバーに依頼し…
await APIRequest.DeleteCardsAsync(LoginController.UserID, cards);
// …再読み込み依頼
await LoadCardsAsync();
// 削除ボタンを有効にする
deleteButton.interactable = true;
}
あとは、カードをいくつか選んで、データベースが更新されているか確認するだけである。


No comments to display
No comments to display