레이블이 Unity인 게시물을 표시합니다. 모든 게시물 표시
레이블이 Unity인 게시물을 표시합니다. 모든 게시물 표시

3월 21, 2016

[HLAPI] IndexOutOfRangeException:NetworkReader:ReadByte out of range : NetBuf sz: ... pos: ... 에러

아마 서버와 클라이언트 간 메시지를 주고 받다가 이런 에러를 보았을 것이다.
처음엔 메시지 크기가 커서 그런 줄 알았는데...
유니티 공식 문서에는 한번에 최대 1400 바이트는 보낼 수 있다고 되어있다.
알고보니 서버의 메시지 클래스와 클라이언트의 메시지 클래스 간 규격이 일치하지 않아서
발생한 문제였다.

예를 들어 서버에서 정의한 사용자 메시지 클래스가

public class MyMessage : MessageBase
 {
  public string a;
  public string b; 
  public string c;
 }
위와 같다면, 클라이언트에서 역시 위와 동일한 코드로 메시지가 정의되어있어야 한다.
구글링해도 명확한 답변이 없어서 잠시 헤맸는데... 쉽게 해결되는 에러였다.

1월 19, 2016

유니티 5 네트워킹 시스템 HLAPI를 사용하여 LAN 멀티플레이 기본 틀 만들기 - 3(완결)


마지막 장입니다!
1 편에서 예고했던 대로, 동일한 네트워크 내에 존재하는 서버나, 클라이언트를 인식하고, 연결하는 것을 해보겠습니다.
예를 들어, 게임에 접속해서 방을 만들고, 접속한 유저들이 자동으로 뜨는 목록을 보고 싶다거나 할 때 유용하게 쓰일 것 같습니다.
다행히도, HLAPI 의 NetworkDiscovery 컴퍼넌트를 사용해서 이를 쉽게 구현해볼 수 있습니다.

이 컴퍼넌트는 전송 계층의 UDP Broadcasting 기능을 사용하며, 따라서 인터넷에서 작동하는 것이라 오로지 LAN 상에서만 사용할 수 있습니다.
컨셉은 단순히 특정 포트번호로 '방송'을 송신하는 개체와 수신하는 개체 두 가지가 있고, 방송을 수신하였을 때 이벤트를 받아서 송신한 개체의 IP에 접속하는 것입니다.(송신할 때 IP 주소나 기타 데이터를 를 같이 내보냅니다.)

역시나, NetworkManager 와 같이 빈 오브젝트에 NetworkDIscovery 컴퍼넌트를 추가하면 됩니다만,
우리는 기본 제공 GUI 를 사용하지 않고 커스텀하기 위해 코드를 조금 더 쳐 보겠습니다.
앞서 작성한 MyNetManager 를 불러와, 다음 노랗게 색칠한 코드 한 줄을 추가합니다.



 











다음은 서버와 클라이언트로 구동을 시작하는 SetupServer, SetupClient 메서드에 각각 
UDP 브로드캐스팅을 시작하는 명령을 넣습니다.
공통으로 Initialize() 를 실행하여 컴퍼넌트를 초기화 한 뒤,
서버에서는 StartAsServer( ), 클라이언트에서는 StartAsClient( ) 를 각각 실행하면
이제 동일한 네트워크 내의 유니티 게임들이 서로를 인식하게 됩니다.






























하지만 이대로 실행하면 인식을 했는지 전혀 알 수가 없겠죠??

클라이언트에서 방송을 수신했을 때 발생하는 이벤트를 가로채서 처리를 해야 합니다.

빈 게임오브젝트를 생성하고 이름을 'Broadcaster'로 변경하고, 다음 C# Script 를 새로 생성하여 붙입니다.

// MyNetworkDiscovery.cs


using UnityEngine;
using System.Collections;
using UnityEngine.Networking;

public class MyNetworkDiscovery : NetworkDiscovery
{

    public override void OnReceivedBroadcast(string fromAddress, string data)
    {
        //서버로부터 브로드캐스트 메시지를 받았을 때 실행됩니다.
        base.OnReceivedBroadcast(fromAddress, data);
        Debug.Log("recived broadcast from : " + fromAddress);

        StartCoroutine("TweenAnim");
    }
}

그리고 NetworkManager 컴퍼넌트의 인스펙터창에서 빈 칸으로 되어있는
NetworkDiscovery 에 Broadcaster 를 다음과 같이 끌어다 놓습니다.











































NetworkDiscovery 로 부터 상속받은 OnReceivedBroadcast 메서드는 fromAddress 에 
브로드캐스트 메시지를 송신한 개체의 IP 주소, 함께 보낸 data 를 인자로 받습니다.
IP 주소 같은 경우, 게임 내에서 빈번하게 쓰일 것이므로 위 콜백에

   NetworkManager.singleton.networkAddress = fromAddress;

코드를 추가하여 네트워크 매니저 싱글턴이 IP 주소를 저장하도록 할 수도 있습니다.

이제 두 모바일 기기를 같은 와이파이로 접속시킨 후, 각각을 서버와 클라이언트로 구동하여,
클라이언트에서는 OnReceivedBroadcast 콜백에서 라벨을 띄워준다거나, 어떤 표시를 하여 송수신이 이루어졌는지 확인해봅시다.

다음 동영상은 서버로부터 메시지를 수신하면 라벨에 트윈 애니메이션을 적용시켜 수신하였음을 알려주는 시연 동영상입니다.



완성된 코드는 다음 저장소에서 다운 받으실 수 있습니다.


지금까지 유니티5의 HLAPI 를 사용해 네트워크 객체를 서로 연결하고, 네트워크 메시지를 수신할 수 있는 기본적인 기능을 구현하는 법을 알아보았습니다.
읽어주셔서 감사드리고, 많은 도움이 됬으면 좋겠습니다^^

1월 12, 2016

유니티 5 네트워킹 시스템 HLAPI를 사용하여 LAN 멀티플레이 기본 틀 만들기 - 2

전 편에서 서버와 클라이언트의 연결까지 되었으니,
이제 서로 메시지를 주고받는 기능을 넣어보겠습니다.

메시지를 보내기 위해서는 메시지의 '타입''자료형'
두가지에 대한 정의가 필요합니다.
간단한 정수나, 문자열, 구조체 등등 보내고 싶은 걸 보냅시다.
저는 간단하게 int 형 변수 하나만 보내보겠습니다.

<자료형>
public class MyMessage : MessageBase
    {
        public int number;
    }
메시지클래스는 MessageBase 기반 클래스를 상속받아 작성해야 합니다. 규칙입니다.

<타입>
public class MyMsgType {
        public static short CustomMsgType= MsgType.Highest + 1;
    };
Msgtype 은 정의를 보면 아래와 같이 여러 네트워크 상황에 대한 상수를 정의해놓고 있습니다. Highest 가 46 으로 가장 높은 값을 가지므로 1을 더한 사용자정의 메시지타입을 정의하였습니다.


다음으로 메시지를 수신하였을 때 호출할 콜백을 정의하고, 등록합니다.

<콜백 정의>
public void OnMessage(NetworkMessage netMsg)
    {
        MyMessage msg = netMsg.ReadMessage<MyMessage>();
        Debug.Log("OnScoreMessage " + msg.number);
    }

<콜백 등록>
public void SetupServer()
    {
        Debug.Log("SetupServer()");
        StartServer();
        NetworkServer.Listen(4444);
        NetworkServer.RegisterHandler( MyMsgType.CustomMsgType, OnMessage); // 서버측에서 콜백을 등록하는 부분입니다.

    }

전 편에서 서버를 셋업하는 코드에 콜백을 등록하는 한 줄만 추가했습니다.
이렇게 하면 서버에서는 클라이언트가 MyMsgType.CustomMsgType(47) 형 메시지를 보낸 것을 감지하면 OnMessage 콜백을 호출하게 되고, 
NetworkMessage 타입 변수를 인자로 받아 메시지의 내용, 보낸 개체의 ip 등등 포함된 정보를 확인할 수 있습니다.
반대로 클라이언트에서 콜백을 등록하려면, NetworkClient 클래스에도 동일한 RegisterHandler 메서드가 있으므로 myClient.RegisterHandler 와 같이 사용하시면 됩니다.

그럼 마지막으로 제일 중요한 메시지를 보내는 부분을 작성하면 되겠네요.

<클라이언트에서 서버로 메시지 송신>
public void SendMessage(int number)
    {
        MyMessage msg = new MyMessage();
        msg.number = number;

        myClient.Send( MyMsgType.CustomMsgType, msg);
    }

서버에서 보낼 때는 NetworkServer.Send , NetworkServer.SendToAll 와 같이 작성하면 됩니다.
코드를 다 작성하였으니 GUI 를 구성하고, Client 버튼에 SetupClient()를, Server 버튼에 SetupServer()를, Send 버튼은 SendMessage(int number) 에 각각 이벤트를 연결 시킵니다.

빌드 후 따로 실행시킨 프로그램은 클라이언트로,
에디터에서는 서버로 각각 접속한 후, 클라이언트에서 Send 버튼을 눌러봅시다.




참조 : 
http://docs.unity3d.com/kr/current/Manual/UNetMessages.html
http://docs.unity3d.com/ScriptReference/Networking.NetworkClient.Send.html

1월 06, 2016

Git 등 SVN 으로 코드관리시 꼭 필요한 폴더

Assets, ProjectSettings 폴더의 내용만 필요하다고 하네요..

유니티 5 네트워킹 시스템 HLAPI를 사용하여 LAN 멀티플레이 기본 틀 만들기

외부 인터넷을 거치지 않고, 로컬 와이파이만을 이용한 멀티 플레이를 구현하기 위해 유니티 매뉴얼을 보다가 좋은 재료를 발견하여 짧게 적어보겠습니다.

본 튜토리얼에서는 LAN 환경에서 네트워크 객체(유니티 애플리케이션)들이 서로의 존재를 인식하고, 접속하여 네트워크 메시지를 주고받고, 각종 콜백처리를 하기 위한 기본 틀을 구현해보겠습니다. 그리고 간단한 UI 구성을 위해 NGUI 를 사용하겠습니다.

유니티가 5.1 버전 이후로 새로운 네트워킹 시스템을 발표하여 이전보다 더 간편하고 쉽게 좋은 성능을 가진 멀티플레이를 구현할 수 있게 되었습니다.

NetworkManager 와 HLAPI(High-Level) 를 사용하여 시작할 수 있습니다.
간단하게 위의 한글 매뉴얼을 흝어보고 오시면 더 이해가 쉽습니다.

먼저 새 프로젝트를 만들고, 빈 게임 오브젝트 하나를 생성합니다.
원래는 'Add Component' 버튼을 눌러 NetworkManager 컴퍼넌트를 추가하는 것으로 끝나지만, 입맛대로 커스터마이징하기 위해 NetworkManager 를 상속한 사용자 정의 스크립트를 생성하겠습니다. 

새로운 C# 스크립트를 생성하여 MyNetManager 로 이름을 변경한 후 빈 게임 오브젝트에 붙입니다.

그리고 아래처럼 코딩해주세요.

using UnityEngine;
using System.Collections;
using UnityEngine.Networking; /* 유니티 네트워킹 API 입니다. */


public class MyNetManager : NetworkManager /* NetworkManager 를 상속합니다. */
{
    NetworkClient myClient;

    /* 확장 함수 */
    public override void OnStartServer() 
    {
        Debug.Log("OnStartServer( )");
    }

    public override void OnStartClient(NetworkClient client)
    {
        Debug.Log("OnStartClient( )");
    }

    public override void OnStopClient()
    {
        Debug.Log("OnStopClient( )");
    }

    /* 사용자 정의 함수 */
    public void SetupServer()
    {
        Debug.Log("SetupServer()");
        StartServer();     
        NetworkServer.Listen(4444);
        NetworkServer.RegisterHandler(MsgType.Connect, OnConnected);

}

    public void SetupClient()
    {
        Debug.Log("SetupClient()");
        StartClient();     

        myClient = new NetworkClient ();
        myClient.Connect("127.0.0.1", 4444);

    }
    public void OnConnected(NetworkMessage netMsg)
    {
        Debug.Log("Connected to server");
    }

}
OnStartServer( ), OnStartClient( ),OnStopClient( ) 는NetworkManager 를 확장한 메서드들입니다. 나중에 이 부분에서 서버를 구동시켰을 때, 클라이언트가 접속했을 때의 처리를 할 것입니다. 이 외의 여러 네트워크 이벤트를 처리할 수 있는 가상 함수들은 공식문서를 참조하시기 바랍니다. 
아래의  SetupServer( ), SetupClient( ) 메서드는 본 튜토리얼에서 NetworkManagerHUD(기본 GUI) 를 사용하지 않을 것이기 때문에 사용자 정의로 직접 서버를 구동하고 클라이언트를 접속시키는 코드를 작성하기 위해 만든 것입니다.
NetworkServer.RegisterHandler 메서드는 서버측에서 네트워크 이벤트를 처리할 처리기를 등록하고 있습니다. 위 코드는 클라이언트 접속시 발생할 이벤트를 처리합니다.
마지막으로 아래와 같이 각각 서버, 클라이언트 모드로 구동하기 위한 버튼을 두 개 배치합니다. 한쪽엔 버튼 클릭시 MyNetManager 의 SetupSever( )를, 다른 하나는 SetupClient( ) 가 실행되도록 클릭 이벤트를 연결합시다.
접속이 이루어졌는지 확인하기 위해, 유니티 에디터에서 서버를 실행하고, PC 버전으로 빌드하여 클라이언트로 실행해봅니다.
그러면 다음과 같이 클라이언트로 접속했을 때 콘솔창에 메시지가 뜨는 것을 볼 수 있습니다.
2편에서는 서버와 클라이언트가 연결 된 상태에서 서로 메시지를 주고받는 기능을 구현해보겠습니다.

12월 18, 2015

Public 필드 선언과 [SerializeField] 선언의 차이



유니티 스크립트에서 public 으로 변수를 선언한 경우 자동적으로 인스펙터창에서 공개 된다.
또한 변수에 [SerializeField] 속성을 지정했을 때에도 마찬가지로 같은 결과를 볼 수 있다.
둘 중에 어떤

일반적으로, 항상 변수를 생성할 때 가능한 최소의 접근 레벨을 갖도록( i.e.private ) 해야 한다.
이에 대한 많은 근거들이 있지만, 그 중에 하나는 여러분이 큰 팀에서 협업을 하거나 또는 혼자서 작업할지라도,
실수로 다른 클래스에서 작성된 코드로 인해 변경되지 않아야 하는 변수들이 변경되기 쉽기 때문이다.
여러분이 선언한 대부분의 클래스 내에서 필요한 변수들은 private 여야 한다.

public 변수 선언으로 유니티가 자동적으로 인스펙터창에 띄워주는 편리한 면이 있지만, 그보다
private 선언 후 [SerializeField] 속성을 지정해 주는 것이 좋다. 어떻게 보면 private 선언의 목적을 깨는 것이지만,
이것은 그나마 다른 스크립트의 오류를 찾는 것보다 훨씬 수월할 것이다.

유니티 공식 문서


유니티 공식 문서를 보면 [SerializeField] 를 거의 사용할 일이 없을 것이라고 나오고, public 변수의 선언을 권장하지만,
다른 여러 프로그래머의 의견은 이것이 나쁜 OOP 습관이라고 주장한다.

한편, 아래는 Unite 2015 Europe 에서
Public 선언이 Private 선언보다 더 나은 성능을 낼 수 있다는 주장을 하고 있는 동영상이다.

유튜브 링크


성능이 중요시 되는 저사양 디바이스에서는 이 점을 고려해서 개발에 참고하면 좋을 듯 하다.

12월 17, 2015

좌표계

좌표계

월드좌표계와 로컬좌표계가 있다.
월드좌표계는 유니티의 씬 내부에서 전체적으로 쓰이는 좌표이고,
로컬좌표계는 어떤 객체마다의 고유한 좌표계를 말한다.
유니티에서 이 두 좌표계의 관계(부모-자식 관계)는 게임오브젝트를 부모 게임오브젝트의 자식으로 등록하는 것으로 구현이 가능하다.
결국, 자식 게임오브젝트의 좌표 = 부모의 월드좌표계 + 자식의 로컬좌표계로 표현 된다.

12월 12, 2015

NGUI vs UGUI ?

NGUI vs UGUI ?


유니티로 작업하면서 UI 관련해서 어떤 툴을 써야할지 검색하다보니 두 가지 선택사항이 있었다.
NGUI(Next-Gen UI) 라는 기존의 범용적으로 쓰이는 플러그인과 유니티 4.6버전부터 새로이 공개된 UGUI가 있다.

NGUI는 일단 95$ 이며, 많은 개발자들이 오랫동안 써와서 레퍼런스가 충분하다는 장점을 가지고 있다. 반면 UGUI는 유니티 자체에서 공식적으로 지원하는 유일한 UI 도구이며, 무료라는 큰 메리트가있지만 역사가 오래되진 않았다.

아래 유니티 포럼 링크를 따라가면 둘의 장단점에 대해 유저들이 다양하고 디테일한 관점에서 분석하여 댓글을 달아놓은 것을 볼 수 있다.

링크

UGUI 제작에 NGUI 개발자가 참여했다는 점에서 둘 중에 무엇을 써도 크게 상관은 없을 듯 하나,
급하지 않다면 UGUI 를 쓰면서 사용이 활발해지기를 기다려도 나쁘지 않을 것 같다는 생각이다.

12월 07, 2015

[Sprites and bones]유니티 애드온으로 2D Skeletal Animation 구현하기

제가 소개할 애드온은 유니티 3D에서 2D Skeletal Animation을 생성하기위한 애드온입니다.
애드온을 사용하여 유니티 에디터에서 직접 뼈 계층 구조를 추가 할 수 있습니다.
스프라이트는 뼈를 부모로 가질 수 있습니다.
뼈는 애니메이터 창에서 다른 게임 오브젝트와 같은 애니메이션을 적용 할 수 있습니다.
뼈는 메쉬 또는 입자 시스템과 같은 스프라이트가 아니더라도 다른 게임 오브젝트로 애니메이션을 구현할 수 있습니다.

아래 주소에서 튜토리얼, github 페이지 등을 참고할 수 있습니다.


http://forum.unity3d.com/threads/release-free-unity-sprites-and-bones-2d-skeleton-animation.219915/



※본 포스트는 위 링크의 동영상을 요약한 것이므로 
페이지에 나와있는 24분 가량의 동영상을 보셔도 되고, 대략적인 방법만 보실 분은 글만 빠르게 스캔하시면 되겠습니다.


아래는 애드온이 제공하는 기능입니다.

#기능



* Skeletons 와 Bones
* IK(Inverse Kinematics)와 targets
* IK Helper 객체
* Bones 와 IK 는 완전히 애니메이션됩니다.
* Bone Splitting ( 뼈 분할 )
* 메쉬 변형
* 가중치 그리기
* 뼈는 Ctrl 키와 왼쪽 클릭으로 추가됩니다.
* 저장 및 포즈의 불러오기. 포즈는 애셋으로 저장됩니다.
* 완전 유니티 에디터에 통합.
* 취소-지원
* 수많은 기즈모
* 나는 역 기구학 언급 했습니까?
* 무료 및 오픈 소스


준비물 : 스프라이트 모음, Sprites and bones 애드온




스프라이트 에디터에서 기본값이 센터로 되어있는 피벗 좌표를 적절하게 설정하고
아래와 같이 씬에 하이어라키에 계층 구조로 배치합니다.




먼저 팔을 회전시켜보자, 이 때 피벗을 커스텀 설정했음에도, 스프라이트 중앙을 기준으로 회전할 수 있는데, 이 때는 유니티 좌측 상단에 Center 로 되어있는 토글 버튼을 pivot 으로 변경해주면 원래의 회전축대로 회전하게 됩니다.


이제 Sprites and Bones 애드온을 쓸 차례인데 우선 메뉴에서 Skeleton을 선택합니다.
그러면 초기에 하나의 Bone이 생성되고 씬에서는 흰 사람모양의 오브젝트가 보입니다.
처음 생성된 Bone 이름을 SpineBone으로 명명하고 노란 네모박스 영역을 머리 전까지 드래그합니다.
팔 부분을 리깅하기 위해서, SpineBone 을 다시 선택하고 Ctrl+좌클릭으로 새로운 Bone을 생성합니다. 이 때 Inspector 창에서 Bone 스크립트 부분을 보면 Snap to Parent 체크란에 체크가 되어있는데 이를 해제하면 리깅 시작점을 옮길 수 있습니다. 팔이나 다리는 이처럼 체크를 해제한 후 시작점을 옮겨서 리깅해야 합니다.


※ 리깅 : 모델링 된 데이터에 뼈를 붙이는 작업. 주로 T자 형태로 펴진 상태에서 한다.

뼈대를 완성하였다면, 다음은 SpineBone을 제외한 각 Bone에 IK를 추가해야 합니다.
IK를 추가하는 방법은 먼저 Inspector 창에서 Add IK 버튼을 누르고 Chain Length 를 지정합니다.
Chain Length 는 해당 파츠에 딸려 움직이는 파츠 수를 입력하면 됩니다. 가령 머리는 한 개 파츠로 이루어져 있으므로 1 이 되고, 손, 팔 하단, 팔 상단으로 이루어진 팔에서 손 파츠의 Chain Length 는 3입니다.
그 다음 Create target helper 를 눌러서 헬퍼를 생성하면 됩니다. Type 옵션은 기본적으로 Wire Cube로 되어있는데, 팔 상단과 다리 상단 파츠만 Wire Sphere로 지정하고 나머지는 기본값을 사용하면 됩니다.
그러면 아래와 같이 리깅이 완료됩니다.











































Skeleton 오브젝트를 선택해서 Edit Mode의 체크를 해제하고, 아무 파츠의 IK를 선택해서 이리저리 움직여봅시다.

하지만 스프라이트와 뼈대가 연동이 되지 않았으므로 골격만 움직일 것입니다.
마지막으로 처음에 계층구조로 배치했던 각 스프라이트들을 해당 파츠 하위 계층으로 모두 옮겨주고, 빈 게임 오브젝트를 생성해서 Player 와 같은 이름으로 변경하고 Skeleton 객체를 하위에 추가합니다.
그러면 뼈대와 스프라이트가 같이 움직이는 모습을 볼 수 있습니다.


이제 모델링이 된 캐릭터에 애니메이션을 입히고 싶다면 Player 객체의 컴퍼넌트로 Animator를 추가하고,  Animation 창을 열어 새로운 애니메이션 파일을 생성합니다.
간단한 예로 30프레임을 선택하고 IK를 이리저리 움직여보며 원하는 동작을 맞춥니다.
그 다음 0프레임의 키프레임을 복사하여 60프레임에 붙여넣고 재생해보면 간단하게 반복되는 애니메이션을 볼 수 있습니다.