1. 유니티 프로젝트에 JSON 설치
PHP는 서버 DB를 탐색한 결과값을 JSON 형식으로 유니티 클라이언트에게 전달할 것이기 때문에
그 결과값을 받아서 처리할 프로젝트에는 당연히 JSON이 설치돼있어야 한다.
요즘 유니티는 Newtonsoft.json 라이브러리 사용을 권장하는 듯하다.
위 링크를 보고서 유니티 패키지 매니저를 통해 설치하고 학습하면 된다.
2. DB에서 테이블 읽기
서버 DB에 위와 같이 몬스터 정보에 관한 테이블이 있다.
(다시 말하면, 서버 DB에 위와 같이 몬스터나 아이템 정보들을 놓고서 이를 클라에서 읽어서 사용하는 건 바보 같은 짓이다)
유니티에는 이런 식으로 몬스터 정보 스트럭쳐 또는 클래스가 있으면
DB의 몬스터 테이블을 읽어서, 각각의 변수들을 매칭시켜서 몬스터 오브젝트를 초기화할 수 있다.
3. 유니티 클라이언트의 PHP 요청 코드
// DB에 저장된 Monster Table에 접근해서, 그 중에서도 특정 몬스터(monster_code)의 정보를 가져오는 코루틴
IEnumerator LoadMonsterInfoCoroutine(int monster_code, System.Action<MonsterInfo> callback)
{
// 1. 불러올 PHP 주소
string LoadMonsterInfoURL = "http://youngju.tk/Roguelike/Monster/LoadMonsterInfo.php";
// 2. WWWForm 초기화
// PHP에게 보낼 입력값 초기화. PHP 마다 당연히 다름
// monster_code가 일치하는 몬스터의 정보를 가져와달라는 요청을 하기위해 monster_code를 input으로 보냄
WWWForm form = new WWWForm();
form.AddField("Input_code", monster_code);
// 3. 해당 입력값으로 PHP에 처리 요청
WWW webRequest = new WWW(LoadMonsterInfoURL, form);
yield return webRequest; // 처리 결과가 올 때까지 기다린다.
// 4. 처리 결과 수신
// euc-kr 인코딩으로 받아서 string 타입으로 변환
System.Text.Encoding enc = System.Text.Encoding.GetEncoding("euc-kr");
string sz = enc.GetString(webRequest.bytes);
// 5. 올바르게 처리 결과를 받았다면, JSON으로 파싱해서 게임 내의 오브젝트 초기화
if (sz.Contains("Load Monster Info Success"))
{
var N = JSON.Parse(sz);
MonsterInfo info = new MonsterInfo();
// 몬스터 스텟 정보 초기화
info.monster_name = N["name"];
info.level = N["level"].AsInt;
info.spriteSheet_name = N["sprite_name"];
info.exp = N["exp"].AsInt;
info.max_HP = N["hp"].AsInt;
info.defence = N["defence"].AsInt;
info.attackDamage = N["attack_damage"].AsInt;
info.attackSpeed = N["attack_speed"].AsFloat;
info.moveSpeed = N["move_speed"].AsFloat;
// 드랍 테이블 초기화
Dictionary<ItemRank, float> dropTable = new Dictionary<ItemRank, float>();
// N["drop_table"]은 JSON 형식으로 저장되있기 때문에 2차적으로 파싱해준다.
var drop = JSON.Parse(N["drop_table"]);
if (drop["consumed"] != null)
dropTable.Add(ItemRank.CONSUMED, drop["consumed"].AsFloat);
if (drop["stone"] != null)
dropTable.Add(ItemRank.STONE, drop["stone"].AsFloat);
if (drop["normal"] != null)
dropTable.Add(ItemRank.NORMAL, drop["normal"].AsFloat);
if (drop["magic"] != null)
dropTable.Add(ItemRank.MAGIC, drop["magic"].AsFloat);
if (drop["rare"] != null)
dropTable.Add(ItemRank.RARE, drop["rare"].AsFloat);
if (drop["unique"] != null)
dropTable.Add(ItemRank.UNIQUE, drop["unique"].AsFloat);
info.dropTable = dropTable;
callback(info);
}
}
[4. 처리 결과 수신 단계]에서 sz를 출력해 보면
{"name":"언데드","sprite_name":"Undead02_Sheet","level":"1","exp":"10","hp":"100","defence":"10","attack_damage":"40","attack_speed":"0.8","move_speed":"6","drop_table":"{"consumed":20,"stone":20,"normal":16,"magic":18,"rare":20,"unique":5}"}
Load Monster Info Success!!
이런 값이 출력된다. 이 문자열은 PHP가 요청에 대한 처리를 하고 그 결과값을 준 것이다.
name이 key, 언데드가 value이며, 모든 속성은 key와 value 쌍으로 구성돼 있다.
그래서 JSON 파싱 후에 info.monster_name = N["name"]; 이런 식으로 초기화가 가능하다.
마지막에 drop_table을 보면, value값이 처음부터 DB에도 JSON 형식으로 저장돼 있기 때문에 value를 한 번 더 JSON 파싱하고 있다.
level은 1부터 999까지 정수가 들어갈 거라고 정해져 있기 때문에 상관없지만,
유저 인벤토리 정보 같은 거는 비었을 수도 있고, 몇 개의 아이템이 존재할지 모르는. 유동적인 상황이라
그런 데이터는 이중 JSON을 활용하면 될 것이다.
그리고 결과값을 성공적으로 받았는지 확인하기 위한 코드
sz.Contains("Load Monster Info Success") 와 같은 코드는 사실 적절하지 않다.
PHP와 통일된 에러코드 enum 데이터를 만들어서 확인하는 것이 좋다.
4. PHP의 DB 탐색 요청 코드
(1) DBConnect.php
<?php
function connectDB()
{
$con = mysql_connect("localhost", "growy", "******") or ("Cannot connect!" .mysql_error());
if (!$con)
die ("could not connect the database" .mysql_error());
return $con;
}
function selectDB($con, $db_name)
{
if (!$con)
$con = connectDB();
mysql_select_db($db_name, $con) or die ("could not select the database" .mysql_error());
}
function setEncodingType($type)
{
mysql_query("set session character_set_connection=".$type.";");
mysql_query("set session character_set_results=".$type.";");
mysql_query("set session character_set_client=".$type.";");
}
?>
다른 .php 스크립트에서 사용할 전역 변수나 전역 함수를 정의한 스크립트다.
connectDB 함수는 DB에 growy라는 계정 이름과 비밀번호가 존재하는지 검사한다. (로그인 같은)
selectDB 함수는 서버 DB에서 DB를 여러 개로 나눌 수 있는데 (폴더 개념), 그중 어떤 폴더 DB에 접근할 것이냐를 정하는 함수다.
setEncodingType은 인코딩 타입을 초기화하는 함수다. 매번 euc-kr로 해줘야 하기 때문이다.
(2) LoadMonsterInfo.php
<?php
include("../DBConnect.php");
// 유니티로부터 입력값 받음
$code = $_POST['Input_code'];
// DB 연결 (로그인 개념)
$con = connectDB();
// 어떤 DB에 접근할건지
selectDB($con, "GameData");
// 인코딩 타입 설정
setEncodingType("euckr");
// 입력값을 활용해서 SQL 쿼리문 실행
$result = mysql_query("SELECT * FROM Monster WHERE `monster_code`='".$code."'");
if(!$result)
die('Could not Connect:' . mysql_error());
// 쿼리문 처리 후 결과값의 갯수. 정확히는 위의 SELECT 쿼리로 선택된 레코드(행)의 갯수.
$numrows = mysql_num_rows($result);
if ($numrows == 0)
{
die("Monster does not exist. \n");
}
else
{
// 결과값 레코드(행)를 key, value 쌍의 map 형태로 변환
$row = mysql_fetch_assoc($result);
if($row != false)
{
// 리턴할 배열 생성
$info = array();
// row -> info 매칭시킴. value값에 한글이 포함되있으면 urlencode 인코딩도 해줌
$info['name'] = urlencode($row['name']);
$info['sprite_name'] = urlencode($row['sprite_name']);
$info['level'] = $row['level'];
$info['exp'] = $row['exp'];
$info['hp'] = $row['hp'];
$info['defence'] = $row['defence'];
$info['attack_damage'] = $row['attack_damage'];
$info['attack_speed'] = $row['attack_speed'];
$info['move_speed'] = $row['move_speed'];
$info['drop_table'] = urlencode($row['drop_table']);
// 이거는 정확히 모르겠지만.. 거의 모든 PHP에서 동일하게 이런식으로 인코딩함
header("Content-type:application/json");
echo urldecode(json_encode($info));
// 에러 메세지와 구분하기 위한 성공 문자열도 같이 리턴해줌
echo ("\n");
echo ("Load Monster Info Success!!");
}
}
mysql_close($con);
?>
아까 유니티에서 호출한 LoadMonsterInfo.php이다.
위 코드는 DB 형태를 먼저 알고 있는 상태에서 SELECT 쿼리문으로 하나의 레코드만 선택될 것이다라는 확신이 있어서 저렇게 처리했는데,
$option = array();
$return_array = array();
for($i = 0; $i < $numrows; $i++)
{
$row = mysql_fetch_array($result);
if($row != false)
{
$option['option_name'] = urlencode($row['option_name']);
$option['tier_count'] = $row['tier_count'];
$option['tier_gap'] = $row['tier_gap'];
array_push($return_array, $option);
}
}
만약에 SELECT 쿼리문으로 여러 개의 레코드가 선택된 경우도 처리해야 한다면
PHP에서는 이런 식으로 for문을 돌려서 사용할 수 있고, array_push를 써서 이중배열을 쓸 수도 있다.
5. PHP의 DB 업데이트 요청 코드
<?php
include("../DBConnect.php");
$user_index = $_POST['Input_user_index'];
$info = $_POST['Input_info'];
$field = $_POST['Input_field'];
$con = connectDB();
selectDB($con, "User");
setEncodingType("euckr");
$check = mysql_query("SELECT * FROM Inventory WHERE `user_index`='".$user_index."'");
$numrows = mysql_num_rows($check);
if ($numrows != 0)
{
$Result = mysql_query( "UPDATE `Inventory` SET `$field`='".$info."' WHERE `user_index`='".$user_index."'");
if($Result)
die("PlayerInventory Save Success. \n");
else
die("PlayerInventory Save error. \n");
}
else
{
die("user_index does not exist. \n");
}
?>
위 스크립트는 유저의 인벤토리 데이터에 변화가 생겼을 때,
서버 DB의 유저 인벤토리 내용도 갱신하도록 요청하는 스크립트다.
'1인 개발 > 개발 환경' 카테고리의 다른 글
클라우드 웹 서버 설치 & 연동하기 #1 (0) | 2023.02.06 |
---|---|
구글 스프레드 시트 연동 (2) | 2021.10.19 |
GitHub에 원격 저장소 생성 및 프로젝트 업로드 (0) | 2021.10.19 |