midas+son의 크리에이티브(creative) 이야기

연산자 오버로딩 중 ==(같다), !=(다르다) 체크는 쌍으로 이루어 져야 한다.

둘 중에 하나만 존재하지 못한다.

아래의 예제 샘플은

새로운 클래스의 key라는 string변수 값이 같은지 체크하여 

bool을 리턴하도록 ==, !=연산자를 오버로딩 하였다.

//새로운 클래스

    public class EquipmentActor 

    {

        public string key;    //키값변수


        //단순하게 아래처럼 비교하면 null 과 비교 할 수 없어서 위험하다.

        //public static bool operator ==(EquipmentActor x, EquipmentActor y)

        //{

        //    if (x.key.Equals(y.key))

        //        return true;

        //    else return false;

        //}

        //public static bool operator !=(EquipmentActor x, EquipmentActor y)

        //{

        //    if (x.key.Equals(y.key))

        //        return false;

        //    else return true;

        //}


  //object로 바꾸어 null비교를 하고 진행한다.

        public static bool operator ==(EquipmentActor x, EquipmentActor y)

        {

            if ((object)x == null && (object)y == null) return true;

            else if (((object)x != null && (object)y != null) && x.key == y.key) return true;

            else return false;

        }


        public static bool operator !=(EquipmentActor x, EquipmentActor y)

        {

            return !(x == y);

        }


//근데 가장 심플한건 if비교 할때 if((object)변수 == nullptr)하는게 제일 깔끔한거 같다. 그러면 위의 object바꾸는건 다 없어질텐데...

}




Ctrl + F5로 Visual Studio에서 실행 시킬 때

위와 같은 에러 창이 나올 경우가 있다.

나 같은 경우 다른 h, cpp를 추가하고 인식을 못해

.sln파일과 Intermediate폴더를 지우고

uproject파일 우측 클릭으로 

Generate Visual Studio project files 를 눌러 

sln을 새로 만들어 실행했을 때 갑자기 발생했는데

해결법은 아래 스크린샷으로 설명하겠다.

시작할 프로그램에 대한 기존 설정이 지워져 

UE4로 엔진 디렉터리의 실행파일을 찾았던 것으로 판단된다.


우리가 만든 프로젝트를 우측 클릭하여 

Set as StartUp Project를 선택하면

다시 Ctrl + F5를 했을 때 정상적으로 내 언리얼 프로젝트가 실행된다.

Error CS0619

'UnrealBuildTool.RulesCompiler.GetModuleFilename(string)'은(는) 사용되지 않습니다. 

'GetModuleFilename is deprecated, use the ModuleDirectory property on any ModuleRules instead to get a path to your module.'


프로젝트.Build.cs 에서 모듈경로 추가하기 위해 사용했던 부분이 

버전 업되면서 바뀌었다.(지금은 4.15.1 사용 중)


    private string ModulePath

    {

        //get { return Path.GetDirectoryName(RulesCompiler.GetModuleFilename(this.GetType().Name)); }    //NO

        get { return ModuleDirectory; }    //YES

    }


수학이나 물리에 대해 깊이 공부를 안했었는데

연휴를 맞아 이 책을 훑어보고 

다시금 공부할때엔 깊게 파고들어야 한다는 것을 깨달았다.


게임 개발을 하고 싶다는 학생이나 초급 프로그래머들은 

필독서가 아닐까 생각이 된다.


아래는 내가 몰랐거나 충격을 받았건 부분들에 대한 정리이다.

이 외에 서점에 가서 보면 알겠지만 처음부터 끝까지 좋은 지식 및 정보들로 가득하다.

(더 깊이 궁금한데 짧게 줄인 부분도 있어서 좀 아쉬운 부분도 몇 있긴하다.)

======================

부동소수점의 원리, 오차 - 오차가 있다는 건 알았는데 왜 그런지는 부동 소수점의 원리에 있었다. 초반부터 크리티컬한 뇌 두드림이었다.

FPU - 부동소수점 처리 장치...처음 들어본 약자

비트연산 응용 - 이 부분보고 if문을 길게 썼던 과거에 이불킥

status |= 석화;         //석화 상태이상 추가

status &= (~석화);    //석화 해제

atan2 - 아크탄젠트2가 있었다는 것과 그 유용성에 대해 처음 알았다.

프래임 드롭대책 - 4가지나 방법이 있는데 하나만 알고 있었다.

벡터 w 성분 - 3차원에서 사용하는 벡터의 4번째 인자의 존재와 1, 0이었을 때의 차이를 이미 알고 있었지만 자세히 설명되어있다.

충돌(선분, 구, 평면, 삼각형) - 한번 보고 이해 못했다. 하지만 중요한 개념이고 다른 곳에서도 꽤 사용될 수학적 지식이라 판단된다.

난수 - 지금까지 알던 rand는 가짜 난수. 진짜 난수란 무엇일까? 난수에 대한 심오함에 새로 공부하게 된다.(끝부분에 급하게 마무리 해서 아쉬운 부분)

======================

Unreal4로 3~4개월 만든 시뮬레이션(Windows PC용)을

Web에서 돌리고 싶다는 Needs가 있어서(왜 있는지 모르겠지만)

시도는 해보았다.


여러가지 문제가 발생 하였는데

정리를 하자면

1. 윈도우용 lib, exe, #include <Windows.h> 등 사용 불가 Error

2. #pragma once 가 cpp에 있으면 Error(h헤더 파일은 당연히 OK)

3. TEXT("한글") Error

4. Runtime으로 메시를 지원해주는 ProceduralMeshComponent 사용불가

5. Plugin으로 쓰고 있던 RamaSaveComponent 사용불가

6. struct 내부에 있는 생성자나 함수 앞에 struct명:: 이 있으면 Error(예로 들면 struct A{ A::A(){} } 이건 Error, struct A{ A(){} } 이건 OK)

7. strtok_s 사용 불가


위의 문제는 어떻게 해서 cpp문제는 다 잡았다.


프로젝트 셋팅에서 타겟 디바이스를 모바일용으로 바꾸고 퀄리티도 낮추었다.


하지만 결국은 LogPlayLevel: BUILD FAILED

PackagingResults:Error: 오류 실행 실패! Unknown Error


눈에 띄는 출력 로그는 아래와 같다.

LogPlayLevel: UnrealBuildTool: INFO:root:Closure compiler (C:\Program Files\Epic Games\UE_4.15\Engine\Extras\ThirdPartyNotUE\emsdk\emscripten\incoming\third_party\closure-compiler\compiler.jar) does not exist, check the paths in C:\Program Files\Epic Games\UE_4.15\Engine\Intermediate\Build\HTML5\.emscripten

LogPlayLevel: UnrealBuildTool: INFO:root:closure compiler will not be available

LogPlayLevel: UnrealBuildTool: error: Linking globals named '_Z14UELinkerFixupsv': symbol multiply defined!

LogPlayLevel: UnrealBuildTool: Traceback (most recent call last):

LogPlayLevel: UnrealBuildTool:   File "C:\Program Files\Epic Games\UE_4.15\Engine\Extras\ThirdPartyNotUE\emsdk\emscripten\incoming\emcc", line 13, in <module>

LogPlayLevel: UnrealBuildTool:     emcc.run()

LogPlayLevel: UnrealBuildTool:   File "C:\Program Files\Epic Games\UE_4.15\Engine\Extras\ThirdPartyNotUE\emsdk\emscripten\incoming\emcc.py", line 1531, in run

LogPlayLevel: UnrealBuildTool:     final = shared.Building.llvm_opt(final, link_opts, DEFAULT_FINAL)

LogPlayLevel: UnrealBuildTool:   File "C:\Program Files\Epic Games\UE_4.15\Engine\Extras\ThirdPartyNotUE\emsdk\emscripten\incoming\tools\shared.py", line 1633, in llvm_opt

LogPlayLevel: UnrealBuildTool:     assert os.path.exists(target), 'Failed to run llvm optimizations: ' + output

LogPlayLevel: UnrealBuildTool: AssertionError: Failed to run llvm optimizations:

LogPlayLevel: UnrealBuildTool: ERROR: UBT ERROR: Failed to produce item: C:\Users\apex\Desktop\gcsvc2_WebGL_\Binaries\HTML5\gcsvc.js

LogPlayLevel: UnrealBuildTool: Total build time: 353.94 seconds (Local executor: 0.00 seconds)


구글링 해보니 환경변수 얘기도 있고 추측글들이 난무하는데

다른 글을 찾아보니 언리얼에서 버그 fix된 것을 발견했다.

https://issues.unrealengine.com/issue/UE-36717

4.16에 고칠 예정이라니...(현재 최신 버전 4.15.1)

찾아보니 더 전에도 같은 버그 report가 있었는데

재현이 안된다고 안 넘어갔었나보다.


==================================

//결론

어짜피 언리얼이나 유니티나 Web용은 아직 최적화가 안되고 신경도 많이 쓰고 있지 않아서

안 될 줄 알고 있었다.

==================================

언리얼 서밋 2017에서 철권의 디렉터 하라다 류 님의 강의 내용을 정리 했습니다.


1. 타겟(사랑받고자 하는 대상, 고객, 유저)에 대해 알아가기

- 타겟을 확실히하자

- 타겟을 좁히고 정하자

- 인기 있게 되려는 노력을 꾸준히 해야 한다.


2. 가설을 세운다

- 어떠한 타겟에 있기가 있을지 가설을 자세히 세운다.

- 가설 없이 캐릭을 만들면 노하우 축적이나 활용 이론을 남기기 어렵다.


3. 피드백 받자

- 개발 중 피드백을 받아도 좋다.(외부 공개가 안될 경우 다른 지사나 다른 팀에 요청)

- 발매 후 대중들의 리서치

- 원하는 결과로 유도하지 말자.


4. 결과를 통계 데이터로 검증

- 수치화 할수 있는 데이터는 전부 수치화 하여 비교 분석한다.


5. 다음 캐릭에 활용

- 1~4 공정을 계속 거치면 노하우가 축적된다.


//사실 인기 순위와 사용률까지 가설을 세워서 개발한다.

//모든것이 계획 대로 되지 않는다.

//캐릭터의 생김새나 취향보다 능력이 좋아 사랑받을 수 도 있고

//사용률이나 인기 순위가 낮아도 너무 개성이 강하면 없앨 수도 없다.

Pawn을 움직이는 방법은 여러가지가 있다.

대표적으로 Actor를 강제로 움직이거나,

MovementComponent(CharacterMovementComponent)를 이용하는 방법.

전자는 물리 적용이 되지 않고 좌표에 맞게 움직이는 것이다.

SetActorRelativeLocation(FVector)를 보통 사용한다.(World도 있다.)


하지만 기본적으로 물리가 적용안되니 떨어지거나 언덕을 오르는데 무리가 있다.

후자인 MovementComponent를 사용하는 것은

간편하지만 고려되어야 할게 좀 있다.

이 글은 어떠한 점을 고려해야 하는지에 대한 글이다.


1.

튜토리얼이나 docs를 보면 간단하게 MovementComponent를 가지는 Pawn에서

AddMovementInput(Direction, Val)함수를 호출하면 알아서 이동하는 것을 볼 수 있다.

튜토리얼들은 기본적으로 PlayerController가 그 Pawn에 들어가 있기에 가능한 것이다.


문제는 캐릭터 시점에서 컨트롤 하지 않거나 여러 캐릭을 컨트롤해야 할 경우

즉, 해당 Pawn에 Possess하지 않거나 DefaultPawn으로 설정 하지 않을 경우 AddMovementInput가 동작 안한다.


이럴 경우에는 설정을 하나 더 해주어야 한다.

블루프린트에서는 위와 같은 디테일 창에서 찾으면 된다.

Auto Possess AI가 기본적으로는 Palced in World로 되어있다.

이 옵션을 SpawnedPlaced in World or Spawned로 바꾸어 주자.

그러면 이제서야 AddMovementInput이 동작하는 것을 볼 수 있다.


2.

AddMovementInput 함수의 인자값에 대한 얘기를 해보자.

(FVector WorldDirection, float ScaleValue, bool bForce /*=false*/)

이렇게 3가지 인자가 들어간다.

1번째는 방향 벡터, 

2번째는 스케일값, 

3번째는 false 일 경우 !IsMoveInputIgnored()를 같이 체크하고, true일경우 ControlInputVector에 바로 움직일 값이 들어간다.


문제는 1, 2번째 인자 값이다.

1번째는 방향 Direction인데 어떠한 큰 값을 적용해도

나중에는 크기 1의 단위 벡터로 되어버린다.

2번째 인자값 스케일도 -1.0f~1.0f 값 사이만 적용이 되어야 한다.

아무리 크게 주어도 더 빨리 움직이지 않는다.

여기서 중요한 규칙이 있다.

단위 벡터화 되는 것이 1번째와 2번째 인자를 곱하고 나서 나중이기 때문에

1번째가 단위 벡터가 아니고 2번째가 -1.0f~1.0f이 아니라면

나중에 계산될 단위 벡터가 스케일을 제대로 따라 가지 않게 된다.

이는 중요하게 입력값 제한을 두어야 한다.

설정에 따른 속도 변화와 부드러운 움직임을 원한다면

1번째는 단위벡터, 2번째는 -1.0f~1.0f를 넣자.(큰값을 넣어봤자 의미도 없고 나중 계산이 달라지므로...)

따라야 한다.


3.

2번 문제처럼 값을 받는다면 도대체 어디에서 속도를 제어 할 수 있는가?

그 값은 CharacterMovementComponent에 있다.

컴포넌트 내부에 MaxWalkSpeed, MaxAcceleration 등등의 float값이 있고

아래처럼 초기값이 들어간다.

...

MaxFlySpeed = 600.0f;

MaxWalkSpeed = 600.0f;

MaxSwimSpeed = 300.0f;

MaxCustomMovementSpeed = MaxWalkSpeed;

...

MaxAcceleration = 2048.0f;

BrakingFrictionFactor = 2.0f; // Historical value, 1 would be more appropriate.

BrakingDecelerationWalking = MaxAcceleration;

...

위의 float값들을 직접 변경하면 속도를 컨트롤 할 수 있다.

Character의 움직임 속도는 가속도를 따른다.

Max Speed를 넘기지 않는 선에서 가속도에 따라 속도가 차츰 올라간다.

적절히 변경해 원하는 속도대로 가게 만들자.


이상... 

여러 캐릭터를 움직이게 프로그래밍하면서 알게 된 내용을 정리해보았다.

나름 시간을 들여 알아보았는데 단순하다.

위와 같은 단순하고 필요한 내용들이 튜토리얼이나 docs에 없었다는게 아쉬울 따름이다.



UGameViewportClient* Viewport = GetWorld()->GetGameViewport();

FVector2D pos = Viewport->GetWindow()->GetPositionInScreen();    //시작점. 전체 화면일 경우 -8, -8로 나왔다.(Win10)

FVector2D WH = Viewport->GetWindow()->GetViewportSize();    //실행창 크기. 위에 타이틀 바가 있다면 그 크기도 포함


ffmpeg라는 동영상 프로그램을 사용하기 위해 위의 값들이 필요했다.

원하는 위치부터 원하는 사이즈 만큼 

인자 값들을 넣기 위해 아래처럼 스트링을 만들어 영상 캡쳐가 성공했다.

int X = 0;    //ffmpeg에 들어갈 인자 값들이 int가 아니면 작동 안한다.

int Y = 0;

int Width = 0;

int Height = 0;


//가로축 컨트롤 + width

if (pos.X < 0)    //인자값 중 -offset_x 값이 -(음수)여도 동작 안한다.

{

X = 0;

Width = WH.X + pos.X * 2;    //더해진 만큼 좌우 값을 빼준다.  pos.X가 -이므로 

}

else

{

X = pos.X;

Width = WH.X;

}

//세로축 컨트롤 + Height

if (pos.Y < 0)    //인자값 중 -offset_y 값이 -(음수)여도 동작 안한다.

{

Y = 0; 

Height = WH.Y + pos.Y * 2;    //더해진 만큼 좌우 값을 빼준다.  pos.Y가 -이므로 

}

else

{

Y = pos.Y; 

Height = WH.Y;

}


//아래 코드는 ffmpeg를 실행하기 위한 코드

FString FilePath = FPaths::ConvertRelativePathToFull(FPaths::Combine(*FPaths::GameDir(), TEXT("Plugins/ThirdParty/ffmpeg/"), TEXT("ffmpeg.exe")));

FString Args = FString::Printf(TEXT("-f gdigrab -offset_x %d -offset_y %d -video_size %dx%d -i desktop -r 24000/1001 -q 1 -vf crop %s"), X, Y, Width, Height, *Filename);    //Filename은 인자로 받아온 저장될 파일 이름, 해상도는 -video_size 1024x768 이런 식으로 들어가야 함.

FString Cmd = FString::Printf(TEXT("%s %s %s"), *FilePath, *Args, *Filename);


TRACE("%s", *Cmd);

if (FPaths::FileExists(FilePath))

{

RecordHandle = FPlatformProcess::CreateProc(*FilePath, *Args, false, true, false, nullptr, 0, nullptr, nullptr);

}



//PlayerInput 제거 - Controller = GetWorld()->GetFirstPlayerController();

UPlayerInput* PlayerInput = Controller->PlayerInput;

PlayerInput->AxisMappings.Empty();    //컨트롤러 인풋에 있는 Axis맵핑 비우기

PlayerInput->ActionMappings.Empty();    //콘트롤러 인풋에 있는 Action맵핑 비우기

// InputComponent 제거 - InputComponent = Character->GetInputComponent();

InputComponent->ClearBindingValues();    //소유한 AxisValue 초기화

InputComponent->ClearActionBindings();    //바인딩된 Action값 비우기

InputComponent->AxisBindings.Empty();    //바인딩된 Axis값 TArray 비우기


키맵핑을 하던 컨트롤러를 다른 곳으로 Pawn으로 옮겨 재사용하기 위해서는

그 플레이어 컨트롤에 들어있는 Input값을 초기화 해주어야 깔끔하다.

이동하기 전에 가지고 있던 InputComponent까지 지울 필요는 없었겠지만

깔끔하게 해보자고 해서 알아봤다.


혹시 키맵핑(바인딩) 설정 방법을 못보고 왔다면 아래 링크에 들어가 보자.

http://midason.tistory.com/418