経緯
Unityでゲームを作っている時に、「スコアをランキング形式にできたらいいな。」と思っていました。
「ゲーセンでもスマホゲーでもランキングがあるとユーザーも燃えるかな」とも、思いました。
正直、めちゃくちゃ時間がかかってしまいどのような手順でどうやったらは覚えていないので、
ご了承ください。
使うプログラムコード
使うコード(ファイル)は4つです。
- config.php
- get_scores.php
- submit_score.php
- ScoreManager.cs
- データベース(MySQL)
になります。
データベース
まず、データベースを作ります。
カラムはid, user_name, score, game_dayとしました。データ型はint(11),varchar(255),int(11), dateとなっています。
私はさくらインターネットのレンタルサーバーを使っているので、その中のデータベース(MySQL)を使っています。
config.php
次にconfig.phpです。これはセキュリティ対策のために設けています。ここにデータベースの接続情報を書いています。以下がコードです。
<?php
// データベース接続情報
define('DB_HOST', 'データベースホスト名');
define('DB_NAME', 'データベース名');
define('DB_USER', 'データベースユーザ名');
define('DB_PASS', 'データベースパスワード');
それぞれの define
行は、指定された名前(例:’DB_HOST’)の定数を定義し、それに特定の値を割り当てています。これらの定数は、スクリプトの他の部分でデータベースに接続するために使用されます。
get_scores.php
まず、コードを書きます。
<?php
// config.phpファイルをインクルードして、データベース接続情報を取得
include_once '/home/mori/www/unity/config.php';
// エラー表示をオンに設定(デバッグ用)
ini_set('display_errors', 1);
// 全てのエラーを表示するように設定(デバッグ用)
error_reporting(E_ALL);
// データベース接続のための新しいmysqliオブジェクトを作成
$mysqli = new mysqli(DB_HOST, DB_USER, DB_PASS, DB_NAME);
// 全てのオリジンからのアクセスを許可するヘッダーを設定(CORSポリシー)
header('Access-Control-Allow-Origin: *');
// レスポンスのContent-TypeをJSON形式であると宣言するヘッダーを設定
header('Content-Type: application/json');
// データベース接続に失敗した場合のエラーハンドリング
if ($mysqli->connect_error) {
// エラーログに接続失敗のメッセージを出力
error_log("Connection failed: " . $mysqli->connect_error);
// 接続失敗のメッセージを出力してスクリプトを終了
die("Connection failed: " . $mysqli->connect_error);
}
// データベースからスコアを取得するSQLクエリを定義
$sql = "SELECT * FROM score ORDER BY score DESC LIMIT 10";
// SQLクエリを実行し、結果を取得
$result = $mysqli->query($sql);
// クエリが失敗した場合のエラーハンドリング
if ($result === FALSE) {
// エラーログにクエリ失敗のメッセージを出力
error_log("Error: " . $sql . "\n" . $mysqli->error);
}
// 結果が1行以上存在する場合
if ($result->num_rows > 0) {
// 結果を格納するための配列を定義
$scores = array();
// 結果セット内のすべての行をループ処理
while ($row = $result->fetch_assoc()) {
// 各行を連想配列として取得し、スコア配列に追加
$scores[] = array(
"player_name" => $row["user_name"],
"score" => intval($row["score"]),
"game_day" => $row["game_day"]
);
}
// スコア配列をJSON形式にエンコードして出力
echo json_encode($scores);
} else {
// 結果が存在しない場合、空の配列をJSON形式にエンコードして出力
echo json_encode(array());
}
// データベース接続を閉じる
$mysqli->close();
?>
// PHPスクリプトの終了(PHPタグは閉じる必要はありません)
このコードはget_scores.phpファイルで、データベースからスコアを取得し、その結果をJSON形式で返すスクリプトです。最初にデータベース接続情報が含まれるconfig.phpファイルをインクルードしています。その後、データベースに接続し、スコアを取得するSQLクエリを実行します。結果が1行以上存在する場合、それらを連想配列の形でJSON形式にエンコードして出力します。結果が存在しない場合、空の配列をJSON形式にエンコードして出力します。最後にデータベース接続を閉じます。
submit_score.php
次にsubmit_score.phpです。コードを書きます。
<?php
// エラー表示を有効にする
ini_set('display_errors', 1); // ランタイムエラーを表示する設定
ini_set('display_startup_errors', 1); // PHPの起動時エラーを表示する設定
error_reporting(E_ALL); // 全てのエラーレベルのエラーを表示する設定
include_once '/home/morip/www/unity/config.php'; // データベース接続情報を含む設定ファイルをインクルードする
// データベース接続
$mysqli = new mysqli(DB_HOST, DB_USER, DB_PASS, DB_NAME); // mysqliオブジェクトを作成し、データベースに接続する
header('Access-Control-Allow-Origin: *'); // CORS設定: すべてのオリジンからのアクセスを許可する
header('Content-Type: application/json'); // 応答データタイプをJSON形式に設定する
if ($mysqli->connect_error) {
die("Connection failed: " . $mysqli->connect_error); // データベース接続エラーがあればスクリプトを終了し、エラーメッセージを表示する
}
$user_name = $_POST["user_name"]; // POSTデータからユーザ名を取得する
$score = $_POST["score"]; // POSTデータからスコアを取得する
$game_day = $_POST["game_day"]; // POSTデータから日付を取得する
$sql = "INSERT INTO score (user_name, score, game_day) VALUES (?, ?, ?)"; // スコアをデータベースに挿入するSQLクエリ
$stmt = $mysqli->prepare($sql); // SQLクエリを準備する
if ($stmt === false) { // クエリの準備が失敗した場合、エラーメッセージをJSON形式で出力してスクリプトを終了する
echo json_encode(["status" => "error", "error" => $mysqli->error]);
exit();
}
$stmt->bind_param("sis", $user_name, $score, $game_day); // SQLクエリのプレースホルダーに値をバインドする
if ($stmt->execute()) { // クエリの実行が成功した場合、成功ステータスをJSON形式で出力する
echo json_encode(["status" => "success"]);
} else { // クエリの実行が失敗した場合、エラーステータスとエラーメッセージをJSON形式で出力する
echo json_encode(["status" => "error", "error" => $stmt->error]);
}
$stmt->close(); // ステートメントを閉じる
$mysqli->close(); // データベース接続を閉じる
?>
このPHPスクリプトは、ユーザー名、スコア、日付の情報をPOSTリクエストから受け取り、それをデータベースに保存します。これらの情報は、Unityゲームから送信され、ゲームプレイヤーのスコアとして保存されます。スクリプトは、操作の成功または失敗に関する情報をJSON形式で返します。
ScoreManager.cs
最後はScoreManager.csです。コードを書きます。
using System.Collections;
using UnityEngine;
using UnityEngine.Networking;
using SimpleJSON;
using System;
using System.Collections.Generic;
public class ScoreManager : MonoBehaviour
{
// スコアを送信するAPIのURL
private string submitScoreURL = "submit_score.phpが入っているPath";
// スコアを取得するAPIのURL
private string getScoresURL = "get_scores.phpが入っているPath";
// スコアを送信するためのメソッド
public void SubmitScore(string playerName, int score)
{
Debug.Log("Submitted user name: " + playerName + ", Submitted score: " + score); // デバッグログを追加
// SendScoreコルーチンを開始
StartCoroutine(SendScore(playerName, score));
}
// スコアを送信するコルーチン
private IEnumerator SendScore(string playerName, int score)
{
// WWWFormを使って送信するデータを作成
WWWForm form = new WWWForm();
form.AddField("user_name", playerName);
form.AddField("score", score);
// 現在の日付を取得し、文字列に変換します。
string gameDay = DateTime.Now.ToString("yyyy-MM-dd");
// 取得した日付を WWWForm に追加します。
form.AddField("game_day", gameDay);
// POSTリクエストを送信
UnityWebRequest www = UnityWebRequest.Post(submitScoreURL, form);
www.timeout = 10; // タイムアウトを 10 秒に設定
yield return www.SendWebRequest();
// リクエストが完了したかどうかをチェック
if (www.isDone)
{
Debug.Log("Request completed");
}
else
{
Debug.Log("Request not completed");
}
// 接続エラーやプロトコルエラーが発生した場合
if (www.result == UnityWebRequest.Result.ConnectionError || www.result == UnityWebRequest.Result.ProtocolError)
{
Debug.LogError("Connection or Protocol Error: " + www.error);
}
else
{
// レスポンスデータをJSON形式でパース
JSONNode response = JSON.Parse(www.downloadHandler.text);
if (response["status"] == "success")
{
Debug.Log("Score submitted successfully");
}
else
{
Debug.LogError("Error submitting score: " + response["error"]);
}
}
}
// スコアデータを取得するメソッド
public void FetchScores()
{
// GetScoresコルーチンを開始
StartCoroutine(GetScores());
}
// スコアデータを取得するコルーチン
private IEnumerator GetScores()
{
// GETリクエストを送信
UnityWebRequest www = UnityWebRequest.Get(getScoresURL);
yield return www.SendWebRequest();
// 接続エラーやプロトコルエラーが発生した場合
if (www.result == UnityWebRequest.Result.ConnectionError || www.result == UnityWebRequest.Result.ProtocolError)
{
Debug.LogError(www.error }
else
{
// レスポンスデータをJSON形式でパース
JSONNode scoresData = JSON.Parse(www.downloadHandler.text);
// パースしたスコアデータを表示
DisplayScores(scoresData);
}
}
// 上位のスコアデータを取得するメソッド
public void GetTopScores(Action<List<ScoreData>> onTopScoresReceived)
{
// GetTopScoresCoroutineコルーチンを開始
StartCoroutine(GetTopScoresCoroutine(onTopScoresReceived));
}
// 上位のスコアデータを取得するコルーチン
private IEnumerator GetTopScoresCoroutine(Action<List<ScoreData>> onTopScoresReceived)
{
// GETリクエストを送信
UnityWebRequest www = UnityWebRequest.Get(getScoresURL);
yield return www.SendWebRequest();
// 接続エラーやプロトコルエラーが発生した場合
if (www.result == UnityWebRequest.Result.ConnectionError || www.result == UnityWebRequest.Result.ProtocolError)
{
Debug.LogError("Error: " + www.error);
Debug.LogError("Error response: " + www.downloadHandler.text);
}
else
{
Debug.Log("JSON data: " + www.downloadHandler.text); // デバッグ行を追加
// レスポンスデータをJSON形式でパース
JSONNode scoresData = JSON.Parse(www.downloadHandler.text);
// ScoreDataのリストを作成
List<ScoreData> topScores = new List<ScoreData>();
for (int i = 0; i < scoresData.Count; i++)
{
// JSONデータからScoreDataオブジェクトを作成
ScoreData scoreData = new ScoreData
{
user_name = scoresData[i]["player_name"],
score = scoresData[i]["score"],
game_day = scoresData[i]["game_day"]
};
// ScoreDataオブジェクトをリストに追加
topScores.Add(scoreData);
}
// 取得したスコアデータをコールバックメソッドに渡す
onTopScoresReceived?.Invoke(topScores);
}
}
// スコアデータを表示するメソッド
private void DisplayScores(JSONNode scoresData)
{
for (int i = 0; i < scoresData.Count; i++)
{
string user_name = scoresData[i]["user_name"];
int score = scoresData[i]["score"];
Debug.Log("Player: " + user_name + ", Score: " + score);
}
}
// ゲームが開始されたときに呼ばれるメソッド
private void Awake()
{
// シーンが変わってもオブジェクトが破壊されないようにする
DontDestroyOnLoad(gameObject);
}
}
// スコアデータを表すクラス
[System.Serializable]
public class ScoreData
{
public string user_name; // プレイヤー名
public int score; // スコア
public string game_day; // ゲームの日付
}
このコードはUnityゲームのスコア管理を行うためのクラスです。主にスコアの送信と取得を行うメソッドが含まれています。また、Awakeメソッドではシーンが変わってもこのオブジェクトが破壊されないようにしています。これにより、ゲーム内の異なるシーン間でスコアデータを共有できます。
ScoreManager.csはUnityのGameSceneのGameDitectorという空の箱を作って、そこにアタッチしています。
苦戦したところ
苦戦したところだらけなのですが、
まず、どれが悪いのかわかりませんでした。
サーバーが悪いのか、
phpプログラムが悪いのか、
C#プログラムが悪いのか、
Unityが悪いのか、
データベースが悪いのか、
全然掴めず、すごく苦労しました。
なので、デバックを多用して、改善に取り組むことをお勧めします。
ここまで読んでいただきありがとうございました。