본문 바로가기
💻 programming/unity

유니티 UDP 소켓 통신 구현하기 - 유니티 UDP 서버 C# 예제 코드 <2>

by 연구원-A 2023. 1. 13.
반응형

[이전 포스팅]

2023.01.13 - [programming] - 유니티 UDP 소켓 통신 구현하기 - 예제 프로젝트 소개 <1>

 

이 포스팅에서는 유니티에 UDP 서버를 구현하고

수신한 UDP 데이터를 이용해 게임 오브젝트를 이동시키는 방법에 대해 설명한다

유니티 씬 만들기

먼저 유니티 게임 씬에 Plane 1개, Sphere 1개를 생성한다

* Sphere position: (x: 0, y: 0.5, z:0)로 수정

* Plane scale: (x: 100, y: 100, z:100)로 수정

Sphere와 Plane을 추가한 유니티 씬

Sphere에 물리엔진을 적용하기 위해 인스펙터에서 Rigidbody를 추가한다

(이대로 Play 하면 Sphere가 Plane위에 가만히 놓여있게 된다)

Sphere에 Rigidbody 컴포넌트 추가하기

 

JSON 데이터 형식 정의하기

일반적으로 서버에서 클라이언트로 데이터를 보낼 때 사용하는 양식. 클라이언트가 사용하는 언어에 관계없이 통일된 데이터를 주고받을 수 있도록, 일정한 패턴을 지닌 문자열을 생성해 내보내면 클라이언트는 그를 해석해 데이터를 자기만의 방식으로 온전히 저장, 표시할 수 있게 된다. (나무위키)

 

이번 샘플 프로젝트에서는 정-말 단순한 형식으로 JSON 데이터를 정의했다 (key 1개)

  * KEY: "direction"

  * VALUE: "up", "down", "left", "right"

{
   "direction":"up"
}

이제 파이썬에서 위처럼 정의된 JSON 문자열을 만들어 보내면, 유니티에서 잘 파싱만 하면 된다

 

 

유니티 C# 스크립트 작성하기

우선 C# 파일 두 개를 만든다 (SphereManager.cs, UdpManager.cs)

UdpManager.cs
0.00MB
SphereManager.cs
0.00MB

 

유니티 Asset에 C# 스크립트 추가하기 (SphereManager.cs, UdpManager.cs)

SphereManager.cs

SphereManger.cs 스크립트는 Sphere의 컴포넌트에 추가한다

(Sphere 인스턴스가 생성될 때 Start()가 호출되고, 매 프레임마다 Update() 함수가 호출됨)

SphereManager.cs 스크립트 추가햐기

간략히 설명하면

Sphere 인스턴스가 생성될 때 Start()가 호출되며 아래와 같은 동작을 수행한다

1. UDPManager udp_manager = new UdpManager("127.0.0.1", 50002); // UDP Manager 생성자 호출

2. udp_manager.initilaize(); // UDP 소켓 리시버 초기화

3. udp_manager.SetMessageCallback(new CallbackDirection(OnDirection)); // UDP 소켓 수신 시 동작 함수 전달

 

UDPManager에 Sphere의 방향을 바꾸는 OnDirection 함수를 전달해서

UDPManager가 UDP 데이터를 수신할 때마다 OnDirection 함수를 호출하도록 구현하였다

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

public class SphereManager : MonoBehaviour
{
    public float speed = 5f;
    private Rigidbody characterRigidbody;
    float inputX, inputZ;

    void Start()
    {
        Debug.Log("Start ShpereMove");
        characterRigidbody = GetComponent<Rigidbody>();
        UdpManager udp_manager = new UdpManager("127.0.0.1", 50002); # UDP 서버 주소, 포트번호
        udp_manager.Initialize();
        udp_manager.SetMessageCallback(new CallbackDirection(OnDirection));
    }

    void Update()
    {
        Vector3 velocity = new Vector3(inputX/3, 0, inputZ/3);
        velocity *= speed;
        characterRigidbody.velocity = velocity;
    }

    void OnDirection(JsonData message)
    {
        string direction = message.direction;
        Debug.Log("CharacterMove : " + direction);
        switch (direction)
        {
            case "up":
                inputZ = 1f;
                inputX = 0f;
                break;
            case "down":
                inputZ = -1f;
                inputX = 0f;
                break;
            case "right":
                inputX = 1f;
                inputZ = 0f;
                break;
            case "left":
                inputX = -1f;
                inputZ = 0f;
                break;
        }
    }
}

UdpManager.cs

UdpManager에 IP주소와 포트번호 ( _IpAddr, _Port)를 할당하여 초기화하면 (Initilalize() 함수 호출)

새로운 Thread에서 ListenForIncommingRequest 함수를 호출한다

 

ListenForIncommingRequest() 함수에서는

  * UDP 소켓으로부터 byte array를 받아오고 (byte[] bytes = listener.Receive(ref groupEP);)

  * byte array를 JSON 형식의 데이터로 파싱한다 ⭐⭐⭐⭐⭐ (var json_data = GetJsonFromByte(bytes);)

  * 마지막으로 SphereManager에서 전달한 OnDirection 함수를 호출한다 (MessageHandler(json_data);)

 

cf. JsonData 클래스는 위에서 정의한 JSON 형식에 맞게 작성하였다.

* KEY: direction

* Value: string

using System.Collections;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using UnityEngine;
using System.Net;
using System.Net.Sockets;
using System.Threading;
using Newtonsoft.Json;

[System.Serializable]
public class JsonData
{
    public string direction { get; set; }
}

public delegate void CallbackDirection(JsonData message);


public class UdpManager
{
    public string _IpAddr = "127.0.0.1";
    public int _Port = 50002;
    CallbackDirection _CallbackDirection;

    #region private members
    private Thread ListenerThread;
    #endregion

    public UdpManager(string IpAddr, int Port) {
        _IpAddr = IpAddr;
        _Port = Port;
    }

    public void Initialize() {
        // Start TcpServer background thread
        ListenerThread = new Thread(new ThreadStart(ListenForIncommingRequest));
        ListenerThread.IsBackground = true;
        ListenerThread.Start();
    }

    void ListenForIncommingRequest() {
        Debug.Log("Start Server : " + _IpAddr + ", " + _Port);
        UdpClient listener = new UdpClient(_Port);
        IPEndPoint groupEP = new IPEndPoint(IPAddress.Any, _Port);
        try {
            while (true){
                byte[] bytes = listener.Receive(ref groupEP);
                // Debug.Log(Encoding.ASCII.GetString(bytes, 0, bytes.Length));
                var json_data = GetJsonFromByte(bytes);
                MessageHandler(json_data);
            }
        } catch (SocketException e) {
            Debug.Log("SocketException " + e.ToString());
        } finally {
            listener.Close();
        }
    }

    public JsonData GetJsonFromByte(byte[] data) {
        string JsonString = Encoding.UTF8.GetString(data);
        Debug.Log(JsonString);
        var JsonData = Newtonsoft.Json.JsonConvert.DeserializeObject<JsonData>(JsonString);
        return JsonData;
    }

    public void SetMessageCallback(CallbackDirection callback)
    {
        if (_CallbackDirection == null) {
            _CallbackDirection = callback;
        } else {
            _CallbackDirection += callback;
        }
    }

    private void MessageHandler(JsonData json_data)
    {
        if (_CallbackDirection != null) {
            _CallbackDirection(json_data);
        }
    }
}

 

유니티 끝

 

하지만 이대로 Play 해보아도 여전히 Sphere가 가만히 놓여있을 뿐 아무런 동작을 하지 않는다

UDP 데이터가 오지 않기 때문에..

 

UDP 데이터를 송신하는 클라이언트를 구현해야 하는데

UDP 클라이언트는 python과 C++로 한 번 구현해보았다

 

(계속)

2023.01.13 - [programming] - 유니티 UDP 소켓 통신 구현하기 - 파이썬 UDP 클라이언트 예제 코드 <3>

2023.01.13 - [programming] - 유니티 UDP 소켓 통신 구현하기 - C++ UDP 클라이언트 예제 코드 <4>

반응형

댓글