페이지

2012년 12월 25일 화요일

Game Object's transform and rigidbody in Unity3D


transform 과 rigidbody의 경우는 게임을 구현하면서 가장 많이 쓰이는 변수들 중 하나이다.
이것들을 매번 함수 내에서 특히 Update()내에 호출을 하게 되면 Unity의 경우 검색 비용이 꽤 비싸서 그다지 추천하지 않는다.
그래서 Awake()단계에서 private 변수로 저장해 놓고 쓰는 것을 권장하는데 나의 경우는 하나의 GameObject에서 여러개의 클래스를 Component 기반으로 사용하기 때문에 이것도 비효율적이라고 생각하여 Property라는 Class에서 아예 이것들을 저장 해놓고 Singleton으로 static 하게 접근하여 사용한다.

예를 들어

public class Properties MonoSingleton <Properties >
{
    public Transform thisTransform;
    public Rigidbody thisRigidbody;

    //*
    //  other properties
    //*

    // This function is called when the instance is used the first time
    // Put all the initializations you need here, as you would do in Awake
    public override void Init()
    {
        // ...

        thisTransform = transform;
        thisRigidbody = rigidbody;
    }

}

public class OtherComponent MonoBehaviour
{
    public void Update()
    {
        Properties .Instance.thisTransform.Translate(Vector3 .up * Time .deltaTime);
    }
}

public class AnotherComponent MonoBehaviour
{
    public void Update()
    {
        Properties .Instance.thisTransform.Translate(Vector3 .up * Time .deltaTime);
    }
}

일단 매번 Awake()내에서 저장 할 필요도 없고 검색 비용도 줄인다. 소규모 프로젝트라면 절약 비용이 미비하겠지만 코드가 좀 더 깔끔해진다는 것 만으로도 좋지 않을까 싶다. 아니면 말고..ㅎㅎ


p.s: 단, 주의 할 점은 Properties가 singleton이기 때문에 Clone 객체들에서는 사용하지 말아야 한다.

2012년 12월 23일 일요일

Singleton in C#


가장 많이 쓰이는 디자인 패턴중 하나인 Singleton C#에서 어떻게 구현하는지 알아보자.

Singleton 매우 간단하지만 경우에 따라 구현방법이 여러가지가 있다
멀티스레드를 고려하지 않은 방법늦은 로딩 인스턴스스레드에서 안전한 인스턴싱그리고 간단하지만높은 성능의 방법등..

이러한 방법들이 가지고 있는 공통적인 특징들은 아래와 같다.
  • 다른 외부 클래스에서 인스턴싱 되는것을 막아준다또한 파생 클래스에서도 인스턴싱   없다.왜냐면 Constructor private으로 선언되기 때문이다.
  • 그리고 싱글톤 클래스는 sealed class 구현된다엄격히 말하면 이것은 불필요하다왜냐면 위에서말한데로 외부에서 인스턴싱되거나 파생이 불가능하기 때문이다하지만 JIT(just-in-time compiler)-인터프리트 방식과 정적 컴파일 방식을 혼합한 것임.-  최적화 하도록 도와줄수도 있다.
  • static변수는 생성된 인스턴스를 하나의 레퍼런스로만 사용되도록 한다.
  • public static 인스턴스를 생성한 클래스의 싱글 레퍼런스를 얻는다는 의미이다.

참고로 모든 싱글톤 구현방식은 접근하기 위한 public static Instance  사용하고 있다. 


not thread-safe

// Bad code! Do not use!
public sealed class Singleton
{
    
private static Singleton instance=null;

    
private Singleton()
    {
    }

    
public static Singleton Instance
    {
        get
        {
            
if (instance==null)
            {
                instance = 
new Singleton();
            }
            
return instance;
        }
    }
}

위에 방식은 멀티스레드 상에서는 안전하지 않은 방법이다다른  스레드가 동시에  인스턴스에 접근하게 된다면 if (instance==null) 를 true로 판단할 수 있다.  


Static Initialization
디자인 패턴에서 static Initialization을 피하는 이유중에 하나가 C++ 의 설계에서 static 변수들의 초기화가 모호성을 남기기 때문이다. 

static변수의 경우 어플리케이션이 시작할때 BSS segment에 할당되는데 변수들간의 할당 순서가 규칙이 없기때문이다. 하지만 .NET Framework에서는 변수의 초기화를 핸들링하므로써 이러한 모호성이 해결되어졌다.

public sealed class Singleton
{
   private static readonly Singleton instance = new Singleton();
  
   private Singleton(){}

   public static Singleton Instance
   {
      get
      {
         return instance;
      }
   }
}

위에 코드에서는 처음으로 참조하는 클래스의 멤버가 인스턴스를 생성하도록 구현되었다. 
CLR(The Common Language Runtime)에서 변수의 초기화를 관리한다. 

sealed class는 이 클래스가 다른곳에서 인스턴스가 추가되지 않도록 파생되는 것을 막아준다. 
그리고 readonly 변수는 오직 static initialization 동안에만 할당되도록 할 수 있다. 
다시말해 런타임시 이 변수에 다른 인스턴스로 초기화 할 수가 없다는 얘기이다.

이 구현방식은 앞서 예제와 비슷한다. 변수를 초기화가 CLR방식에 의존하고 있는거 빼고는... 
싱글톤이 가지고 있는 두가지 문제를 해결해주고 있다. 
첫째는 전역적 접근이 가능하다는 것과 둘째는 인스턴싱을 핸들링하므로써 늦은 초기화가 가능하다는 것이다. 
위에서 보는 바와 같이 이 Singleton instance는 private static 멤버변수로 참조되어지기 때문에 어떠한 클래스가 이를 호출하기 전까진 인스턴싱되지 않을 것이다. 
이 방식은 디자인패턴에서의 lazy instantiation 의 형태를 보여준다. 

또한 디자인 패턴에서 말하는 초기화 모호성을 피하기 위해 nondefault constructor를 사용하거나 초기화 되기전 다른 task들을 수행해야했다. 
하지만 .NET Framework 에서는 이러한 문제를 해결하기 위한 initialization을 수행하기때문에 위와 같은 옵션을 사용 할 필요가 없다. .NET의 static initalization을 싱글톤을 구현하기 알맞은 접근방법을 가지고 있다.

참고 사이트:
http://msdn.microsoft.com/en-us/library/ff650316.aspx
http://csharpindepth.com/articles/general/singleton.aspx 

Multithreaded Singleton
아래의 코드는 멀리스레드 환경에서도 안전성을 위해 구현된 방식이다. 글 쓰다 지쳐서 걍 코드만 남김..;;;
더 자세한 내용은 아래 참고사이트를 보면 됨.(-_ -ㅋ)
using System;

public sealed class Singleton
{
   private static volatile Singleton instance;
   private static object syncRoot = new Object();

   private Singleton() {}

   public static Singleton Instance
   {
      get
      {
         if (instance == null)
         {
            lock (syncRoot)
            {
               if (instance == null)
                  instance = new Singleton();
            }
         }

         return instance;
      }
   }
}


Singleton for Unity 3D

Unity에서 Singleton 패턴을 사용 할 경우 Unity 엔진의 특성에 맞게 신경 써야 할 것들이 있다. 하지만 기본적은 lazy instantiation 같은 개념은 동일하다. 또한 Generic 형식으로 사용하게 되면 원하는 클래스에 상속만 해주면 간단히 Singleton을 만들 수 있다.

아래 링크에 그에 대한 내용들이 있다.



2012년 12월 18일 화요일

StartCoroutine 과 InvokeRepeating


Unity엔진에서는 쉽게 사용 할 수 있는 두 가지 방식의 Coroutine함수를 제공한다.

Coroutine 에 대한 자세한 내용은 wikipedia 참조: http://en.wikipedia.org/wiki/Coroutine#Implementations_for_C.2B.2B

간단히 말하면 Coroutine은 여러개의 서브루틴을 생성할 수 있게 해준다. 하나의 함수에 여러개의 엔트리포인트를 둘 수 있다. 즉 하나의 함수내에서 원하는 리턴 포인트를 찍고 외부로 수행 권한을 넘긴다. 그런 후 조건이 충족되면 다시 넘겼던 포인트로 되돌아오게 된다. 이는 실행 상태를 지연시킬 수 있어 유용하며 멀티스래드와도 많이 비교되는 부분이다.
하지만 스래드의 경우 우선권에 따라 스케줄링이 되거나 Lock을 걸어 우선권을 줘야 하며 구현하는 것도 복잡한 편이고 여러가지 문제를 야기 시킨다. 반면 Coroutine은 특정 포인트에서만 스케줄링이 되며 lock을 걸지 않으며 동기화 하지 않는다.

어쨋든 내가 이 글에서 말하려고 하는 것은 이러한 Coroutine을 Unity엔진에서 간단하게 몇가지 함수로 제공해주고 있어 그것들은 좀 살펴보자는 것이다.

두 가지가 있는데 StartCoroutine과 Invoke이다.
StartCoroutine의 경우 직접 entry point를 지정하여 리턴할 수 있고, Invoke의 경우 함수 이름과 delay시간을 주어 함수내부에서 delay 하여 실행시켜 준다. 만약 좀 더 세심한 조작을 하고 싶다면 Coroutine함수를 작성하여 StartCoroutine을 이용해 작동 시키면 된다.

두 가지 방식 모두 사용이 편리하며 필요에 맞게 쓰면 되는데, 아래 간단한 예제를 보자.


For StartCoroutine(): 

Enemy Respawn 시간을 지연하는 예제

       void Start () 
       {
         StartCoroutine(SpawnAfterSeconds());
    }

       IEnumerator SpawnAfterSeconds()
    {
        while ( true)
        {
            yield return new WaitForSeconds (Random .Range (minInterval, maxInterval));

            ThrowUpEnemy();
        }
    }

위와 같이 Coroutine함수 내부에서 지연시간을 임의로 지정 할 수 있다. 또한 아래와 같은 것도 가능하다.

       void Start () 
       {
         StartCoroutine(DoSeveralTimes());
    }

       private IEnumerator DoSeveralTimes()
    {
        while ( true)
        {
            yield return new WaitForSeconds (Sometime));

            DoSomething();

                    yield return new WaitForSeconds (Sometime));

                    DoAnotherthing();

                    yield return new WaitForSeconds (Sometime));

                    DotheLastThing();
        }
    }



For InvokeRepeating(): 

Enemy Respawn 시간을 지연하는 예제

     InvokeRepeating( "ThrowUpEnemy", 1f, Random.Range(minRSInterval, respawnInterval));
     
첫번째 인자는 함수이름이고 두번째는 함수가 실행 될 지연시간이다. 마지막 인자는 함수가 첫 실행 된 후 일정시간 간격으로 반복호출을 하는 시간을 넘겨주면 된다. 
하지만 위의 함수를 실행하면 제대로 동작하지 않는다. InvokeRepeating의 경우 당연히 처음으로 넘긴 파라미터 값의 시간이 적용되기 때문이다.
이것이 StartCoroutine과 InvokeRepeating함수를 구분해서 사용해야 하는 이유이다.

     InvokeRepeating( "ThrowUpEnemy", 1f, IntervalTime);

또한 StartCoroutine이 StopCoroutine이 있듯이 Invoke함수도 CancelInvoke()함수가 있다. 그리고 IsInvoking()함수도 제공한다.
     
p.s: 추가적으로 안 사실하나 더 StartCoroutine의 경우 GameObject의 Active 가 꺼지면 실행이 중단된다. 그래서 만약 대기상태에 있던 코루틴이 중간에 분기점을 잃어버리면 다음 코드를 수행하지 못하게 된다.

반면 InvokeRepeating 함수는 오브젝트의 Active가 꺼져도 수행을 한다.


2012년 12월 9일 일요일

Test







Bullet Physics Library 를 이용해 강체 시뮬레이션을 적용해 보았다. 발목, 팔꿈치, 종아리같은 부분들은 Hinge Constrains 사용했고, 기타 어깨, 골반, 목 등은 Twist Cone Constrains를 연결했다.