페이지

2013년 8월 9일 금요일

C++ 개발자를 위한 Visual Studio 2013 - #1

이 글은 아래 글을 번역한 글임을 밝혀둡니다. 번역이 매끄럽지 않을 수도 있습니다. 잘못된 부분이 있다면 댓글로 남겨주세요.

원문 링크

2013년 7월 18일 목요일

내가 알아둬야 할 것들

이제 중급 레벨의 프로그래머 정도 되면 프로그래밍 언어만 알아서는 안된다는걸 자동적으로 느끼게 된다. 일단 나보다 실력이 좋은 사람들이 마구마구 아는 척을 해대면 무슨말인지 모르고 멍때리고 있을 순 없잖아!

내가 아직 제대로 이해하지 못하는 있는 것 중 하나가 CPU cache 에 관련 된 것이다.
뭐 대충 access memory 시간을 줄이기 위한 것이란건 알겠는데, 그래서 이게 어떻게 동작을 하는지 또 이게 문제가 생길 수 있는 여지가 뭔지 또는 이걸 이해하므로써 어떤 이득을 얻을 수 있는지 알고 싶어졌다.. 마구마구

그러던 중 오늘 아주 좋은 동영상을 발견해서 소개하려고 한다. 영어라는 점이 아쉽지만 내용은 정말 좋다.

[동영상 링크]



2013년 7월 16일 화요일

Unity Shader 의 Time 값


유니티에서 셰이더 코드를 짜는데 Time값을 _Time을 사용한다. 근데 이 time 값이 float4 타입인데 각 element가 아래 보는거와 같이 x = t/20, y = t, z = t*2, w = t*3 이따위다...

float4 _Time : Time (t/20, t, t*2, t*3)

왜 저렇게 값을 넣어놨는지 모르겠지만 처음에 저것 때문에 계산이 제대로 안되서 해멨음.. 신발

[참고] http://docs.unity3d.com/Documentation/Components/SL-BuiltinValues.html

2013년 7월 9일 화요일

C# 에서 readonly 와 const 의 차이

readonly 는 선언만 해도 된다. (생성자 함수내에서만 초기화 가능)
const 는 선언과 동시에 값을 설정해야 한다.

class Foo
{
    public readonly float foo1;
    public const float foo2 = 0.25f;

    Foo()
    {
             foo1 = 0.1f;
    }
}

2013년 6월 27일 목요일

AOT 컴파일러 와 Generic type 사용

사용 언어: C#

오늘 문제가 됐던 버그가 iOS버전으로 빌드를 하니 제네릭 클래스 타입을 사용하는 부분에서 crash가 생겼다. 안드로이드 버전에서는 문제가 안됐었는데 iOS버전에서만 crash가 나는 것이었다.

처음엔 원인을 몰라 당황했는데 유니티 트러블 슈팅 관련 문서를 읽어보니. 눈에 띄는 것이 있었다. "AOT": 현재 XCode에서 사용하는 컴파일 방식인데 이 방식은 JIT랑은 다른 형식의 컴파일 형태이다. 두 컴파일 방식에 대한 차이는 이곳 에서 확인 할 수 있다.

AOT방식에 사용제한이 있는데 generic parameter을 사용할때 explicit 한 type만 사용가능하다. 그래서 현재 프로젝트에서 generic type 을 generic container에 인자로 사용하자 크래쉬가 발생한 것이다.

해결한 방법으로는 현재 레퍼런스 타입을 받으므로 Systme.Object 를 인자로 받게 하고 다시 값을 얻어올때 generic type으로 캐스팅하도록 하였더니 이 문제를 해결 할 수 있었다.

문제가 됐던 코드 방식은 이것:

public class Test<T> : MonoBehaviour where T : Test<T>
{
        private Dictionary<int, T> m_test = new Dictionary<int, T>();

        public void foo()
       {
               Test<T> test = m_test[0];
       }    
}


해결 코드는 이것:

public class Test<T> : MonoBehaviour where T : Test<T>
{
        private Dictionary<int, System.Object> m_test = new Dictionary<int, System.Object>();

        public void foo()
       {
               Test<T> test = (Test<T>)m_test[0];
       }    
}


2013년 6월 22일 토요일

C++ 생성자 함수에서 virtual 함수를 호출 할 수 있을까?

이 글은 c++에서 왜 생성자 함수내에서 가상함수 호출을 하면 안되는가 대한 이유를 설명하는 글입니다. 아래 링크의 내용을 참고하였으나 제 마음대로 해석한 내용이므로 원본의 문장과는 다를 수 있습니다.

http://www.stroustrup.com/bs_faq2.html#vcall


답은 호출 할 수 있다. 하지만 사용하는데 조심해야 한다.
예상치 못한 일이 발생 할 수 있기 때문이다. 생성자 함수 내에서 가상함수를 호출하는 메카니즘은 사실 무의미하다. 왜냐면 파생클래스로부터의 오버라이딩이 아직 발생하지 않았기 때문이다. 모든 객체들은 "base before derived" 라는 기본 개념을 기반으로 설계 되어진다.

아래 예제 코드를 보자.

 #include<string>
 #include<iostream>
 using namespace std;

 class B {
 public:
  B(const string& ss) { cout << "B constructor\n"; f(ss); }
  virtual void f(const string&) { cout << "B::f\n";}
 };

 class D : public B {
 public:
  D(const string & ss) :B(ss) { cout << "D constructor\n";}
  void f(const string& ss) { cout << "D::f\n"; s = ss; }
 private:
  string s;
 };

 int main()
 {
  D d("Hello");
 }
 
출력 결과:
 B constructor
 B::f
 D constructor

D::f 가 아니다!! 어찌보면 너무 당연해보이지만.. 만약 B::B()로부터 D::f()가 호출되기 위해서 그 규칙이 다르다면 어떨지 생각해봐라. D::D() 생성자는 아직 실행되기도 전이기 때문에 D::f() 함수는 초기화 되지도 않은 string s를 할당하려고 할 것이다. 그 결과는 거의 곧 바로 크래쉬가 날 것이 뻔하다.

소멸자는 "derived class before base class" 을 기반으로 한다. 그래서 가상함수들은 생성자함수 내에서 처럼 동작한다. 오직 로컬 정의들만 사용되어진다. 그리고 현재 지워진 파생된 클래스를 건드리지 않기 위해 호출을 하지 않는다.

그동안 이것은 구현상의 제약사항으로 제안되어져 왔다. 하지만 그렇지 않다. 실제론 일반 함수내에서 가상함수를 호출하는 것처럼 생성자에서 호출하는 것은 안전하지 않은 규칙으로 여기는 것이 더 쉬울것이다.

C++ 생성자 함수가 가상함수가 될 수 있을까?

이 글은 c++에서 왜 생성자 가상함수가 허용되지 않는가에 대한 이유를 설명하는 글입니다.
아래 링크의 내용을 참고하였으나 제 마음대로 해석한 내용이므로 원본의 문장과는 다를 수 있습니다.

http://www.stroustrup.com/bs_faq2.html#virtual-ctor


답은 될 수 없다.

왜냐면 virtual 함수의 역할은 정해진 정보를 주기 위한 것이다.
OOP 에서 "virtual"의 기능은 확실한 타입을 모르는 객체의 인터페이스를 알게 해주기 위한 것이다.


하지만 하나의 객체를 생성하기 위해선 완전한 정보가 필요하다. 어떤 타입의 객체를 생성하려고 하는지 말이다. 그렇기 때문에 논리적으로 생성자 함수는 virtual 이 될 수가 없다.

간접적으로 생성자 함수를 virtual 로 호출하는 방법은 물론 있다.

아래의 예를 보자.


 struct F { // interface to object creation functions
  virtual A* make_an_A() const = 0;
  virtual B* make_a_B() const = 0;
 };

 void user(const F& fac)
 {
  A* p = fac.make_an_A(); // make an A of the appropriate type
  B* q = fac.make_a_B(); // make a B of the appropriate type
  // ...
 }

 struct FX : F {
  A* make_an_A() const { return new AX(); } // AX is derived from A
  B* make_a_B() const { return new BX(); } // BX is derived from B
 };

 struct FY : F {
  A* make_an_A() const { return new AY(); } // AY is derived from A
  B* make_a_B() const { return new BY(); } // BY is derived from B
 };

 int main()
 {
  FX x;
  FY y;
  user(x); // this user makes AXs and BXs
  user(y); // this user makes AYs and BYs

  user(FX()); // this user makes AXs and BXs
  user(FY()); // this user makes AYs and BYs
  // ...
 }

위 예제는 factory 패턴의 다른 형식이다. 여기서 요점은 user()가 AX 와 AY와 같은 클래스들에 대해 전혀 모른다는 것이다.



2013년 5월 21일 화요일

struct 와 class In C#

C++의 경우 struct와 class의 차이는 선언된 변수나 함수가 기본적으로 public 이나 private이냐의 차이이다. class의 경우 private이 기본으로 선언된다. 이는 C++ 언어의 encapsulation 의 메카니즘에 의한 차이로 보인다.

그럼 C#에서 struct와 class 의 차이는 뭘까..

C++에서와 달리 두가지 모두다 private 으로 기본 선언된다.
또한 Structs는 파라미터가 없는 Constructor를 가질 수 없다.

// 구글을 검색하면 아래와 같은 글이 많이 보인다.. 하지만 몇가지 아티클과 책을 읽은 후 이부분에 의심이 생기기 시작함... 흠..

  • struct의 경우 stack 에 저장되고 class의 경우 heap에 저장된다.


저 말은 struct가 value type이기 때문에 stack 에 저장된다고 하는거 같지만.
아래 글을 보면 의심이 들기도 한다. 단지 value type이라고 해서 stack에 저장되지 않는다는 말이다. 메모리의 어떤위치에 저장 될 것인가는. 변수의 life time에 따라 컴파일에서 정해진다고 한다. 그래서 일반적으로 (MS VS의 경우) 임시변수나 로컬변수가 stack 에 저장된다는 것이다.
http://blogs.msdn.com/b/ericlippert/archive/2010/09/30/the-truth-about-value-types.aspx

struct로 생성 할 경우 memory fragmentation 의 경우도 발생되지 않을 것 같고.. Garbage Collection의 호출도 줄 일 수 있을거 같다...고 생각했지만 지금은 이부분도 좀더 생각해봐야겠다..;;

여기서 분명히 다른 점은 struct의 경우 함수 parameter로 넘길때 pass by value로 인식하기 때문에 reference 로 값을 넘겨야한다. 반대로 class의 경우 pass by reference 로 넘어간다.

아래 struct가 pass by value로 처리되는 예제이다.



public struct TestStruct
{
    public int value;
}

public class TestClass
{
    void function(TestStruct ts)
    {
        ts.value = 5;
    }

    void main()
    {
        TestStruct ts = new TestStruct();
        ts.value = 10;

        function(ts);

        Console.WriteLine("{0}", ts.value);
    }
}


Output: 10

이로인해 생기는 문제가 있는데..


public struct TestStruct
{
    public int value;
}

public class TestClass
{
    void function(TestStruct ts)
    {
        ts.value = 5;
    }

    void main()
    {
        TestStruct ts = new TestStruct();
        ts.val = 10;

        List<TestStruct> list = new List<TestStruct>();
        list.Add(ts);

list[0].val  = 5;

        Console.WriteLine("{0}", list[0].val);
    }
}

위에서 처럼 List에 Add했을 경우 임시변수에 값을 할당하게 되어 에러가 발생한다.

간단히 해결하는 방법은



void main()
{
    TestStruct ts = new TestStruct();
    ts.val = 10;

    List<TestStruct> list = new List<TestStruct>();
    list.Add(ts);    

TestStruct temp = new TestStruct();
temp.val = 6;
list[0]  = temp;

    print(list[0].val);
}

메모리를 새로 할당해서 값을 셋팅한후 넘겨주거나
Custom Container를 만들어서 operator = 오버라이딩을 할까 했으나...-_-

msdn에 문서에서 = 은 오버라이딩을 할 수가 없다...oTL

http://msdn.microsoft.com/ko-kr/library/8edha89s(v=vs.71).aspx

뭐 그래서 위에처럼 처리하면 될 것 같다...털썩..

아 그리고 오버라이딩이 안된다면 Deep Copy를 이용해 하는 방법도 있을듯... 이건 직접 해보지 않아 아직 확실하진 않다..-_-ㅋ


어제 저걸 고민하다가 Unity3D에서는 struct를 어떻게 사용했나 궁금해서 봤더니..
Vector가 struct로 구현되어 있었다.. 반면 GameObject의 겨우 extern 의 외부 객체들을 들고 있어 class로 구현되어 있는 것을 볼 수 있었다..




2013년 5월 20일 월요일

Visual Studio 와 Mono Develop 의 소스코드 인코딩 방식

개인적으로 MS Visual Studio가 편하기 때문에 주로 VS로 소스코드를 작성하고 있다. 하지만 Unity에서 디버깅을 하기 위해선 Mono 역시 버릴 수 없는 상황인지라.. 두 에디터를 동시에 사용하고 있는데.. 두 에디터의 소스코드 저장방식이 달라 몇가지 불편한 점이 있다.

<Line Einding 방식>
Visual Studio : Windows (CR LF)
Mono : Unix (LF)

<소스코드 인코딩 방식>
Visual Studio : Korean - Codepage 949
Mono : UTF-8

위와 같이 되어있기때문에 VS에서 한글 주석을 달으면 Mono에서 한글이 깨져나오게 된다.
그래서 한가지 방식으로 통일하였는데.. 대부분 Unity 에디터에서 파일을 생성하기 때문에 Mono방식으로 통일하기로 했다.
또한 Codepage 949 방식은 MS가 개발한 한글 인코딩 방식으로 EUC-KR과 비슷한 방식이다. 하지만 Mono에서는 Codepage 949 형식을 지원하지 않기때문에 충돌이 날 수 있다.

결론은 UTF-8방식으로 인코딩을 설정하면 된다.

VS에서 설정을 바꾸려면 프로젝트가 열린 상태에서

FILE > Advanced Save Options 에서 Encoding: Unicode (UTF-8 with signature) - Codepage 65001
Line endings: Unix(LF)

p.s: 인터넷 서핑하다 발견한 한가지 사실: Unix(LF)로 저장할시 Visual Studio에서 break point가 잘 안 잡힐 수 있다고 한다. 내경우엔 현재 VS로 디버깅 할 일이 없어.. 그냥 쓰기로 함.

위와 같이 설정하더라도 매번 소스파일을 생성할때 기본값으로 생성되므로 매크로를 이용해 UTF-8파일을 생성하고 싶다면 아래 링크를 참고하면 될 것 같다.

http://nving.tistory.com/66

2013년 1월 23일 수요일

Generic Tuple in C# without .NET 4.0

Unity3D에서 Tuple을 사용해보려니 .Net 2.0 에서는 지원이 안된다.

그래서 직접 만들어보기로 함..

namespace를 내 이름으로 하고 사용하니 왠지 기분이 좋아졌다~ (이런 뵨태기질;;)

어쨋든~! Tuple은 여러가지 데이터형을 순열로 사용할때 정말 유용한거 같다.

앞으로 자주 사용할 듯~ (아래 코드의 안전성은 보장 못함...)



// Generic Tuple

using System;
using System.Collections.Generic;

namespace Claire
{  
    public class Tuple<T1, T2>
    {
        public T1 First { get; private set; }
        public T2 Second { get; private set; }
        internal Tuple(T1 first, T2 second)
        {
            First = first;
            Second = second;
        }
    }

    public class Tuple<T1, T2, T3>
    {
        public T1 First { get; private set; }
        public T2 Second { get; private set; }
        public T3 Third { get; private set; }
        internal Tuple(T1 first, T2 second, T3 third)
        {
            First = first;
            Second = second;
            Third = third;
        }
    }

    public static class Tuple
    {
        public static Tuple<T1, T2> Create<T1, T2>(T1 first, T2 second)
        {
            var tuple = new Tuple<T1, T2>(first, second);
            return tuple;
        }

        public static Tuple<T1, T2, T3> Create<T1, T2, T3>(T1 first, T2 second, T3 third)
        {
            var tuple = new Tuple<T1, T2, T3>(first, second, third);
            return tuple;
        }
    }
}


// how to use

using Claire;

Tuple<int , float , string > doSomthing()
{
     Tuple< int, float, string> tuple = new Tuple< int, float, string>(8, 0.5f, "hello");
     return tuple;
}

void Example () {

        Tuple< int, float, string> tuple = doSomthing();

        var item1 = tuple.First;
        var item2 = tuple.Second;
        var item3 = tuple.Third;
}

2013년 1월 2일 수요일

Unity3D: Texture를 돌리고 돌리고~

이 글은 제 전 티스토리 블로그에서 옮겨 왔습니다.


2D 텍스쳐를 어떻게 돌릴까?
현재 Unity3D의 기본 GUI는  GUIUtility.RotateAroundPivot 함수로 GUI를 회전시킬수가 있다. 하지만 OnGUI()함수 자체가 이벤트 발생시에만 호출되기 때문에 실시간 Rotation Animation을 표현하기엔 부족하다.

그래서 직접 2D Texture Mesh를 생성해서 Texture을 회전 시켜봤다.
Material 회전 시키는 코드는 완전 초간단하다. Unity Documents 에서 Material.SetMatrix() 함수에 대한 예제를 참고하면 된다. 여기

위에 링크에서 부족한 부분은 Texture의 Alpha처리 부분이 없다는 것이다. 이 부분은 Unity3D 내장 쉐이더인 Unlit-Alpha.shader 코드를 참고하면 된다. Light의 적용을 받을 필요없는 2D Texture를 위한 것이다.

이제 남은 문제는 회전이 되는 Texture의 Pivot이 맞지 않는다는 것이다. 내가 원하는 것은 중점을 기준으로 돌고 도는 텍스처이기 때문... 원점을 기준으로 도는 텍스쳐를 원한다면 위에 코드로 충분할지도...

여기서 먼저 Unity엔진의 UV좌표를 이해해야 한다. 일반적으로 다른 엔진에서의 UV좌표는 아래와 같다.



하지만 Unity3D의 UV좌표는 좀 다르다. (왜 그런지는 나도 모르겠음;;;)
그래서 텍스쳐의 좌표계를 생각해 UV를 맞춘다면 낭패다. Unity의 UV좌표계는 아래와 같기 때문이다.



여튼 좌표계가 저따위니 적절하게 UV를 이동한 후 Material에서 position을 offset 만큼 이동시켜 회전시키면 된다.
(일단 다른 방법은 잘 몰라 이렇게 했음..-_-;;)


보통 Sprite Animation을 할때 기준점으로 부터 이미지 사이즈만큼 Frame별로 그려주면서 Animation을 하는데 그걸 이용하면 간단하게 이동시킬 수 있다.

여기서 Mesh에 직접 접근하기 위해 MeshFilter와 MeshRenderer를 생성하고 몇가지 설정을 한다.
view source
print?
1.mesh_renderer.castShadows = false;
2.mesh_renderer.receiveShadows = false;
3.mesh_renderer.sharedMaterial = material; // 회전시킬 material 설정

그리고 2D 텍스쳐를 찍어줄 카메라로 부터 해상도 값을 계산해 온다. (화면 높이값 적용)
view source
print?
1.float pixelPerWorldUnit = Camera.mainCamera.orthographicSize * 2 / defCameraPixels;




생성한 mesh의 vertices값에 이를 곱해 해상도를 적용시켜 줄 수 있다. 마지막으로 offset이 적용된 uv값을 아까 좌표계에 맞게 설정해주면 끝!! 이 아니고;;; 생성한 최종 메쉬를 Mesh Filter에 적용시켜줘야 끝난다.
view source
print?
01.mesh.vertices = new Vector3[] {
02.new Vector3(0, -texHeight) * pixelPerWorldUnit,
03.new Vector3(0, 0) * pixelPerWorldUnit,
04.new Vector3(texWidth, -texHeight) * pixelPerWorldUnit,
05.new Vector3(texWidth, 0) * pixelPerWorldUnit
06.};
07. 
08.mesh.triangles = new int[] { 0, 1, 3, 0, 3, 2 };
09. 
10.// Assign the basis of the uv coordinate to the uv of texture
11.mesh.uv = new Vector2[] {
12.new Vector2(topLeft.x, topLeft.y),
13.new Vector2(topLeft.x, topLeft.y + 1f),
14.new Vector2(topLeft.x + 1f, topLeft.y),
15.new Vector2(topLeft.x + 1f, topLeft.y + 1f)
16.};
17. 
18.mesh.Optimize();
19.mesh.RecalculateNormals();
20.mesh.RecalculateBounds();
21.meshFilter.sharedMesh = mesh;





Unity Editor에서의 설정 모습은 아래와 같다.




offset 을 (-0.5, -0.5) 로 설정한 이유는 아까 Unity의 UV좌표계가 왼쪽 아래부터 (0, 0) 좌표이기 때문이다. 더 간단한 방법이 있는지는 나도 모르겠지만 일단은 이렇게 하는걸로~-_-ㅋ