Unity/서버

210727_ 유니티 RestAPI

minquu 2021. 7. 27. 13:15
반응형

유니티와 서버 통신을 하는 것이다.

 

먼저 express 서버를 만든다.

 

새로운 서버 만들기 

 

폴더 들어가서 초기화 해주고

 

 

익스프레스와 노드몬 설치

 

app.js 만들어서

 

기본 3030 포트 서버를 만들어준다.

 

 

 

실행

 

 

정상 작동함

 

----

 

랭킹 서버를 만들 것이다.

 

 

먼저 스코어 라우터를 만들어서

 

바디에서 id 와 score를 받아준다.

 

res.로 200 찍어주고, end() 를 꼭 해줘야한다.

 엔드 안하면 무한 루프

 

 

 

정상적으로 찍힘

 

 

 

저렇게 넣으면 딕셔너리 처럼 , 키 : 밸류 형식으로 넣을 수 있음 ! 꿀팁!

 

 

 

 

키 밸류기 때문에 키만 넣어줘도, 밸류 값이 나옴 ㅇㅇ

 

const express = require('express');
const app = express();


let users = {};

app.use(express.json());

app.get('/', (req, res) => {
    res.send('hello');
});

app.post('/score', (req, res) => {
    const {id, score} = req.body;

    let result = {
        cmd: -1,
        message: ''
    }
    
    if(users[id] === undefined){
        //아직 등록이 한번도 안된 유저 신규 등록
        users[id] = score;
        result.cmd = 1001;
        result.message = '점수가 신규 등록 되었습니다.';
    }
    else{
        if(score > users[id]){ //등록 하려고 하는 점수가 저장된 점수보다 크다면 갱신
            users[id] = score;
            result.cmd = 1002;
            result.message = '점수가 갱신 되었습니다.';
        }
        else{
            result.cmd = 1003;
        }
    }
    console.log(users);
    res.send(result);

})
app.listen(3030, () => {
    console.log("3030 server on");
});

 

 

 

신규 등록

 

점수 갱신

 

갱신이 아닌 그냥 점수일때

 

----

키 밸류 사용 방법도 좋지만

 

배열로 해야 나중에 역직렬화 하기도 편해서

 

위에 users 를 배열로 바꿔준다.

 

const express = require('express');
const app = express();

let users = [];

app.use(express.json());

app.get('/', (req, res)=>{
    res.send('hello express!');
});

app.post('/score', (req, res)=>{
    const { id, score } = req.body;

    let result = {
        cmd: -1, 
        message: ''
    };

    let user = users.find(x=>x.id == id);

    if( user === undefined){
        //아직 등록이 한번도 안된 유저 신규 등록 
        users.push( { id, score } );

        result.cmd = 1001;
        result.message = '점수가 신규 등록 되었습니다.';

    }else{
        
        console.log(score, id, user.score);

        if( score > user.score){   //등록 하려고 하는 점수가 저장된 점수보다 크다면 갱신 
            
            user.score = score;

            result.cmd = 1002;
            result.message = '점수가 갱신 되었습니다.';

        }else{
            result.cmd = 1003;
        }
    }

    console.log(users);
    res.send(result);
});

app.listen(3030, ()=>{
    console.log('server is running at 3030 port.');
});

 

일단 배열로 이어서 한다.

 

---

 

테스트 .js 만들어서 테스트한다.

 

저장된 배열 솔트 (정렬) 하는 것 내림 차순

 

배열의 0~2 인덱스 반환한다.

 

 

const { restart } = require("nodemon");

var users = [
{id : 'min@gaga', score: 200},
{id : 'mddd@gaga', score: 100},
{id : '111@gaga', score: 300},
{id : 'rrrrr@gaga', score: 500},
{id : 'qqqq1@gaga', score: 700},


]

var result;

result = users.sort(function(a,b) {
    return b.score - a.score; 

})

result = result.slice(0,3);  // 0 ~ 2 // =
console.log(result);

 

 

이걸 가져다가 쓴다.

 

app.js

 

추가 해준다.

 

const express = require('express');
const { restart } = require('nodemon');
const app = express();

let users = [];

app.use(express.json());

app.get('/', (req, res)=>{
    res.send('hello express!');
});

app.post('/score', (req, res)=>{
    const { id, score } = req.body;

    let result = {
        cmd: -1, 
        message: ''
    };

    let user = users.find(x=>x.id == id);

    if( user === undefined){
        //아직 등록이 한번도 안된 유저 신규 등록 
        users.push( { id, score } );

        result.cmd = 1001;
        result.message = '점수가 신규 등록 되었습니다.';

    }else{
        
        console.log(score, id, user.score);

        if( score > user.score){   //등록 하려고 하는 점수가 저장된 점수보다 크다면 갱신 
            
            user.score = score;

            result.cmd = 1002;
            result.message = '점수가 갱신 되었습니다.';

        }else{
            result.cmd = 1003;
        }
    }

    console.log(users);
    res.send(result);
});

app.get('/scores/top3', (req,res) => {
    let result = users.sort(function (a,b){
        return b.score - a.score;
    });

    result = result.slice(0,3);

    res.send({
        cmd: 1101,
        message: '',
        result
    });
});

app.get('/scores/:id', (req,res) => {

});

app.listen(3030, ()=>{
    console.log('server is running at 3030 port.');
});

 

 

포스트맨에서 

 

4개 넣어준다.

get 스코어스 하면 3등까지 나온다.

 

이제 아이디를 쿼리로 검색할 수있는 것 추가 

 

 

const express = require('express');
const app = express();

let users = [];

app.use(express.json());

app.get('/', (req, res)=>{
    res.send('hello express!');
});

app.post('/scores', (req, res)=>{
    const { id, score } = req.body;

    let result = {
        cmd: -1, 
        message: ''
    };

    let user = users.find(x=>x.id == id);

    if( user === undefined){
        //아직 등록이 한번도 안된 유저 신규 등록 
        users.push( { id, score } );

        result.cmd = 1001;
        result.message = '점수가 신규 등록 되었습니다.';

    }else{
        
        console.log(score, id, user.score);

        if( score > user.score){   //등록 하려고 하는 점수가 저장된 점수보다 크다면 갱신 
            
            user.score = score;

            result.cmd = 1002;
            result.message = '점수가 갱신 되었습니다.';

        }else{
            result.cmd = 1003;
        }
    }

    console.log(users);
    res.send(result);
});

app.get('/scores/top3', (req, res)=>{
    let result = users.sort(function (a, b) {
        return b.score - a.score;
    });
    
    result = result.slice(0, 3);    // 0 ~ n-1

    res.send({
        cmd: 1101,
        message: '',
        result
    });
});

app.get('/scores/:id', (req, res)=>{
    console.log('id: ' +  req.params.id);
    let user = users.find(x=>x.id == req.params.id);
    if(user === undefined){
        res.send({
            cmd: 1103,
            message: '잘못된 id입니다.',
        });
    }else{
        res.send({
            cmd: 1102,
            message: '',
            result: user
        });
    }
});


app.listen(3030, ()=>{
    console.log('server is running at 3030 port.');
});

 

전체 코드

 

검색 가능

 

기본적인 API 서버 완성

 

 

---

 

유니티 프로젝트 만들기

 

API : https://docs.unity3d.com/kr/2020.3/Manual/UnityWebRequest.html 

 

UnityWebRequest - Unity 매뉴얼

UnityWebRequest는 HTTP 요청을 구성하고 HTTP 리스폰스를 처리하기 위한 모듈식 시스템을 제공합니다. UnityWebRequest 시스템의 주요 목표는 Unity 게임이 최신 웹 브라우저 백 엔드와 상호작용할 수 있도

docs.unity3d.com

 

 

유니티 메뉴얼

 

먼저 

json.net 인풋해주기

 

(dll 오류 뜨면 폴더하나 지우기)

 

 

 

UI 만들기

 

 

스크립터 만들기

 

RankMain 만들어서

 

넣어주기

 

 

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
public class RankMain : MonoBehaviour
{

    public string host;
    public int port;
    public string top3Uri;
    public string idUri;
    public string postUri;
    public Button btnGetTop3;
    public Button btnGetId;
    public Button btnPost;
    // Start is called before the first frame update
    void Start()
    {

        this.btnGetTop3.onClick.AddListener(() => {
            var url = string.Format("{0}:{1}/{2}", host, port, top3Uri);
            Debug.Log(url);
        });
        this.btnGetId.onClick.AddListener(() => {
            var url = string.Format("{0}:{1}/{2}", host, port, idUri);
            Debug.Log(url);
        });
        this.btnPost.onClick.AddListener(() => {
            var url = string.Format("{0}:{1}/{2}", host, port, postUri);
            Debug.Log(url);
        });

    }

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

 

 

준비완료

 

패킷 클래스 설계 / 또는 구조체 설계

 

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class Protocols 
{
    public enum eType { 
        POST_SCORE = 1000
    }

    public class Packets {

        public class common {
            public int cmd;
        }

        public class req_scores : common {
            public string id;
            public int score;
        }

        public class res_scores : common {
            public string message;
        }

        public class user {
            public string id;
            public int score;
        }

        public class res_scores_top3 : res_scores {
            public user[] result;
        }

        public class res_scores_id : res_scores
        {
            public user result;
        }
    }
}

 

 

 

 

---

 

스크립터블 오브젝트 활용

https://docs.unity3d.com/kr/2019.4/Manual/class-ScriptableObject.html

 

ScriptableObject - Unity 매뉴얼

ScriptableObject는 클래스 인스턴스와는 별도로 대량의 데이터를 저장하는 데 사용할 수 있는 데이터 컨테이너입니다. ScriptableObject의 주요 사용 사례 중 하나는 값의 사본이 생성되는 것을 방지하

docs.unity3d.com

 

 

 

 

스크립터 만들어주기

 

SpawnManagerScriptableObject.cs

 

 

 

ing UnityEngine;

[CreateAssetMenu(fileName = "Data", menuName = "ScriptableObjects/SpawnManagerScriptableObject", order = 1)]
public class SpawnManagerScriptableObject : ScriptableObject
{
    public string prefabName;

    public int numberOfPrefabsToCreate;
    public Vector3[] spawnPoints;
}

 

 

저게 생긴다.

 

테스트를 해보니 잘 만들어진다.

 

이제 우리 걸로 만들어준다.

 

 

우리가 뚫을 퍼블릭 값을 넣고

 

만들어주면

 

값을 넣을수있다.

 

이제 퍼블릭으로 

 

 

 

뚫어서 넣고

 

이제 저 스크립타블 오브젝트에 있는 값을 이용할 수있다 .

 

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
using Newtonsoft.Json;

public class RankMain : MonoBehaviour
{
    public string host;
    public int port;
    public string top3Uri;
    public string idUri;
    public string postUri;

    public SpawnManagerScriptableObject scriptableObject;

    public Button btnGetTop3;
    public Button btnGetId;
    public Button btnPost;

    void Start()
    {
        this.btnGetTop3.onClick.AddListener(() => {
            var url = string.Format("{0}:{1}/{2}", host, port, top3Uri);
            Debug.Log(url);
        });
        this.btnGetId.onClick.AddListener(() => {
            var url = string.Format("{0}:{1}/{2}", host, port, idUri);
            Debug.Log(url);
        });
        this.btnPost.onClick.AddListener(() => {
            var url = string.Format("{0}:{1}/{2}", host, port, postUri);
            Debug.Log(url); //http://localhost:3030/scores

            var req = new Protocols.Packets.req_scores();
            req.cmd = 1000; //(int)Protocols.eType.POST_SCORE;
            req.id = scriptableObject.id;
            req.score = scriptableObject.score;
            //직렬화 
            var json = JsonConvert.SerializeObject(req);
            Debug.Log(json);
            //{"id":"hong@nate.com","score":100,"cmd":1000}



        });
    }

}

 

 

 

포스트 할 준비 끝

 

전체 추가 코드

 

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
using Newtonsoft.Json;
using UnityEngine.Networking;
using System.Text;

public class RankMain : MonoBehaviour
{
    public string host;
    public int port;
    public string top3Uri;
    public string idUri;
    public string postUri;

    public SpawnManagerScriptableObject scriptableObject;

    public Button btnGetTop3;
    public Button btnGetId;
    public Button btnPost;

    void Start()
    {
        this.btnGetTop3.onClick.AddListener(() => {
            var url = string.Format("{0}:{1}/{2}", host, port, top3Uri);
            Debug.Log(url);
        });
        this.btnGetId.onClick.AddListener(() => {
            var url = string.Format("{0}:{1}/{2}", host, port, idUri);
            Debug.Log(url);
        });
        this.btnPost.onClick.AddListener(() => {
            var url = string.Format("{0}:{1}/{2}", host, port, postUri);
            Debug.Log(url); //http://localhost:3030/scores

            var req = new Protocols.Packets.req_scores();
            req.cmd = 1000; //(int)Protocols.eType.POST_SCORE;
            req.id = scriptableObject.id;
            req.score = scriptableObject.score;
            //직렬화  (오브젝트 -> 문자열)
            var json = JsonConvert.SerializeObject(req);
            Debug.Log(json);
            //{"id":"hong@nate.com","score":100,"cmd":1000}

            StartCoroutine(this.PostScore(url, json, (raw) => {
                Protocols.Packets.res_scores res = JsonConvert.DeserializeObject<Protocols.Packets.res_scores>(raw);
                Debug.LogFormat("{0}, {1}", res.cmd, res.message);
            }));

        });
    }

    private IEnumerator PostScore(string url, string json, System.Action<string> callback)
    {

        var webRequest = new UnityWebRequest(url, "POST");
        var bodyRaw = Encoding.UTF8.GetBytes(json); //직렬화 (문자열 -> 바이트 배열)

        webRequest.uploadHandler = new UploadHandlerRaw(bodyRaw);
        webRequest.downloadHandler = new DownloadHandlerBuffer();
        webRequest.SetRequestHeader("Content-Type", "application/json");

        yield return webRequest.SendWebRequest();

        if (webRequest.result == UnityWebRequest.Result.ConnectionError || webRequest.result == UnityWebRequest.Result.ProtocolError)
        {
            Debug.Log("네트워크 환경이 안좋아서 통신을 할수 없습니다.");
        }
        else
        {
            Debug.LogFormat("{0}\n{1}\n{2}", webRequest.responseCode, webRequest.downloadHandler.data, webRequest.downloadHandler.text);
            callback(webRequest.downloadHandler.text);
        }
    }

}

 

 

 

 

 

플레이해서 등록하면

 

포스트가 되는 것

 

 

서버에서 날라옴

 

 

바꿔주고 

 

등록하면

 

 

추가 등록이 댐

 

 

----

 

get 해보기

 

 

 

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
using Newtonsoft.Json;
using UnityEngine.Networking;
using System.Text;

public class RankMain : MonoBehaviour
{
    public string host;
    public int port;
    public string top3Uri;
    public string idUri;
    public string postUri;

    public SpawnManagerScriptableObject scriptableObject;

    public Button btnGetTop3;
    public Button btnGetId;
    public Button btnPost;

    void Start()
    {
        this.btnGetTop3.onClick.AddListener(() => {
            var url = string.Format("{0}:{1}/{2}", host, port, top3Uri);
            Debug.Log(url);

            StartCoroutine(this.GetTop3(url, (raw) =>
            {
                var res = JsonConvert.DeserializeObject<Protocols.Packets.res_scores_top3>(raw);
                Debug.LogFormat("{0}, {1}", res.cmd, res.result.Length);
                foreach (var user in res.result)
                {
                    Debug.LogFormat("{0} : {1}", user.id, user.score);
                }
            }));


        });
        this.btnGetId.onClick.AddListener(() => {
            var url = string.Format("{0}:{1}/{2}", host, port, idUri);
            Debug.Log(url);
        });
        this.btnPost.onClick.AddListener(() => {
            var url = string.Format("{0}:{1}/{2}", host, port, postUri);
            Debug.Log(url); //http://localhost:3030/scores

            var req = new Protocols.Packets.req_scores();
            req.cmd = 1000; //(int)Protocols.eType.POST_SCORE;
            req.id = scriptableObject.id;
            req.score = scriptableObject.score;
            //직렬화  (오브젝트 -> 문자열)
            var json = JsonConvert.SerializeObject(req);
            Debug.Log(json);
            //{"id":"hong@nate.com","score":100,"cmd":1000}

            StartCoroutine(this.PostScore(url, json, (raw) => {
                Protocols.Packets.res_scores res = JsonConvert.DeserializeObject<Protocols.Packets.res_scores>(raw);
                Debug.LogFormat("{0}, {1}", res.cmd, res.message);
            }));

        });
    }

    private IEnumerator GetTop3(string url, System.Action<string> callback)
    {

        var webRequest = UnityWebRequest.Get(url);
        yield return webRequest.SendWebRequest();
        if (webRequest.result == UnityWebRequest.Result.ConnectionError || webRequest.result == UnityWebRequest.Result.ProtocolError)
        {
            Debug.Log("네트워크 환경이 안좋아서 통신을 할수 없습니다.");
        }
        else
        {
            callback(webRequest.downloadHandler.text);
        }
    }

    private IEnumerator PostScore(string url, string json, System.Action<string> callback)
    {

        var webRequest = new UnityWebRequest(url, "POST");
        var bodyRaw = Encoding.UTF8.GetBytes(json); //직렬화 (문자열 -> 바이트 배열)

        webRequest.uploadHandler = new UploadHandlerRaw(bodyRaw);
        webRequest.downloadHandler = new DownloadHandlerBuffer();
        webRequest.SetRequestHeader("Content-Type", "application/json");

        yield return webRequest.SendWebRequest();

        if (webRequest.result == UnityWebRequest.Result.ConnectionError || webRequest.result == UnityWebRequest.Result.ProtocolError)
        {
            Debug.Log("네트워크 환경이 안좋아서 통신을 할수 없습니다.");
        }
        else
        {
            Debug.LogFormat("{0}\n{1}\n{2}", webRequest.responseCode, webRequest.downloadHandler.data, webRequest.downloadHandler.text);
            callback(webRequest.downloadHandler.text);
        }
    }

}

 

 

 

나온다.

----

id로 검색하기

 

 

 

 

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
using Newtonsoft.Json;
using UnityEngine.Networking;
using System.Text;

public class RankMain : MonoBehaviour
{
    public string host;
    public int port;
    public string top3Uri;
    public string idUri;
    public string postUri;

    public SpawnManagerScriptableObject scriptableObject;

    public Button btnGetTop3;
    public Button btnGetId;
    public Button btnPost;

    void Start()
    {
        this.btnGetTop3.onClick.AddListener(() => {
            var url = string.Format("{0}:{1}/{2}", host, port, top3Uri);
            Debug.Log(url);

            StartCoroutine(this.GetTop3(url, (raw) =>
            {
                var res = JsonConvert.DeserializeObject<Protocols.Packets.res_scores_top3>(raw);
                Debug.LogFormat("{0}, {1}", res.cmd, res.result.Length);
                foreach (var user in res.result)
                {
                    Debug.LogFormat("{0} : {1}", user.id, user.score);
                }
            }));


        });


        this.btnGetId.onClick.AddListener(() => {
            var url = string.Format("{0}:{1}/{2}", host, port, idUri);
            Debug.Log(url);

            StartCoroutine(this.GetId(url, (raw) => {

                var res = JsonConvert.DeserializeObject<Protocols.Packets.res_scores_id>(raw);
                Debug.LogFormat("{0}, {1}", res.result.id, res.result.score);

            }));
        });


        this.btnPost.onClick.AddListener(() => {
            var url = string.Format("{0}:{1}/{2}", host, port, postUri);
            Debug.Log(url); //http://localhost:3030/scores

            var req = new Protocols.Packets.req_scores();
            req.cmd = 1000; //(int)Protocols.eType.POST_SCORE;
            req.id = scriptableObject.id;
            req.score = scriptableObject.score;
            //직렬화  (오브젝트 -> 문자열)
            var json = JsonConvert.SerializeObject(req);
            Debug.Log(json);
            //{"id":"hong@nate.com","score":100,"cmd":1000}

            StartCoroutine(this.PostScore(url, json, (raw) => {
                Protocols.Packets.res_scores res = JsonConvert.DeserializeObject<Protocols.Packets.res_scores>(raw);
                Debug.LogFormat("{0}, {1}", res.cmd, res.message);
            }));

        });
    }

    private IEnumerator GetTop3(string url, System.Action<string> callback)
    {

        var webRequest = UnityWebRequest.Get(url);
        yield return webRequest.SendWebRequest();
        if (webRequest.result == UnityWebRequest.Result.ConnectionError || webRequest.result == UnityWebRequest.Result.ProtocolError)
        {
            Debug.Log("네트워크 환경이 안좋아서 통신을 할수 없습니다.");
        }
        else
        {
            callback(webRequest.downloadHandler.text);
        }
    }

    private IEnumerator GetId(string url, System.Action<string> callback)
    {
        var webRequest = UnityWebRequest.Get(url);
        yield return webRequest.SendWebRequest();

        Debug.Log("--->" + webRequest.downloadHandler.text);

        if (webRequest.result == UnityWebRequest.Result.ConnectionError || webRequest.result == UnityWebRequest.Result.ProtocolError)
        {
            Debug.Log("네트워크 환경이 안좋아서 통신을 할수 없습니다.");
        }
        else
        {
            callback(webRequest.downloadHandler.text);
        }
    }

    private IEnumerator PostScore(string url, string json, System.Action<string> callback)
    {

        var webRequest = new UnityWebRequest(url, "POST");
        var bodyRaw = Encoding.UTF8.GetBytes(json); //직렬화 (문자열 -> 바이트 배열)

        webRequest.uploadHandler = new UploadHandlerRaw(bodyRaw);
        webRequest.downloadHandler = new DownloadHandlerBuffer();
        webRequest.SetRequestHeader("Content-Type", "application/json");

        yield return webRequest.SendWebRequest();

        if (webRequest.result == UnityWebRequest.Result.ConnectionError || webRequest.result == UnityWebRequest.Result.ProtocolError)
        {
            Debug.Log("네트워크 환경이 안좋아서 통신을 할수 없습니다.");
        }
        else
        {
            Debug.LogFormat("{0}\n{1}\n{2}", webRequest.responseCode, webRequest.downloadHandler.data, webRequest.downloadHandler.text);
            callback(webRequest.downloadHandler.text);
        }
    }

}

 

 

 

검색  되었다..

 

 

반응형