본문 바로가기

- Programming/- C#

★ 7. c# 네트워크 개발 p6

반응형

==================================================================

모든 출처는

- 유니티 개발자를 위한 C#으로 온라인 게임 서버 만들기 - 저자 이석현, 출판사 한빛미디어

그리고 URL :

http://lab.gamecodi.com/board/zboard.php?id=GAMECODILAB_Lecture_series&no=66

==================================================================


C#으로 게임 서버 만들기 - 6. 유니티 버전 에코 클라이언트 구현


3-2. 유니티 엔진과 연동하는 방법


앞에서 만들어본 에코 클라이언트의 유니티 버전을 만들어 보겠습니다. c#을 이용하였기 때문에 유니티 엔진에서도 우리가 만든 네트워크 라이브러리를 그대로 사용할 수 있습니다. 유니티에서는 모노 런타임으로 c#코드를 수행 시키기 때문에 마이크로 소프트에서 제공하는 .Net 프레임 워크 런타임과는 약간 다르게 작동합니다. 하지만 대부분의 기능들이 호환되기 때문에 별다른 코드 수정 없이 유니티 엔진으로 연동하는것이 가능합니다.


준비 작업

먼저 dll 파일을 생성하여 유니티 프로젝트에 추가해보도록 하겠습니다.

FreeNet 프로젝트는 기본적으로 Class Library로 설정되어 있기 때문에 빌드를 하면 dll파일이 생성됩니다. 빌드할 때 주의할 점이 있는데 Target Framework의 닷넷 버전을 3.5로 맞춰야 한다는 점입니다. 유니티 4 버전에서 사용하는 모노 런타임이 닷넷 3.5까지 지원하기 때문입니다. 혹시나 이 부분이 4.0이상으로 되어있다면 유니티 프로젝트에 dll파일을 추가하였을 때 컴파일 에러가 발생할 수 있습니다.



생성된 dll 파일을 유니티 프로젝트에 추가해보도록 하겠습니다.

FreeNetUnitySample라는 이름으로 유니티 프로젝트를 하나 생성합니다.

Asset/FreeNet 라는 폴더를 생성한 뒤

빌드 된 FreeNet.dll 파일을 이 폴더로 복사합니다.



이제 준비 작업을 마쳤으니 본격적으로 코드를 작성해 보도록 하겠습니다.


프로젝트 구성

유니티 엔진과 연동할 때 한가지 알아두어야 할 부분은 메인 스레드에서 작업이 수행되도록 코딩해야 한다는 것입니다. 유니티 엔진에서 제공되는 대부분의 기능들은 메인 스레드에서만 작동되도록 설계되어 있기 때문입니다. 그런데 우리가 작성한 네트워크 라이브러리는 메인 스레드와는 별도로 작동되는 워커 스레드에서 소켓 송, 수신 처리가 이루어집니다.

그렇다면 메인 스레드에서 작동되도록 코드를 다시 작성해야 하는 것일까요?

그렇지 않습니다.

소켓 송, 수신 처리는 네트워크 라이브러리에서 작성한 내용 그대로 워커 스레드에서 처리하되 메인 스레드로 건내주어 처리할 수 있도록 시스템을 구성하면 됩니다.

그럼 앞부분에서 만들었던 에코 클라이언트 프로그램을 유니티 프로젝트로 변경하는 작업을 통해 네트워크 라이브러리가 어떤 방식으로 유니티 엔진과 연동 되는지 알아보도록 하죠.


프로젝트는 다음과 같이 구성됩니다.


FreeNet/

FreeNet.dll

네트워크 라이브러리 파일


CFreeNetUnityService.cs

네트워크 라이브러리와 유니티 프로젝트를 연결해주는 기능


CFreeNetEventManager.cs

네트워크 라이브러리에서 발생되는 이벤트들을 관리하는 기능


CRemoteServerPeer.cs

클라이언트에서 통신을 수행할 대상이 되는 서버 객체


Resource/scripts/

CGameMain.cs

화면 UI를 담당하는 스크립트


CNetworkManager.cs

로직 처리 부분에서 서버 접속, 이벤트, 핸들링, 메시지 핸들링을 수행하는 객체


protocol.cs

서버와 통신에 필요한 프로토콜이 정의되어 있는 파일


유니티 프로젝트에서 우리가 추가한 네트워크 라이브러리를 사용하기 위해서는 메인 스레드에서 작동되도록 구성해야 한다고 했습니다. 그 역할을 담당하는 클래스가 CFreeNetUnityService입니다.

이 클래스는 MonoBehaviour를 상속 받았기 때문에 유니티 프로젝트 내에서 자유롭게 사용 가능한 클래스입니다. 또한, 네트워크 라이브러리에 접근하여 송, 수신된 데이터를 관리하기도 합니다.

네트워크 라이브러리에서 발생되는 모든 이벤트들은 이 클래스를 통해서 유니티 프로젝트로 전달됩니다.


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
public class CFreeNetUnityService : MonoBehaviour
{
    CFreeNetEventManager event_manager;
 
    // 연결된 게임 서버 객체
    IPeer gameserver;
 
    // TCP 통신을 위한 서비스 객체
    CNetworkService service;
 
    // 접속 완료시 호출되는 델리게이트. 어플리케이션에서 콜백 메소드를 설정하여 사용한다.
    public delegate void StatusChangedHandler(NETWORK_EVENT status);
    public StatusChangedHandler appcallback_on_status_changed;
 
    // 네트워크 메시지 수신시 호출되는 델리게이트. 어플리케이션에서 콜백 메소드를 설정하여 사용한다.
    public delegate void MessageHandler(CPacket msg);
    public MessageHandler appcallback_on_message;
cs


먼저 MonoBehaviour를 상속 받은 CFreeNetUnityService 클래스를 만듭니다. 네트워크에서 발생되는 이벤트들을 관리하기 위하여 CFreeNetEventManager 객체를 선언하였습니다.

이 객체는 네트워크 라이브러리에서 발생되는 이벤트들과 수신된 데이터를 보관해 놓는 역할을 합니다. 자세한 내용은 나중에 설명해드리겠습니다.


IPeer gameserver;
접속할 게임 서버 객체입니다. 에코 클라이언트 콘솔 프로그램에서 사용했던 것과 같은 개념입니다.

CNetworkService service;
네트워크 라이브러리의 기능을 수행하는 서비스 객체입니다.

그 다음으로는 네트워크 상태 변경과 메시지 수신을 통보 받을 델리게이트를 선언해줍니다.

여기까지는 이전에 보았던 에코 클라이언트 콘솔 프로그램과 개념적으로 큰 차이가 없습니다.

이제 유니티 프로젝트에서 어떻게 네트워크 이벤트들을 받아 처리할 수 있는지 살펴봅시다.


1
2
3
4
5
6
void Awake()
{
    CPacketBufferManager.initialize(10);
    this.event_manager = new CFreeNetEventManager();
}
 
cs


유니티 엔진에서 초기화를 담당하는 코드는 Awake 메소드에 작성하게 됩니다.

에코 클라이언트 콘솔 프로그램에서 작성했던 것처럼 CPacketBufferManager를 초기화 해줍니다. CFreeNetEventManager 클래스는 MonoBehaviour를 상속 받은 클래스가 아니므로 new를 통해 생성합니다.


1
2
3
4
5
6
7
8
9
10
11
12
public void connect(string host, int port)
{
    // CNetworkService 객체는 메시지의 비동기 송, 수신 처리를 수행한다.
    this.service = new CNetworkService();
 
    // endpoint 정보를 갖고 있는 Connector 생성. 만들어둔 NetworkService 객체를 넣어준다.
    CConnector connector = new CConnector(service);
    // 접속 성공시 호출될 콜백 메소드 지정.
    connector.connected_callback += on_connected_gameserver;
    IPEndPoint endpoint = new IPEndPoint(IPAddress.Parse(host), port);
    connector.connect(endpoint);
}
cs


서버에 접속하기 위한 메소드입니다.

CNetworkservcie와 CConnector를 생성하여 서버에 접속을 수행합니다.


1
2
3
4
5
6
7
8
9
10
11
12
/// <summary>
/// 접속 성공시 호출될 콜백 메소드
/// </summary>
/// <param name="server_token"></param>
void on_connected_gameserver(CUserToken server_token)
{
    this.gameserver = new CRemoteServerPeer(server_token);
    ((CRemoteServerPeer)this.gameserver).set_eventmanager(this.event_manager);
 
    // 유니티 어플리케이션으로 이벤트를 넘겨주기 위해서 매니저에 큐잉 시켜준다.
    this.event_manager.enqueue_network_event(NETWORK_EVENT.connected);
}
cs


서버에 접속이 성공했을 때 호출되는 콜백 메소드입니다.

서버 객체를 생성해주고 Awake 메소드에서 생성한 CFreeNetEventManager 객체를 설정해줍니다. 이 작업을 통해서 유니티 프로젝트와 네트워크 라이브러리간의 연결 통로가 구성되는 것입니다.

이후에도 설명하겠지만 CFreeNetEventManager 클래스는 네트워크 라이브러리에서 발생되는 이벤트들과 송, 수신된 메시지들을 담고 있는 컨테이너들로 구성된 클래스입니다.

메인 스레드와 워커 스레드에서 모두 사용되는 객체이기 때문에 lock으로 동기화 처리를 구현해 놓았습니다.


접속 완료 통보를 받으면 이 사실을 유니티 프로젝트에서 감지할 수 있도록 해줘야 하는데 CFreeNetEventManager.enqueue_network_event 메소드를 통해서 이루어집니다. 미리 정의된 enum 값인 NETWORK_EVENT.connected를 새로운 이벤트로 큐잉 시켜줍니다.

여기서 한가지 의문점을 가져볼 수 있는데요.
이 메소드에서 직접 유니티 엔진으로 접속 완료 통보를 호출하지 않고
왜 번거롭게 큐잉 처리를 하여 한단계 더 거치도록 구현해 놓은 것일까요?
게다가 이 클래스는 MonoBehaviour까지 상속 받고 있는 것을 보니
유니티 엔진 내부의 메소드들도 마음껏 호출할 수 있을텐데 말이죠.

그럼 on_connected_gameserver 가 어떻게 호출되는지 다시한번 생각해 봅시다.
CConnector 객체를 생성하고 connector.connected_callback 델리게이트에 설정해주면
자동으로 호출되죠. 어디서 호출 되나요? 네트워크 라이브러리 내에서 접속 완료
통보가 들어왔을 때 호출됩니다. 네트워크 라이브러리는 비동기 소켓 메소드를
사용하여 작성되었다는 것을 기억하시나요? 접속 완료 통보 역시 메인 스레드와는 별도로
수행되는 워커 스레드에서 호출됩니다. 따라서 on_connected_gameserver 메소드는
워커 스레드에서 호출되는 메소드라고 볼 수 있죠.
이 메소드에서 유니티 엔진의 메소드를 직접 호출하거나 게임 로직 처리를 담당하는
클래스의 메소드를 호출해버리면 메인 스레드가 아니라서 사용할 수 없다는
에러 메시지가 출력되거나 로직을 담당하는 데이터가 깨지게 되겠죠.
따라서 별도의 컨테이너를 통해서 큐잉 처리하여
간접적으로 처리하도록 구성할 필요가 있습니다.


아직까지는 유니티 프로젝트의 로직 내에서 이 이벤트를 어떻게 빼오는지 모릅니다.

다음에 나올 Update 메소드에서 이 부분을 어떻게 처리하는지 살펴보겠습니다.


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
/// <summary>
/// 네트워크에서 발생하는 모든 이벤트를 클라이언트에게 알려주는 역할을 Update에서 진행한
/// FreeNet 엔진의 메시지 송, 수신 처리는 워커스레드에서 수행되지만 유니티의 로직 처리는
/// 메인 스레드에서 수행되므로 큐잉 처리를 통하여 메인 스레드에서 모든 로직 처리가
/// 이루어지도록 구성하였다.
/// </summary>
void Update()
{
    // 수신된 메시지에 대한 콜백.
    if (this.event_manager.has_message())
    {
        CPacket msg = this.event_manager.dequeue_network_message();
        if (this.appcallback_on_message != null)
        {
            this.appcallback_on_message(msg);
        }
    }
 
    // 네트워크 발생 이벤트에 대한 콜백.
    if (this.event_manager.has_event())
    {
        NETWORK_EVENT status = this.event_manager.dequeue_network_event();
        if (this.appcallback_on_status_changed != null)
        {
            this.appcallback_on_status_changed(status);
        }
    }
}
cs


Update 메소드는 유니티 엔진에서 매 프레임마다 호출되는 메소드입니다.

이 메소드에서 CFreeNetEventManager 객체에 접근하여 네트워크 이벤트와 수신된 메시지를 가져오게 됩니다. 먼저 has_message 메소드를 통해 수신된 데이터가 있는지 확인한 후 true가 리턴되면 dequeue_network_message 메소드를 통해서 메시지를 하나 꺼내옵니다.

그리고 미리 지정해 놓은 델리게이트를 호출하여 유니티 프로젝트 내의 로직 처리 부분으로 통보해줍니다. 네트워크 이벤트에 대해서도 동일한 로직으로 구성합니다.


바로 이 부분이 네트워크 라이브러리와 유니티 프로젝트와의 연결 고리가 되는 부분입니다. 지금 설명 드리고 있는 CFreeNetUnityService 클래스는 MonoBehaviour를 상속 받아 생성하였기 때문에 유니티 프로젝트 내의 다른 소스 코드들과 자연스럽게 어우러질 수 있습니다.
그렇다면 네트워크 라이브러리에 접근하여 각종 네트워크 이벤트들과 수신된 메시지를 가져오는 저 코드는 문제가 없을까요? 분명히 데이터 송, 수신 처리는 메인 스레드와는 별도로 작동된다고 알고 있는데 말이죠. CFreeNetEventManager 클래스의 소스 코드를 통해서 이 부분이 어떻게 구현되어 있는지 살펴보겠습니다.


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
using UnityEngine;
using System.Collections;
using System.Collections.Generic;
using FreeNet;
 
namespace FreeNetUnity
{
    public enum NETWORK_EVENT : byte
    {
        // 접속 완료
        connected,
        
        // 연결 끊김
        disconnected,
 
        // 끝
        end
    }
 
    /// <summary>
    /// 네트워크 엔진에서 발생된 이벤트들을 큐잉시킨다.
    /// 워커 스레드와 메인 스레드 양쪽에서 호출될 수 있으므로 스레드 동기화 처리를 적용하였다.
    /// </summary>
    public class CFreeNetEventManager : MonoBehaviour
    {
        // 동기화 객체
        object cs_event;
 
        // 네트워크 엔진에서 발생된 이벤트들을 보관해놓는 큐
        Queue<NETWORK_EVENT> network_events;
 
        // 서버에서 받은 패킷들을 보관해놓는 큐
        Queue<CPacket> network_message_events;
 
        public CFreeNetEventManager()
        {
            this.network_events = new Queue<NETWORK_EVENT>();
            this.network_message_events = new Queue<CPacket>();
            this.cs_event = new object();
        }
 
        public void enqueue_network_event(NETWORK_EVENT event_type)
        {
            lock (this.cs_event)
            {
                this.network_events.Enqueue(event_type);
            }
        }
 
        public bool has_event()
        {
            lock (this.cs_event)
            {
                return this.network_events.Count > 0;
            }
        }
 
        public NETWORK_EVENT dequeue_network_event()
        {
            lock (this.cs_event)
            {
                return this.network_events.Dequeue();
            }
        }
 
        public bool has_message()
        {
            lock (this.cs_event)
            {
                return this.network_message_events.Count > 0;
            }
        }
 
        public void enqueue_network_message(CPacket buffer)
        {
            lock (this.cs_event)
            {
                this.network_message_events.Enqueue(buffer);
            }
        }
 
        public CPacket dequeue_network_message()
        {
            lock (this.cs_event)
            {
                return this.network_message_events.Dequeue();
            }
        }
    }
}
cs


코드가 짧은 편이라 전체 소스 코드를 보면서 설명드리겠습니다.

CFreeNetEventManager 클래스는 네트워크 라이브러리에서 수신된 메시지들과 이벤트들을 보관해 놓는 큐로 구성되어 있습니다. 이 큐에 데이터를 넣고 빼올 때 동기화 처리를 적용하였기 때문에 유니티 엔진이 작동되는 메인 스레드에서 호출하여도 충돌나지 않을 수 있는 것이죠.


이제 이렇게 구성한 요소들을 더 편리하게 사용할 수 있도록 네트워크 매니저를 만들어 보겠습니다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
public class CNetworkManager : MonoBehaviour {
    CFreeNetUnityService gameserver;
 
    void Awake()
    {
        // 네트워크 통신을 위해 CFreeNetUnityService 객체를 추가합니다.
        this.gameserver = gameObject.AddComponent<CFreeNetUnityService>();
 
        // 상태 변화 (접속, 끊김 등)를 통보 받을 델리게이트 설정.
        this.gameserver.appcallback_on_status_changed += on_status_changed;
 
        // 패킷 수신 델리게이트 설정
        this.gameserver.appcallback_on_message += on_message;
    }
 
    void Start()
    {
        connect();
    }
 
    public void connect()
    {
        this.gameserver.connect("127.0.0.1"7979);
    }
 
    /// <summary>
    /// 네트워크 상태 변경시 호출될 콜백 메소드
    /// </summary>
    /// <param name="status"></param>
    void on_status_changed(NETWORK_EVENT status)
    {
        switch (status)
        {
            // 접속 성공
            case NETWORK_EVENT.connected:
                {
                    Debug.Log("on connected");
 
                    CPacket msg = CPacket.create((short)PROTOCOL.CHAT_MSG_REQ);
                    msg.push("Hello!");
                    this.gameserver.send(msg);
                }
                break;
 
            // 연결 끊김
            case NETWORK_EVENT.disconnected:
                {
                    Debug.Log("disconnected");
                }
                break;
        }
    }
 
    void on_message(CPacket msg)
    {
        // 제일 먼저 프로토콜 아이디를 꺼내온다.
        PROTOCOL protocol_id = (PROTOCOL)msg.pop_protocol_id();
 
        // 프로토콜에 따른 분기 처리
        switch (protocol_id)
        {
            case PROTOCOL_CHAT_MSG_ACK :
                {
                    string text = msg.pop_string();
                    GameObject.Find("GameMain").GetComponent<CGameMain>().on_receive_chat_msg(text);
                }
                break;
        }
    }
 
    public void send(CPacket msg)
    {
        this.gameserver.send(msg);
    }
}
cs


중요하게 봐야 할 부분은 CFreeNetUnityService 객체를 생성하고 패킷 수신시 호출될 콜백 메소드를 설정하는 부분입니다. CFreeNetUnityService 클래스는 네트워크 라이브러리와 어느정도 연관이 있는 부분이기 때문에 이 클래스에서 바로 로직 처리까지 구현하는 방법은 권장하지 않습니다.

따라서 CNetworkManager 클래스의 델리게이트를 통해 네트워크 라이브러리에서 발생된 이벤트들을 로직 처리 부분까지 전달하여 사용할 수 있도록 구성하는 것이 좋습니다.


시스템을 이렇게 구성하였을 때 얻는 이점으로는 게임 로직 처리 부분에서 네트워크 라이브러리가 하는 일에 신경 쓰지 않고 핸들링된 메시지 처리에 집중할 수 있다는 부분입니다.

또한, 게임 로직이 어떻게 구성되어 있던 네트워크 라이브러리와 연결된 클래스에서는 메시지를 받아서 넘겨주는 역할만 충실히 수행하면 되기 때문에 서로 간의 연결이 느슨해지는 결과가 나타납니다. 결국 또다른 프로젝트에서도 재사용하기 쉬운 형태가 되며 로직 구현부의 코드를 수정하다가 네트워크 관련 코드를 건드릴 일도 없기 때문에 버그가 발생할 가능성도 줄어 들게 됩니다.


다음으로 화면 UI를 구성하는 스크립트를 통해서 에코 클라이언트의 유니티 버전을 완성해 보도록 하겠습니다. 화면 UI는 CGameMain 클래스에서 담당합니다.


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
using UnityEngine;
using System.Collections;
using System.Collections.Generic;
using FreeNet;
using VirusWarGameServer;
 
public class CGameMain : MonoBehaviour {
 
    string input_text;
    List<string> received_texts;
    CNetworkManager network_manager;
 
    Vector2 currentScrollPos = new Vector2();
 
    void Awake()
    {
        this.input_text = "";
        this.received_texts = new List<string>();
        this.network_manager = GameObject.Find("NetworkManager").GetComponent<CNetworkManager>();
    }
 
    public void on_receive_chat_msg(string text)
    {
        this.received_texts.Add(text);
        this.currentScrollPos.y = float.PositiveInfinity;
    }
 
    void OnGUI()
    {
        // Received text.
        GUILayout.BeginVertical();
        currentScrollPos = GUILayout.BeginScrollView(currentScrollPos,
            GUILayout.MaxWidth(Screen.width), GUILayout.MinWidth(Screen.width),
            GUILayout.MaxHeight(Screen.height - 100), GUILayout.MinHeight(Screen.height - 100));
 
        foreach (string text in this.received_texts)
        {
            GUILayout.BeginHorizontal();
            GUI.skin.label.wordWrap = true;
            GUILayout.Label(text);
            GUILayout.EndHorizontal();
        }
 
        GUILayout.EndScrollView();
        GUILayout.EndVertical();
 
        // Input.
        GUILayout.BeginHorizontal();
        this.input_text = GUILayout.TextField(this.input_text, GUILayout.MaxWidth(Screen.width - 100), GUILayout.MinWidth(Screen.width - 100),
            GUILayout.MaxHeight(50), GUILayout.MinHeight(50));
 
        if (GUILayout.Button("Send", GUILayout.MaxWidth(100), GUILayout.MinWidth(100), GUILayout.MaxHeight(50),
            GUILayout.MinHeight(50)))
        {
            CPacket msg = CPacket.create((short)PROTOCOL.CHAT_MSG_REQ);
            msg.push(this.input_text);
            this.network_manager.send(msg);
 
            this.input_text = "";
        }
        GUILayout.EndHorizontal();
    }
}
cs


on_receive_chat_msg 메소드는 CNetworkManager 클래스에서 새로운 메시지를 수신했을 때 호출되는 메소드입니다. 서버로부터 텍스트를 수신 받으면 스트링 리스트에 추가하여 OnGUI 메소드에서 화면 출력시 사용합니다.

OnGUI 메소드의 내용은 유니티 GUI 관련 코드로 구성되어 있습니다.

수신된 메시지를 스크롤 뷰를 통해 보여주고 서버에 전송할 메시지를 입력 받을 TextField가 존재합니다.


에코 서버와 유니티 버전 에코 클라이언트를 구동시킨 모습입니다.




에코 클라이언트 유니티 버전의 프로젝트 구성 요소

네트워크 라이브러리와 연동되는 부분을 위주로 설명하다 보니 유니티 쪽의 설명이 부족한 것 같아 내용을 추가하여 설명드립니다. 사용된 유니티 엔진의 버전은 4.5.5f1입니다. (현재 이 글을 카피하여 작성중인 저는 5.3.5f1 입니다.) 윈도우 에디터 환경에서는 무료 버전으로도 테스트가 가능하지만 안드로이드 폰으로 빌드를 하려면 Android Pro 버전이 있어야 소켓 기능을 사용할 수 있습니다.

(5.0 이상 버전에서는 좀 다를 것 같아서 검색해보니 프로 버전이 아니더라도 사용 가능한 것 같은데 확실해지면 다시 글을 수정하여 올리겠습니다.)



씬의 구성 요소입니다.

Main Camera는 기본으로 생성된 카메라를 그대로 사용합니다.

GameObject -> Create Empty 메뉴를 통해서 NetworkManager와 GameMain 오브젝트를 생성해 줍니다. 다른 컴포넌트는 추가할 필요 없이 트랜스폼만 갖고 있는 가장 기본적인 오브젝트들로 구성합니다.



NetworkManager 오브젝트의 Inspector 화면입니다.

CNetworkManager.cs 스크립트를 연결 시켜줍니다.

화면에 출력되는 오브젝트가 아니기 때문에 Transform 값은 의미가 없습니다.



GameMain 오브젝트의 Inspector 화면입니다.

CGameMain.cs 스크립트가 연결되어 있으며 Transform 값은 의미가 없습니다.


콘솔 프로그램으로 작성된 에코 클라이언트를 유니티 버전으로 만들어 보면서

유니티 엔진과 네트워크 라이브러리가 어떻게 연동되는지 알아봤습니다.

다음 강좌에서는 유저들끼리 실시간 대전이 가능한 온라인 퍼즐 게임을 만들어보면서

온라인 게임의 로직이 어떻게 구성되는지 더 깊게 들어가 보도록 하겠습니다.


반응형

'- Programming > - C#' 카테고리의 다른 글

★ 9. c# 네트워크 개발 p8  (1) 2017.02.13
★ 8. c# 네트워크 개발 p7  (0) 2017.02.13
★ 6. c# 네트워크 개발 p5  (0) 2017.02.10
★ 5. c# 네트워크 개발 p4  (0) 2017.02.09
★ 4. c# 네트워크 개발 p3  (0) 2017.02.09