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

2013년 7월 31일 수요일

유니티 prefab에서도 enum은 int일뿐

사실 C++ 에서 enum을 많이 쓰는 이유는 enum자체가 int값이랑 별 다르지 않기 때문입니다. 예를 들어

enum eAnimal
{
    ANIMAL_DOG,
    ANIMAL_CAT
};

이 있으면 ANIMAL_DOG은 0, ANIMAL_CAT은 1에 불과할 뿐이죠.

int dogIndex = ANIMAL_DOG;

이런게 아무 문제 없이 되었으니까요. 이제는 C#부터 비롯한 다양한 언어에서(아마 C++ 차세세대 규격도 그랬던듯) enum을 strong type으로 다뤄서 저런게 쉽게 안되게 하긴 하는데 그래도..

int dogIndex = (int)ANIMAL_DOG;

하면 여전히 dogIndex는 0이 됩니다.

근데 C#에서 재밌는게 enum으로 부터 스트링 값을 뽑아올수가 있지요. 즉 ANIMAL_DOG을 실행중에 "ANIMAL_DOG"이라 문자열로 가져올수도 있는겁니다. 그래서 유니티를 만지면서 함 실험을 해봤습니다. 유니티에서 prefab을 만들면 그 값을 기억하는데 이때 enum 형을 기억하면 과연 문자열을 기억하는지 아니면 int로 기억하는지 궁금했어요.

사실 은근 string으로 저장되길 바랬습니다... string으로 저장하면 이런게 되거든요..
만약 prefab에 ANIMAL_CAT을 저장해눴는데.. 그 후에 eAnimal에 새로운 놈을 추가하면..

enum eAnimal
{
    ANIMAL_DOG,
    ANIMAL_MOO,
    ANIMAL_CAT
};

나중에 그 prefab을 읽어와도 숫자 1이 아닌 "ANIMAL_CAT"으로 읽히기에 중간에 다른 놈을 삽입해도 아무 문제가 없다는거죠...

근데 실험해보니... ANIMAL_MOO가 읽히더군요 -_-;

뭐 그래서 유니티에서 prefab저장할때 enum은 int 저장되는겁니다... 뭐 이거의 장점도 있어요.... 이름 바꾸긴 편하죠.. ANIMAL_CAT을 ANIMAL_MEOW로 바꿔도 아무 문제가 없거든요.

그럼 이상 후다닥 -_-

2013년 1월 7일 월요일

XNA의 몰락. 그리고 대안

XNA가 처음 나왔을 때부터 열심히 사용했었다. 첨에는 PC와 Xbox 360용으로 돌 수 있는 게임을 만들 수 있다는게 맘에 들어서였고, 또 Xbox 360에 올리면 수익모델이 있다는 것도 맘에 들었었다. 하지만 불행히도 엑박에서 XNA 게임이 차지하는 비중이 매우 미미한 수준에서 그친 이후 XNA는 여러번 변화를 겪게 된다. XNA 4.0부터던가... 윈폰 7.0용으로 게임을 만들려면 XNA를 써야 했다. 모바일 플랫폼을 지원하려고 노력을 퍼붓다 보니 당연 PC나 엑박용 지원은 미미해졌고 그로 인해 XNA의 입지가 조금씩 애매해져갔다.

물론 그럼에도 난 XNA를 자주 사용했는데... 스크린 스페이스 데칼 프리젠테이션에 사용한 데모 프로그램도 XNA로 만든 거였다. XNA가 가지고 있는 컨텐츠 파이프라인이라던가 C#으로 쉽게 DirectX 기능을 사용할 수 있다는 사실이 프로토타입을 만드는데는 아주 적합했다고 할까...?

하지만 이제 윈폰 8이 XNA 지원을 중지한다고 한다. 그 대신 WinRT를 사용한다고... Visual Studio 2012도 XNA를 지원하지 않는다.. (뭐 어떻게 사용할 수 있는 꼼수는 있다.) 물론 Visual Studio 2010을 사용하며 XNA 4.0을 계속 사용할 수는 있지만 XNA에는 다음과 같은 제약이 있다.

  • 64비트를 지원하지 않음
  • DirectX 9기능까지만 지원함. (즉 DX11 기능을 쓸 수가 없음)

이제 대안을 찾을 때... 결국 XNA가 좋았던 이유는 다음의 두가지였다.
  • C#지원
  • 컨텐츠 파이프라인

그러니 이것만 어떻게 해결할 수 있는 대안을 찾으면 되는게 아닌가...?

C# 지원

C#을 지원하는(정확히 말하면 어떤 .NET 언어 라도 지원하는) DirectX 로는 SharpDXSlimDX가 있다. 둘다 API도 매우 비슷하고 사용법도 거의 같다. 그리고 둘다 64비트를 지원한다. 최근에 64비트용 프로토타입을 만들 일이 있어서 둘다 사용해봤는데... 사용해본 바로는 SharDX가 SlimDX보다 낫다. 이유는 다음의 2개:

  • SharpDX가 속도가 더 빠름 (API 호출의 부하가 적음)
  • SharpDX가 설치가 더 쉬움(그냥 DLL파일만 복사해놓으면 끝)
고로 SharpDX를 사용하면 C# 지원문제는 해결..

컨텐츠 파이프라인
사실 이 부분은 딱히 방법이 안보인다. 아직... 물론 C#자체 또는 SharpDX자체에서 텍스처 파일 로딩기능은 거의 다 구현해놨으니 이건 큰문제가 아닌데... 오디오 라던가 메쉬 같은건 여전히 좀 문제다.

하지만 비주얼 스튜디오 2012에 FBX를 이용하는 예제가 이미 포함되어있고, MS에서 WinRT를 더 제대로 지원하려면 컨텐츠 파이프라인을 좀더 낫게 만들어 주지 않을까? 하는 기대만 한다...

아니면 내가 시간이 좀 나면 짬짬이 이런걸 만들어서 공개하고 싶기도 한데... 현재 내 스케줄을 봤을때 그건 거의 불가능할듯...


결론 - SharpDX
난 일단 SharpDX로 갈아타기로 했다. 프로토타입이나 툴에서 필요한 메모리가 32비트에서 지원할수 있는 용량을 이미 넘어섰기때문이다. 컨텐츠 파이프라인은 텍스처나 메쉬정도는 이미 지원되니.. 나머지거야 어떻게든 닥칠때마다 해결하면 되겠지.


2012년 12월 3일 월요일

Fast Object Picking

예제코드 다운받기

  • C#과 XNA를 이용해서 만들었습니다.
  • XNA를 설치하셔야 합니다.


게임보다는 에디터 따위의 툴에서 더 유용한 기법입니다. 예전에 개인 프로젝트에서 장난삼아 만들어 봤던 놈인데 그뒤로도 몇번이나 동일한 기법을 여러 툴에서 구현하다보니 올리면 유용하겠다 싶어서..... 이미 알고계시는 분들도 많겠지만 혹시나 모르시는 분들을 위해 여기 올리면 좋겠다고 생각해서 올립니다.

이 기법이 해결하려고 하는 건 간단합니다. 맵 에디터 같은 프로그램에서 화면에 있는 물체를 마우스로 클릭해서 선택하는걸 졸라~ 빠르게 구현하는 겁니다.

말로는 쉽죠?... 그런데 보통 구현들 어떻게 하셨나요?

흔히 쓰던 광선 vs AABB 충돌검출 방법
제가 흔히 봤던 방법중 하나는 마우스 클릭한 위치부터 화면 안쪽으로 광선(ray)를 쏴주면서 그 광선과 각 물체의 AABB의 충돌검사를 한뒤 가장 가까이에 있는 물체를 선택하는 거였습니다.

예를 들어 아래 이미지에서 오른쪽 가장자리가 화면이라고 가정하면 이런식으로 ray를 쏴서 aabb를 찾는거죠.


그런데 이 방법에 문제들이 좀 있습니다

  • 월드에 물체들이 많으면 충돌검사 시간 꽤 걸립니다
  • AABB와 충돌검사를 하므로 픽셀단위로 정확한 충돌검사가 불가능합니다.



제가 쓰는 렌더타겟과 물체 ID를 이용한 방법
저는 GPU를 이용해서 위 문제점들을 해결했습니다. 알고리듬은 매우 간단합니다. 자세한 코드는 위에 첨부해 놓은 예제코드를 봐주세요.
  1. 각 물체마다 고유 해쉬 아이디(32비트 정수)를 부여한다.
  2. 화면크기와 동일한 A8R8G8B8 렌더타겟을 하나 만든다. (이후 ID맵이라 부름)
  3. ID Map맵 렌더타겟을 설정한다.
  4. 모든 물체를 그려주면서 물체 해쉬(32비트)값을 8비트씩 짤라 R,G,B,A채널에 써준다
  5. 마우스가 클릭된 위치의 픽셀을 읽어온다
  6. 그 해쉬값과 동일한 물체를 선택한다.
  7. 선택된 물체로 하고 싶은 짓을 한다 -_-
  8. 끝 -_-

해쉬아이디
해쉬 아이디는 아무렇게나 생성이 가능합니다. 각 물체마다 고유하기만 하면 되죠. 제 예제에서는 그냥 물체 이름인 string으로부터 해쉬 아이디를 생성했습니다.

해쉬아이디를 색상으로 바꾸는 법
해쉬 아이디를 RGBA로 바꾸는 코드는 다음과 같습니다.

private static Color HashIDToColour(int hash)
{
    int a = (hash >> 24) & 0xff;
    int b = (hash >> 16) & 0xff;
    int g = (hash >> 8) & 0xff;
    int r = hash & 0xff;

    return new Color(r, g, b, a);
}

이 후 이걸 셰이더 함수로 대입해준 뒤

ColourFX.Parameters["Colour"].SetValue(HashIDToColour(go.Hash).ToVector4());

셰이더 안에서 다음과 같이 그려만 주면 됩니다.
float4 ps(VtxOut In) : COLOR
{
return Colour;
}

해쉬아이디 읽어오기
매우 간단합니다. 그냥 그 픽셀값을 32비트로 읽어오면 끝입니다. (이미 해쉬 ID에서 색상으로 변환할때 byte순서및 엔디안 문제를 고려했거든요

public int PickObject(int x, int y)
{
    int hash = Hash.INVALID;

    int [] pickedPixel = new int[1];

    IDMap.GetData<int>(0, new Rectangle(x, y, 1, 1), pickedPixel, 0, 1);

    hash = pickedPixel[0];

    return hash;
}

예제 결과
일단 제 샘플 코드를 실행해보면 다음과 같은 그림이 보일겁니다.

메인 화면에 3개의 공이 있고.. 오른쪽 아래는 ID맵입니다.

여기서 파란색 공위에 마우스를 클릭하면 다음과 같이 됩니다.

현재 선택된 물체를 노란색으로 표현했습니다. 그리고 현재 선택된 물체를 ID 맵에 안그려서 다시 한번 더 클릭을 하면 그뒤에 있는 물체가 대신 선택되게 만들었습니다.


이정도면 대충 보여드린듯 하죠? 자세한건 직접 받아서 실행해보세요.. -_-;

기타 응용
최근에 이 기법을 응용해서 물체 ID 대신에 물체의 깊이를 저장도 해봤답니다. 마우스 클릭 위치 근처에 있는 복셀(voxel)들을 전부다 고칠일이 있어서... 그냥 마우스 클릭 깊이만 찾아다 그로부터 월드위치 구한 뒤, octree를 뒤져서 근처에 있는 복셀들을 찾아냈죠.

기타 등등의 응용법이 있을 겁니다.



오랜만에 글써본 포프였습니다.



2012년 7월 9일 월요일

XML 데이터를 읽어올수 있는 C# 클래스 자동생성하기

예전에 작성해놨던 작업일지에 보니 이게 있네요. 그래서 까먹기전에 재빨리 포스팅합니다. 뭐 혹시 다른 분께 도움이 될 수도 있으니....

XML 파일을 그대로 읽어올 수 있는 C# 클래스를 자동으로 생성하는 것... 예전에 CAT 릭을 사용한 애니메이션 데이터를 3DS Max에서 읽어와서 Unity안에서 쓰려고 할 때 시도해봤던 겁니다. FBX파일을 CAT 릭에서 IK를 사용한 애니메이션 데이터를 제대로 export하지 않거든요. FBX에서에서 돌게하는 유일한 법은 애니메이션을 각 프레임 별로 bake하는건데 전 key frame 정보를 사용하고 싶었어요. 그래서 FBX 대신에 3DS Max에서 곧바로 XML 파일로 애니메이션을 저장할 수 있는 자체 기능을 써볼려고 했죠.

XML 파일을 저장해낸 뒤에 이 파일을 한번 문서편집기에서 열어봤죠. 데이터 구조가 그리 복잡하지 않다면 XML 파일을 곧바로 deserialize할 수 있는 C# 클래스를 손수 작성할수 있을거라 생각했거든요. 근데.... 그게 아니더군요.. 너무 복잡해요 -_-;;; 그래서 다음으로 생각한게.. '이걸 자동으로 해주는 툴이 있지 않을까?' 하는 것...

구글에서 검색을 해보니 스택오버플로우 에서 비주얼 스튜디오에 딸려나오는 xsd.exe을 이용하면 이게 가능하더다군요:
뭐 결국 제가 실행해야할 컴맨드라인 명령은 다음 두 줄 뿐이었습니다.

xsd test.xml /classes -> test.xsd
xsd test.xsd /classes -> test.cs

첫째 라인은 XML 파일로부터 XML 스키마 파일을 만드는거구요. 두번째 라인은 스키마 파일로 부터 C# 클래스를 생성하는 겁니다. 일단 test.cs 파일을 만든 뒤에 제가 할 일은 C#에서 자체적으로 지원하는 XML Serializer를 이용해서 C# 개체로 모든 데이터를 읽어오는 게 전부였죠. 대충 이렇게 보이는 코드입니다.

XmlSerializer x = new System.Xml.Serialization.XmlSerializer(typeof(test));
FileStream stream = File.Open(@"test.xml", FileMode.Open);
var a= (test)x.Deserialize(stream);

매우 간단하죠? 저도 너무 간단해서 놀랐어요 -_-;;; 근데 불행히도 XML 파일에서 제가 원하던 키프레임 정보를 읽어올 수 있어서..... 결국 사용하지 않았죠.. 뭐든간에... 알아두면 유용한 지식!


2012년 6월 16일 토요일

Unity는 C#으로 작성했다?

요즘들어 Unity가 인기를 많이 끌고 있고, 저도 어쩌다 유니티를 주무르는 동안에 발견한 내용들을 짧게짧게 블로그에 올려볼 생각입니다. 유니티를 체계적으로 배우시고 싶다면 아마 게임개발포에버에서 도플광어님이 연재하시는 글을 보시는게 젤 좋을겁니다.

자, 그렇다면 오늘의 짧은 정보는... 과연 Unity는 어떤 언어로 작성되었나? 입니다. Unity가 Mono를 이용해서 .NET 프레임워크를 자체적으로 지원하고, 저희가 유니티에서 사용할 수 있는 스크립트 언어도 C# 이다보니

'유니티가 C#으로 작성 되었구나. 그럼 이 엔진이 속도가 너무 느리지 않을까?'

라는 걱정을 하는 분들이 계실겁니다. (저도 그랬다는..)

그래서 그에 대한 답을 좀 찾아보니 영문 질답란에 이미 유니티 개발자가 답을 해놨더군요.


이걸 대충 번역하면...

"유니티는 C++로 작성되었으나 다음과 같은 예외가 있습니다.


.NET API를 노출시켜서 여러분들이 C++로 게임을 짤 필요를 없게 만든 것 뿐이지요. 자바스크립트나 C# 또는 Boo를 이용해서 코딩을 할 수 있는 이유가 바로 .NET API 덕분입니다.


에디터 프로그램의 UI는 C#으로 작성했습니다. 유니티를 사용하시는 게임 개발자들에게 공개된 API와 거의 동일한 API를 쓰지요. (아직 공개되지 않는 API를 조금 쓰기도 합니다만 그리 많진 않습니다.)


이것이 바로 "UnityEngine.dll 파일을 다른 C# 프로젝트에서 사용할 수 있나요?"라는 질문에 대한 답이 언제나 "아니요"인 이유입니다. UnityEngine.dll에는 거의 아무 기능도 들어있지 않습니다. 이건 그저 C#/자바스크립트 호출들을 C++ 엔진에 연결해줄 뿐입니다. C++ 엔진이 없이는 그냥 속빈강정이랄까요?"



한마디로 대충 정리하면:

  • 유니티 내부는 C++이여서 C#보단 성능이 빠릅니다. 단, C#/C++ 컨텍스트 스위치 되는 부분에서 약간의 성능저하는 있을수 있습니다만 언리얼엔진에 비해 크게 문제는 안될거 같습니다.
  • 유니티 에디터는 C#으로 작성되어 있습니다. (에디터에서 리플렉션을 자동으로 지원해주는것도 이때문일듯...)
  • 유니티를 사랑합니다.. (응?)


참조: