본문 바로가기

Unity3D SNS/Day 01

Unity3D SNS 1일차

Unity 3D SNS 강좌 정리 (1일차)


실습환경
Unity 3D v.5, Windows 7, APM (Apache,PHP, MySQL) 오토셋 v.9
APM은 오토셋 9 다운로드 및 설치
CodeIgniter : PHP 기반의 MVC 프레임워크
IDE : NetBeans 8 (PHP용)

총 24시간 강좌의 내용
Unity와 웹서버의 연동 (Unity 의 WWWForm 과 서버측 PHP 연동)
간단한 Unity 게임을 작성하고 안드로이드 폰에 탑재
Google Play  가입 요구됨 (평생 3만원)
Google Play, 페이스북, 푸시연동 예정
In-App 결재  : Asset Store 참조하여 설정하면 됨
유니티에서 Prime31 이용하여 구글 결제, PHP로 작성하기가 유리함, 구글에서 영수증 검증하는 방법
Prime31 결재 모듈


강사의 사이트 참조


제 1일차 강좌 개요
유니티게임(WWWForm) -> PHP -> MySQL 연동할 예정
PHP -> json -> Unity 3D (Asset 이용 json 파싱)
윈도우기반 APM 모듈을 설치하고 유니티 화면에서 로그인할 때 서버측 PHP와 연동하는 절차

CodeIgniter 설치
CodeIgniter 프레임워크(PHP기반의 MVC 프레임워크)를 사용하면 보다 쉽게 PHP를 사용할 수 있다
오픈 소스
다운로드하여 압축해제/PHP프로젝트 폴더에 모두 복사해 넣으면 된다
넷빈즈의 프로젝트 컬럼에 위의 코드들이 보이면 설치 성공

오토셋 9 다운로드 설치
NetBeans(PHP용) 에디터 사용
오토셋 매니저에서 넷빈즈 프로젝트 폴더를 지정하여 아파치 웹서버에 연결해준다
넷빈즈에서 프로젝트를 작성하고 프로젝트가 저장되는 루트디렉토리를 오토셋의 홈디렉토리로 지정한다


웹서버의 홈디렉토리 설정하기
설치된 아파치 웹서버가 넷빈즈의 프로젝트 폴더를 홈디렉토리로 참조하도록 설정하는 절차
오토셋 매니저에서 설정 > 웹서버 기본설정 을 선택하고 홈디렉토리 지정 후 하단의 '변경사항적용' 을 누른다
제어 > 웹서버 재시작 을 선택하면 웹서버의 홈디렉토리가 넷빈즈 프로젝트 폴더로 지정된다

넷빈즈에서 다음과 같이 index.php 파일을 작성한다
index.php
<html>
<body>
<?php
   //phpinfo();
   //echo "Hello PHP";
   function ShowMessage($msg) {
      echo $msg;
   }
   $value = "Hello World";
   ShowMessage($value);
?>
</body>
</html>

PHP에서 배열을 사용하는 예
array.php
<?php
   //배열생성
   $values = array(10,20,30,40);
   $sum =0;
   for($i=0; $i< count($values); $i++) {
      $sum += $values[$i];
   }
   echo "합계: " . $sum;
?>

PHP에서 JSON 문자열을 생성하고 출력하는 예
json.php
<?php
   $json["userid"]="1";
   $json["email"] = "unity@email.com";
   echo json_encode($json);
?>
위의 파일을 웹브라우저에서 요청하면 다음과 같은 결과를 볼 수 있다
{"userid":"1","email":"unity@email.com"}

MySQL Workbench를 설치하고 MySQL 관리
워크벤치에서 Connection 을 임의의 이름으로 새로 만들고 (화면상단 좌측 'MySQL Connections '+' 클릭) 
비밀번호(autoset) 설정하고 OK 누르면 새로운 연결이 생성된다
참고: 오토셋을 설치하면 MySQL의 디폴트 비밀번호는 autoset 으로 설정되어 있다
새로 생성된 연결을 누르면 SQL을 입력할 수 있는 창(Query 창)이 열린다

create table user (id int, name varchar(20));
select id, name from user where id=1;

insert into user (id, name) values ('1', 'kim');
update user set name='abcd' where id='1';
delete from user where id='1';

Workbench 에서 새로운 테이블 생성
워크벤치 좌측 메뉴컬럼 하단의 SCHEMAS  빈 부분에서 마우스 우측> create new schema를 선택하여 새로운 데이터베이스를 생성하고 user : root,  password : autoset 으로 설정한다
새로 생성된 스키마에서 마우스 우측을 클릭하여 set default schema 선택
tables 노드에서 마우스 우측을 눌러서 새로운 테이블 생성
컬럼이름 부분을 선택하여 컬럼을 추가하고 바로 우측에서 데이터 타입 및 자동증가(AI)를 설정한다
INSERT  문장을 실행하여 테이터를 삽입한다
SELECT 실행결과 확인창에서도 왼쪽 컬럼을 클릭하여 직접 테이블에 데이터를 삽입/수정/삭제하고 Apply 버튼을 누르면 테이블에 반영된다

database.php
<?php
   //update myusr set where id=
   // 데이터베이스 연결
   // 쿼리작성/실행/결과처리
   $connect = mysqli_connect("localhost", "root", "autoset", "test") or
   die("Error".mysql_errno($link));

   $updateQuery = "UPDATE `test`.`myusr` SET `name`='abcd' WHERE `id`='1';";
   $result = mysqli_query($connect, $updateQuery);

   if($result) {
     echo "success";
   } else {
     echo "fail";
   }
?>

INSERT, SELECT
<?php

// 요청 파라미터 추출
//$id = $_GET['id'];
$name = $_GET['name'];

// 데이터베이스 연결
$connect = mysqli_connect("localhost", "root", "autoset", "test") or
die("Error".mysql_errno($link));

// INSERT 문장 실행
$insertQuery = "insert into test.myusr (name, date) values('$name',now());";
$result = mysqli_query($connect, $insertQuery);

// SQL문장실행 성공여부
if($result) {
   echo "success";
} else {
   echo "fail";
}

// SELECT 문장 실행
$selectQuery = "SELECT * FROM test.myusr;";
$result = mysqli_query($connect, $selectQuery);

// 결과 출력
while($r = $result->fetch_assoc() ) {
   print_r($r); ?> <br>
<?php
}

// 데이터베이스 접속해제
mysqli_close($connect);
?>


유니티(HTTP Client)와 PHP(HTTP Server) 간의 통신 예제

Unity 3d v5 실행 2D 프로젝트 생성
Scene 뷰에 다음과 같은  UI 콤포넌트를 설정한다
Component > UI > Button 을 선택하면 계층뷰에 UI를 설정할 수 있다

InputTextField, Button, Text 등을 설정하여 로그인 박스를 구성한다
EmptyObject 를 계층뷰에 추가하고 CJoinManager.cs 스크립트를 생성한 후에 스크립트를 드래그하여 EmptyObject 에 포함한다 
버튼에 onClick() 함수 붙이기: 
버튼을 선택하고 인스펙터 뷰에서 onClick()설정 섹션의 + 기호를 눌러서 위에서 작성한 GameObject 를 등록하고 함수(clickJoin())를 찾아서 설정한다
Play 모드에서 버튼의 작동여부를 확인한다

유니티 측 Client 코드
GameObject 에 C# 스크립트를 생성하고 다음과 같이 작성한다
//CJoinManager.cs

using UnityEngine;
using System.Collections;
using UnityEngine.UI;

public class CJoinManager : MonoBehaviour {
public Text resultText; // 계층뷰에서 Text콤포넌트를 드래그하여 할당한다
public InputTextField nameInputField; // 계층뷰에서 InputTextField를 드래그하여 할당한다

// Use this for initialization
void Start () {
}

// Update is called once per frame
void Update () {
}

public void clickJoin() {
   //resultText.text = nameInputField.text;
   StartCoroutine ( DatabaseInsert () );
}

// INSERT(HTTP)
IEnumerator DatabaseInsert(){
   // URL
   string url = "http://localhost/database.php";
   // FORM 객체 생성- POST
   WWWForm sendData = new WWWForm ();
   // POST 파라미터 설정
   sendData.AddField ("name", nameInputField.text);
   // 통신객체 생성
   WWW webSite = new WWW (url, sendData);
   // 대기
   yield return webSite;       // 웹사이트에서 응답이 다운로드될 때까지 기다림
   // 에러처리
   if (webSite.error == null) {
      resultText.text = webSite.text;
   } else {
      resultText.text = webSite.error;
   }
}
}


서버측 PHP 코드
<?php
   //$id = $_GET['id']; // GET 방식 요청의 경우
   $name = $_POST['name']; // POST 방식 요청의 경우
   //
   // 삽입
   $insertQuery = "insert into test.myusr (name, date) values('','');";
   $connect = mysqli_connect("localhost", "root", "autoset", "test") or
   die("Error".mysql_errno($link));
   //$updateQuery = "UPDATE `test`.`myusr` SET `name`='$name' WHERE `id`='$id';";
   //$result = mysqli_query($connect, $updateQuery);

   $insertQuery = "insert into test.myusr (name, date) values('$name',now());";
   $result = mysqli_query($connect, $insertQuery);

   if($result) {
      echo "success";
   } else {
      echo "fail";
   }

   $selectQuery = "SELECT * FROM test.myusr;";
   $result = mysqli_query($connect, $selectQuery);
   while($r = $result->fetch_assoc() ) {
      print_r($r); ?> <br>
<?php }

   mysqli_close($connect);
?>

CodeIgniter 다운로드 및 압축해제
압축해제 후 구성 폴더와 파일들을 모두 넷빈즈 프로젝트 폴더로 드래그하여 복사하면 넷빈즈의 프로젝트 창에 그대로 반영된다

CodeIgniter 이용 Controller 작성하고 호출하기
넷빈즈 프로젝트의 application>controllers 아래에 Cisample.php 를 생성하고 아래와 같이 작성한다
<?php
   class Cisample extends CI_Controller {
      function __construct() {
         parent::__construct();
      }

   // http://localhost/index.php/cisample/index 으로 호출가능
   // CodeIgniter 에서는 index.php가 기본 경로가 된다
   function index() {
      echo "SAMPLE HELLO";
   }
}
?>

위에서 작성한 CodeIgniter 콘트롤러 호출 http://localhost/index.php/cisample/index
클래스내에 test() 함수가 있다면 위의 URL 끝에 index 를 test 로 바꾸어 http://localhost/index.php/cisample/test 으로 요청하면 된다

CodeIgniter 에서 데이터베이스 설정하기
config폴더에서 database.php를 더블클릭하여 데이터베이스 설정을 적절히 변경한다
$db['default'] = array ( ~ 부분을 변경하면 된다 
$db['test'] = array (
......
    .......
 - user          : 'root',
 - password     : 'autoset',
 - host          : 'localhost',
 - database     : 'test',
  .............

CodeIgniter 콘트롤러를 이용하여 클라이언트 파라미터를 추출하여 데이터베이스에 저장하는 예
http://localhost/index.php/cisample/insert?name=king 으로 요청하면 아래의 코드가 서버에서 실행된다

<?php
class Cisample extends CI_Controller {
   function __construct() {
      parent::__construct();
   }

   // http://localhost/index.php/cisample/index 으로 호출가능
   // CodeIgniter 에서는 index.php가 기본 경로가 된다
   function index() {
      echo "SAMPLE HELLO";
   }

   function insert() {
      //echo "test insert";
      $name = $this->input->get('name');
      $this->load->database('test');
      $insertQuery = "insert into test.myusr (name, date) values('$name',now());";
      $insertResult = $this->db->query($insertQuery);
      if($insertResult) {
        echo "success";
      }else{
        echo "fail";
      }
   }
}
?>

CodeIgniter 를 이용하여 로그인 기능을 구현하는 예
넷빈즈의 프로젝트에 application/controllers 아래에 Logincontroller.php를 작성하고 아래와 같이 입력한다
Logincontroller.php
<?php
class Logincontroller extends CI_Controller {
   function __construct() {
      parent::__construct();
      //모델객체 로드
      $this->load->model("Loginmodel","login_model"); //login_model은 참조변수로 사용됨
   }

   // http://localhost/index.php/logincontroller/index 으로 접속가능
   public function index() {
      echo "LoginController";
   }

   // http://localhost/index.php/logoncontroller/user_join?user_name=aaa&user_pw=ccc
   public function user_join() {
      //응답 딕셔너리 생성,
      $response_data = array(
        "result_code" => "100" //CODE 100 :실패
      );
      $user_name = $this->input->post("user_name");
      $user_pw=$this->input->post("user_pw");

      if($user_name==null) {
         $user_name=$this->input->get("user_name");
      }
      if($user_pw==null) {
         $user_pw=$this->input->get("user_pw");
     }

     if($user_name==null || $user_pw==null){
        $response_data['result_code']="101"; // 데이터 없음
        echo json_encode($response_data);
        return;
      }

      //모델객체 호출
      //$result = $this->login_model->login_test();
      $result = $this->login_model->user_join($user_name, $user_pw); // 모델의 함수호출
      //결과처리
      if($result !=null){
         $response_data['result_code'] = $result;
      }
      echo json_encode($response_data);
   }

   public function user_login() {
      $response_data = array(
         "result_code" => "100"
      );
      $user_name = $this->input->post("user_name");
      $user_pw = $this->input->post("user_pw");
      if($user_name==null){
         $user_name = $this->input->get("user_name");
      }
      if($user_pw==null){
         $user_pw = $this->input->get("user_pw");
      }
      if($user_name==null || $user_pw==null) {
         $response_data['result_code'] = "101";
         echo json_encode($response_data);
         return;
      }
      $result = $this->login_model->user_login($user_name, $user_pw);
      if($result != null) {
         $response_data['result_code'] = "000";
         $response_data['user_id'] = $result;
      }
      echo json_encode($response_data);
   }
}
?>


넷빈즈의 프로젝트에 application/models 아래에 Loginmodel.php를 작성하고 아래와 같이 입력한다
Loginmodel.php
<?php
class Loginmodel extends CI_Model {

var $user_db;

function __construct() {
   parent::__construct();
   $this->user_db = $this->load->database('test', true);
}

function login_test() {
   return "TEST OK";
}

/* SQL 파라미터를 사용하지 않는 경우
public function user_join($user_name, $user_pw) {
   // 데이터베이스 작업
   //$this->load->database('test'); // 코드를 생성자로 이동했음
   $joinQuery = "INSERT INTO `test`.`myusr` (`name`, `password`, `date`) VALUES ('$user_name', '$user_pw', now());";
   $joinResult = $this->user_db->query($joinQuery);
   if($joinResult){
      return "success";
   }
}*/

// SQL 파라미터를 사용하는 경우
public function user_join($user_name, $user_pw) {
   $joinQuery = "INSERT INTO `test`.`myusr` (`name`, `password`, `date`) VALUES (?,?, now());";
   $param = array($user_name, $user_pw);
   $joinResult = $this->user_db->query($joinQuery, $param);
   if($joinResult){
      return $this->user_db->insert_id(); // 새로 삽입된 아이디를 리턴함
   }
}

public function user_login($user_name, $user_pw){
   $select_query = "select * from myusr where name=? and password=?;";
   $param = array($user_name, $user_pw);
   $selectResult = $this->user_db->query($select_query, $param);
   if($selectResult->num_rows()>0) {
      foreach($selectResult->result() as $row) {
         return $row->id;
      }
   }
   return null;
}
}
?>

Unity 3D에서 JSON 파서 사용하기
Window > Asset Store > 로그인 후
Asset Store 에서 JSON으로 검색
검색 결과 리스트에서 BoomlagonJSON 선택
불러오기 > Import 선택하면 설치 완료

BoomlagoonJSON 사용법
using Boomlagoon.JSON;
......
JSONObject json = JSONObject.Parse(webSite.text);
Debug.Log(json.GetString("user_id"));
resultText.text = "userid="+json.GetString("user_id");


위의 PHP 콘트롤러에 접속하는 유니티 클라이언트 코드
CJoinManager.cs
using UnityEngine;
using System.Collections;
using UnityEngine.UI;
using Boomlagoon.JSON;  // JSON 파서 사용

public class CJoinManager : MonoBehaviour {
public Text resultText;
public InputField nameInputField;
public InputField passwordField;

// Use this for initialization
void Start () {
}

// Update is called once per frame
void Update () {
}

public void clickJoin() {
   //resultText.text = nameInputField.text;
   StartCoroutine (userJoin());
   //Application.LoadLevel ("JoinScene2");
}

IEnumerator userJoin() {
   string url = "http://localhost/index.php/logincontroller/user_join";
   WWWForm sendData = new WWWForm ();
   sendData.AddField ("user_name", nameInputField.text);
   sendData.AddField ("user_pw", passwordField.text);
   WWW webSite = new WWW (url, sendData);
   yield return webSite;
   if (webSite.error == null) {
      Debug.Log (webSite.text);
   }
}

public void clickLogin(){
   StartCoroutine ( userLogin () );
}

IEnumerator userLogin() {
   string url = "http://localhost/index.php/logincontroller/user_login";
   WWWForm sendData = new WWWForm ();
   sendData.AddField ("user_name", nameInputField.text);
   sendData.AddField ("user_pw", passwordField.text);
   WWW webSite = new WWW (url, sendData);
   yield return webSite;
   if (webSite.error == null) {
      Debug.Log (webSite.text);
      // JSON 파싱예
      JSONObject json = JSONObject.Parse(webSite.text);
      Debug.Log(json.GetString("user_id"));
      resultText.text = "userid="+json.GetString("user_id");
   } else {
      Debug.Log (webSite.error);
   }
}

/*
// INSERT(HTTP)
IEnumerator DatabaseInsert(){
   // URL
   string url = "http://localhost/database.php";
   // FORM 객체 생성- POST
   WWWForm sendData = new WWWForm ();

   // POST 파라미터 설정
   sendData.AddField ("name", nameInputField.text);
   // 통신객체 생성
   WWW webSite = new WWW (url, sendData);
   // 대기
   yield return webSite;
   // 에러처리
   if (webSite.error == null) {
      resultText.text = webSite.text;
   } else {
      resultText.text = webSite.error;
   }
}*/
}


Coroutine 선언의 예
IEnumerable PostScore(string username, int score)
{
   var hash = Md5Sum(username + score + _secretKey);
   var highscoreUrl = _addScoreUrl + "name=" + WWW.EscapeURL(username) + "&score=" + score + "&hash=" + hash;
   var hsPost = new WWW(highscoreUrl); 
   yield return hsPost;                            // Wait until the download is done
   if(hsPost.error != null)
   {
      print("There was an error posting the high score: " + hsPost.error);
   }
}

yield 다음에 사용되는 형식
yield break : 코루틴을 즉시 멈춘다
yield return null - Update구문의 수행이 완료될 때까지 대기한다.

yield return new WaitForSeconds(1) 와 같은 형식으로 사용되는 경우
WaitForEndOfFrame - 현재 프레임의 렌더링 작업이 끝날 때까지 대기한다.
WaitForFixedUpdate - FixedUpdate구문의 수행이 완료될 때까지 대기한다.
WaitForSeconds - 지정한 초만큼 대기한다.
WWW - 웹에서 데이터의 전송이 완료될 때까지 대기한다 (WaitForSeconds or null처럼 재시작한다)
Another coroutine - 새로운 코루틴은 yielder가 재시작되기 전에 완료 될 것이다.