Skip to content

CloudBread를 사용하여 Flappy Bird 게임에 서버 구축 CloudBread Unity SDK 사용

HONGYOONSEOK edited this page Sep 10, 2016 · 2 revisions

by 홍윤석 (yshong93)
Updated : 2016 / 09 / 10

CloudBread 캠프 미션!

Flappy Birds 게임에 CloudBread 게임 서버 연동하기!


1. Flappy Bird 게임 만들기 소개

Flappy Bird 라는 게임은 매우 단순한 Running 게임 입니다. gmae screenshot

원본 게임은 현재 앱 스토어에는 제공되지 않고 있지만 지속적으로 패러디 게임들이 개발되고 있습니다. 뿐만 아니라 개발 하기 매우 쉽기 때문에 처음 게임을 개발을 해보시는 분들에게 부담 없이 만들어 볼 수 있을 최적의 게임이라고 생각합니다.

실제 유니티를 사용하여 게임을 만드는 과정은 이 블로그를 참고하시면, Unity 3D를 사용하여 개발을 진행할 수 있습니다. 실제 데모는 이 곳에서 해 볼 수 있습니다.

아래 주소에서 제공해주는 필요한 소스코드를 다운받으면, 바로 게임에 CloudBread 를 붙여 볼 수 있습니다. https://github.com/dgkanatsios/FlappyBirdClone

아래 링크에서는 개발 하는 과정의 영상을 볼 수 있습니다. https://youtu.be/umWGSm0h8kE

youtube

CloudBread 를 사용하여 게임 만들기 는 다음과 같은 순서로 진행됩니다.

  1. Flappy Bird 게임 만들기
  2. Facebook 로그인 기능 구현
  3. Azure 에서 제공하는 Facebook 인증기능 사용하기
  4. CloudBread 의 회원가입 API 호출
  5. CloudBread 의 랭킹 API 호출

2. Unity 에서 Flappy Bird 실행하기

Unity Play Scene Asset - Scenes - mainGame 을 열면 위와 같이 게임을 실행 할 수 있습니다.

3. Unity 에 로그인 씬 만들기

마우스 오른쪽 버튼 클릭 - Create - Scene 을 클릭하여 loginGame 씬 생성

Unity Play Scene

Assets – ScripsFacebookLoginScript.cs (C# Script) 생성하기

Unity Play Scene

loginGame 씬에서 마우스 오른쪽 – UI – Button 클릭 해서 버튼 만들기

Unity ui button add

마우스 오른쪽 – Create Empty 클릭해서 FacebookLoginManager라는 Gameobject 만들기

Unity create empty

아까 만든 FacebookLoginScript를 드래그 해서 FacebookLoginManager에 추가하기

Unity inspector

아래 순서대로 새로 만든 버튼 클릭 – Inspector - On Click () - + 버튼 클릭 None (Object)에 FacebookLoginManager 드래그해서 놓기 Unity add button1 Unity add button2

FacebookLoginScipt.cs 에 아래와 같이 FacebookLoginBtnClick() 메소드 추가

using UnityEngine;
using System.Collections;

public class FacebookLoginScript : MonoBehaviour {

	// Use this for initialization
	void Start () {

	}

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

	}

    public void FacebookLoginBtnClick()
    {

    }
}

No Function – FacebookLoginScript – FacebookLoginBtnClicked() 클릭 register button event


4. Facebook 앱 추가 및 Azure Mobile App 에 Facebook 등록하기 (생략)

(캠프에서는 하나의 데모 CloudBread 서버만을 사용하기 때문에, Azure에 Facebook 을 등록하는 실습에 어려움이 있습니다. 현장 캠프 미션에서는 설정하는 방법을 생략하고, 미리 설정된 계정을 사용합니다.)

  1. Facebook에 앱 추가하기
  2. Azure Mobile App 에 페이스북 앱 추가하기

아래 사이트에서 자세하게 볼 수 있습니다. https://azure.microsoft.com/ko-kr/documentation/articles/mobile-services-how-to-register-facebook-authentication/

중요 Advanced 탭을 클릭하고, Valid OAuth redirect URIs에 아래URL 형식 입력한 다음 Save Changes를 클릭합니다. https://[mobile_service].azure-mobile.net/login/facebook

5. Facebook SDK 를 이용한 로그인 구현

Facebook App ID : 207398879650397
  1. SDK 다운로드 하기 (https://developers.facebook.com/docs/unity)
  2. Unity 프로젝트에 SDK 추가하기 (위 사이트의 Getting Started 참고)
  3. loginGame 에서 버튼 클릭했을 때, Facebook Permission 받아오도록 구현 (위 사이트의 Example 참고해서 FacebookLoginScript 구현)
using UnityEngine;
using System.Collections;
using System.Collections.Generic;
using Facebook.Unity;
using UnityEngine.SceneManagement;


public class FacebookLoginScript : MonoBehaviour {

    // Awake function from Unity's MonoBehavior
    void Awake()
    {
        if (!FB.IsInitialized)
        {
            // Initialize the Facebook SDK
            FB.Init(InitCallback, OnHideUnity);
        }
        else {
            // Already initialized, signal an app activation App Event
            FB.ActivateApp();
        }
    }

    private void InitCallback()
    {
        if (FB.IsInitialized)
        {
            // Signal an app activation App Event
            FB.ActivateApp();
            // Continue with Facebook SDK
            // ...
        }
        else {
            Debug.Log("Failed to Initialize the Facebook SDK");
        }
    }

    private void LoginwithPermissions()
    {
        var perms = new List<string>() { "public_profile", "email", "user_friends" };
        FB.LogInWithReadPermissions(perms, AuthCallback);
    }

    private void AuthCallback(ILoginResult result)
    {
        if (FB.IsLoggedIn)
        {
            // AccessToken class will have session details
            var aToken = Facebook.Unity.AccessToken.CurrentAccessToken;
            // Print current access token's User ID
            Debug.Log(aToken.UserId);
            // Print current access token's granted permissions
            foreach (string perm in aToken.Permissions)
            {
                Debug.Log(perm);
            }

            // 유저 이름(닉네임) 불러오기
            FB.API("me?fields=name", HttpMethod.GET, NameCallBack);


            // 인증 토큰 가져오기
            // TODO : CloudBread 클래스 생성 후, Login API 호출

        }
        else {
            Debug.Log("User cancelled login");
        }
    }

    public void StartGame()
    {
        SceneManager.LoadScene("mainGame");
    }

    private void NameCallBack(IGraphResult result)
    {
        string userName = (string)result.ResultDictionary["name"];
        print(userName + "님 안녕하세요^^");
        PlayerPrefs.SetString("nickName", userName);
    }

    private void OnHideUnity(bool isGameShown)
    {
        if (!isGameShown)
        {
            // Pause the game - we will need to hide
            Time.timeScale = 0;
        }
        else {
            // Resume the game - we're getting focus again
            Time.timeScale = 1;
        }
    }

    public void FacebookLoginBtnClick()
    {
        if (!FB.IsLoggedIn)
            LoginwithPermissions();
    }
}

페이스북 SDK 를 사용하여 로그인 성공했을 때 Facebook Login Success

지금까지 페이스북 SDK 를 사용하여 User Access Token, User ID, User Name 을 받는 것을 진행하였습니다. 앞으로 이어질 내용에서는 CloudBread-Unity-SDK를 사용하여 애저 인증 기능을 사용하는 방법과 CloudBread 서버를 사용하는 방법에 대해 자세히 다루겠습니다.


6. CloudBread 사용하기 (CloudBread-Unity-SDK 사용)

CloudBread 는 HTTP RESTful API 를 지원하기 때문에, 어떠한 플랫폼에서든 Http Request만으로 사용 할 수 있습니다. 하지만 저희가 공식적으로 진행하고 있는 Unity SDK 프로젝트를 사용하면 훨씬 쉽게 CloudBread와 Unity를 연동 할 수 있습니다. 저희는 이번 캠프에서는 Unity-SDK 를 사용하여 개발하는 방법에 대해 소개를 하고 있으며, 만약 Http Request 를 직접 구현하고 싶으시다면, 다음 문서를 참조해주세요.

프로젝트에 CloudBread Unity SDK 추가하기

  1. 레포지토리의 코드를 다운받습니다. Assets 안에 있는 CloudBread 폴더와 Editor Default Resources 폴더를 프로젝트에 추가합니다.

참조 : CloudBread-Unity-SDK 사용법 가이드

Assets/CloudBread/CB.Settings 파일을 설정을 다음과 같이 변경합니다. CB.Settings

Azure 인증이란?

여기서 말하는 Azure 인증 이란 Facebook 과 CloudBread 사이의 oAuth 2.0 인증 방식의 기능을 말합니다. Azure 에서 기본적으로 제공하는 기능이며, 이 기능을 사용하면 페이스북 로그인 구현을 매우 간단하게 구현 할 수 있습니다.

Facebook SDK 에서 받아온 User Token을 사용하여, 애저에서 발급하는 토큰을 발급받을 것입니다. 애저 인증 기능을 사용하기 위해서는 HTTP Request 할 때, Header 에 애저에서 발급받은 토큰을 항상 넣어야 한다는 것입니다.

다음은 HTTP Request 를 하기 위해 꼭 필요한 헤더 값 입니다.

"Accept" : "application/json"
"Content-Type" : "application/json"
"X-ZUMO-FEATURES" : "AJ"
"ZUMO-API-VERSION" : 2.0.0
"x-zumo-auth" : "<애저에서 발급받은 토큰>"

이 때, Azure 에서 발급받은 토큰을 x-zumo-auth 에 넣어 http 호출을 함으로써, 비정상적인 Http 호출을 막을 수 있습니다.

Azure 토큰 발급받기

Azure 에 요청을 하여 x-zumo-auth 에 넣을 토큰을 받아오는 과정입니다. 다음과 같은 RESTful API 호출을 통해 Azure로 부터 토큰을 받아 올 수 있습니다.

Address :

https://cb2-auth-demo.azurewebsites.net/.auth/login/facebook

Request Data :

{
  "access_token" : "페이스북에서 발급 받은 토큰"
}

Response Data :

{
  "authenticationToken" : "<발급받은 Token>",
  "user" : {
    "userId" : "sid:<발급받은 sid>"
  }
}
  • authenticationToken 을 호출 할 때마다 헤더에 X-ZUMO-AUTH 값으로 넣어야만 서버로부터 응답을 받을 수 있다.
  • userId의 sid 를 게임에서 유일한 값 (memberID)로 사용

Asstes/CloudBread/Protocols 폴더에 CloudBread.POST.AzureAuth.cs(C# Scripts) 파일 생성 다음과 같이 스크립트 작성하기.

using UnityEngine;
using System;


namespace CloudBread
{
	public partial class AzureAuth
	{
		//https://cb2-auth-demo.azurewebsites.net/.auth/login/facebook
    // 호출 할 Address
		const string _url = ".auth/login/facebook";

    // API 호출 시 Request Data
		[Serializable]
		public struct Post
		{
			[SerializeField]
			public string access_token;
		}

    // API 호출 시 Response Data
		[Serializable]
		public struct Receive
		{
			[SerializeField]
			public string authenticationToken;

			[SerializeField]
			public User user;
		}

		[Serializable]
		public struct User
		{
			[SerializeField]
			public string userId;
		}

		static public void Request(Post postData_, System.Action<Receive> callback_, System.Action<string> errorCallback_ = null)
		{
			CloudBread.Request(CloudBread.MakeFullUrl(_url), JsonUtility.ToJson(postData_), callback_, errorCallback_);
		}
	}
}

FacebookLoginScipt.cs 스크립트에서 CloudBread 클래스의 Login 함수 호출하기 (// TODO 부분에 추가하기)

CloudBread cb = new CloudBread();
cb.Login(AzureAuthentication.AuthenticationProvider.Facebook, aToken.TokenString, Callback_login);

Login 콜백 함수 구현

private void Callback_login(string id, WWW www)
{
    print(www.text);
    string resultJson = www.text;

    JsonReader jsonReader = new JsonReader();
    AuthData resultData = jsonReader.Read<AuthData>(resultJson);

    // Azure 인증을 위해 발급받은 Azure Token 을 헤더에 추가
    AzureMobileAppRequestHelper.AuthToken = resultData.authenticationToken;

    // 게임에서 사용 할 userId 를 PlayerPrefs 에 저장
    // 원래 게임에서는 아래와 같이, Azure 에서 제공해 주는 userID 를 넣는 것이 맞지만,
    // 데모 서버를 사용하는 분들은 임의의 아이디를 넣어서 다른 사람들과 충돌이 나지 않도록 합시다
    PlayerPrefs.SetString("userId", resultData.user.userId);

}

애저에서 Token과 UserID를 json 형식 제공

{
  "authenticationToken" : "<발급받은 Token>",
  "user" : {
    "userId" : "sid:<발급받은 sid>"
  }
}

7. CloudBread 에 회원가입하기

  1. Facebook에서 UserID와 이름을 가져와서 CloudBread 서버에 등록하기 CBInsRegMember API 호출 (PostMan 참고)

  2. 유효한 사용자이면, 게임 화면으로 넘어가기

  • CBInsRegMember API호출 시, 새로 가입 된 사용자 응답
{
  "result": "2"
}
  • CBInsRegMember API호출 시, 중복된 사용자(이미 가입된 사용자)
status : 500 Internal Server Error

CloudBread.cs 파일에 CloudBread 서버에 회원 등록을 하기 위해 CBInsRegMember API 를 호출하도록 구현

public void CBInsRegMember(Action<string, WWW> callback)
{
    var ServerEndPoint = ServerAddress + "api/CBInsRegMember";

   WWWHelper helper = WWWHelper.Instance;
   helper.OnHttpRequest += OnHttpRequest;

   MemberData memberData = new MemberData
   {
        MemberID_Members = (string)PlayerPrefs.GetString("userId"),
        EmailAddress_Members = (string)PlayerPrefs.GetString("userId"),
        Name1_Members = (string)PlayerPrefs.GetString("nickName")
   };

   JsonWriter jsonWriter = new JsonWriter();
   string jsonBody = jsonWriter.Write(memberData);

   helper.POST("CBInsRegMember", ServerEndPoint, jsonBody);

   Callback = callback;
}

FacebookLoginManager.cs 의 Login 콜백 함수에서 바로 CBInsRegMember 호출

private void Callback_login(string id, WWW www)
{
    print(www.text);
    string resultJson = www.text;

    JsonReader jsonReader = new JsonReader();
    AuthData resultData = jsonReader.Read<AuthData>(resultJson);

    // Azure 인증을 위해 발급받은 Azure Token 을 헤더에 추가
    AzureMobileAppRequestHelper.AuthToken = resultData.authenticationToken;

    // 게임에서 사용 할 userId 를 PlayerPrefs 에 저장
    PlayerPrefs.SetString("userId", resultData.user.userId);

    // CBInsRegMember 함수를 사용하여 CloudBread 에 memberId 등록하기
    CloudBread cb = new CloudBread();
    cb.CBInsRegMember(Callback_CBInsRegMember);
}
    public void Callback_CBInsRegMember(string id, WWW www)
    {
        if (www.error != null) //새로 회원 가입
        {
            print("이미 가입된 회원");
            StartGame();


        }
        else //이미 가입된 회원
        {
            print("새로 가입한 회원");
            StartGame();
        }
    }

FacebookLoginManager.cs 스크립트 완성

using UnityEngine;
using System.Collections;
using System.Collections.Generic;
using Facebook.Unity;
using Assets.Scripts.CloudBread;
using JsonFx.Json;
using UnityEngine.SceneManagement;

public class FacebookLoginScript : MonoBehaviour {

// Use this for initialization
void Start () {
}
// Update is called once per frame
void Update () {
}

    // Awake function from Unity's MonoBehavior
    void Awake()
    {
        if (!FB.IsInitialized)
        {
            // Initialize the Facebook SDK
            FB.Init(InitCallback, OnHideUnity);
        }
        else {
            // Already initialized, signal an app activation App Event
            FB.ActivateApp();
        }
    }

    private void InitCallback()
    {
        if (FB.IsInitialized)
        {
            // Signal an app activation App Event
            FB.ActivateApp();
            // Continue with Facebook SDK
            // ...
        }
        else {
            Debug.Log("Failed to Initialize the Facebook SDK");
        }
    }

    private void LoginwithPermissions()
    {
        var perms = new List<string>() { "public_profile", "email", "user_friends" };
        FB.LogInWithReadPermissions(perms, AuthCallback);
    }

    private void AuthCallback(ILoginResult result)
    {
        if (FB.IsLoggedIn)
        {
            // AccessToken class will have session details
            var aToken = Facebook.Unity.AccessToken.CurrentAccessToken;
            // Print current access token's User ID
            Debug.Log(aToken.UserId);
            // Print current access token's granted permissions
            foreach (string perm in aToken.Permissions)
            {
                Debug.Log(perm);
            }

            // 유저 이름(닉네임) 불러오기
            FB.API("me?fields=name", HttpMethod.GET, NameCallBack);


            // 인증 토큰 가져오기
            CloudBread cb = new CloudBread();
            cb.Login(AzureAuthentication.AuthenticationProvider.Facebook, aToken.TokenString, Callback_login);
        }
        else {
            Debug.Log("User cancelled login");
        }
    }

    private void Callback_login(string id, WWW www)
    {
        print(www.text);
        string resultJson = www.text;

        JsonReader jsonReader = new JsonReader();
        AuthData resultData = jsonReader.Read<AuthData>(resultJson);

        AzureMobileAppRequestHelper.AuthToken = resultData.authenticationToken;



        PlayerPrefs.SetString("userId", resultData.user.userId);

        CloudBread cb = new CloudBread();
        cb.CBInsRegMember(Callback_CBInsRegMember);

    }

    public void Callback_CBInsRegMember(string id, WWW www)
    {
        if (www.error != null) //새로 회원 가입
        {
            print("이미 가입된 회원");
            StartGame();


        }
        else //이미 가입된 회원
        {
            print("새로 가입한 회원");
            StartGame();
        }
    }

    public void StartGame()
    {
        SceneManager.LoadScene("mainGame");
    }

    private void NameCallBack(IGraphResult result)
    {
        string userName = (string)result.ResultDictionary["name"];
        print(userName + "님 안녕하세요^^");
        PlayerPrefs.SetString("nickName", userName);
    }

    private void OnHideUnity(bool isGameShown)
    {
        if (!isGameShown)
        {
            // Pause the game - we will need to hide
            Time.timeScale = 0;
        }
        else {
            // Resume the game - we're getting focus again
            Time.timeScale = 1;
        }
    }

    public void FacebookLoginBtnClick()
    {
        if (!FB.IsLoggedIn)
            LoginwithPermissions();
    }
}

8. CloudBread에 게임 기록 저장하기

  1. ScoreManagerScript.cs 파일 열기

  2. 게임 끝났을 때, CBComUdtMmberGameInfoes 호출을 위해, GameState가 Dead 일 때 추가하기

  3. CBComUdtMmberGameInfoes API 호출을 위해 MemberGameInfo 클래스 생성 (PostMan 참조)

  4. CloudBread 클래스에 CBComUDTMemberGameInofes 호출 구현

        public void CBComUdtMemberGameInfoes(Action<string, WWW> callback)
        {
            var ServerEndPoint = ServerAddress + "api/CBComUdtMemberGameInfoes";

            WWWHelper helper = WWWHelper.Instance;
            helper.OnHttpRequest += OnHttpRequest;

            MemberGameInfo gameinfoData = new MemberGameInfo
            {
                MemberID = (string)PlayerPrefs.GetString("userId"),

                Level = 2,
                Points = PlayerPrefs.GetInt("bestScore")
            };

            JsonWriter jsonWriter = new JsonWriter();
            string jsonBody = jsonWriter.Write(gameinfoData);

            helper.POST("CBComUdtMemberGameInfoes", ServerEndPoint, jsonBody);

            Callback = callback;
        }

        class MemberGameInfo
        {
            public string MemberID;
            public int Level = 0;
            public int Exps = 0;
            public int Points = 0;
            public string UserSTAT1;
            public string UserSTAT2;
            public string UserSTAT3;
            public string UserSTAT4;
            public string UserSTAT5;
            public string UserSTAT6;
            public string UserSTAT7;
            public string UserSTAT8;
            public string UserSTAT9;
            public string UserSTAT10;
            public string sCol1;
            public string sCol2;
            public string sCol3;
            public string sCol4;
            public string sCol5;
            public string sCol6;
            public string sCol7;
            public string sCol8;
            public string sCol9;
            public string sCol10;

        }
  1. ScoreManagerScript 에서 CBComDUTMemberGameInfoes 호출하기

기존에는 항상 게임 상태와 상관없이 항상 Update 되던 함수를 게임 플레이 중인 모드와 게임이 죽었을 때 모드로 분리

    if (GameStateManager.GameState == GameState.Playing)
       {
           if (previousScore != Score) //save perf from non needed calculations
           {
               if (Score < 10)
               {
                   //just draw units
                   Units.sprite = numberSprites[Score];
               }
               else if (Score >= 10 && Score < 100)
               {
                   (Tens.gameObject as GameObject).SetActive(true);
                   Tens.sprite = numberSprites[Score / 10];
                   Units.sprite = numberSprites[Score % 10];
               }
               else if (Score >= 100)
               {
                   (Hundreds.gameObject as GameObject).SetActive(true);
                   Hundreds.sprite = numberSprites[Score / 100];
                   int rest = Score % 100;
                   Tens.sprite = numberSprites[rest / 10];
                   Units.sprite = numberSprites[rest % 10];
               }
           }

       }
       else if (GameStateManager.GameState == GameState.Dead)
       {
       }

게임 상태가 죽었을 때(GmaeStateManage.Gamestate == Gamestate.Dead) CB CBComUdtMemberGameInfoes 호출하기

else if (GameStateManager.GameState == GameState.Dead)
{
    if (bestScore >= Score)
            {
                setScorewithSpirte(BestScoreUnit, bestScore);
            }
            else
            {
                PlayerPrefs.SetInt("bestScore", Score);
                setScorewithSpirte(BestScoreUnit, Score);

                if (deadRefreshFlag)
                {
                    deadRefreshFlag = false;
                    CloudBread cb = new CloudBread();
                    cb.CBComUdtMemberGameInfoes(Callback_Success);

                }
            }
}
public void Callback_Success(string id, WWW www)
{
    print("[" + id + "] Success");
}

페이스북 로그인 실습 참조

페이스북 로그인 서비스의 보안 정책 때문에, 아래와 같이 페이스북 앱 ID 와 가상 사용자 앱 엑세스 토큰을 제공.

Facebook Test ID

ID : [email protected]
PW : cloud bread
access Token :
EAAC8oNCMYl0BAEeBpbo8Eech8f19oHTvrx8O5Aau3ba1UqRHJ9DmsWFr0MTp2cfIoKMZAfxE0BEc3Mbew7ktSZCctVc2ZAqFZCAtfFG3KtSTWDySJVt0Qjro5ZAkEams2bng9Axbaqd6ERvijfnx57pD6lXToXhpwoNbhOsFatQZDZD
Clone this wiki locally