다음 참조: http://wapel.de/?p=603

위에 나온 deps 모두 깔고 다음 명령어 실행

sudo apt-get install libc6-armhf-cross libc6-dev-armhf-cross
sudo ln -s /lib/arm-linux-gnueabihf/libpthead-2.14.so /usr/lib/arm-linux-gnueabihf/libpthread.so.0
sudo rm -rf /usr/lib/chromium-browser/libs

여기 에서 raspberry-gcc6.3.0-r4.exe를 설치하고 TOOLS/UpdateSysroot.bat를 실행한다.
다음 경로를 붙여넣는다.

/lib
/usr/include
/usr/lib
/usr/local/include
/usr/local/lib
/opt

이제 하단 체크박스를 클릭하고 sync 한다.

./configure -release -opengl es2 -device linux-rasp-pi3-g++ -platform win32-g++ -device-option CROSS_COMPILE="/c/SysGCC/raspberry/bin/arm-linux-gnueabihf-" -sysroot /c/SysGCC/raspberry/arm-linux-gnueabihf/sysroot -opensource -confirm-license -make libs -prefix /usr/local/qt5pi -extprefix /root/qt5pi -hostprefix /root/qt5 -no-use-gold-linker -v -no-gbm -nomake examples -nomake tests -qt-freetype -qt-harfbuzz -skip qtscript -no-xcb

2019.05.04 deps 수정

http://www.michalorzelek.com/blog/tutorial-creating-outline-effect-around-objects/


  1. 위 링크 가서 마테리얼 받아준 뒤 콘텐츠 폴더에 넣습니다.
  2. 위 링크에서 나온대로 PostProcess 설정을 합니다.
  3. Build.cs로 가서 PublicDependencyModuleNames에 ProceduralMeshComponent를 추가해 줍니다.
  4. 언리얼 에디터로 가서 C++ 클래스를 추가합니다. 언리얼은 투명한 Mesh에는 아웃라인을 그려주지 않아서 투명하지 않은 더미 렌더러를 만들 것입니다.
    1. 모든 클래스 표시
    2. SpineSkeletonRendererComponent를 부모 클래스로 선택합니다.
    3. 이름을 정하고 생성합니다. 이 예제에서는 OutlineRenderComponent로 가정하겠습니다.
  5. 생성된 헤더 파일에 생성자를 만듭니다. 인수는 const FObjectInitializer& ObjectInitializer입니다.
  6. 소스 파일에도 구현부를 생성합니다. 부모 클래스 초기화도 잊지 마세요.
  7. 생성자 구현부에 다음과 같은 코드를 씁니다. http://cpp.sh/9y2zl
    1. /Game/Materials/Spine/SpineUnlitNormalMaterial로 부터 머티리얼 인스턴스를 가져옵니다.
    2. NormalBlendMaterial 변수에 가져온 머리티얼 인스턴스 오브젝트를 넣었습니다.
    3. bRenderInMainPass를 비활성화 해 (간단히 설명해) 모습을 숨기고 아웃라인에 필요한 Depth가 검출 가능하도록 렌더링 합니다.
  8. 언리얼 에디터로 돌아와 콘텐츠 브라우저 하단에 뷰 옵션에서 플러그인 콘텐츠를 볼 수 있도록 토글합니다.
  9. SpinePlugin 콘텐츠에서 SpineUnlitNormalMaterial를 복사해 /Game/Materials/Spine/ 폴더 안에 복사합니다. 폴더가 없다면 만듭니다.
  10. 복사한 머티리얼을 열어 Blend 모드를 Masked로 변경하고 오파시티에 있는 핀의 경로에 존재하는 노드를 오파시티 마스크에 연결합니다.
  11. 저장한 뒤 SpineSkeletonRendererComponent와 SpineSkeletonAnimationComponent가 있는 액터에 OutlineRenderComponent도 붙여줍니다.
    1. 가시성을 위해 SpineSkeletonRendererComponent에 Attach 해주는 게 좋을 것 같다고 개인적으로 생각합니다.
    2. Spine Runtime은 Owner의 모든 컴포넌트를 찾아서 동작하므로 Attach 관계는 상관 없습니다.
  12. 이제 OutlineRenderComponent의 SetRenderCustomDepth(true)를 호출하면 설정한 대로 외곽선이 표시됩니다.


극혐 에러입니다. 구글링 해도 해결 방법도 없고...


MO를 쓰다가 MO2로 넘어왔는데 (LE 버전 스카이림)

갑자기 FNIS가 작동을 안 하더군요.


겪은 문제점은 다음과 같습니다:

  • 스카이림 버전을 1.8.151로 인식함
  • 스카이림 설치가 잘못되었다고 함
  • 에러코드 2012 (파일을 생성할 수 없음)



해결 방법은 다음과 같습니다.


  1. overwrite 등에 있는 (data)\tools에 있는 FNIS 관련 파일을 모두 지운다
  2. MO에서 FNIS 안에 있는 tools 폴더를 "스카이림 폴더\Data" 안에 넣는다
  3. 옮긴 파일을 MO로 연다.


해결.

우리는 앞서 재장전 시스템을 만들었다. 그럼 이제 연사가 가능한 총을 만들어 보자.


우선 마우스 왼쪽 버튼이 release 됐을 때 행동을 구현해야 한다. 완전 간단하다. Character.h를 열어 총을 쏘고있는지 체크할 bool 타입의 변수 하나와 StopFire() 함수를 정의하고 Character.cpp에서 구현과 입력 설정을 마치자.

bool isFiring;
...중략...
void StopFire();
...후략...
void AFPSCppCharacter::StopFire() {
	isFiring = false;
}
InputComponent->BindAction("Fire", IE_Released, this, &AFPSCppCharacter::StopFire);


이제 마우스 왼쪽 버튼을 누르고 있다면 탄환 발사 코드가 계속 반복되어야 한다. 하지만 그저 반복 코드만 넣는다면 결국 무한 루프에 빠져 프로그램은 멈출 것이다.  그렇다면 우리가 필요한 것은 딜레이다. 이제부터 그걸 만들어 보자.


일단 Character.h에 타이머를 정의해주자.

FTimerHandle _timer;


그 다음 Character.cpp를 열어서 OnFire() 함수 구현부를 if문으로 감싸서 isFiring을 체크하도록 만들고 하단 부에 타이머를 넣는다.

if (isFiring) {
	중략...

	GetWorld()->GetTimerManager().SetTimer(_timer, this, &AFPSCppCharacter::OnFire, .075f, false);
}


이렇게 쓴다면 발사 버튼을 누를 때 모든 코드를 최소한 한 번은 실행하고 그 다음 isFiring이 true라면 0.075초 대기한 다음, AFPSCppCharacter의 OnFire 함수를 부를 것이다. 그렇다. 즉 이 코드는 OnFire를 재귀함수로 만들어준다.


결국 버튼이 release 될 때까지 발사 코드를 실행하고 0.075초를 기다린 후 다시 발사하는 코드인 것이다.


하지만 무언가가 빠졌다. 우리는 isFiring을 초기화한 적이 없다. 그럼 이제 알아보기 쉽게 왼쪽 버튼을 눌렀을 때 isFiring을 초기화 해보자.


다음 코드를 찾는다.

InputComponent->BindAction("Fire", IE_Pressed, this, &AFPSCppCharacter::OnFire);


다음과 같이 바꾼다.

InputComponent->BindAction("Fire", IE_Pressed, this, &AFPSCppCharacter::StartFire);


이제 StartFire()를 Character.h에서 정의하고, Character.cpp에서 다음과 같이 구현한다.


void AFPSCppCharacter::StartFire() {
	isFiring = true;
	OnFire();
}


그럼 이제 마우스 왼쪽 버튼을 누르면 isFiring을 true 값으로 바꾸고 OnFire() 함수로 진입할 것이다. 그렇다면 이제 버튼을 누르면 총을 쏘고, 버튼을 놓으면 발사가 멈출 것이다.

일단 C++ 기반의 일인칭 프로젝트를 만든다. 초반 과정은 생략하자.


그 다음 빌드 해서 디버그 하면 언리얼 엔진이 뜨는데, 프로젝트 세팅에 들어가 입력 관련해서 Reload를 추가 후 버튼을 매핑 하자.


이제 우리가 Character에서 정의해야 할 건 다음과 같다.


  • 탄환 값
  • 재장전 버튼
  • 재장전 버튼에 의한 재장전 구현

Character.h를 열어 정수로 이루어질 탄환 값과 재장전 함수를 정의하자. 우리는 하나의 캐릭터만 가지고 있고, 총기 자체도 캐릭터에 귀속되어 하나 밖에 없으니 그냥 자체적으로 최대 장탄수를 넣는 게 낫다.


/** Simple Reload System */
void Reload();

int32 GunAmmo;


아직 재장전이 완성된 것은 아니다. 이제 구현부로 가서 재장전 키를 인식하는 것과 실제로 작동하는 재장전을 만들어보자. 아까도 말 했지만 총기가 하나 밖에 없으니 그냥 고정된 값을 쓰는 게 이해에 도움이 더 잘 될 것이다.


InputComponent->BindAction("Reload", IE_Pressed, this, &AFPSCppCharacter::Reload);
void AFPSCppCharacter::Reload() {
	GunAmmo = 10;
}


초기 장탄 값은 아직 초기화 되어있지 않지만, 어쨌든 우리는 10으로 설정하면서 탄을 10번 밖에 쏘지 못 하도록 정의했다. 그럼 지금부터 발사 구현 부에서 총알이 0이라면 발사되지 않도록 만들어 보자.

OnFire() 함수에서 구현할 것은 다음과 같다.


  • 장탄 수가 0인지 체크
  • 쏘고 나면 장탄 수 감소
별로 어렵지 않다. 프로그래밍의 기초만 있으면 구현 가능하다.


void AFPSCppCharacter::OnFire() {
	if (GunAmmo != 0) {

		...중략...

		GunAmmo--;
	}

}


실제로 앞서 말했듯, 한 거라곤 고작 기존 코드를 if문으로 감싸서 지금 탄창이 0이 아니면 원래대로 진행하는 코드를 만들었을 뿐이다. 그리고 다른 모든 코드를 진행하고 마지막에 장탄 값 하나를 빼주는 것 뿐이고, 그 이외의 구현할 것은 아무것도 없다.


이제 BeginPlay()로 가서 초기 탄창 값을 초기화 해주자.


GunAmmo = 10;


소스를 모두 저장하고, 이제 언리얼 엔진으로 가서 컴파일을 누른 뒤 총을 발사 해보자. 10발을 모두 발사하면 더 이상 발사되지 않고, R키를 누른 후 다시 발사 된다. 간단한 재장전 시스템을 만들었다. 물론 현실적으로 게임을 만들 때 이런 식으로 만들면 문제가 많지만, 좀 더 자세하게 구현해낸다고 해도 이런 형식으로 굴러간다. 즉 재장전의 개념이 탄창을 꽉 채운다는 개념만으로 만들었기 때문에 재장전 딜레이도 없고, 기존 코드 특성상 발사 속도 제한도 없어 매우 허술해 보이지만, 차근차근 만들어 나간다면 분명 좋은 게임을 만들 수 있을 것이다.

'공부 > UnrealEngine 4' 카테고리의 다른 글

[Material Nodes] Flipbook 오프셋 적용  (0) 2019.07.22
[UE4] Spine2d Outline  (0) 2018.10.08
[UE4/C++] 연사 가능한 총을 만들어 보자  (0) 2016.08.04

때론 게임에선 이미지를 띄울 때 사용자로 부터 입력 값을 받아야 할 때가 있습니다. SDL의 이벤트 핸들링 시스템을 이용하면 이런 것도 가능합니다.


            //Main loop flag
            bool quit = false;

            //Event handler
            SDL_Event e;

코드는 SDL이 초기화 되고, (이전 튜토리얼에서 쓰인) 미디어가 불러와진 다음, 사용자가 종료 했는지에 대한 여부를 추적하고 종료 플래그를 선언합니다. 우리는 이 부분부터 응용 프로그램을 시작했기 때문에, 확실하게 false로 초기화 되었습니다.


또한 SDL_Event 공용체를 선언해야 합니다. SDL 이벤트는 키를 누르거나, 마우스의 움직임, 게임패드의 동작 혹은 그 외 등등, 같은 것들입니다. 이번엔 프로그램을 종료시키는 종료 이벤트를 배워 봅시다.


            //While application is running
            while( !quit )
            {


이전 튜토리얼에서, 우리는 프로그램을 닫으려면 몇 초 동안 기다려야 했었습니다. 이번엔 사용자가 끄기 전까지 만든 프로그램은 대기하도록 해보겠습니다.


사용자가 종료를 원하지 않는 상태라면 우리는 프로그램을 계속 루프해야 합니다. 프로그램이 활성화 되어있는 동안 계속 실행되는 루프를 메인 루프, 가끔은 게임 루프라고 불립니다. 이건 어떤 게임이든 핵심적인 요소지요.


                //Handle events on queue
                while( SDL_PollEvent( &e ) != 0 )
                {
                    //User requests quit
                    if( e.type == SDL_QUIT )
                    {
                        quit = true;
                    }
                }


메인 루프의 상단에는 이벤트 루프가 있습니다. 이건 전부 빌 때까지 이벤트 큐를 계속 처리합니다.


여러분이 키를 누르거나, 마우스를 이동하거나, 터치스크린을 터치한다면, 이벤트 큐에 이벤트를 넣어줍니다.


이벤트 큐는 이벤트를 순서대로 저장하고 이를 처리하기 위한 대기 시간이 발생합니다. 무슨 이벤트가 발생했는지 알고싶을 때, SDL_PollEvent를 호출하여 가장 최근에 발생한 이벤트를 얻어 폴링합니다. SDL_PollEvent는 이벤트 큐에서 가장 최근의 이벤트를 가지고 있으며 이벤트를 SDL_Event에 넣고 함수로 전달합니다.


SDL_PollEvent는 큐가 빌 때까지 계속 반복됩니다. 큐가 비어있다면, SDL_PollEvent는 0을 반환합니다. 그래서 결국 이 코드 쪼가리는 이벤트 큐가 빌 때까지 이벤트를 계속 폴링해줍니다. 만약 이벤트 큐에 SDL_QUIT 이벤트가 있다면(which is the event when the user Xs out the window[각주:1]), 우리가 응용 프로그램을 종료 할 수 있도록 true로 플래그를 설정하고 프로그램을 종료합니다.


                //Apply the image
                SDL_BlitSurface( gXOut, NULL, gScreenSurface, NULL );
            
                //Update the surface
                SDL_UpdateWindowSurface( gWindow );
            }


프레임에 대한 이벤트 처리를 완료한 다음, 화면에 그리며 (이전 강좌에서 이야기했던 것과 같이) 업데이트 합니다. 종료 플래그가 true로 설정되어있는 경우, 응용 프로그램은 루프가 끝나면 종료됩니다. false로 되어있다면 윈도우의 x 버튼을 누르기 전까지 계속 동작합니다.

  1. 능력 부족으로 인한 번역 불가 [본문으로]