반응형

 

 

ReaderWriterLock 은 EnterWriterLock 호출 되기 전까진 EnterReadLock과 ExitReadLock  구간을 lock 없이 읽을 수 있는데 EnterWriterLock  이 호출 되면 이때 lock 이 점유되어 놓아지기 전까지 EnterReadLock ~ ExitReadLock   구간을 다른 스레드가 접근하지 못하게 된다

 

아이템 업데이트를 서버에서 잠깐 처리해야 할때 흔히자읂은 업데이트에서 사용 될 수도 있다

 

 

 

반응형
반응형

SpinLock 사용 예시

 

static SpinLock _lock

 

Enter 에서 현재 lock 을 제대로 잡았는지에 대한 상태를 lockTaken 변수를 통해서 알 수 있게 된다

 

Enter(Boolean) 메서드 호출에서 예외가 발생하는 경우에도 안정적인 방식으로 잠금을 얻으며 잠금을 얻었는지 확인하기 위해 lockTaken을 안정적으로 검사할 수 있습니다.
Exit() 잠금을 해제합니다.
Exit(Boolean) 잠금을 해제합니다.

 

 

//      SpinLock.IsThreadOwnerTrackingEnabled
    static void SpinLockSample2()
    {
        // Instantiate a SpinLock
        SpinLock sl = new SpinLock();

        // These MRESs help to sequence the two jobs below
        ManualResetEventSlim mre1 = new ManualResetEventSlim(false);
        ManualResetEventSlim mre2 = new ManualResetEventSlim(false);
        bool lockTaken = false;

        Task taskA = Task.Factory.StartNew(() =>
        {
            try
            {
                sl.Enter(ref lockTaken);
                Console.WriteLine("Task A: entered SpinLock");
                mre1.Set(); // Signal Task B to commence with its logic

                // Wait for Task B to complete its logic
                // (Normally, you would not want to perform such a potentially
                // heavyweight operation while holding a SpinLock, but we do it
                // here to more effectively show off SpinLock properties in
                // taskB.)
                mre2.Wait();
            }
            finally
            {
                if (lockTaken) sl.Exit();
            }
        });

 

 

 

ref : https://learn.microsoft.com/ko-kr/dotnet/api/system.threading.spinlock?view=net-7.0

반응형
반응형

lock 클래스는 임의로 만든 lock 클래스이고 실제 중요한것은 AutoResetEvent 이다

 

초기 AutoResetEvent 값은 true 였고, 초기 값을 false 로 설정 하는것도 가능하다

 

waitOne() 함수를 만나 상태가 false 가 되면서 락을 소유하게 된다, 그래서 다른 스레드는 더이상 접근하게 못하게 되다가

 

.Set() 을 하는 순간 AutoResetEvent  가 다시 true 상태가 됨으로 이후 부턴 다른 스레드도

waitOne 에서 락을 획득하여 들어올 수 있게 된다

 

 

AutoResetEvent 는 waitOne 에 진입함과 동시에 자신의 상태를 false 로 만든다는 특징이 있다

 

 

 

 

이렇게 코드를 돌릴때 한쪽에서만 공유변수 _num 을 증가 또는 감소 할 수 있게 된다

 

 

 

 

 

ManualResetEvent

이건 문을 자동으로 닫진 않는다 즉 waitOne 에서 lock 을 획득하면 자도으로 ManualResetEvent 상태를 false 로 만들지 않는다는 것이고 이것을 수기로 해줘야 한다

 

ManualResetEvent.Reset();  false 로 바꾸는 함수

 

 

 

이렇게 하면 문제가 발생하는데

reset 과 watiOne 사이에 여러 스레드가 동시에 들어올 수 있기 때문에 정확한 처리가 되지 않는다

 

하지만 의도가 여러 스레드의 입장이 동시에 가능하게 할 것이라면 이 처리는 맞게 된다

 

 

 

ex ManualResetEvet 를 댐 같은것이라 생각하면 예시가 될 수 있는데

 

사용자가 게임 진입 버튼을 누르기 전까지 ManualResetEvet  에 의해 모든 스레드가 대기 하고 있다가

 

버튼 을 누르면 인터넷으로부터 데이터를 받아오거나 로컬 파일을 동시에 읽는다던가 맵을 이동하는 등

여러 스레드들을 버튼 누른 시점 이후부터 동작하게 하기위한 스레드 대기의 용도로 사용 할 수 도 있다

 

 

spinlock 은 유저모드에서 실행되는거라 cpu 타임만 갉아 먹는데

AutoResetEvent, ManualResetEvent 은 커널모드에서 돌아가는 것이기 때문에 context switch 가 일어나게 되어 성능이 유저모드에 비해 떨어 질 수가 있다

 

 

 

 

 

 

커널모드 동기화 중에 다른 것이 Mutex 가 있다

위의  WiatOne(문을 잠금) 으로 lock 을 소유하고 ReleaseMutex(문을 다시 연다) 를 통해서 mutex 의 lock 을 놓아준다

 

 

 

mutex 와 autoResetEvent 의 차이는

 

Mutex 는 lock을  잡을떄 카운팅을 할 수 있고 mutex 는 스레드 id 또한 갖고 있어서 

문제가 있을때 유용한데 추가 비용이 좀 더 들어간다

 

 

 

 

반응형
반응형

스레드 컨텍스트 스위칭을 일으킬 수 있는 명령들

 

반응형

'프로그래밍(Programming) > C#' 카테고리의 다른 글

SpinLock  (0) 2022.11.27
c# AutoResetEvent, ManualResetEvent (커널모드)  (0) 2022.11.27
Interlocked.CompareExchange(ref _lockecd, desired, expected)  (0) 2022.11.27
Deadlock 현상  (0) 2022.11.27
lock 과 Monitor 임계영역  (0) 2022.11.26
반응형

 

int ddd = Interlocked.CompareExchange(ref _lockecd, desired, expected);

 

 

CompareExchange 이걸 풀어보면

 

if( _lockecd == expected )

{

   _lockecd  = desired;

  return expected;

}else{

 return _locked;

}

 

같으면 expected 를 리턴하고

다르면 _locked 를 리턴한다

 

그리고 같으면 _locked 가 desired 값으로 바뀐다

 

 

그래서 아래 코드는 값이 바뀔때까지 계속 실행된다

 

 

 

    // AddToTotal safely adds a value to the running total.
    public float AddToTotal(float addend)
    {
        float initialValue, computedValue;
        do
        {
            // Save the current running total in a local variable.
            initialValue = totalValue;

            // Add the new value to the running total.
            computedValue = initialValue + addend;

            // CompareExchange compares totalValue to initialValue. If
            // they are not equal, then another thread has updated the
            // running total since this loop started. CompareExchange
            // does not update totalValue. CompareExchange returns the
            // contents of totalValue, which do not equal initialValue,
            // so the loop executes again.
        }
        while (initialValue != Interlocked.CompareExchange(ref totalValue, 
            computedValue, initialValue));
        // If no other thread updated the running total, then 
        // totalValue and initialValue are equal when CompareExchange
        // compares them, and computedValue is stored in totalValue.
        // CompareExchange returns the value that was in totalValue
        // before the update, which is equal to initialValue, so the 
        // loop ends.

        // The function returns computedValue, not totalValue, because
        // totalValue could be changed by another thread between
        // the time the loop ends and the function returns.
        return computedValue;
    }

 

주석 설명들에 어떤것과 어떤것이 비교가 되는지 빠져 있어 헷갈린다

ms 홈페이지 설명글은 항상 느끼는 거지만 글자만 있지 내용을 파악하기엔 내용이 부실하게 빠져 있거나 부족하거나 이상한 부분들이 많다

 

 

ref : https://learn.microsoft.com/ko-kr/dotnet/api/system.threading.interlocked.compareexchange?view=net-7.0

반응형
반응형

atomic 을 사용한다고 가시성이 해결 되지 않고 단지

 

동일한 객체에 대해서 동일한 수정순서를 관찰 할 수가 있다

수정 순서란 : 이전에  B 값에서 C 값으로 바뀐 순서를 말하며 B  로 수정순서가 완료됐었을때는 B 이전의 상태인 A 로 되돌아가지 못한다 ( A 가 과거의 값이였다는 가정하에)

 

그래서 atomic 을 사용한다고 해서 가시성과 코드 재배치의 문제가 해결 되지 않는다

하지만...

 

 

 

B  에서 C로 아직 바뀌지 않은 상태는 현재 cpu 에서 캐쉬쪽은 최신 값일순 있어도 아직 메모리에는 이전 값이 있게 되는 현상을 말한다

즉 atomic 을 사용했어도 이 현상이 있기 때문에

 

가시성, 코드 재배치 문제의 해결이 되지 않음 단지 동일한 객체에 대해서 다른스레드 들에서 동일한 수정순서는 관찰 할수 있다

 

 

그런데 atomic 을 사용할대 뒤의 인자 memory_order:.... 를 어떤걸 쓰느냐에 따라

가시성과 코드 재배치의 문제가 해결이 되느냐 안되느냐가 결정이 된다

 

ex : memory_order::memory_order_seq_cst

 

1. Sequentially consistent  (가장 엄격, 최적화 여지 적음)  seq_cst  , default 버전

     가시성, 코드 재배치 문제가 해결된다 , 그냥 atomic 을 쓰면 default 인자로  memory_order::memory_order_seq_cst         이 들어가기 때문에 가시성과 코드 재배치 문제가 해결된다

 

2. Acquire-Release ( acquire , release) : 두개를 써주는데  이 경우에는

 

memory_order::memory_order_release  : 

release명령 이전 메모리 명령들이, 해당 명령 이후로 재배치 되어 들어가는것을 막아준다

즉 release 위에 있는 것들음 메모리 명령 재배치가 일어날 수는 있다는 것

 

그리고 acquire 로 같은 변수를 읽는 스레드가 있다면

memory_order::memory_order_acquire

release 이전의 명령들이 acquire 하는 순간에 관찰 가능 해진다 (즉 가시성이 보장이 된다는 것)

 

acquire 아래 있는 명령들이 acqurie 위로 올라가는 재배치를 막아 준다는 것이면서 

release 이전의 내용들이 acquire 이후에는 모두다 메모리에서 제대로 보여지는 상태가 보장이 되어

가시성이 보장이 된다는 것이다

release 이전의 value = 10 이 acqure 이후에는 value 에서는 가시성이 보장이 acquire 로 되었음으로 

acquire 이후로 정확하게 가시성을 보장받아 value== 10 이 된다

 

그래서 1,3 주간 단계라 할 수 있다

 

 

3) Released (relaxed) : memory_order::memory_order_relaxed

최적화 여지가 많음

     코드 재배치와 가시성이 해결되지 못하고,
     단지 동일객체에 대한  동일 수정순서만 보증해준다 (즉 현재 값 B 과 변경 예상인 값C 들 보다 더 과거 값이(A) 되진 않는다는 것 )

 

 

 

 

 

정리하자면 1,2 번을 쓰고 왠만하면 seq_cst 를 쓰자

 

그런데 Intel, AMD 에서는 seq_cst 를 자체적으로 보장해줘서 seq_cst 를 기본으로 써도 성능차이는 거의 나지 않는다 하지만 ARM  에서는 차이가 발생한다

 

자바에선 volatile키워드로 가시성 문제를 해결하는데 c++ 은 단지 코드 최적화만 막아준다

 


 

 

 

exchange 가 있는 이유는 이 함수로 바꿔야지 prev 에 데이터가 flag 에 변경되어진 값을 스레드 세이프하게 받을수 있음

그렇지 않고 

bool prev = flag ; 이렇게 받으면 prev 의 값과 flag 값이 스레드 타이밍상 이 순간에서조차 다른 값일 수 있다

 

 

 

원자적으로 바꾸는 다른 예시..

compare_change-strong 이 함수는 아래  if 처럼 동작한다

 

반응형
반응형

 

    class SessionManager
    {
        static object _lock_session = new object(); 
        public static void TestSession()
        {
            lock(_lock_session)
            {}
        }
        public static void Test()
        {
            lock (_lock_session)
            {
                UserManager.TestUser(); 
            }
        }
    }
    class UserManager
    {
        static object _lock_user = new object();
        public static void TestUser()
        {
            lock(_lock_user)
            {}
        }
        public static void Test()
        {
            lock(_lock_user)
            {
                SessionManager.TestSession(); 
            }
        }
    }

    class Program
    {
            

        static int number = 0;
        static object _obj = new object(); 

        static void Thread_1()
        {
            for(int i=0; i<100; i++)
            {
                SessionManager.Test(); 
                
            }
        }

        static void Thread_2()
        {
            for(int i=0; i<100; i++)
            {
                UserManager.Test(); 
            }
        }

        static void Main(string[] args)
        {
            Task t1 = new Task(Thread_1);
            Task t2 = new Task(Thread_2);

            t1.Start();
            t2.Start();

            Task.WaitAll(t1, t2);

            Console.WriteLine("데드락에 걸리지 않았다!");
        }
    }

 

 

Thread_1 에서 SessionManger.Test를 호출할 때 _lock_session을 사용해서 Lock을 걸고

이때 동시에 Thread_2 는 UserManger.Test를 호출해서 _lock_user를 사용해서 Lock을 건다.

 

Thread_1은 SessionManger.Test를 통해 UserManger.TestUser를 호출할 때 _lock_user로 Lock을 걸어야 한다.

근데 Thread_2에서 _lock_user를 사용하고 있다보니까 Lock을 걸 수 없고

 

마찬가지로, Thread_2 역시 UserManger.Test를 통해 SessionManager.TestSession을 호출할 때
_lock_session으로 Lock을 걸어야 하는데

Thread_1에서 이미 사용하고 있다보니 Lock을 걸 수 없다 보니

 

데드락에 빠지게 된다.

 

 

 

 

 

번외로, 데드락이 나면 예외처리로 고치기 보다는 차라리 크래쉬를 내는 것이 더 좋다.

Lock 구조에 문제기 있어 데드락이 발생한거기 때문에 그 상황에서 예외처리를 하는 것은 장기적인 관점에서 좋지 않다. 훗날 어떤 예외가 튀어 나올지 모르기 대문이다.

 

또 데드락이 무서운 이유는 조금의 시간차가 있으면 발생하지 않을 수 있기 때문이다.

위 코드에서

t1.Start();

Thread.Sleep(100); 

t2.Start();

t1과 t2의 시작 시간을 다르게 해주면 코드가 정상적으로 작동하는 것을 볼 수 있다.

 

이것은 임시적인 방인이다

 

 

ref :https://velog.io/@kkuoo7/DeadLock

반응형
반응형

 

간단한 기능인데 : on, off 기능으로 에디터에서 default 값을 어떻게 선택하느냐에 따라 on, off 중 하나만 out 으로 내보낸다

 

 


 

여기서 맨 밑의 Keyword는 약간 다른 종류인데, 이 Keyword 하위에 있는 요소들(Boolean, Enum)은 셰이더의 정적 분기를 위한 노드라고 한다.

경우에 따라서 출력되는 게 다른 상황에서, 셰이더의 Variant를 만들어내기 위해서 나온 기능이라고 하는데,

최적화에는 별로 좋지 않다고 하고..

나뉘는 경우의 수마다 다른 셰이더로 쳐 진다고 한다.

 

직접 사용해보자. 우선은 Keyword 아래의 Boolean부터 꺼낸다.

 

이 Boolean은 인스펙터 창에서 체크박스 형태로 나타나게 되는데,

그 체크박스의 체크 여부에 따라서 최종 결과물이 달라지는 형태이다.

즉, 체크박스에 체크가 되어있다면 위에서 On의 결과물이 Out으로 나가게 될 것이고,

반대로 체크가 되어있지 않다면 Off의 결과물이 Out으로 나가게 될 것이다.

 

간단하게 예제를 만들어보자.

아까 위에서 만들었던 곱하기의 결과물이 On으로, 그리고 곱하기를 하지 않은 Sample Texture 2D 상태가 Off로 들어오도록 노드를 연결해준다.

그런 다음 Boolean을 Base Color에 연결해준다.

그러면 이런 모습이 되고,

직접 씬에서 본다면

 

이렇게 Boolean의 On/Off 여부에 따라 결과물이 달라지는 것을 볼 수 있다.

 

 

 

 


Boolean Keywords

Boolean Keywords are either on or off. This results in two shader variants. Shader Graph uses the value in the Reference name field for the on state, and automatically defines the off state as an underscore ( _ ).

To expose a Boolean Keyword in the Material Inspector, its Reference name must include the _ON suffix. For example, BOOLEAN_A506A032_ON.

 

 

ref : https://docs.unity3d.com/Packages/com.unity.shadergraph@8.1/manual/Keywords.html

ref : https://rusalgames.tistory.com/34

반응형

'게임엔진(GameEngine) > Unity3D' 카테고리의 다른 글

C#은 stackless 코루틴  (0) 2022.12.04
Unity vertex / fragment Shader01 -빨강 쉐이더  (0) 2022.12.02
Unity Audio  (0) 2022.11.23
UnityEngine.Random  (0) 2022.11.16
UI 버튼 바인딩  (1) 2022.11.06
반응형

현업 C/C++/STL/3D게임프로그래밍/3D수학/Shader/엔진 통합과정 과외

 

 

3DMP

 

 

수학/프로그램은 샤프를 들기 이전에 상상에서부터 시작되어야 합니다!

 

현업 3D 게임엔진 프로그래머가 전수해드리는 고농축 고퀄리티의 과외를 만나 보실 수 있습니다

 

 

 

 

이력 ]

  • 넥슨, NHN, 넷마블 다수 동종/타종 근무 경험 및 다년간 현업 개발자
  • 3D 게임 엔진 프로그래머/ 3D,2D 게임/ 언리얼 엔진/ 엔진 프로그래머/ 메인 PC 게임 개발 / 모바일 게임/엔진 개발
  • 미들웨어(엔진)/ 일반 프로그램 프로젝트 제작
  • 고차원 수학/ 물리등의 알고리즘과 관련된 특수 프로그램 제작
  • 게임업계 대기업 넥슨,넷마블, NC 외 P사, 중견 B사 등등의 기업에 합격시킨 노하우
  • 다수, 다양한 분들에 대한 다수의 과외 경험으로 축적된 노하우 보유
  • 기타 일반 프로젝트 경험
  • 과외 등록 완료, 남,  성범죄 "완전 무(無)"!!

 

[이직/신입 취직하신 분들]

3DMP 에서 배워 이직/합격/수강하신 분들의 주요 회사들입니다
EA 코리아, 넥슨 코리아, NC, 넷마블, 넷게임즈
크래프톤, 펍지, 펄어비스, 블루홀
스마일게이트, NHN, 기타 등등 ..

 

 

[ 수강하셨던 분들 ]

  • 동종 현업 3N 사 포함/비 동종업계 현업 프로그래머 분들 다수 수강
  • 해외 글로벌 대기업 E* 사, 국내 일반/게임업계 대기업 현업 프로그래머, TA, 아티스트, 기획자
  • 국내 현업 게임 및 비게임 업계 종사자 대기업, 중견, 벤처 회사원과 기업가, 사업가(CEO)
  • 대학생(취준생), 서울대, 고려대, 일본게이오대 등등 SKY 포함 상위 10% 이상 대학생, 그 외 학교 및 유학생
    하위권 에서도 포기하지 않고 노력했을때 대기업에 간 선례들이 많이 있습니다, 포기하지 마세요!
  • 전공자/컴퓨터 관련 전공자가 아니지만 게임이나 프로그램 쪽으로 진로를 희망하시는 분들
    (비전공자 일지라도 많은 상당수의 분들이 원하는 성과를 이루고 가셨습니다)
  • 전문 프로그래머로 진로를 정하시거나 또는 희망하시는 분들
  • 프로그램을 혼자 하기엔 막연하여 그룹(단체) 배워보고자 하시는 분들
  • 초심자, 프로그램 배워보려 했지만 진입 장벽 때문에 포기하신 분들
     

   [ 과외 대상 ]

  • 지식이 전무한 초보 부터 현업 프로그래머 까지 

 

[수업의 목표]

  • 학원 같은 대규모 기업체가 아니기 때문에 개인 강의 여건상 많은 분들을 가르치진 못하지만
    포트폴리오 꼼수가 아닌 확실한 것을 하나하나 이해될 때까지 알려드리려 노력하고
    수강생분들의 앞길에 실질적으로 도움이 될 수 있는 방안을 깊이 있게 고민하고 해결책을 드리려 합니다
  • 진도만을 위한 도서 위주의 수업 또는 깊은 이해가 동반되지 않는 겉핥기로는 진정한 프로그래머
    또는 게임/엔진 프로그래머의 문턱에 조차 도달할 수 없습니다 이해 될때까지 하는 자세가 중요합니다

 

 

  [ 통합 진행 과목 ]

 

방향에 따른 과목들은 상담을 통해 설정됩니다 
추가 과목에 대한 추가 비용은 들지 않으며 희망하시는 과목들을 선정해서 들으실 수 있습니다

  • C언어/C++,C++ 11~,C# 현업 디버깅 스킬 , 시스템 베이스
  • 현업 3D 실전 수학, 일반 수학, 2D수학, 미들웨어, STL 기반 자료구조와 알고리즘
  • 최적화 알고리즘, 대규모 디자인패턴(설계-디자인)
  • 멀티쓰레드 프로그래밍, 기초 물리(Physics), 캐릭터, 지형 엔진 & Tools,
  • 다이렉트X : DirectX9/ DirectX12, Direct 를 통한 실전 수학 활용
  • 그래픽스 : DX shader(HLSL), Unity URP, ShaderLab, 3D 애니메이션
  • 게임 엔진 : Unreal 4, Unity3D
  • 포트폴리오 과정
    [주의] 학교 과제를 의뢰 받지 않습니다

 

[ 과외 기본 방향성 : 개인 맞춤형 최적화 설정]

 

수업은 수강자분의 현재 상태에 맞는 최적화된 맞춤 방식으로 진행되는 것을 원칙으로 하며 모든 과목은

최종적으로는 혼자만의 생각으로 수학/프로그램을 만들어 나가는 것을 진행 시 가장 우선적인 목표 진행합니다

  • 과목은 상담하면서 희망하시는 목표에 맞춰 의논후 함께 설정하게 됩니다
  • 상담을 통하여 수강생의 현재 실력 기준으로 이미 알고 있는 것들은 시간과 비용을 아끼기 위하여
    반복하여 진행하지 않고 부족한 부분은 채워 알아야 하는 부분을 협의후에 목표하시는 방향에 맞춰 진행하게 됩니다
  • 수업은 원격으로 진행, 모든 자료는 수강생의 PC 에 남게 되고 대부분 수강생이 타이핑을 하며 실전 문제를 해결 하는 방식
    과외 기본 목표 : 혼자만의 힘으로 희망하는 수준까지의 프로그램 실력을 쌓는 것을 기본 목표로 합니다

 

 [ 과외 안내 ]


   진행 방식 :  개인 과외 1:1 진행 방식 / 1:N 방식

  • 1 Section 기준 = 4주/4회, 추가 섹션은 및 진행은 별도 문의주시면 답변해드립니다
  • 요일 : 협의
  • 1 회당(1 섹션) 시간 : 1:00, 섹션당 추가에 따른 누적 할인 적용
  • 수강 기간 : 한 달 단위로 수강, 수강생이 희망하는 달까지 진행되며 한달 단위 결제방식입니다
  • 과외 장소/방식 : 원격으로만 진행 (카메라 Off ) 
    원격과 오프라인과의 차이는 다년간 노하우로 거의 없다고 보시면 됩니다
  • 납부 방식 : 한 달 단위, 한달 단위로 연장 하는 방식
  • 그룹 과외  : 그룹(2인 이상은) 별도 문의 , 추가 할인 적용

 

[수강 특전]  : 3DMP 네트워크

  • 현업 게임 프로그래머들과 함께 지속적인 '3DMP 네트워크 데이' 에서 정보 교류활동의 기회

 

 

[문의]

상담 시 최대한 오픈 마인으로 임해주시면 좋은 시너지 효과가 나올 수 있습니다!

상담 전용 카톡 플러스 친구 (별도의 친구 추가 없이 상담 가능)

https://pf.kakao.com/_uxabzK

 

상담 전용 카톡 아이디 : 게임프로그래밍 과외

반응형
반응형

Monitor

이를 위해 Monitor 라는 기능이 존재한다.
사용하기 전 변수를 선언해주자.

static object _obj=new object();

Monitor는 다음과 같이 사용하면 된다.

Monitor.Enter(_obj);
//소스코드
Monitor.Exit(_obj);
using System;
using System.Collections.Generic;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
namespace ServerCore
{
    class Lock_Basic
    {
        //1. 전역변수 메모리 값을 레지스터에 갖고오고, 레지스터 값을 증가시키고, 그 값을 메모리 값에 넣는다
        static object _obj = new object();
        static int number = 0;
        static void Thread_1()
        {

            for (int i = 0; i < 10000; i++)
            {
                Monitor.Enter(_obj);
                number++;
                Monitor.Exit(_obj);
                
            }
        }
        static void Thread_2()
        {
            for (int i = 0; i < 10000; i++)
            {

                Monitor.Enter(_obj);
                number--;
                Monitor.Exit(_obj);

            }
        }
        static void Main(string[] args)
        {
            Task t1 = new Task(Thread_1);
            Task t2 = new Task(Thread_2);
            t1.Start();
            t2.Start();
            Task.WaitAll(t1, t2);
            Console.WriteLine(number);
        }
    }
}

 

이와 같이 실행한다면 증감연산자는 원자적으로 동작하지 않음에도 불구하고 잘 작동하는 점을 확인할 수 있을 것이다.

아시다시피 동시 다발적으로 쓰레드가 공유 변수에 접근시 문제가 발생했고, 이를 우리는Critical Section(임계영역)이라고 첫 시간때 이야기를 하였다.

그러나 Monitor.Enter, Monitor.Exit을 통해 이를 방지하여 이 사이의 코드는 사실상 싱글 쓰레드와 같은 상태가 되어버렸다. 이러한 조치를 Mutual Exclusion(상호배제)라고도 설명하였다.

Enter, Exit은 쉽게 이야기하면 1인용 화장실과 같다.
Enter(화장실 들어가서 문잠금)를 통해 한 사람이 화장실을 이용하고 있으면 Exit(화장실을 다 쓰고 문 열기)하기 전까지 바깥의 사람들은 화장실을 사용할 수 없다.

적어도 그 순간만큼은 소스코드에 다른 쓰레드가 끼어들 껀덕지가 없다는 뜻이다.

하지만, Monitor의 경우에도 단점이 있는데, 만약 Exit를 선언하지 않게 된다면 어떻게 될까? 화장실은 계속 이용하고 있고, 영영 화장실을 나가지 않게 되면 바깥의 사람들은 주구장창 기다려야 할 것이다.

이러한 상황을 데드락이라고 하는데, 이는 다음 시간에 자세히 살펴볼 것이다.

따라서 Monitor를 쓰는 경우 모든 경우(예외 처리)까지 Exit을 고려해야 하는 골치아픈 단점이 있어 거의 대부분 쓰이지 않는다.

 

 

 

처럼 Enter, Exit으로 샌드위치처럼 코드를 감쌌지만

lock(_obj){
//소스코드
}

Lock에선 이와 같이 소스코드를 넣어주면, lock 구절이 끝난 이후엔 자동으로 Monitor.Exit을 하게 되는 효과를 가지게 된다.

 

 


lock은 thread-unsafe(안전하지 않은 스레드)코드를 안전하게 사용하고자 할때 사용한다. 즉, 동기화 처리를 할때 사용하는 구문이다.

그렇다면, thread-unsafe한 코드는 어떤 코드인가 다음 예제를 보자.

       static public class Division
        {
            static int num1 = 100, num2 = 5;

            static public void Divide()
            {
                try
                {
                    if (num2 != 0) Console.WriteLine(num1 / num2);
                    num2 = 0;
                }
                catch (Exception e)
                {
                    Debug.WriteLine(string.Format("Err : {0}", e));
                }
            }
        }

 

 

Divide 함수가 스레드 하나의 함수에 의해 호출되면 안전하게 동작하겠지만, 두개 이상의 스레드에서 동시에 실행되면 thread-unsafe한 상태가 된다.

첫번째 실행된 스레드가 num2 = 0으로 할당하는 시점에 두번째 실행된 스레드가  num1/num2를 수행하게되면 DivideByZeroException("0으로 나누려 했습니다.")을 발생시키면서 정상 동작을 못하는 상황이 발생될 수 있기때문이다.

thread-safe(스레드에 안전한 코드)로 만들려면 아래 클래스와 같이 수정해주면된다.

    static public class Division
    {
        static int num1 = 100, num2 = 5;
        static readonly object divisionlocker = new object();

        static public void Divide()
        {
            try
            {
                lock (divisionlocker)
                {
                   if (num2 != 0) Console.WriteLine(num1 / num2);
                    num2 = 0;
                }
            }
            catch (Exception e)
            {
                Debug.WriteLine(string.Format("Err : {0}",e));
            }
        }
    }

 

lock 구문에 의해 첫번째로 실행된 스레드의 코드 처리 위치가 lock 구문안에 있다면, 두번째로 실행된 스레드는 첫번째 실행된 스레드가 lock구문 밖으로 나올때까지 기다렸다가 실행하여 thread-safe하게 동작한다.

 

 

 

ref : https://velog.io/@tunadolphin/C%EA%B3%BC-%EC%9C%A0%EB%8B%88%ED%8B%B0%EB%A1%9C-%EB%A7%8C%EB%93%9C%EB%8A%94-MMORPG-%EA%B2%8C%EC%9E%84-%EA%B0%9C%EB%B0%9C-%EC%8B%9C%EB%A6%AC%EC%A6%88-Part4-lock-%EA%B8%B0%EC%B4%88

ref : https://sixthman23.tistory.com/entry/lock-%EA%B7%B8%EB%A6%AC%EA%B3%A0-MonitorEnterMonitorExit

반응형
반응형
class Program
    {

        static int number = 0; 

        static void Thread_1()
        {
            for(int i=0; i<100000; i++)
            {
                number++; 
            }
        }

        static void Thread_2()
        {
            for(int i=0; i<100000; i++)
            {
                number--; 
            }
        }

        static void Main(string[] args)
        {
            Task t1 = new Task(Thread_1);
            Task t2 = new Task(Thread_2);

            t1.Start();
            t2.Start();

            Task.WaitAll(t1, t2);

            Console.WriteLine(number);
        }
   }

number에 1을 더하는 연산을 10만번, number에 1을 빼는 연산을 10만번 수행한다.

우리는 당연히 number에 0이 할당되었을 것이라 생각 할수 있지만 그렇지 않다

 

하지만 결과는 전혀 엉뚱한 값이 나온다.

이런 현상이 발생하는 이유는 Race Condition(경합 조건) 때문이다.

 

 

 

코드를 자세히 살펴 보자.

number++;

number--; 

두 코드는 어셈블리 언어의 관점에서 아래와 같이 해석할 수 있다.

다른 스레드
int temp = number; 

temp += 1; 

number = temp; 



다른 스레드
int temp = number; 

temp -= 1;

number = temp;

 


전역변수의 값을 다른 스레드에서 각각 서로 변경하려다보니 이전 값으로 덮어 쓰게 되어 문제가 발생함

 

이것은 가시성 문제가 아니다

 

 

이러한 문제를 해결하려면 원자성(Atomicity)을 보장해주면 된다.

원자성이란? 더 이상 쪼개질 수 없는 성질로써, 어떠한 작업이 실행될때 언제나 완전하게 진행되어 종료되거나, 그럴 수 없는 경우 실행을 하지 않는 경우를 말한다

즉, number++와 number-- 연산이 원자성을 보장하여 한꺼번에 실행되거나 아예 실행하지 않도록 해주면 된다.

 

 

static void Thread_1()
{
	for(int i=0; i<100000; i++)
    	{
    		Interlocked.Increment(ref number);
    	}
}

static void Thread_2()
{
	for(int i=0; i<100000; i++)
	{
    		Interlocked.Decrement(ref number); 
	}

Interlocked 계열은 All or Nothing. 즉, 실행을 하거나 안되거나 이다.

Interlocked 하나가 실행되면 다른 Interlocked은 대기하고 있는다.

이렇게 함으로써 동시다발적으로 경합을 하면 최종승자만이 작업을 할 수 있고 결과를 보장받을 수 있다.

하지만 작업 속도는 당연히 원래 연산보다 느려질 수 밖에 없다. 한 작업이 끝날 때 까지 다른 작업이 대기해야 하기 때문

 

 

번외로, 만약에 우리가 number란 값을 추출하고 싶다면 어떻게 해야할까?

for(int i=0; i<100000; i++)
{
	prev = number; 
	Interlocked.Increment(ref number);
	next = number; 
}
int afterValue = Interlocked.Increment(ref number);

위 코드와 같이 Interlocked 함수의 반환 값을 통해 number의 값을 추출해야 한다.

 

이렇게 바로 받아오는건 스레드 세이프한데

 

아래 처럼 처리하면 next = number; 에서 스레드 세이프 하지 않게 된다

이 순간다른 스레드가 값을 바꿀 수있다

for(int i=0; i<100000; i++)
{
	prev = number; 
	Interlocked.Increment(ref number);
	next = number; 
}
반응형

'프로그래밍(Programming) > C#' 카테고리의 다른 글

Deadlock 현상  (0) 2022.11.27
lock 과 Monitor 임계영역  (0) 2022.11.26
MemoryBarrier 와 연산 순서/가시성  (0) 2022.11.25
A* (Astar) , JPS  (0) 2022.10.28
Tree height  (0) 2022.10.27
반응형

 

명령의 순서와 가시성에 도움을 주는 명령어 

최적화를 막아준다

 


Memory barrier

메모리 베리어는 membar, memory fence, fence instruction으로 알려져 있다. 메모리 배리어는 CPU나 컴파일러에게 barrier 명령문 전 후의 메모리 연산을 순서에 맞게 실행하도록 강제하는 기능이다. 즉 barrier 이전에 나온 연산들이 barrier 이후에 나온 연산보다 먼저 실행이 되는게 보장되어야 하는 것이다.

Memory barrier는 현대 CPU가 성능을 좋게 하기 위해 최적화를 거쳐 순서에 맞지 않게 실행시키는 결과를 초래할 수 있기 때문에 필수적이다. 이런 메모리 연산(load/store)의 재배치는 single 쓰레드로 실행할 때는 알아차리기 어렵지만, 제대로 관리되지 않는 한 병행적으로 작동되는 프로그램과 디바이스 드라이버에서 예상할 수 없는 결과를 초래하기도 한다. 싱글 쓰레드에서는 하드웨어가 명령어 통합성을 강제하기 때문에 싱글 쓰레드에서는 문제가 되지 않는다.

즉 정리하자면 성능을 좋게 만드려고 코드의 순서를 바꿔서 실행시킬 수 있는데, 이런 것을 막고 순서대로 코드가 실행되게 하도록 강제하는 것이다. 아래 간략화 된 코드를 보자. 실제로 동작하는 코드는 아니고, 핵심적인 부분만 남겨놓은 코드다.

static void Thread1() {
  y = 1; // Store y
  r1 = x; // Load x
}

static void Thread2() {
  x = 1; // Store x
  r2 = y; // Load y
}

static void Main(string[] args) {
  while(true) {
    x = y = r1 = r2 = 0; // 반복문의 초기부분마다 모든 변수의 값을 0으로 할당한다.
    
    Task t1 = new Task(Thread1);
    Task t2 = new Task(Thread2);
    
    t1.Start();
    t2.Start();
    
    Task.WaitAll(t1, t2);
    
    if (r1 == 0 & r2 == 0) 
      break;
  }
  System.out.println("While문이 break되었다.");
}

메모리 연산인 store과 load를 하는 Thread1, Thread2가 있고, 이를 Main 함수 안에서 실행시킨다. 위의 동작을 봤을 때 우리가 기대하는 것은 초기에 0으로 설정되어 있는 x, y 값이 1로 바뀌고 r1, r2에 이어서 1이 할당되면서 while 문이 계속 반복되어 무한 루프를 도는 것이다. 하지만 실제로 실행해 보면(위의 코드가 실행되는 건 아니다) While문이 break되었다. 로그가 찍히는 경우가 생긴다.

어떻게 이런 일이 가능한 것일까? 분명 while문을 빠져 나오는 조건은 r1과 r2가 모두 0일 때였다. 이 반복문을 빠져나왔다는 것은 원래 아래의 순서대로 실행되어야 할 코드가

x = 0 // 반복문 안
y = 0

x = 1 // 쓰레드 안
y = 1

r1 = x // 쓰레드 안
r2 = y

실제로는 아래와 같이 실행되는 것이다.

x = 0 // 반복문 안
y = 0

r1 = x // 쓰레드 안
r2 = y

// 여기에서 반복문 빠져나옴!

x = 1 // 쓰레드 안
y = 1

성능의 최적화를 위해서 코드의 순서를 이렇게 뒤집어서 실행시킬 수 있는데, 싱글 쓰레드에서는 문제가 되지 않지만 멀티 쓰레드에서는 문제가 된다. 따라서 이를 방지하기 위해, 명령어를 순서대로 실행시키기 위해 메모리 배리어를 사용한다.

static void Thread1() {
  y = 1; // Store y
  
  Thread.MemoryBarrier(); // 메모리 배리어
  
  r1 = x; // Load x
}

static void Thread2() {
  x = 1; // Store x
  
  Thread.MemoryBarrier(); // 메모리 배리어
  
  r2 = y; // Load y
}

static void Main(string[] args) {
  while(true) {
    x = y = r1 = r2 = 0; // 반복문의 초기부분마다 모든 변수의 값을 0으로 할당한다.
    
    Task t1 = new Task(Thread1);
    Task t2 = new Task(Thread2);
    
    t1.Start();
    t2.Start();
    
    Task.WaitAll(t1, t2);
    
    if (r1 == 0 & r2 == 0) 
      break;
  }
  System.out.println("While문이 break되었다.");
}

위의 코드와 같이 메모리 배리어를 설정하면 배리어 위의 코드가 배리어 아래의 코드와 순서가 바뀌어서 실행될 수 없게 된다. 이렇게 코드의 실행 순서를 보장해 주는 것이 메모리 배리어의 역할이다.

종류

메모리 배리어는 메모리 read/write가 내가 예상한 대로 실행되게 하는 명령어의 클래스다. 예를 들어 full fence(barrier)는 fence 전의 모든 read/write 는 fence 이후의 read/write 을 실행시키기 전에 모두 실행되어야 한다는 것이다. 메모리 배리어의 종류는 아래와 같다.

  1. Full Memory Barrier : read/write 를 둘 다 막는다.
  2. Store Memory Barrier : write만 막는다.
  3. Load Memory Barrier : read만 막는다.

메모리 배리어는 하드웨어 개념임을 기억해야 한다. 높은 레벨의 언어에서는 mutex와 세마포어를 이용해서 이를 해결한다. 낮은 레벨에서 메모리 배리어를 사용하고 메모리 배리어를 명시적으로 사용하는 것을 구현하는 것은 불필요할 수 있다. 메모리 배리어의 사용은 하드웨어 구조를 자세히 공부한 것이 전제가 되어야 하고, 애플리케이션 코드보다는 디바이스 드라이버에서 많이 사용된다.

 

 

 

 

 

 

 


메모리 배리어(Memory Barrier)

이 코드를 읽고 실행해보자.

 

 

using System;
using System.Net;
using System.Net.Sockets;
using System.Threading;
using System.Threading.Tasks;

namespace ServerCore
{

    class Program
    {
        static int x = 0;
        static int y = 0;
        static int r1 = 0;
        static int r2 = 0;


        static void Thread_1()
        {
            y = 1; // Store y
            r1 = x; // Load x 
        }

        static void Thread_2()
        {
            x = 1; // Store x 
            r2 = y; // Load y 
        }

        static void Main(string[] args)
        {
            int count = 0;
            while(true)
            {
                count++;
                x = y = r1 = r2 = 0;

                // t1, t2야 일해!
                Task t1 = new Task(Thread_1); 
                Task t2 = new Task(Thread_2);

                t1.Start();
                t2.Start();


                // t1 t2가 일이 끝날 때까지 Main Thread는 대기 
                Task.WaitAll(t1, t2);
                // t1,t2가 x,y,r1,r2를 모두 1로 바꾸어 주었으므로
                // 이론상으로는 무한반복 되어야 한다. 
                if (r1 == 0 && r2 == 0)
                    break;
            }

            Console.WriteLine($"{count}번 만에 r1, r2가 0이 되었습니다. ");

        }
    }
}

 

 

싱글쓰레드에서는 절대로 마지막 반복문을 빠져나올 수 없다.

하지만 생각보다 반복문을 잘 빠져나오는 것을 알 수 있다.


이는, 멀티쓰레드에서는 하드웨어 최적화가 적용되기 때문이다.

즉, 하드웨어가 쓰레드(Thread_1,Thread_2)에 준 연산들이 서로 상관이 없는 연산이라고 생각하면 의 연산 순서를 임의로 바꾸어서 연산하는 경우도 있기 때문이다.

이 때 메모리 배리어(Memory Barrier)를 사용한다.


메모리 배리어를 사용한 를 실행해보면 반복문을 빠져나오지 못한다.

 

메모리 베리어를 통해 코드 재배치 가시성을 확보 할 수 있다.

 

 

using System;
using System.Net;
using System.Net.Sockets;
using System.Threading;
using System.Threading.Tasks;

namespace ServerCore
{

    class Program
    {
        static int x = 0;
        static int y = 0;
        static int r1 = 0;
        static int r2 = 0;


        static void Thread_1()
        {
            y = 1; // Store y

            Thread.MemoryBarrier(); // notify other Thread stored 'y'
            r1 = x; // Load x 
        }

        static void Thread_2()
        {
            x = 1; // Store x 
            Thread.MemoryBarrier(); // notify other Thread stored 'x'
            r2 = y; // Load y 
        }

        static void Main(string[] args)
        {
            int count = 0;
            while(true)
            {
                count++;
                x = y = r1 = r2 = 0;

                // t1, t2야 일해!
                Task t1 = new Task(Thread_1); 
                Task t2 = new Task(Thread_2);

                t1.Start();
                t2.Start();


                // t1 t2가 일이 끝날 때까지 Main Thread는 대기 
                Task.WaitAll(t1, t2);

                // Memory Barrier로 코드 재배치와 가시성이 확보된 상태 
                if (r1 == 0 && r2 == 0)
                    break;
            }

            Console.WriteLine($"{count}번 만에 r1, r2가 0이 되었습니다. ");

        }
    }
}

 

 

 

 

 

 


 

 

ref : https://yoojin99.github.io/cs/Memory-Barrier/

반응형

'프로그래밍(Programming) > C#' 카테고리의 다른 글

lock 과 Monitor 임계영역  (0) 2022.11.26
Interlocked.Increment 과 Race Condition  (0) 2022.11.26
A* (Astar) , JPS  (0) 2022.10.28
Tree height  (0) 2022.10.27
다익스트라(Dijkstra) 최단경로  (0) 2022.10.27
반응형

 

 

In Unreal Engine blueprint widget, you can create different shapes through an array of points. There is no line drawing in the user widget with an array of points in C++, so we will use UWidgetBlueprintLibrary. In order to draw the shape, we must use UWidgetBlueprintLibrary::DrawLines. And then another trouble awaits us — we cann’t override OnPaint.

All animals are equal, but some animals are more equal than others.

So, we’ll just override NativePaint.

int32 UPaintWidget::NativePaint(const FPaintArgs &Args, 
const FGeometry &AllottedGeometry, const FSlateRect &MyCullingRect, FSlateWindowElementList &OutDrawElements, int32 LayerId, const FWidgetStyle &InWidgetStyle, bool bParentEnabled) const
{
    Super::NativePaint(Args, AllottedGeometry, MyCullingRect,  OutDrawElements, LayerId, InWidgetStyle, bParentEnabled);    FPaintContext Context(AllottedGeometry, MyCullingRect, OutDrawElements, LayerId, InWidgetStyle, bParentEnabled);    UWidgetBlueprintLibrary::DrawLines(Context, Points, FLinearColor::Blue, true, 10.0f);    return LayerId + 1;
}

We simply created a variable of the array of points in the class and by updating this array, our shape is also updated.

The complete code of this project is available on GitHub.

 

 

https://medium.com/@qerrant/gesture-recognizer-for-unreal-engine-24422d5868ba

반응형
반응형

사운드 구현

- 사운드를 구현하려면 소리를 내는 파트(Audio Source)와 소리를 듣는 파트(Audio Listener) 그리고 음원 파일(Audio Clip)이 필요하다

- 유니티로 import된 음원 파일을 오디오 클립이라고 부른다

- 오디오는 일반적으로 Singleton 방식을 이용해서 하나의 게임오브젝트가 사운드 출력을 총괄하도록 관리한다

AudioListener 컴포넌트

- 이 컴포넌트는 Main Camera에 기본 컴포넌트로 미리 추가되어 있다. 이 컴포넌트에 대해서는 별도로 속성을 이용해 제어할 수 없다

- 이 컴포넌트는 모든 게임오브젝트에 붙일 수 있지만 일반적으로 게임세계를 바라보는 유저의 눈에 해당하는 카메라 게임오브젝트에 포함되는 게 좋으며 조작하고 있는 캐릭터에 붙이기도 한다

- Audio Listener는 씬 내에 하나만 존재해야 한다(카메라가 여러 대인 경우 Audio Listener가 모든 카메라에서 활성화되어 있지는 않은지 꼭 확인하자). 반대로 Audio Source는 씬 내에 여러 개가 있을 수 있다

Audio Source 컴포넌트 1

Audio Source 컴포넌트 2

 

Audio Source 컴포넌트

- 씬 내에서 음원을 재생하는 컴포넌트

AudioClip: 재생할 음원 파일

Output: 재생되는 음원의 출력을 특정 Audio Listener 또는 Audio Mixer로 설정(?)

Mute: 음원이 재생되고 있는 상태로 음소거시킨다

Bypass Effects: 모든 Filter effects를 즉시 켜거나 끈다

Bypass Listener Effects: 모든 Listener effects를 즉시 켜거나 끈다

Bypass Reverb Zones: 모든 Reverb Zones를 즉시 켜거나 끈다

Play On Awake: 컴포넌트가 활성화 상태일 때 씬 실행과 동시에 음원을자동재생한다. 체크해제할 경우 스크립트에서 Play 함수로 재생할 수 있다

Loop: 반복재생 여부 설정

Priority: 씬 내의 여러 음원들 중에서 재생 우선순위 설정. default는 128이며 0이 최우선 순위, 256이 마지막 순위이다

Volume: Audio Listener로부터 1유닛(=1미터) 만큼 떨어지 거리에 있을 때를 기준으로 볼륨 설정

Pitch

Stereo Pan

Spatial Blend: 3D엔진이 Audio Source에 영향을 주는 정도를 설정

Reverb Zone Mix

3D Sound Settings: Spatial Blend에 값에 따라 비례적으로 영향을 받는다

Doppler Level: doppler effect의 적용 정도를 설정

Spread: 3D 스테레오나 멀티채널 사운드일 경우 스피커 공간에서의 spread angle을 조절

Volume Rolloff: Audio Source로부터 거리가 멀어질수록 볼륨이 감소하는 세 가지 Preset 옵션을 제공. 하단의 Curved Editor에서 감소 효과를 세부적으로 조절 가능

Min Distance: 볼륨을 100%로 들을 수 있는 범위. 3D공간에서 소리를 '더 크게' 만드려면 값을 높인다. 이 범위를 벗어나면 소리가 점점 줄어든다

Max Distance: 볼륨을 50% 이상으로 들을 수 있는 최대 범위. 즉, Max Distance 를 벗어나면 볼륨이 더 이상 감소하지 않고 50%로 들린다

Audio Clip의 인스펙터 뷰

Audio Clip

Force To Mono: 멀티채널(스테레오 포함) 음원을 싱글채널(모노)로 변환한다(down-mixing). 변환 후에는 기존 음원보다 소리가 작아지므로 데이터를 peak-

normalize 한다

Normalize: 변환 과정에서 normalize 여부를 설정

Load In Background: 체크하면 오디오 클립의 로드를 메인 쓰레드가 아닌 별개의 쓰레드에서 delayed time에 진행하므로 메인 쓰레드를 block하지 않는다. 유니티는 씬이 플레이되기 전에 씬 내에서 사용할 모든 오디오 클립을 로드해놓도록 기본설정되어 있으므로 이 속성은 default로 체크해제 되어 있다. Audio Clip 컴포넌트가 Background에서 음원을 로드하는 중이면 음원 로드를 완료할 때까지 컴포넌트는 작동이 지연된다

Ambisonic: 음원파일에 Ambisonic-encoded audio가 포함된 경우 체크. Listener의 방향orientation에 따라 회전하는 soundfield를 지원하는 포맷으로 음원을 저장

한다. XR이나 360도 영상에서 유용하게 사용된다

Load Type: 유니티가 실시간으로 음원을 불러올 때 사용할 방식을 설정

Decompress On Load: 오디오 파일을 압축되지 않은 상태로 메모리에 올리는 방식. 용량이 큰 오디오 파일은 성능상 오버헤드를 발생시키므로 용량이 작은 오디오 파일에

적합하다. 메모리는 많이 사용하지만, CPU 자원은 덜 소모한다

Compressed In Memory: 오디오 파일을 압축된 상태로 메모리에 올리는 방식. 실행할 때 압축을 해제하기 때문에 약간의 성능상 오버헤드를 발생시킨다. 큰 사이즈의

오디오 파일에 적합하다. 품질을 떨어뜨리면 파일크기를 줄일 수 있으므로 음질손상이 없는 범위 내에서 적절하게 Quality 값 설정

Streaming: Persistent Memory(HDD, Flash Driver)에 저장된 오디오 파일을 스트리밍 방식으로 재생한다. 따라서 오디오 파일을 저장하기 위한 메모리가 필요 없다

Preload Audio Data: 씬이 로드될 때 씬에서 사용할 모든 오디오 클립도 미리 로드하도록 설정. default로 체크되어 있다. 체크해제할 경우 AudioSource.Play/PlayOneShot 함수가 처음 호출되는 시점에 음원이 로드된다. 또는 AudioSource.LoadAudioData 함수로 원하는 시점에 로드하고 AudioSource.UnloadAudioData 함수로 언로드할 수 있다. 오디오 클립을 처음 재생하는 시점에 음원이 로드될 경우 렉이 발생할 수 있다는 점을 유의하자

Compression Format: 압축 포맷은 오디오 파일의 용도에 따라 다르게 설정해야 한다. 유니티에서 지원하는 오디오 압축 포맷의 종류는 다음과 같다

PCM: 음질이 좋은 대신 파일의 크기가 비교적 크기 때문에 효과음처럼 재생시간이 짧은 오디오 파일에 적합하다(비압축 포맷)

ADPCM: 압축률이 PCM 대비 3.5배이기에 파일의 크기가 작아서 메모리는 덜 쓸 수 있는 반면 CPU 자원은 좀 더 사용한다. 노이즈가 발생하기에 노이즈가 약간 있더라도

크게 상관 없는 총 소리나 발걸음 소리 같은 음원에 적합하다

Vorbis / MP3: 중간길이 정도의 효과음 또는 배경음악에 적합하다. PCM에 비해 음질은 떨어지지만 압축률(Quality 설정)을 조절할 수 있다. 압축률이 100%이면 PCM

과 동일해진다. 보통 70% 정도로 설정

*Vorbis의 Quality 값을 낮추게 되면 압축이 일어나므로 값을 다시 올리더라도 원래 상태로 돌아오지 않는다(다시 로드해야함)

HEVAG: PS Vista 고유의 압축 포맷으로 ADPCM과 유사하다

Quality: 압축되는 오디오 클립에 대한 압축률 설정. Vorbis 포맷에만 적용된다

Sample Rate Setting

Preserve Sample Rate: default 설정. 기존 음원파일의 sample rate를 유지

Optimize Sample Rate: 음원파일을 분석해서 가장 높은 주파수에 맞게 sample rate를 최적화

Override Sample Rate: sample rate를 수동으로 override 한다

오디오 설정 팁

- import한 오디오 클립은 2개의 채널로 이뤄져 있다(스테레오 방식. 각 채널은 좌,우 스피커로 구분해서 소리를 따로 보낸다). 그러나 음향 효과를 극대화한 게임이 아니라면 모노(소리의 방향구분 X)로 변환하는 걸 권장한다. 특히 모바일 게임의 경우 스테레오 사운드는 용량 부족과 성능 저하의 원인이 될 수 있다

*유니티는 스테레오, 모노 방식 외에도 최대 8개까지의 멀티채널을 지원한다

- raw 파일 음원은 용량이 크다. wav 파일은 용량이 크지만 바로 출력할 수 있기에 실행속도가 빠르다. mp3 파일은 용량이 작지만 압축 해제한 후 출력되기에 실행속도가 상대적으로 느리다(딜레이 발생) *유니티는 aif, wav, mp3, ogg 포맷의 음원을 지원한다

1. wav 파일: 즉시 출력돼야 하는 효과음(칼이나 총알의 충돌 등)에 사용

2. mp3 파일: 약간의 딜레이가 허용되는 인물의 대사나 배경음악에 사용

- 배경음악이나 환경음은 대부분 용량이 크기에 실행시 미리 압축을 풀어서 메모리에 보관할 경우 가용 메모리가 줄어든다. 따라서 용량이 큰 음원은 다음과 같은 import 설정을 권장한다

1. Streaming+Vorbis

2. Compressed in Memory+Vorbis+Quality(70%)

- 오디오의 발생 빈도나 오디오 파일의 크기에 따라서는 다음과 같은 import 설정을 권장한다

1. 자주 발생하며 파일크기가 작은 경우: Decompress On Load+PCM(or ADPCM)

2. 자주 발생하며 파일크기가 중간인 경우: Compressed in Memory+ADPCM

3. 가끔 발생하며 파일크기가 작은 경우: Compressed in Memory+ADPCM

4. 가끔 발생하며 파일크기가 중간인 경우: Compressed in Memory+Vorbis

- 음원은 import될 때 설정된 플랫폼(build target)에 적합한 타입(Load Type)으로 변환(re-encode)된다(default는 Decompress On Load+Vorbis). Load Type은 원본을 크게 손상시키지 않는 PCM이나 Vorbis/MP3 타입이 주로 쓰인다. PCM은 비압축포맷이기에 메모리에서 바로 읽어들일 수 있으므로 CPU의 부담이 덜하다. Vorbis/MP3 포맷은 불필요한 소리 정보를 잘라내는 방식으로 압축한다. ADPCM은 메모리 성능과 CPU 성능 사이에서 타협한 방식인데, PCM에 비해 CPU를 살짝 더 사용하지만 꽤 많이 압축되기에 메모리 부담을 줄인다.


Scripting API

AudioSource

public AudioClip clip: 재생할 default 오디오 클립. 다음에 재생할 오디오 클립을 clip에 담는 방법은 아래 코드 참고

public class ExampleClass : MonoBehaviour { public AudioClip otherClip; IEnumerator Start() { AudioSource audio = GetComponent<AudioSource>(); audio.Play(); yield return new WaitForSeconds(audio.clip.length); audio.clip = otherClip; audio.Play(); } }

public void PlayOneShot(AudioClip clip, float volumeScale = 1.0F)

- 오디오 클립(첫 번째 인수)을 특정 볼륨값(두 번째 인수)으로 재생한다. 볼륨값은 0에서 1사이의 실수로 설정

public void Play(ulong delay = 0)

- clip속성에 저장된 오디오 클립을 재생한다

- 인수는 재생하기까지 딜레이를 부여하는 값인데, 현재는 잘 사용되지 않고 PlayDelayed 함수로 대체해서 사용한다

public void Stop()

- 오디오 클립 재생을 멈춘다. 다시 Play할 경우 처음부터 재생한다

public void Pause()

- 오디오 클립 재생을 일시정지한다

public void UnPause()

- 일시정지된 오디오 클립을 정지된 시점부터 다시 재생한다

- Play 함수도 일시정지된 오디오 클립을 재생시킬 수 있다. 단, Play 함수는 어떤 상태의 오디오 클립에 대해서도 재생하도록 만들지만(create a new playback voice),

UnPause 함수는 일시정지된 상태가 아닌 오디오 클립을 재생할 수는 없다


*진폭은 소리의 크기, 진동수는 소리의 높낮이를 의미한다. 짧은 시간 동안 진동수가 많은 것은 고음을 의미한다

*압축된 음원파일은 가청영역대 이외의 부분을 잘라낸 후 아날로그 정보를 비슷한 주파수끼리 묶어서 디지털화한 것

 

 

ref : https://m.blog.naver.com/PostView.naver?isHttpsRedirect=true&blogId=dj3630&logNo=221461311485

<참고자료>

- 이재현, <절대강좌! 유니티>, 2018

- https://docs.unity3d.com/Manual/class-AudioSource.html

- https://docs.unity3d.com/Manual/class-AudioListener.html

- https://docs.unity3d.com/Manual/class-AudioClip.html

반응형

'게임엔진(GameEngine) > Unity3D' 카테고리의 다른 글

Unity vertex / fragment Shader01 -빨강 쉐이더  (0) 2022.12.02
Keyword Boolean  (0) 2022.11.27
UnityEngine.Random  (0) 2022.11.16
UI 버튼 바인딩  (1) 2022.11.06
앵커  (0) 2022.11.06
반응형

흔히 쓰이는 렌더링 효과 중에는 한 단계에서 GPU가 자원 R에 자료를 기록하고, 이후의 한 단계에서 그 자원 R의 자료를 읽어서 사용하는 식으로 구현하는 것들이 많다. 그런데 GPU가 자원에 자료를 다 기록하지 않았거나 기록을 아예 시작하지도 않은 상태에서 자원의 자료를 읽으려 하면 문제가 생긴다. 이를 자원 위험 상황(Resource hazard)이라고 부른다.

 

이 문제를 해결하기 위해, Direct3D는 자원들에게 상태를 부여한다. 새로 생성된 자원은 기본 상태(default state)로 시작한다. 임의의 상태 전이를 Direct3D에게 '보고'하는 것은 전적으로 응용 프로그램의 몫이다. 

 

여러 리소스 상태들

이 덕분에, GPU는 상태를 전이하고 자원 위험 상황을 방지하는 데 필요한 일들을 자유로이 진행할 수 있다. 예를 들어 텍

응용 프로그램이 상태 전이를 Direct3D에게 보고함으로써, GPU는 자원 위험을 피하는 데 필요한 조치를 할 수 있다.(이를테면 모든 쓰기 연산이 완료되길 기다린 후에 자원 읽기를 시도하는 등)

 

상태 전이를 보고하는 부담을 프로그래머에게 부여한 것은 성능 때문이다. 응용 프로그램 개발자는 그러한 전이들이 언제 일어나는지 미리 알고 있다. 만일 이러한 전이를 자동으로 추적하게 한다면 성능에 부담이 생긴다.

 

자원 상태 전이는 전이 자원 장벽(Transition resource barrier)들의 배열을 설정해서 지정한다. 배열을 사용하는 덕분에, 한 번의 API 호출로 여러 개의 자원을 전이할 수 있다. 코드에서 자원 장벽은 D3D12_RESOURCE_BARRIER_DESC 구조체로 서술된다. 

 

다음 메서드를 호출하여 위 구조체를 사용한 자원 장벽을 설정할 수 있다.

 

다음은 호출 예를 보여주는 코드이다.

mCommandList->ResourceBarrier(1, 
&CD3DX12_RESOURCE_BARRIER::Transition(
CurrentBackBuffer(),
D3D12_RESOURCE_STATE_PRESENT,
D3D12_RESOURCE_STATE_RENDER_TARGET));

이 코드는 화면에 표시할 이미지를 나타내는 텍스처 자원을 제시 상태(Presentation state)에서 렌더 대상 상태로 전이한다. 자원 장벽이 명령 목록에 추가됨을 주목하기 바란다. 전이 자원 방벽이라는 것을, GPU에게 자원의 상태가 전이됨을 알려주는 하나의 명령이라고 생각하기 바란다. 명령을 통해서 자원 상태 전이를 알려주는 덕분에, GPU는 이후의 명령들을 실행할 때 자원 위험 상황을 피하는 데 필요한 단계들을 밟을 수 있다. 

 

 

ref : https://lipcoder.tistory.com/60

반응형
반응형

Description

Easily generate random data for games.

This static class provides several easy game-oriented ways of generating pseudorandom numbers.

The generator is an Xorshift 128 algorithm, based on the paper Xorshift RNGs by George Marsaglia. It is statically initialized with a high-entropy seed from the operating system, and stored in native memory where it will survive domain reloads. This means that the generator is seeded exactly once on process start, and after that is left entirely under script control.

For more details on the seed, including how to manage it yourself, see InitState. To learn how to save and restore the state of Random, see state.

Versus System.Random

This class has the same name as the .NET Framework class System.Random and serves a similar purpose, but differs in some key ways:

Static vs instanced
UnityEngine.Random is a static class, and so its state is globally shared. Getting random numbers is easy, because there is no need to new an instance and manage its storage. However, static state is problematic when working with threads or jobs (the generator will error if used outside the main thread), or if multiple independent random number generators are required. In those cases, managing instances of System.Random would be a better option.

Float upper bounds are inclusive
All properties and methods in UnityEngine.Random that work with or derive work from float-based randomness (for example value or ColorHSV) will use an inclusive upper bound. This means that it is possible, though as rare as any other given value, for the max to be randomly returned. In contrast, System.Random.NextDouble() has an exclusive maximum, and will never return the maximum value, but only a number slightly below it.

Performance
Methods in UnityEngine.Random have been measured to be between 20% and 40% faster than their equivalents in System.Random.

Name resolution ambiguity
Because the classes share the name Random, it can be easy to get a CS0104 "ambiguous reference" compiler error if the System and UnityEngine namespaces are both brought in via using. To disambiguate, either use an alias using Random = UnityEngine.Random;, fully-qualify the typename e.g. UnityEngine.Random.InitState(123);, or eliminate the using System and fully-qualify or alias types from that namespace instead.

Static Properties

insideUnitCircle Returns a random point inside or on a circle with radius 1.0 (Read Only).
insideUnitSphere Returns a random point inside or on a sphere with radius 1.0 (Read Only).
onUnitSphere Returns a random point on the surface of a sphere with radius 1.0 (Read Only).
rotation Returns a random rotation (Read Only).
rotationUniform Returns a random rotation with uniform distribution (Read Only).
state Gets or sets the full internal state of the random number generator.
value Returns a random float within [0.0..1.0] (range is inclusive) (Read Only).

Static Methods

ColorHSV Generates a random color from HSV and alpha ranges.
InitState Initializes the random number generator state with a seed.
Range Returns a random float within [minInclusive..maxInclusive] (range is inclusive).
 

 

ref : https://docs.unity3d.com/ScriptReference/Random.html

반응형

'게임엔진(GameEngine) > Unity3D' 카테고리의 다른 글

Keyword Boolean  (0) 2022.11.27
Unity Audio  (0) 2022.11.23
UI 버튼 바인딩  (1) 2022.11.06
앵커  (0) 2022.11.06
Has Exit Time  (0) 2022.11.03
반응형

이름과 동일한 컴포넌트를 gameObject 에서 찾아 _objects 에 기록해 놓는다 (코드에서 UI 찾아 접근하기 위해서)

찾을땐 enum 으로 찾으면 되는데 for( int i 에서 names 가 enum 에 대한 string 순서를 따라 감으로)

 

 

그다음 Bind 까지 해서 미리 objects 에 담아 놓는다

 

 

 

담아 놓은 것을 찾는 경우..

 

GameObject 는GetComponent로 부터 찾을 수 없는데 이 함수는 찾으려는 대상은 Monobehaviour 또는 컴포넌트로부터 상속 받은 것이어야 하거나 인터페이스여야 한다

그래서 위 구문으론 GameObject를 찾을 수 없고 별도로 마들어 줘야한다

 

 

 

 

 

사용법 예시

 

 

 

enum 을 제외한 UI_Button 을 베이스 클래스로보고 이것을 상속받아 필요한 자식 클래스에서 Bind 하여 바로 사용 할수 있도록 확장 할 수도 있다

반응형

'게임엔진(GameEngine) > Unity3D' 카테고리의 다른 글

Unity Audio  (0) 2022.11.23
UnityEngine.Random  (0) 2022.11.16
앵커  (0) 2022.11.06
Has Exit Time  (0) 2022.11.03
픽킹, 카메라에서 Raycast로 구해보기  (0) 2022.11.01
반응형

 

4개의 앵커가 있는데 주황색의 거리

전체 화면의 좌상단과 앵커 좌상단까지의 비율적인 거리를 애커가 나타내고 (주황색)

 

좌상단의 앵커와 button 의 좌상단 까지의 위치까지는 고정거리로 표시가 됩니다 (노란색)

즉 노란색 공간이 작아지면 버튼 자체도 작아지게 됩니다, 절대적 거리이므로

 

이런 작업은 4번 반복하여 버튼 사각형 위치 포인트들이 만들어집니다

 

 

위 상태에서 아래 처럼 앵커를 변경하면 화면과 앵커까지의 비율은 고정이 되고 

앵커와 버튼까지의 거리는 고정이 됨으로 회색부분의 화면을 줄이면

 

버튼과 앵커까지의 고정거리 (노란색 위치)가 줄어들게 되면서 버튼 자체가 축소 되는것을 볼 수 있습니다

 

 

 

 

 

 

앵커와 버튼 위치가 같을땐 

 

 

 

회색 부분 화면을 줄이면 주황색 부분 비율은 유지 하면서 버튼이 사이즈가 주는 것을 알 수있습니다

즉 화면 사이즈의 비율대로 줄이고 싶을때 이렇게 처리 하면 됩니다

 

 

 

 

 

앵커가 이렇게 중앙에 배치되게 되면 화면 크기가 줄어도 버튼 크기는 그대로가 된다

 

 

반응형

'게임엔진(GameEngine) > Unity3D' 카테고리의 다른 글

UnityEngine.Random  (0) 2022.11.16
UI 버튼 바인딩  (1) 2022.11.06
Has Exit Time  (0) 2022.11.03
픽킹, 카메라에서 Raycast로 구해보기  (0) 2022.11.01
충돌 액션 매트릭스  (0) 2022.10.30
반응형

 

 

 

 

Software companies have a problem. There’s not enough candidates that can code C++. 
This was the consensus in a webinar from ProfitView, a crypto trading tools developer, on high frequency trading using C++. Anthony Peacock, formerly a quant at both Citi and Citadel said “it’s impossible to find people with a really high level of C++ skills which is exactly what every trading company wants.”  

This isn’t just a British or American issue, nor is it one specific to high frequency trading. Rainer Grimm, who has been a professional C++ trainer since 2008, affirmed that C++ education in Germany is equally “terrible” and added that “there’s a big demand for C++ not only in this domain but also the automotive” industry. 

Where are all the C++ programmers? People are seemingly scared away from the language by a terrible stigma: the notion that it is a legacy program. With big names in tech such as Microsoft Azure CEO Mark Russinovich calling people to “deprecate” C++ “for the sake of security and reliability,” in favour of Rust, this is hardly surprising.

However, reports of C++'s death may be premature. ProfitView CEO Richard Hickling, a former software engineer at Barclays and Bank of America, said “the death of C++ has been reported many times.” Hickling pointed to Java, which has long “seemed to be replacing C++ itself,” but hasn't. 

So where have all the C++ developers gone? The Stack Overflow Survey 2022 reported almost a drop of almost two percentage points in respondents this past year using C++ (from 24.3% to 22.5%), even while the percentage of professional developers using it rose. The good news, though, is that 34.7% of respondents learning to code are using C++, placing it in the top 6 programming languages of that category.

The real problem is that C++ is neither easy nor loved. Rust got an 87% approval rate in the "most loved" category of the Stack Overflow Survey. However, only 9.3% of respondents used Rust at all and only 8.8% did so professionally. C++, meanwhile, languished at 48%.


Even so, C++ regularly appears in the top 4 of the TIOBE index, earning it a place in their “big 4.” 


The reality is that there are plenty of C++ jobs available in finance, and that compared to other languages there are comparatively few people to fill them. The language may be hard. But it's also worth it. 

 

 

by Alex McMurray 2 days ago

 

기사 : 

https://www.efinancialcareers.com/news/2022/11/why-is-there-a-drought-in-the-talent-pool-for-c-developers

반응형
반응형

 

Has Exit Time

 

어떤 한 애니 상태에서 Run 인상태에서 Has Exit Time 체크 되어 있다면 Run 애니가 끝나면다른 상태로 탈출한다는 뜻을 말하니다

 

Has Exit Time 아 체크 해제 되어 있다면 Run 상태에서 계속 반복적인 애니만 재생 되게 됩니다

 

만약 Run -> wait 으로 has Exit Time 에 체크되어 있고 반대의 상황에서도 체크 되어 있다면

 

Run 애니가 끝날때 Wait 애니가 재생 되고 Wait 애니가 끝나면 다시 Run 애니가 재생되는 상태가 됩니다

 

 

 

Fixed Duration 이 켜져 있다면 0.6875 초 이후에 다른 애니도 탈출한다는 것이고

Fixed Duration 이 꺼져 있다면 0.6875퍼센트 애니에 도달(완료) 했을때 다른 애니도 탈출한다는 것입니다

 

 

Transition Duration 은 겹치는 상태가 몇초가 될것인지를 결정

 

 

여러 트렌지션이 있을때의 순서를 정할수 도 있다

 

 

하단 파란색 시간 간격은 애니메이션 Blending 되는 간격을 말한다

 

즉 이 간격을 조절하면 Transition Duration  시간과  Exit Time 또한 같이 변하게 된다

반응형

'게임엔진(GameEngine) > Unity3D' 카테고리의 다른 글

UI 버튼 바인딩  (1) 2022.11.06
앵커  (0) 2022.11.06
픽킹, 카메라에서 Raycast로 구해보기  (0) 2022.11.01
충돌 액션 매트릭스  (0) 2022.10.30
Unity 코루틴(Coroutine) 이해하기: 동작원리 및 구현  (0) 2022.10.10
반응형

ScreenToWorldPoint 가 스크린 좌표에서 월드 좌표로 변환 시켜주는 코드

반응형
반응형

플레이어와 큐브의 충돌 조건시

OnCollisionEvent 가 호출 되는 조건

 

플레이어 : rigidbody 있고, isKinematic off 상태,  collider가 있고, IsTrigger 가 off 인 상태 => 리지드바디 콜라이더

큐브 오브젝트 : rigidbody는 없고 ,  collider가 있는 상태

 

간단하게 정리 하자면 플레이어는 컴포넌트가 다 있는데 모두다 끈 상태이고

양쪽다 collider가 있다는 가정하에 어느 한쪽이든 (Rigidbody) 가 있다면 OnCollisionEvent 는 호출 됩니다

 

하지만 isKinematic 이 켜져 있으면 충돌이벤트가 호출 될 수도 안될 수도 있으니 경우의 수를 봐야합니다

 

큐브는 콜라이더만 있는 상태라면 OnCollisionEvent 가 호출 됩니다

 

 

 

OnTriggerEnter 발동조건은

둘다 콜라이더가 있어야 하고 , 둘중 하나는 IsTrigger : on 이어야 한다, 둘중 하나는 Rigidbody 여야 한다

 

 

https://docs.unity3d.com/Manual/CollidersOverview.html

 

 

 

 

 

반응형
반응형

 

 

 

 

dx 는 왼손 법칙을 사용

opengl 은 오른손 법칙 사용

 

그래서 깊이가 서로 반대로 표현 되게 됩니다

 

 

깊이에 관련된 내용중 하나는 아래와 같은 것이 있습니다

 

 

동차 좌표란 투영행렬연산 까지 거친 좌표를 말하는데

투영행렬까지 연산을 하면  x,y 가  -1~1 그리고 z 는 0~1 사이 범위로 아직 가있지 않게 되고

이것을 동차 좌표라 합니다(동차 절단공간Homogeneous clip space)

 

이때 투영행렬가지 연산 된것을 z 로 나누게 되면 그제서야 직사각형 

 x,y 가  -1~1 그리고 z 는 0~1 

의 범위안에 들어오게 됩니다

 

 

나눈 좌표를 Normalized Coordination space 라 합니다

 

 

반응형
반응형

 

아래와 같은 길이 있을때

녹색 : 시작점

빨간색 : 도착점

노란색 화살표가 가르키는 곳 : 또 다른 길

 

시작점에서 끝점까지 이동 할때 노란색 화살표가 가리키는 곳으로 가게 되면 더 먼 경로로 가게 된다는 것을 알 수 있는데

Astar 에선 거리 측정을 두가지로 한다

1 현재 플레이어 위치와 목표 지점

2 현재 플레이어 위치 기준 이동 누적 거리가 얼마나 되는가

 

1,2 이것을 합산하여 거리 비교 판단을 하게 되어 더 가까운 쪽을 우선적으로 거리를 선택하여 도착점 까지 가게 되어 최단 경로를 구하게 됩니다
(최단 경로는 종점에서 역으로 시작점으로 가면서 현재 점의 부모 위치인 역으로 올라가는 방식입니다)

즉 여기선 아래쪽의 최단 경로를 찾게 된다는 애기가 됩니다

 

 

알고리즘 처리는 우선순위 큐를 활용해 현재 후보 노드들 중에서 합산 거리 가장 가까운 노드 기준으로 계속 탐색을 하는 방식입니다

 

 

 

 

 

코드는 4방향과 8방향이 있는데

4방향은 거리 10, 14를 모두 1로 처리해놓으면 됩니다

using System;
using System.Collections.Generic;
using System.Text;

namespace Algorithm
{
	class Pos
	{
		public Pos(int y, int x) { Y = y; X = x; }
		public int Y;
		public int X;
	}

	class Player
	{
		public int PosY { get; private set; }
		public int PosX { get; private set; }
		Random _random = new Random();
		Board _board;

		enum Dir
		{
			Up = 0,
			Left = 1,
			Down = 2,
			Right = 3 
		}

		int _dir = (int)Dir.Up;
		List<Pos> _points = new List<Pos>();

		struct Node : IComparable<Node>
        {
			public int F;
			public int G;
			public int X;
			public int Y;

            int IComparable<Node>.CompareTo(Node other)
            {
				return F == other.F ?  0 : (F < other.F ? 1 : -1);
            }
        }

		public void Initialize(int posY, int posX, Board board)
		{
			PosY = posY;
			PosX = posX;
			_board = board;

			AStar();

		}

		void AStar()
        {
            //점수 매기기
            //F = G + H
            //F = 최종 점수 (작을 수록 좋음, 경로에 따라 달라짐)

            //G = 시작점에서 해당 좌표까지 이동하는데 드는 비용 ( 작을 수록 좋음, 경로에 따라 달라짐 ), 현재 노드에서 열린 다음 노드까지
            //H = 목적지에서 얼마나 가까운지 (작을 수록 좋음, 고정)

            //open (거리 계산용) 배열과
            //closed 이미 방문한 노드인지 파악하기 위한배열
            //이 둘의 배열이 각각 존재한다

            // open 한 x,y 위치에 목적지까지 거리가 얼마나 되는지 거리 계산 후 저장한다 그렇지 않은것들은 max distance,  H 와 유사
            //오픈 리스트에 있는 정보들 중에서, 가장 좋은 후보를 뽑기 위해 => 우선순위 큐를 쓴다 => 저장 되는 기준은 거리가 짭을 수록 순위가 높은 것

            //방문 하게 되면 closed 가 된다, 방문한 곳은 더이상 조사하지 않는다

            //이동할수 있는 노드라면 예약을 해 놓는다, 상 하 좌우 좌표 확인해서 예약 한다(open)

            //F = G(현재 노드에서 다음 노드까지 거리) + H(현재 노드에서 목적지 까지의 거리H)

            //open 된 것들 중에서  계산한 F 가 open 되어진 즉 이전에 계산했던 open 거리보다 보다 작다면 더이상 계산 안함
            //=> open 된것과 현재 노드 기준 사방향을 보면서 가장 적은 비용의 F 를 구해서 다시 open[nextX, nextY] = F 에 넣어 준다
            //그리고 이것을 우선순위 Q 에 넣어 준다


			//4방향일때 
            // 			int[] deltaX = new int[] { 0, -1, 0, 1 };
            // 			int[] deltaY = new int[] { -1, 0, 1, 0 };
            // 			int[] cost = new int[] { 1, 1, 1, 1 };

			//8방향 일때
            int[] deltaX = new int[] { -1, 0,  1, 0,		-1,  1,  1, -1 };
            int[] deltaY = new int[] {  0, -1, 0, 1,		-1, -1 , 1,  1 };
            int[] cost =   new int[] { 10, 10, 10, 10,	14, 14, 14, 14 };

            bool[,] closed = new bool[_board.Size, _board.Size];
			int[,] open = new int[_board.Size, _board.Size];

			Pos[,] parent = new Pos[_board.Size, _board.Size];

			for(int i=0;i<_board.Size;++i )
            {
                for (int j = 0; j < _board.Size; ++j)
                {
					open[i, j] = int.MaxValue;
                }
            }


			PriorityQueue<Node> pq = new PriorityQueue<Node>();

			//초기 H 
			open[PosY, PosX] =  10 *  Math.Abs(_board.DestX - PosX) + Math.Abs(_board.DestY - PosY);
			pq.Push(new Node() { F=  open[PosY, PosX], G=0, X = PosX, Y = PosY });
			parent[PosY, PosX] = new Pos(PosY, PosX);

			while(pq.Count > 0)
            {
				//현재 후보군들 중에서 목표와 가장 근접한(계산식상) 후보를 하나 뽑는다
				Node node = pq.Pop();

				//방문했던 노드는 건너띔
				if(closed[node.Y, node.X])
                {
					continue;
                }
				closed[node.Y, node.X] = true;
				if (node.X == _board.DestX && node.Y == _board.DestY)
                {
					break;
                }

				//4방향으로 이동 할수 있는 노드인지 확인해서 가능하다면 예약(open) 에 거리 계산 한다
				for (int i = 0; i < deltaY.Length; ++i)
				{
					int nextX = node.X + deltaX[i];
					int nextY = node.Y + deltaY[i];

					//테두리를 벗어나지 않은 경우 and  벽이 아니며 and 방문을 안했었다면 새로운 open 노드를 찾는다
					if ((nextX < 0 || nextX >= _board.Size || nextY < 0 || nextY >= _board.Size) ||	
						(_board.Tile[nextY, nextX] == Board.TileType.Wall) ||
						closed[nextY, nextX])
					{
						continue;
					}


					int g = node.G + cost[i];
					//예약 가능 다음 노드 후보들에 대한 거리 계산을 하고
					//목적지와 다음 노드 같의 거리 임으로 이 차이가 더 작은 곳으로 
					int h =  10 *  Math.Abs(_board.DestX - nextX) + Math.Abs(_board.DestY - nextY);		
					//이전에 했었던 거리 계산이 더 작다면 skip
					if(open[nextY, nextX] < g + h)
                    {
						continue;
                    }
					 
					//거리를 갱신한다 더 짧은 거리로
					open[nextY, nextX] = g + h;
					pq.Push(new Node() { F = g + h, G = g, X = nextX, Y = nextY });

					parent[nextY, nextX] = new Pos(node.Y, node.X);

				}



			}

			calculatePath(parent);
		}

		void calculatePath(Pos[,] parent)
        {
			int x = _board.DestX;
			int y = _board.DestY;
			while(parent[y,x].X != x || parent[y, x].Y != y)
            {
				_points.Add(new Pos(y, x));
				Pos pos = parent[y, x];
				x = pos.X;
				y = pos.Y;
			}
			_points.Add(new Pos(y, x));
			_points.Reverse();
		}
			

		

		const int MOVE_TICK = 10;
		int _sumTick = 0;
		int _lastIndex = 0;
		public void Update(int deltaTick)
		{
			if (_lastIndex >= _points.Count)
				return;

			_sumTick += deltaTick;
			if (_sumTick >= MOVE_TICK)
			{
				_sumTick = 0;

				PosY = _points[_lastIndex].Y;
				PosX = _points[_lastIndex].X;
				_lastIndex++;
			}
		}
	}
}

 

 

 

 

노란 블럭 : 이동 경로

녹색 : 플레이어

붉은색 : 종점

왼쪽 상단 : 시작점

 

 

 

단점 : 목표하는대상과 거리가 멀 수록 성능이 떨어진다 => 조사 해야 하는 주변 블락 들이 많아지게 됨으로

장점 : BFS 보다 주변 환경이 반영된 좀 더 빠른 길 찾기가 가능하다, BFS 는 조사하는 범위가 계속 커진다

astar 는 BFS 보다는 조사 범위가  타일 하나의  누적 이동 거리+  플레이어의 위치에서 목표지점까지의 거리

를 고려하게 됨으로 좀 덜 커질 수 있다

 

 

거리 찾는데 Astar 도 느린건 마찬가지다 그렇다면?

JPS : 대상지점까지 거리를 찾는건 Astar 와 유사한데 길을 찾을때 대상 목표지점과 플레이어 사이에 벽같은게 있다면 벽면의 외각 부분에 우선순위가 높아 그쪽을 먼저 향해 가는 방식으로 주변 모든 타일을 게속 탐색하면서 가는 방식보다 성능이 빠르다

 

반응형

'프로그래밍(Programming) > C#' 카테고리의 다른 글

Interlocked.Increment 과 Race Condition  (0) 2022.11.26
MemoryBarrier 와 연산 순서/가시성  (0) 2022.11.25
Tree height  (0) 2022.10.27
다익스트라(Dijkstra) 최단경로  (0) 2022.10.27
BFS 길찾기  (0) 2022.10.26
반응형

 

main 스레드와 새로만든 스레드 사이를 혼용해서 스레드를 사용하고자 할때 (UI 에 어떤 정보를 넘겨서 보여주고 싶을때 유용하다)

 

 

package com.jjjjj.myasynctask;

import androidx.appcompat.app.AppCompatActivity;

import android.os.AsyncTask;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import android.widget.ProgressBar;

public class MainActivity extends AppCompatActivity {

    ProgressBar progressBar;

    int value;


    BackGroundTask task = new BackGroundTask();
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        progressBar = findViewById(R.id.progressBar);

        Button button = findViewById(R.id.button);
        button.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                task.execute();     //BackGroundTask-AsyncTask 를 실행시킨다
            }
        });

        Button cancel = findViewById(R.id.button2);
        cancel.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                task.cancel(true);
            }
        });

    }

    class BackGroundTask extends AsyncTask<Integer, Integer, Integer>{

        //onPreExecute, onPostExecute, onProgressUpdate 전과 후에 UI 업데이트를 하고 싶을 때 사용 할 수 있다
        @Override
        protected void onPreExecute() {     //스레드 실행 전
            //super.onPreExecute();
            value = 0;
            progressBar.setProgress(value);

        }

        @Override
        protected void onPostExecute(Integer integer) { //스레드 실행 후(스레드 종료 후)
            //super.onPostExecute(integer);
            progressBar.setProgress(0);
        }

        @Override
        protected void onProgressUpdate(Integer... values) { // update, publishProgress(value); 이걸 통해서 이쪽으로 넘어온다
            //super.onProgressUpdate(values);
            progressBar.setProgress(values[0].intValue());
        }

        @Override
        protected Integer doInBackground(Integer... integers) { //스레드로 동작 하는 부분
            //return null;
            while(isCancelled() == false)
            {
                value += 1;

                if(value >= 100)
                {
                    break;
                }

                //onProgressUpdate 를 호출한다
                publishProgress(value);

                try{
                    Thread.sleep(1000);
                }
                catch (Exception e)
                {

                }
            }

            return value;       //onPostExecute 가 호출되게 된다
        }
    }

}

 

기본 화면

 

 

 

실행 버튼 누르면 progress bar 가 증가한다

 

취소 누르면 더이상 progress 바가 증가 하지 않는다

 

 

반응형
반응형
package com.jjjjj.mythread;

import androidx.annotation.NonNull;
import androidx.appcompat.app.AppCompatActivity;

import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.util.Log;
import android.view.View;
import android.widget.Button;
import android.widget.TextView;


public class MainActivity extends AppCompatActivity {

    TextView textView;
    //MainHandler handler;

     Handler handler = new Handler();

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        Button button = findViewById(R.id.button);
        textView = findViewById(R.id.textView);

        button.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                BackGroundThread thread = new BackGroundThread();
                thread.start();
            }
        });

        handler = new Handler();
    }

    //inner class
    //스레드는 Thread클래스를 상속 받고서 실행 가능하다
    class BackGroundThread extends Thread{
        int value =0;
        public void run()
        {
            for(int i=0;i<100;++i)
            {
                try {
                    Thread.sleep(1000);
                }
                catch (Exception e)
                {

                }

                value+=1;
                Log.d("my thread", "value : " + value);
                //textView.setText(value);      //여기선 스레드 간섭으로 크래쉬 남
                /*
                Message message = handler.obtainMessage();
                Bundle bundle = new Bundle();
                bundle.putInt("value", value);
                message.setData(bundle);

                handler.sendMessage(message);
                */


                //이렇게 하면 Runnable 이 main 스레드로 전달 되면서
                // 이 쓰레드가 메인 스레드에서 실행된다
                handler.post(new Runnable() {
                    @Override
                    public void run() {
                        textView.setText("" + value );
                    }
                });


            }
        }
    }

    //스레드 마다 핸들러를 만들수 있는데 이곳에 정의하면 main thread 에 handler 가 정의 된다
    /*
    class MainHandler extends Handler {

        //handleMessage  main thread 에서 동작한다
        @Override
        public void handleMessage(@NonNull Message msg) {
            super.handleMessage(msg);
            Bundle bundle = msg.getData();
            int value = bundle.getInt("value");
            textView.setText("" + value );

        }
    }
    */
}

 

다음 처럼 숫자가 증가하게 됩니다

반응형

'App' 카테고리의 다른 글

android : AsyncTask  (0) 2022.10.27
Android : Activity  (0) 2022.10.19
Java overriding methods when creating new instance of a class  (0) 2022.10.13
(View,ViewGroup) 안드로이드 View와 ViewGroup  (0) 2022.10.12
반응형
using System;
using System.Collections.Generic;

namespace TreeHeight
{

    class TreeNode<T>
    {
        public T data { get; set; }
        public List<TreeNode<T>> children { get; set; } = new List<TreeNode<T>>();
    }

    class Program
    {

        static TreeNode<string> makeTree()
        {
            TreeNode<string> root = new TreeNode<string>() { data = "Root" };
            {
                TreeNode<string> node = new TreeNode<string>() { data = "left" };
                node.children.Add(new TreeNode<string>() { data = "cc" });
                root.children.Add(node);
            }
            {
                TreeNode<string> node = new TreeNode<string>() { data = "design" };
                node.children.Add(new TreeNode<string>() { data = "combat" });
                node.children.Add(new TreeNode<string>() { data = "econo" });
                node.children.Add(new TreeNode<string>() { data = "abc" });
                root.children.Add(node);
            }
            {
                TreeNode<string> node = new TreeNode<string>() { data = "right" };
                node.children.Add(new TreeNode<string>() { data = "bb" });
                root.children.Add(node);
            }
            return root;
        }

        static int getTreeheight(TreeNode<string> root)
        {
            int height = 0;
            foreach(var elem in root.children)
            {
                int treeHeight = getTreeheight(elem) + 1;
                if(treeHeight > height)
                {
                    height = treeHeight;
                }
            }

            return height;
        }

        static void Main(string[] args)
        {
            var tree = makeTree();
            int treeHeight = getTreeheight(tree);
            Console.WriteLine(treeHeight);

        }
    }
}

 

결과는 2

 

 

 

반응형

'프로그래밍(Programming) > C#' 카테고리의 다른 글

MemoryBarrier 와 연산 순서/가시성  (0) 2022.11.25
A* (Astar) , JPS  (0) 2022.10.28
다익스트라(Dijkstra) 최단경로  (0) 2022.10.27
BFS 길찾기  (0) 2022.10.26
Comparer<T> Class  (0) 2018.10.15
반응형

Dijkstra 

 

다음과 같은 노드가 있다 가정하에 접근해봅니다

 

 

 

 

출발에서 A 로 갈때 미리 8을 a 까지드는 비용을 누적으로 구해놓고

출발 -> B -> A 로 가는 경로가 누적적으로 구해지면서 A에 도달했을때 기존 누적값과 누적해서 공통인 노드에 도달했을때 즉 A 에 도달했을때 누적된 것 중 더 빠른 거리를 경로로 선택하는 것

 

 

using System;

namespace GraphTraversal
{
    class Graph
    {
        // -1 은 연결 안된 상태를 표시
        int[,] adj = new int[6, 6]
        {
            { -1, 15, -1, 35, -1, -1 },
            { 15, -1, 5, 10, -1, -1 },
            { -1, 5, -1, -1, -1, -1 },
            { 35, 10, -1, -1, 5, -1 },
            { -1, -1, -1, 5, -1, 5 },
            { -1, -1, -1, -1, 5, -1 },
        };

        public void Dijikstra(int start)
        {
            bool[] visited = new bool[6];
            int[] distance = new int[6];
            Array.Fill(distance, Int32.MaxValue);
            int[] parent = new int[6];

            distance[start] = 0;        //자기자신은 0 거리로 만든다
            parent[start] = start;      //자기자신의 부모는 자기자신으로

            while (true)
            {
                // 제일 좋은 후보를 찾는다. 

                // 가장 유력한 정점의 거리와 번호를 저장한다.
                int closet = Int32.MaxValue;
                int now = -1;

                for (int i = 0; i < 6; i++)
                {
                    // 이미 방문한 정점은 스킵
                    if (visited[i])
                        continue;
                    // 아직 발견(예약)된 적이 없거나, 아직 방문하지 않고 거리만 계산한 노드 중에서 가장 짧은 노드를 찾아내기 위한 과정
                    if (distance[i] == Int32.MaxValue || distance[i] >= closet)
                        continue;

                    closet = distance[i];
                    now = i;
                }

                //now : 아직 방문하지 않은 노드 중 가장 짧은 거리가 짧은 노드
                if (now == -1) 
                    break;

                visited[now] = true;

                for (int next = 0; next < 6; next++)
                {
                    // 연결되지 않은 정점은 스킵한다
                    if (adj[now, next] == -1)
                        continue;

                    // 이미 방문한 정점은 스킵
                    if (visited[next])
                        continue;

                    //now : 1  ,
                    //next : 3
                    // 새로 조사된 정점의 최단 거리를 계산한다.
                    //                      15  +  10
                    int nextDist = distance[now] + adj[now, next];
                    // 이전까지 누적해서 계산 했던 노드 길이distance[next]보다,
                    // 현재 계산하고있는 누적 길이(nextDist)가 더 작다면 next Dist 즉 더 짧은 경로로 변경한다
                    if (nextDist < distance[next])
                    {
                        distance[next] = nextDist;      //해당 노드의 현재 까지 짧은 거리
                        parent[next] = now;             //해당 노드의 현재까지 짧은 거리의 부모

                        //각 노드에 누적된 거리가 계속 갱신 됨으로 
                    }
                }
            }
        }
    }
    class Program
    {

        static void Main(string[] args)
        {

            Graph dijik = new Graph();

            dijik.Dijikstra(0);

        }
    }
}

 

실행 결과

 

 

 벨만 포드 알고리즘과 다익스트라 알고리즘의 가장 큰 차이점은 벨만 포드 알고리즘은 방향 그래프에서 음의 가중치를 지닌 간선이 존재해도 사용할 수 있다 라는 점입니다. 따라서 음의 가중치를 가진 방향 그래프를 주면서 최단 거리를 구하라고 한다면 다익스트라 알고리즘이 아닌 벨만 포드 알고리즘을 사용 해야 합니다. 

 

 

 

 

ref : 한곳 더 있는데 url 을 잊어버림

ref : https://velog.io/@changhee09/%EC%95%8C%EA%B3%A0%EB%A6%AC%EC%A6%98-%EC%B5%9C%EB%8B%A8%EA%B2%BD%EB%A1%9C-%EB%B2%A8%EB%A7%8C-%ED%8F%AC%EB%93%9C

https://www.youtube.com/watch?v=F-tkqjUiik0

반응형

'프로그래밍(Programming) > C#' 카테고리의 다른 글

A* (Astar) , JPS  (0) 2022.10.28
Tree height  (0) 2022.10.27
BFS 길찾기  (0) 2022.10.26
Comparer<T> Class  (0) 2018.10.15
out var  (0) 2018.09.09
반응형

출구 만들기

보드에 출구(목적지)를 표시해야 한다. Board 클래스에 DestY DestX 프로퍼티를 정의해서 렌더링 할 때 목적지를 노란색으로 표시하도록 코드를 수정한다.

 

class Board
{
    ...
    
    public int DestY { get; private set; }
    public int DestX { get; private set; }
    
    ...
    
    public void InitializeBoard(int size, Player player)
    {
        ...
        
        DestY = Size - 2;
        DestX = Size - 2;
        
        ...
    }
    
    public void Render()
    {
        ...
 
        for (int y = 0; y < Size; y++)
        {
            for (int x = 0; x < Size; x++)
            {
                ...
                
                else if (y == DestY && x == DestX)
                    Console.ForegroundColor = ConsoleColor.Yellow;
                    
                ...
            } 
        }
    }
}

이제는 Board에 목적지 정보가 있으니 Player 쪽에서 목적지 정보를 받아올 필요가 없어졌다.

public void InitializePlayer(int posY, int posX, Board board)
{
    PosY = posY;
    PosX = posX;
 
    _board = board;
}

 


BFS 길찾기 알고리즘

인접 행렬 또는 인접 리스트 없이 타일 정보만 이용해서 가상의 그래프를 그려낼 수 있고 BFS 길 찾기 알고리즘을 적용할 수 있다. 하나의 타일을 노드라고 생각하고 상하좌우 바닥(노드)이 Empty라면 연결된 노드, Wall이라면 연결되지 않은 노드라고 판단할 수 있다.

 

BFS 코드를 작성하기 전에 미로 상의 X, Y좌표를 담을 Pos클래스를 정의했다.

class Pos
{
    public int Y;
    public int X;
 
    public Pos(int y, int x) { Y = y; X = x; }
}

 

※ BFS 코드 ※

여느 BFS 코드와 유사하다. 주목할 포인트는 parent path를 정의해서 찾아왔던 길을 기록하는 코드이다.

List<Pos> path = new List<Pos>();
 
...
 

   BFS();


...
 
private void BFS()
{
    int[] dirY = new int[] { -1, 0, 1, 0 };
    int[] dirX = new int[] { 0, -1, 0, 1 };
 
    bool[,] found = new bool[_board.Size, _board.Size];
    Pos[,] parent = new Pos[_board.Size, _board.Size];
 
    Queue<Pos> q = new Queue<Pos>();
    q.Enqueue(new Pos(PosY, PosX));
 
 
    found[PosY, PosX] = true;
    parent[PosY, PosX] = new Pos(PosY, PosX);
 
    while(q.Count > 0)
    {
        Pos pos = q.Dequeue();
 
        int nowY = pos.Y;
        int nowX = pos.X;
 
        for (int i = 0; i < 4; i++)
        {
            int nextY = nowY + dirY[i];
            int nextX = nowX + dirX[i];
 
            if (nextX <= 0 || nextX >= _board.Size || nextY <= 0 ||  nextY >= _board.Size)
                continue;
            if (_board.Tile[nextY, nextX] == Board.TileType.Wall)
                continue;
            if (found[nextY, nextX])
                continue;
 
            q.Enqueue(new Pos(nextY, nextX));
            found[nextY, nextX] = true;
            parent[nextY, nextX] = new Pos(nowY, nowX);
        }
    }
    
    CalcPathFromParent(parent);
}
 
private void CalcPathFromParent(Pos[,] parent)
{
    int y = _board.DestY;
    int x = _board.DestX;
 
    while (parent[y, x].Y != y || parent[y, x].X != x)
    {
        path.Add(new Pos(y, x));
 
        Pos pos = parent[y, x];
        y = pos.Y;
        x = pos.X;
    }
    path.Add(new Pos(y, x));
    path.Reverse();
}

 

 

BFS를 통해 path 정보를 채웠으니 Player가 자신의 위치를 갱신할 수 있도록 코드를 수정한다.

const int MOVE_TICK = 100;
private int _sumTick = 0;
int _index = 0;
 
public void Update(int deltaTick)
{
    if (_index >= path.Count)
        return;
 
    _sumTick += deltaTick;
 
    if (_sumTick >= MOVE_TICK)
    {
        _sumTick = 0;
 
        PosY = path[_index].Y;
        PosX = path[_index].X;
 
        _index++;
    }
}

 

똑똑한 Player로 진화했다.

 

 

ref : https://kangworld.tistory.com/57

반응형

'프로그래밍(Programming) > C#' 카테고리의 다른 글

Tree height  (0) 2022.10.27
다익스트라(Dijkstra) 최단경로  (0) 2022.10.27
Comparer<T> Class  (0) 2018.10.15
out var  (0) 2018.09.09
dynamic 형식 사용  (0) 2018.09.08
반응형

현업 C/C++/STL/3D게임프로그래밍/3D수학/Shader/엔진 통합과정 과외

 

 

3DMP

 

 

수학/프로그램은 샤프를 들기 이전에 상상에서부터 시작되어야 합니다!!

 

현업 3D 게임엔진 프로그래머가 전수해드리는 고농축 고퀄리티의 과외를 만나 보실 수 있습니다

 

 

 

 

이력 ]

  • 넥슨, NHN, 넷마블 다수 동종/타종 근무 경험 및 다년간 현업 개발자
  • 3D 게임 엔진 프로그래머/ 3D,2D 게임/ 언리얼 엔진/ 엔진 프로그래머/ 메인 PC 게임 개발 / 모바일 게임/엔진 개발
  • 미들웨어(엔진)/ 일반 프로그램 프로젝트 제작
  • 고차원 수학/ 물리등의 알고리즘과 관련된 특수 프로그램 제작
  • 게임업계 대기업 넥슨,넷마블, NC 외 P사, 중견 B사 등등의 기업에 합격시킨 노하우
  • 다수, 다양한 분들에 대한 다수의 과외 경험으로 축적된 노하우 보유
  • 기타 일반 프로젝트 경험
  • 과외 등록 완료, 남,  성범죄 "완전 무(無)"!!

 

 

[ 수강하셨던 분들 ]

  • 동종 현업 3N 사 포함/비 동종업계 현업 프로그래머 분들 다수 수강
  • 해외 글로벌 대기업 E* 사, 국내 일반/게임업계 대기업 현업 프로그래머, TA, 아티스트, 기획자
  • 국내 현업 게임 및 비게임 업계 종사자 대기업, 중견, 벤처 회사원과 기업가, 사업가(CEO)
  • 대학생(취준생), 서울대, 고려대, 일본게이오대 등등 SKY 포함 상위 10% 이상 대학생, 그 외 학교 및 유학생
    하위권 에서도 포기하지 않고 노력했을때 대기업에 간 선례들이 많이 있습니다, 포기하지 마세요!
  • 전공자/컴퓨터 관련 전공자가 아니지만 게임이나 프로그램 쪽으로 진로를 희망하시는 분들
    (비전공자 일지라도 많은 상당수의 분들이 원하는 성과를 이루고 가셨습니다)
  • 전문 프로그래머로 진로를 정하시거나 또는 희망하시는 분들
  • 프로그램을 혼자 하기엔 막연하여 그룹(단체) 배워보고자 하시는 분들
  • 초심자, 프로그램 배워보려 했지만 진입 장벽 때문에 포기하신 분들
     

 

   [ 과외 대상 ]

  • 처음부터 끝까지 완성도 있는 프로그램 체계를 제대로 쌓고싶으신 분
  • 견고하면서 제대로 된 스킬을 배우고 싶으신 분
  • 자료를 찾아봐도 정확한 답을 찾기 어려웠던 분
  • 실력을 더 쌓아가고 싶은 현업 프로그래머
  • 다른 교육원에서 실질적인 배움을 채워주지 못한 부분에 갈증을 느끼시는 분 또는 취준생
  • 취직/이직/현업에서 사용되는 3D/2D 게임 프로그래밍의 스킬을 쌓고 싶은 분
  • 만드는 것에만 열중해 설계를 보지 못하는 분이나 프로젝트 규모가 커질 수록
    문제를 명확하게 해결하는데 한계를 느끼신 분
  • 박사/석사/전공생/비전공생/유학생/컴퓨터 전공 관련 또는 프로그래밍에 관심이 있는 분
  • 3D 그래픽스 수학을 배우고자 하는 분(기초부터 현업 수준까지)
  • 수학적 개념이 어느 정도 있고 응용은 하지만 원하는 결과를 잘 못 만들어내는 분
  • 일정 수준의 수학 실력은 되긴 하나 좀 더 심도 있는 프로그램적 수학 스킬을 원하시는 분 
  • 초심자 or 기초가 부족하거나 또는 약간의 기초는 있지만 어떻게 공부해야 할지 막연하신 분

 

[수업의 목표]

  • 학원 같은 대규모 기업체가 아니기 때문에 개인 강의 여건상 많은 분들을 가르치진 못하지만
    포트폴리오 꼼수가 아닌 확실한 것을 하나하나 이해될 때까지 알려드리려 노력하고
    수강생분들의 앞길에 실질적으로 도움이 될 수 있는 방안을 깊이 있게 고민하고 해결책을 드리려 합니다
  • 진도만을 위한 도서 위주의 수업 또는 깊은 이해가 동반되지 않는 방식 즉 겉핥기로는 진정한 프로그래머
    또는 게임/엔진 프로그래머의 문턱에 조차 도달할 수 없습니다는 점을 기억해주시면 될 것 같습니다

 


[이직/신입 취직하신 분들]

3DMP 에서 배워 이직/합격/수강하신 분들의 주요 회사들입니다

EA 코리아, 넥슨 코리아, NC, 넷마블, 넷게임즈
크래프톤, 펍지, 펄어비스, 블루홀
스마일게이트, NHN, 기타 등등 ..

 

 

  [ 통합 진행 과목 ]

 

방향에 따른 과목들은 상담을 통해 설정됩니다 
추가 과목에 대한 추가 비용은 들지 않으며 희망하시는 과목들을 선정해서 들으실 수 있습니다

  • 일반 수학, 2D수학 미들웨어, STL 기반 자료구조와 알고리즘,
  • 최적화 알고리즘, 설계-디자인(Design Pattern)
  • C언어/C++,C++ 11~, 현업 디버깅 스킬 , 시스템 베이스
  • 기초수학, 현업 3D 실전 수학, 수학적용을 위한 실전 코드베이스 작업
  • DirectX9 / DX12,  DX를 통한 실전 수학 활용 병행
  • 멀티쓰레드 프로그래밍, 기초 물리(Physics), 캐릭터, 지형 엔진 & Tools,
  • Graphics : 3D 애니메이션, Shader, DX shader(HLSL), Unity shader
  • 게임 엔진 : Unreal 4, Unity3D
    [주의] 학교 과제 의뢰는 받지 않습니다(스스로 혼자 만들어 내는것을 목표로 합니다)

 

[ 과외 기본 방향성 : 개인 맞춤형 최적화 설정]

 

수업은 수강자분의 현재 상태에 맞는 최적화된 맞춤 방식으로 진행되는 것을 원칙으로 하며 모든 과목은

최종적으로는 혼자만의 생각으로 수학/프로그램을 만들어 나가는 것을 진행 시 가장 우선적인 목표 진행합니다

 

  • 이해가 빠른 편인지 느린편인지 사전에 말씀해주시면 더 욱 좋습니다
  • 과목은 위에 나열한 과목들이 있지만 원하는 과목 또는 수준에 따라 먼저 시작하게 되는 과목이
    변경되니 과목 란에서 이런 과목이 있다는 것만 참고하시면 됩니다
  • 철저히 수강생 맞춤형으로 진행됩니다(수강자 분의 수준이 고려된 최적의 선에서 시작합니다)
    상담을 통하여 수강자의 현재 실력 기준으로부터 부족한 부분은 채워 나가고
    수강생의 목표에 따라 추가해야 할 부분은 상담을 통해 얘기를 나누고 협의하에 방향을 함께 정합니다
  • 수업은 원격으로 진행되며 수강자가 본인의 컴퓨터에서 직접 코드를 작성하면서 진행됩니다(이론과 코드가 병행)
  • 과외 기본 목표 : 혼자만의 힘으로 희망하는 수준까지의 프로그램 실력을 쌓는 것을 기본 목표로 합니다

 

 [ 과외 안내 ]

  • 예약 대기 : 예약 신청 시 공석이 생길 경우 우선적으로 연락을 드리고 있습니다
    신청자가 많은 경우 대기 기간이 발생할 수 있습니다

   진행 방식 :  개인 과외 1:1 진행 방식 / 1:N 방식

  • 1 Section 기준 = 4주/4회, 추가 섹션은 및 진행은 별도 문의주시면 답변해드립니다
  • 1 회당(1 섹션) 시간 : 1:00, 섹션당 추가에 따른 누적 할인 적용
  • 수강 기간 : 한 달 단위로 수강, 수강생이 희망하는 달까지 진행되며 한달 단위 결제방식입니다
  • 과외 장소/방식 : 코로나로 인하여 온라인으로만(원격, 카메라 Off ) 진행하고 있습니다
    온라인은 오프라인과 거의 동일하게 진행되며 다년간 온라인과 오프라인의 노하우로
    둘 방식에는  큰 차이가 없다고 보시면 됩니다
    해외에 거주하신 분들의 경우 네트워크 속도에 유의해 주시기 바랍니다(사전 네트워크 테스트 가능)
  • 납부 방식 : 한 달 단위, 해외에 계신 분들에 한하여 페이팔로 결제 가능
  • 그룹 과외  : 그룹(2인 이상은) 별도 문의 , 추가 할인 적용

 

[수강 특전]  : 3DMP 네트워크

  • 현업 게임 프로그래머들과 함께 지속적인 '3DMP 네트워크 데이' 에서 정보 교류활동의 기회

 

 

[문의]

상담 시 최대한 오픈 마인으로 임해주시면 좋은 시너지 효과가 나올 수 있습니다!

상담 전용 카톡 플러스 친구 (별도의 친구 추가 없이 상담 가능)

https://pf.kakao.com/_uxabzK

 

상담 전용 카톡 아이디 : 게임프로그래밍 과외

반응형

+ Recent posts