https://www.acmicpc.net/contest/view/116

 

2015 ACM-ICPC 연습

 

www.acmicpc.net

오늘 늦잠 잘 정도로 컨디션도 안좋고, 오른쪽 손목이 좋지 않아서 조금만 하려고 했습니다.

하지만 막상 문제를 풀려니까 저번에 풀려고 적어 놓은 문제가 상당히 어려웠습니다.

그래서 남은 시간 문제 해석을 다 해놓고 해결방안이 기억이 난다면 메모를 해놓으려 합니다.

해석 하고 나니까 영어로 된 문제 중 쉬운 문제가 많아 슬펐습니다.

Party

문제

남성 그룹과 여성 그룹이 파티를 갖을 때마다 데이트를 1번 한다.

남성 그룹의 그룹원들이 최소 1번 이상 데이트를 할 때까지 개최되어야 하는 파티의 최소 수를 구해야 한다.만약 파티 횟수와 관계 없이 데이트가 불가능한 남성 그룹원이 발생한다면 impossible을 출력한다.

 

입력

n

m f

w1 m1 ... mw

.....

wf m1 .... mw

 

n = 테스트 횟수

m = 파티에 참여하는 남성의 수

f = 파티에 참여하는 여성의 수

w* = *번째 여성이 데이트 하고 싶은 남성의 수

m* = 여성이 데이트 하고 싶은 남성의 순번

 

출력

모든 남성들이 최소 1번 이상 데이트를 할 수 있는 파티 횟수

횟수와 관계 없이 데이트를 못하는 남성이 있을 경우 impossible 출력

 

해결방안

1. m*의 값이 0부터 m까지 모두 존재하는지 확인. 없을 경우 impossible

2. 

 

Save the computer!

문제

한 학기동안 컴퓨터를 맞췄을 때, 학기동안 파산하지 않을 확률을 구해야 한다.

컴퓨터는 여러 부품으로 되어 있는데, 하나라도 망갖기면 컴퓨터가 구동하지 않는다.

남은 돈으로 망가진 부품을 새로 교체 할 수 있다.

i번째 부품이 학기동안 k번 망가질 확률은 e^-a(i) * a(i)^k / k!이다.

a(i) = i번째 부품이 학기동안 망가지는 횟수의 기대값

k = 망가지는 횟수

 

입력

n

c b

a(0) .... a(c)

p(0) .... p(c)

 

n = 테스트 횟수

c = 부품 갯수

b = 컴퓨터 부품 구매에 투자 할 수 있는 잔액

a(i) = i번째 부품이 학기동안 망가지는 횟수의 기대값

p(i) = i번째 부품의 가격

 

출력

파산하지 않는 최고 확률

 

Fridge of Your Dream

컴퓨터 옆에 설치한 냉장고는 안에 콜라 무게를 LED로 표현한다.

이를 10진수로 변환하여 화면에 표현하자

 

입력

n

b

 

n = 테스트 횟수

b = 24자리의 이진수 숫자

 

출력

입력된 이진수 수를 십진수로 표현

 

해결방안

1. 2^0에서 2^24까지 값이 저장된 메모리를 생성한다.

2. 입력된 수를 뒤에서부터 문자 하나씩 받아 메모리 값과 곱하여 합산한다.

 

 

Scorched Earth

문제

포트리스와 같은 게임으로 적과 자신의 위치, 미사일을 쏘는 각도, 속도, 중력가속도(9.8m/s^2), 바람이 존재한다.

이 때 주어진 조건 하에 어느 속도로 쏴야 적을 맞추는지 연산해야 한다.

 

입력

n

x1 y1 x2 y2 w d

 

n = 테스트 횟수

(x1, y1) = 자신의 탱크 위치

(x2, y2) = 상대의 탱크 위치

w = 바람 세기(가속도)

d = 각도

 

출력

적을 맞출 수 있는 속도 v

제한 된 속도 내로 맞추지 못한다면 impossible을 출력한다.

 

해결방법

t1 = 포탄이 최대 높이까지 도달하는데 걸리는 시간

t2 = 포탄이 최대 높이에서 적 탱크까지 도달하는데 걸리는 시간

H = 최대 도달 높이

h = 자신의 탱크와 적의 탱크 사이의 높이 차

D = 자신의 탱크와 적의 탱크 사이의 거리 차

 

Vcos(d)(t1 + t2) + w(t1 + t2)^2 /2 = D 공식이 만족해야 함.

t1 = Vsin(d) / g

t2 = (2(H-h)/g)^0.5

H = (V sin(d))^2 / 2g

 

Free Willy

단어 두개와 몇가지 조합이 주어진다.

첫번째 단어에서 최소한의 조합을 이용해 두번째 단어를 만드는 횟수를 구하자.

만약 주어진 횟수 안에 불가능하면, whalemeat를 출력하자.

 

입력

n

N P L

W1 W2

P(0)

...

p(P)

 

n = 테스트 횟수

N = 단어 길이

P = 조합 갯수

L = 제한 횟수

P(i) = 조합

 

출력

제한 횟수보다 더 적게 맞출 수 있다면 그 횟수를 출력한다.

만약 불가능하면, whalemeat를 출력한다.

'개발일지 > Algorithm' 카테고리의 다른 글

20.06.09 - 2015 ACM-ICPC 연습  (0) 2020.06.09
20.06.02 2015 ACM-ICPC 연습  (0) 2020.06.02
20.05.19 - 2015 ACM-ICPC 연습  (0) 2020.05.19
20.05.12 - 2015 ACM-ICPC 연습  (0) 2020.05.12
20.05.05 - 2015 ACM-ICPC 연습  (0) 2020.05.05

14일에 발견했던 Multiplay 상에서의 Climb 기능 문제를 오늘에서야 해결했습니다.

 

그것도 너무나도 당연한 것을 확인하지 않고 말입니다.

 

문제 현상은 Climb 시 Client의 Character는 움직이지 않는 것이었습니다.

 

확인한 원인은 Character에 Attach 되어있는 MovementComponent가 가진 변수 MovementMode가 Interaction 시 값이 변했다가 몇 tick만에 원상복귀 되기 때문입니다.

 

처음에는 구현 방식을 살펴보고, 그 다음에는 통신 문제. 마지막으로 내부 구현상의 문제로 예상하고 살펴보았으나 별 다른 효과를 얻지 못했습니다.

 

그러던 중, 23일에 RPC 함수로 한번 묶어볼까 싶은 아이디어를 떠올렸습니다.

 

그리고 오늘, 구현 결과 기능이 작동하였습니다.

 

생각해보면 너무나도 당연한 것이었습니다.

 

Multiplay 상에서 Character가 움직이는 방식은 현재 두가지입니다.

 

하나는 유저가 조작 권한을 가지고 있는 Controller가 Character에 Possess 하여 직접 움직이는 것.

 

다른 하나는 다른 Client가 조작하는 Character의 트리거가 RPC를 통해 Replicated 되어 그 유저의 조작에 따라 동일하게 움직이는 것.

 

즉, Character는 각 Client에게 별도로 선언이 되어 있다는 것입니다.

 

이는 MovementComponent 또한 Client별로 하나씩 따로 존재한다는 것을 의미합니다.

 

때문에 정상적으로 움직이려면 모든 Client에서 동일한 Character의 움직임이 공유되어야 하고, 이는 RPC를 이용해야 한다는 것을 의미합니다.

 

MovementComponent라는 낯선 Component의 사용. 그리고 RPC 구조와 목적의 망각이 2주 가까운 시간의 낭비를 야기했습니다.

 

그리고 기능은 작동하지만, 완벽한 것은 아닙니다.

 

Client에서 Climb 할 때, Trigger 조작이 처음 1회 때 씹힙니다.

 

무슨 말인가 하면, Trigger 값이 처음 1회 때에는 변하지 않고, 그 뒤로는 정상적으로 들어갑니다.

 

결과적으로 기능이 동작은 하지만, Trigger를 로그로 찍어보면 값이 반대로 움직이고 있다는 것입니다.

 

오랫동안 고민한 문제가 어이 없게 풀리면서 긴장도 풀리고, 컨디션도 급격히 안좋아졌습니다.

 

이것 저것 시도를 해보았으나 유의미한 결과를 얻지 못하였고, 이 문제 해결은 수요일로 보류하겠습니다.

 

이전 3일 정도 적었던 이슈들은 평소에 이슈를 관리하던 GitKraken Board롤 옮겼습니다.

'개발일지 > Treasure Hunter' 카테고리의 다른 글

20.05.28 개발일지  (0) 2020.05.28
20.05.27 개발일지  (0) 2020.05.27
20.05.23 개발일지  (2) 2020.05.23
20.05.21 개발일지  (0) 2020.05.21
20.05.20 개발일지  (0) 2020.05.20

EnableInput can only be specified on a PlayerController for itself 에러 발생.

한번 사라졌다가 rebuild 한 후 다시 발생.

시도해볼 해결 방안 링크를 첨부한다.

https://stackoverflow.com/questions/59857336/how-to-fix-enableinput-can-only-be-specified-on-a-pawn-for-its-controller-erro

 

How to fix "EnableInput can only be specified on a Pawn for its Controller" error in output log

I'm creating a weapon system for my third person shooter project and I'm having some problems getting input to the gun. With the way I have set up my classes, I have ACharacter parented player cla...

stackoverflow.com

Client에서 Interaction시 MovementMode가 변경 되었다가 다시 원상복귀 됨.

MovementComponent 내에 MovementMode가 호출되는 함수를 탐색해 보았다.

MovementMode
  DefaultLandMovementMode
    SetDefaultMovementMode
      Character::PostInitializeComponent
        - Controller is False, so SetDefautlMovementMode called
  GroundMovementMode
    SetGroundMovementMode
    ApplyNetworkMovementMode
  SetMovementMode
    SetDefaultMovementMode
    SetGroundMovementMode

OnMovementModeChanged
  SetMovementMode
  Character::OnMovementModeChanged

그 결과 Character의 PostInitializeComponent에서 Controller가 없을 경우, MovementComponent::SetDefaultMovementMode가 호출이 된다.

이 함수 내부에는 GameMode를 MOVE_Walk로 초기화 하는 구문이 있다.

 

이것이 원인이라면 Character를 찾지 못했다는 warning과 문제가 하나로 합쳐진다.

하지만 Interaction시 한번 수정되었다가 몇 tick 이후 원상복귀 된다는 점에서 이 부분이 문제라 하기에는 애매하다.

 

그래서 MovementComponent의 OnMovementModeChanged를 탐색해보았다. 

로그에서 Override 한 OnMovementModeChanged 함수의 로그가 찍혀있었기 때문이다.

하지만 이렇다 할 원인을 찾지 못했다.

 

현재 여기까지 탐색을 함.

다음 시도는 "Character에서 MovementComponent가 가지고 있는 MovementMode의 RPC Function 구현"입니다.

 

"ProcessRemoteFunction: No owning connection for actor THCharacterBP_Sample_C_0" Warning 발생

Controller와 Character가 연결되지 않아서 발생했다.

단순 네트워크 문제라는 것도 있고, Possess를 하라는 글도 있다.

이에 대해 그나마 가장 자세한 문서를 첨부한다.

https://dawnarc.com/2017/09/ue4no-owning-connection-for-actor-xxx.-function-xxx-will-not-be-processed/

 

[UE4]No owning connection for actor XXX. Function XXX will not be processed

keywords:UE4, Dedicated Server, Replication Issue Client execute a server function failed: LogNet: Warning: UIpNetDriver::ProcesRemoteFunction: No owning connection for actor TopDownCharacter_C_0. Function ServerMoveToDest will not be processed. Cause: B

dawnarc.com

우선 Possess를 따로 어떻게 해줘야 하는지를 잘 모르겠다.

그 부분을 먼저 고민해야 할 것 같다.

 

만약 Listen Server라서 발생하는 문제 중 하나라면 Climb 관련 기능들을 모두 Dedicate Server 구현 이후로 미뤄야 한다.

하지만 다른 Animation에서는 이런 문제가 발생하지 않는 것으로 보아 이런 가능성은 매우 낮다고 생각한다.

'개발일지 > Treasure Hunter' 카테고리의 다른 글

20.05.27 개발일지  (0) 2020.05.27
20.05.25 개발일지  (0) 2020.05.25
20.05.21 개발일지  (0) 2020.05.21
20.05.20 개발일지  (0) 2020.05.20
20.05.18 개발일지  (0) 2020.05.18

DirectX 9, 10의 D3DX Library에, 11에서는 XNA Math에서 3차원 그래픽에 필요한 것들을 지원한다.

코드상에서는 xnamath.h를 include 하면 사용이 가능하다고 설명되어 있다.

하지만 현재 예시 DirectX12 코드에서는 관련 기능들이 DiretXMath.h에 선언이 되어 있다.

명확하게 이전 되었다는 내용을 찾지는 못했지만, xnamath.h와 DirectXMath.h의 document를 보면 설명이 동일하다.

때문에 여기서는 DirectXMath.h를 기준으로 설명하겠다.

또한 관련해서 답이 충분히 될법한 링크 또한 첨부한다.

https://docs.microsoft.com/en-us/windows/win32/dxmath/pg-xnamath-migration#header-changes

 

Code Migration from the XNA Math Library - Win32 apps

Code Migration from the XNA Math Library In this article --> This overview describes the changes required to migrate existing code using the XNA Math library to the DirectXMath library. The DirectXMath library uses a new set of headers. Replace the xnamath

docs.microsoft.com

 

이 Library는 벡터 연산에 매우 유용하다.

이를 설명하기 위해서는 먼저 Library의 연산 과정을 조금 언급해야 한다.

DirectXMath Library는 Windows와 XBox 360에서 사용가능한 특별한 하드웨어 레지스터들을 활용한다.

Windows에서는 SSE2(Streaming SIMD Extensions) 명령집합을 사용하는데,
SIMD의 명령덜은 128비트 너비의 SIMD(Single Instruction Multiple Data) 레지스터들을 이용해
한 명령에서 32비트 float나 int 4개를 동시에 처리가 가능하다.

 

DirectXMath에서 핵심 Vector 형식은 SIMD 하드웨어 레지스터들에 대응되는 XMVECTOR이다.

#if defined(_XM_SSE_INTRINSICS_) && !defined(_XM_NO_INTRINSICS_)
typedef __m128 XMVECTOR;
#elif defined(_XM_ARM_NEON_INTRINSICS_) && !defined(_XM_NO_INTRINSICS_)
typedef float32x4_t XMVECTOR;
#else
typedef __vector4 XMVECTOR;
#endif

128bit 크기로, 하나의 SIMD 명령으로 처리되는 4개의 32bit float 값들로 이루어져있다.

저기서 _XM_SSE_INTRINSICS_는 SSE나 SSE2를, _XM_ARM_NEON_INTRINSICS_는 Windows RT.

즉, 모바일 플랫폼을 나타낸다.

_XM_NO_INTRINSICS_는 Windows 환경이 아닌 곳에서 DirectXMath가 따로 사용되는 경우를 일컫는다.

 

여기서 __m128은 특별한 SIMD 형식이다.

이 형식의 Vector들은 계산 시 반드시 SIMD의 장점을 취하게 된다.

게다가 2, 3차원 Vector일 경우, 사용하지 않는 element를 0으로 설정해 동일하게 SIMD의 장점을 취한다.

 

다만 여기에는 몇가지 규칙이 있는데, 플랫폼마다 규칙이 다르다.

특히 Windows 23bit와 64bit, XBox 360의 규칙들이 서로 다르다.

이런 차이로부터 독립적이기 위해, XMVECTOR 대신 CXXMVECTOR 형식과 FXMVECTOR형식을 사용한다.

// Fix-up for (1st-3rd) XMVECTOR parameters that are pass-in-register for x86, ARM, ARM64, and vector call; by reference otherwise
#if ( defined(_M_IX86) || defined(_M_ARM) || defined(_M_ARM64) || _XM_VECTORCALL_ ) && !defined(_XM_NO_INTRINSICS_)
typedef const XMVECTOR FXMVECTOR;
#else
typedef const XMVECTOR& FXMVECTOR;
#endif

// Fix-up for (4th) XMVECTOR parameter to pass in-register for ARM, ARM64, and x64 vector call; by reference otherwise
#if ( defined(_M_ARM) || defined(_M_ARM64) || defined(_M_HYBRID_X86_ARM64) || (_XM_VECTORCALL_ && !defined(_M_IX86) ) ) && !defined(_XM_NO_INTRINSICS_)
typedef const XMVECTOR GXMVECTOR;
#else
typedef const XMVECTOR& GXMVECTOR;
#endif

// Fix-up for (5th & 6th) XMVECTOR parameter to pass in-register for ARM64 and vector call; by reference otherwise
#if ( defined(_M_ARM64) || defined(_M_HYBRID_X86_ARM64) || _XM_VECTORCALL_ ) && !defined(_XM_NO_INTRINSICS_)
typedef const XMVECTOR HXMVECTOR;
#else
typedef const XMVECTOR& HXMVECTOR;
#endif

// Fix-up for (7th+) XMVECTOR parameters to pass by reference
typedef const XMVECTOR& CXMVECTOR;

 

32bit와 64bit의 차이는 복사 전달과 참조전달이다.

FXMVECTOR, CXMVECTOR. 더 나아가 GXMVECTOR, HXMVECTOR의 차이는 다음과 같다.

매개변수로 XMVECTOR을 전달 할 때,

  1. 1, 2, 3번째 XMVECTOR는 FXMVECTOR로 전달해야 한다.
  2. 4번째 XMVECTOR는 GXMVECTOR로 전달해야 한다.
  3. 5, 6번째 XMVECTOR는 HXMVECTOR로 전달해야 한다.
  4. 7번째 이후로는 CXMVECTOR로 전달해야 한다.

단, 생성자에서는 __vectorcall의 제한으로 인해, 규칙이 더 간단해진다.

  1. 처음 3개의 XMVECTOR는 FXMVECTOR로 전달한다.
  2. 그 이후에는 모두 CXMVECTOR로 전달한다.

함수에 전달 할 때 XMVECTOR가 연속적이지 않아도 상관 없다.

XMVECTOR들만 순번을 매겨 위 형식을 적용한다.

이와 관련된 문서를 첨부한다.

https://docs.microsoft.com/en-us/windows/win32/dxmath/pg-xnamath-internals

 

Library Internals - Win32 apps

Library Internals In this article --> This topic describes the internal design of the DirectXMath library. Calling Conventions To enhance portability and optimize data layout, you need to use the appropriate calling conventions for each platform supported

docs.microsoft.com

 

XMVECTOR는 16Byte 경계에 정렬되어야 하는데, Local/Global Value에서는 자동으로 정렬된다.

Field의 경우, XMVECTOR 대신 XMFLOAT2, XMFLOAT3, XMFLOAT4를 사용하는 것이 바람직하다.

// 2D Vector; 32 bit floating point components
struct XMFLOAT2
{
    float x;
    float y;

    XMFLOAT2() = default;

    XMFLOAT2(const XMFLOAT2&) = default;
    XMFLOAT2& operator=(const XMFLOAT2&) = default;

    XMFLOAT2(XMFLOAT2&&) = default;
    XMFLOAT2& operator=(XMFLOAT2&&) = default;

    XM_CONSTEXPR XMFLOAT2(float _x, float _y) : x(_x), y(_y) {}
    explicit XMFLOAT2(_In_reads_(2) const float *pArray) : x(pArray[0]), y(pArray[1]) {}
};
// 3D Vector; 32 bit floating point componentsstruct XMFLOAT3
{
    float x;
    float y;
    float z;

    XMFLOAT3() = default;

    XMFLOAT3(const XMFLOAT3&) = default;
    XMFLOAT3& operator=(const XMFLOAT3&) = default;

    XMFLOAT3(XMFLOAT3&&) = default;
    XMFLOAT3& operator=(XMFLOAT3&&) = default;

    XM_CONSTEXPR XMFLOAT3(float _x, float _y, float _z) : x(_x), y(_y), z(_z) {}
    explicit XMFLOAT3(_In_reads_(3) const float *pArray) : x(pArray[0]), y(pArray[1]), z(pArray[2]) {}
};
// 4D Vector; 32 bit floating point components
struct XMFLOAT4
{
    float x;
    float y;
    float z;
    float w;

    XMFLOAT4() = default;

    XMFLOAT4(const XMFLOAT4&) = default;
    XMFLOAT4& operator=(const XMFLOAT4&) = default;

    XMFLOAT4(XMFLOAT4&&) = default;
    XMFLOAT4& operator=(XMFLOAT4&&) = default;

    XM_CONSTEXPR XMFLOAT4(float _x, float _y, float _z, float _w) : x(_x), y(_y), z(_z), w(_w) {}
    explicit XMFLOAT4(_In_reads_(4) const float *pArray) : x(pArray[0]), y(pArray[1]), z(pArray[2]), w(pArray[3]) {}
};

하지만 이 형식들을 계산에 직접 사용하면 SIMD의 장점을 취하지 못한다.

SIMD를 활용하려면 이 형식의 Instance를 XMVECTOR 형식으로 변환해야 한다.

이를 위해 DirectXMath에서는 다양한 적재 함수들을 제공한다.

XMVECTOR    XM_CALLCONV     XMLoadInt2(_In_reads_(2) const uint32_t* pSource);
XMVECTOR    XM_CALLCONV     XMLoadInt2A(_In_reads_(2) const uint32_t* PSource);
XMVECTOR    XM_CALLCONV     XMLoadFloat2(_In_ const XMFLOAT2* pSource);
XMVECTOR    XM_CALLCONV     XMLoadFloat2A(_In_ const XMFLOAT2A* pSource);
XMVECTOR    XM_CALLCONV     XMLoadSInt2(_In_ const XMINT2* pSource);
XMVECTOR    XM_CALLCONV     XMLoadUInt2(_In_ const XMUINT2* pSource);

XMVECTOR    XM_CALLCONV     XMLoadInt3(_In_reads_(3) const uint32_t* pSource);
XMVECTOR    XM_CALLCONV     XMLoadInt3A(_In_reads_(3) const uint32_t* pSource);
XMVECTOR    XM_CALLCONV     XMLoadFloat3(_In_ const XMFLOAT3* pSource);
XMVECTOR    XM_CALLCONV     XMLoadFloat3A(_In_ const XMFLOAT3A* pSource);
XMVECTOR    XM_CALLCONV     XMLoadSInt3(_In_ const XMINT3* pSource);
XMVECTOR    XM_CALLCONV     XMLoadUInt3(_In_ const XMUINT3* pSource);

XMVECTOR    XM_CALLCONV     XMLoadInt4(_In_reads_(4) const uint32_t* pSource);
XMVECTOR    XM_CALLCONV     XMLoadInt4A(_In_reads_(4) const uint32_t* pSource);
XMVECTOR    XM_CALLCONV     XMLoadFloat4(_In_ const XMFLOAT4* pSource);
XMVECTOR    XM_CALLCONV     XMLoadFloat4A(_In_ const XMFLOAT4A* pSource);
XMVECTOR    XM_CALLCONV     XMLoadSInt4(_In_ const XMINT4* pSource);
XMVECTOR    XM_CALLCONV     XMLoadUInt4(_In_ const XMUINT4* pSource);

또한, 반대로 XMVECTOR Instance를 XMFLOAT* 형식으로 변환하는 저장 함수도 제공한다.

void        XM_CALLCONV     XMStoreInt2(_Out_writes_(2) uint32_t* pDestination, _In_ FXMVECTOR V);
void        XM_CALLCONV     XMStoreInt2A(_Out_writes_(2) uint32_t* pDestination, _In_ FXMVECTOR V);
void        XM_CALLCONV     XMStoreFloat2(_Out_ XMFLOAT2* pDestination, _In_ FXMVECTOR V);
void        XM_CALLCONV     XMStoreFloat2A(_Out_ XMFLOAT2A* pDestination, _In_ FXMVECTOR V);
void        XM_CALLCONV     XMStoreSInt2(_Out_ XMINT2* pDestination, _In_ FXMVECTOR V);
void        XM_CALLCONV     XMStoreUInt2(_Out_ XMUINT2* pDestination, _In_ FXMVECTOR V);

void        XM_CALLCONV     XMStoreInt3(_Out_writes_(3) uint32_t* pDestination, _In_ FXMVECTOR V);
void        XM_CALLCONV     XMStoreInt3A(_Out_writes_(3) uint32_t* pDestination, _In_ FXMVECTOR V);
void        XM_CALLCONV     XMStoreFloat3(_Out_ XMFLOAT3* pDestination, _In_ FXMVECTOR V);
void        XM_CALLCONV     XMStoreFloat3A(_Out_ XMFLOAT3A* pDestination, _In_ FXMVECTOR V);
void        XM_CALLCONV     XMStoreSInt3(_Out_ XMINT3* pDestination, _In_ FXMVECTOR V);
void        XM_CALLCONV     XMStoreUInt3(_Out_ XMUINT3* pDestination, _In_ FXMVECTOR V);

void        XM_CALLCONV     XMStoreInt4(_Out_writes_(4) uint32_t* pDestination, _In_ FXMVECTOR V);
void        XM_CALLCONV     XMStoreInt4A(_Out_writes_(4) uint32_t* pDestination, _In_ FXMVECTOR V);
void        XM_CALLCONV     XMStoreFloat4(_Out_ XMFLOAT4* pDestination, _In_ FXMVECTOR V);
void        XM_CALLCONV     XMStoreFloat4A(_Out_ XMFLOAT4A* pDestination, _In_ FXMVECTOR V);
void        XM_CALLCONV     XMStoreSInt4(_Out_ XMINT4* pDestination, _In_ FXMVECTOR V);
void        XM_CALLCONV     XMStoreUInt4(_Out_ XMUINT4* pDestination, _In_ FXMVECTOR V);

또한 XMVECTOR Object의 내용을 설정하는 용도의 함수들을 정의하고 있다.

//(0, 0, 0, 0)
XMVECTOR    XM_CALLCONV     XMVectorZero();

// (1, 1, 1, 1)
XMVECTOR    XM_CALLCONV     XMVectorSplatOne();

//(x, y, z, w)
XMVECTOR    XM_CALLCONV     XMVectorSet(float x, float y, float z, float w);

//(value, value, value, value)
XMVECTOR    XM_CALLCONV     XMVectorReplicate(float Value);

//(Vx, Vx, Vx, Vx)
XMVECTOR    XM_CALLCONV     XMVectorSplatX(FXMVECTOR V);

//(Vy, Vy, Vy, Vy)
XMVECTOR    XM_CALLCONV     XMVectorSplatY(FXMVECTOR V);

//(Vz, Vz, Vz, Vz)
XMVECTOR    XM_CALLCONV     XMVectorSplatZ(FXMVECTOR V);

//(Vw, Vw, Vw, Vw)
XMVECTOR    XM_CALLCONV     XMVectorSplatW(FXMVECTOR V);

이 외에 XMVECTOR Instance의 한 성분만 읽거나, 변경하는 조회, 설정 함수들도 제공한다.

float       XM_CALLCONV     XMVectorGetX(FXMVECTOR V);
float       XM_CALLCONV     XMVectorGetY(FXMVECTOR V);
float       XM_CALLCONV     XMVectorGetZ(FXMVECTOR V);
float       XM_CALLCONV     XMVectorGetW(FXMVECTOR V);

XMVECTOR    XM_CALLCONV     XMVectorSetX(FXMVECTOR V, float x);
XMVECTOR    XM_CALLCONV     XMVectorSetY(FXMVECTOR V, float y);
XMVECTOR    XM_CALLCONV     XMVectorSetZ(FXMVECTOR V, float z);
XMVECTOR    XM_CALLCONV     XMVectorSetW(FXMVECTOR V, float w);

 

Const XMVECTOR Instance에는 반드시 XMVECTORF32 형식을 사용해야 한다.

간단히 말해, 초기화 구문을 사용하고자 할 때에는 항상 XMVECTORF32를 사용해야 한다는 것이다.

XMVECTORF32는 16Byte 경계로 정렬된 Structure로, XMVECTOR로의 변호나 연산자를 지원한다.

// Conversion types for constants
__declspec(align(16)) struct XMVECTORF32
{
    union
    {
        float f[4];
        XMVECTOR v;
    };

    inline operator XMVECTOR() const { return v; }
    inline operator const float*() const { return f; }
#if !defined(_XM_NO_INTRINSICS_) && defined(_XM_SSE_INTRINSICS_)
    inline operator __m128i() const { return _mm_castps_si128(v); }
    inline operator __m128d() const { return _mm_castps_pd(v); }
#endif
};

또한 XMVECTORU32를 이용해 정수 자료를 담은 상수 XMVECTOR를 생성하는 것도 가능하다.

__declspec(align(16)) struct XMVECTORU32
{
    union
    {
        uint32_t u[4];
        XMVECTOR v;
    };

    inline operator XMVECTOR() const { return v; }
#if !defined(_XM_NO_INTRINSICS_) && defined(_XM_SSE_INTRINSICS_)
    inline operator __m128i() const { return _mm_castps_si128(v); }
    inline operator __m128d() const { return _mm_castps_pd(v); }
#endif
};
XMGLOBALCONST XMVECTORU32 g_XMMaskX                 = { { { 0xFFFFFFFF, 0x00000000, 0x00000000, 0x00000000 } } };
XMGLOBALCONST XMVECTORU32 g_XMMaskY                 = { { { 0x00000000, 0xFFFFFFFF, 0x00000000, 0x00000000 } } };
XMGLOBALCONST XMVECTORU32 g_XMMaskZ                 = { { { 0x00000000, 0x00000000, 0xFFFFFFFF, 0x00000000 } } };
XMGLOBALCONST XMVECTORU32 g_XMMaskW                 = { { { 0x00000000, 0x00000000, 0x00000000, 0xFFFFFFFF } } };

XMVECTOR에는 Vector의 덧셈, 뺄셈, 스칼라 곱셈을 위한 Operator Overloading이 지원된다.

보통은 직관적이라 활성화 하지만, 일부 응용 프로그램은 성능상의 이유로 이를 비활성화 한다.

Operator Overloading을 비활성화 하고 싶으면 macro 상수 XM_NO_OPERATOR_OVERLOADS를 정의해야 한다.

// Vector operators

#ifndef _XM_NO_XMVECTOR_OVERLOADS_
XMVECTOR    XM_CALLCONV     operator+ (FXMVECTOR V);
XMVECTOR    XM_CALLCONV     operator- (FXMVECTOR V);

XMVECTOR&   XM_CALLCONV     operator+= (XMVECTOR& V1, FXMVECTOR V2);
XMVECTOR&   XM_CALLCONV     operator-= (XMVECTOR& V1, FXMVECTOR V2);
XMVECTOR&   XM_CALLCONV     operator*= (XMVECTOR& V1, FXMVECTOR V2);
XMVECTOR&   XM_CALLCONV     operator/= (XMVECTOR& V1, FXMVECTOR V2);

XMVECTOR&   operator*= (XMVECTOR& V, float S);
XMVECTOR&   operator/= (XMVECTOR& V, float S);

XMVECTOR    XM_CALLCONV     operator+ (FXMVECTOR V1, FXMVECTOR V2);
XMVECTOR    XM_CALLCONV     operator- (FXMVECTOR V1, FXMVECTOR V2);
XMVECTOR    XM_CALLCONV     operator* (FXMVECTOR V1, FXMVECTOR V2);
XMVECTOR    XM_CALLCONV     operator/ (FXMVECTOR V1, FXMVECTOR V2);
XMVECTOR    XM_CALLCONV     operator* (FXMVECTOR V, float S);
XMVECTOR    XM_CALLCONV     operator* (float S, FXMVECTOR V);
XMVECTOR    XM_CALLCONV     operator/ (FXMVECTOR V, float S);
#endif /* !_XM_NO_XMVECTOR_OVERLOADS_ */

수학과 관련된 Library답게, DirectXMath Library에서도 다양한 공식에서 사용되는 상수의 근사값을 지원한다.

XM_CONST float XM_PI        = 3.141592654f;
XM_CONST float XM_2PI       = 6.283185307f;
XM_CONST float XM_1DIVPI    = 0.318309886f;
XM_CONST float XM_1DIV2PI   = 0.159154943f;
XM_CONST float XM_PIDIV2    = 1.570796327f;
XM_CONST float XM_PIDIV4    = 0.785398163f;

또한 radian과 degree를 변환하는 함수들도 제공한다.

// Unit conversion

inline XM_CONSTEXPR float XMConvertToRadians(float fDegrees) { return fDegrees * (XM_PI / 180.0f); }
inline XM_CONSTEXPR float XMConvertToDegrees(float fRadians) { return fRadians * (180.0f / XM_PI); }

그리고 최솟값, 최댓값을 위한 매크로 함수들도 정의한다.

#if defined(__XNAMATH_H__) && defined(XMMin)
#undef XMMin
#undef XMMax
#endif

template<class T> inline T XMMin(T a, T b) { return (a < b) ? a : b; }
template<class T> inline T XMMax(T a, T b) { return (a > b) ? a : b; }

 

XMVECTOR가 Operator Overloading으로만 연산을 하는 것은 아니다.

XMVECTOR 연산에 필요한 여러 함수들을 따로 지원하기도 한다.

/****************************************************************************
 *
 * 3D vector operations
 *
 ****************************************************************************/

bool        XM_CALLCONV     XMVector3Equal(FXMVECTOR V1, FXMVECTOR V2);
uint32_t    XM_CALLCONV     XMVector3EqualR(FXMVECTOR V1, FXMVECTOR V2);
bool        XM_CALLCONV     XMVector3EqualInt(FXMVECTOR V1, FXMVECTOR V2);
uint32_t    XM_CALLCONV     XMVector3EqualIntR(FXMVECTOR V1, FXMVECTOR V2);
bool        XM_CALLCONV     XMVector3NearEqual(FXMVECTOR V1, FXMVECTOR V2, FXMVECTOR Epsilon);
bool        XM_CALLCONV     XMVector3NotEqual(FXMVECTOR V1, FXMVECTOR V2);
bool        XM_CALLCONV     XMVector3NotEqualInt(FXMVECTOR V1, FXMVECTOR V2);
bool        XM_CALLCONV     XMVector3Greater(FXMVECTOR V1, FXMVECTOR V2);
uint32_t    XM_CALLCONV     XMVector3GreaterR(FXMVECTOR V1, FXMVECTOR V2);
bool        XM_CALLCONV     XMVector3GreaterOrEqual(FXMVECTOR V1, FXMVECTOR V2);
uint32_t    XM_CALLCONV     XMVector3GreaterOrEqualR(FXMVECTOR V1, FXMVECTOR V2);
bool        XM_CALLCONV     XMVector3Less(FXMVECTOR V1, FXMVECTOR V2);
bool        XM_CALLCONV     XMVector3LessOrEqual(FXMVECTOR V1, FXMVECTOR V2);
bool        XM_CALLCONV     XMVector3InBounds(FXMVECTOR V, FXMVECTOR Bounds);

bool        XM_CALLCONV     XMVector3IsNaN(FXMVECTOR V);
bool        XM_CALLCONV     XMVector3IsInfinite(FXMVECTOR V);

XMVECTOR    XM_CALLCONV     XMVector3Dot(FXMVECTOR V1, FXMVECTOR V2);
XMVECTOR    XM_CALLCONV     XMVector3Cross(FXMVECTOR V1, FXMVECTOR V2);
XMVECTOR    XM_CALLCONV     XMVector3LengthSq(FXMVECTOR V);
XMVECTOR    XM_CALLCONV     XMVector3ReciprocalLengthEst(FXMVECTOR V);
XMVECTOR    XM_CALLCONV     XMVector3ReciprocalLength(FXMVECTOR V);
XMVECTOR    XM_CALLCONV     XMVector3LengthEst(FXMVECTOR V);
XMVECTOR    XM_CALLCONV     XMVector3Length(FXMVECTOR V);
XMVECTOR    XM_CALLCONV     XMVector3NormalizeEst(FXMVECTOR V);
XMVECTOR    XM_CALLCONV     XMVector3Normalize(FXMVECTOR V);
XMVECTOR    XM_CALLCONV     XMVector3ClampLength(FXMVECTOR V, float LengthMin, float LengthMax);
XMVECTOR    XM_CALLCONV     XMVector3ClampLengthV(FXMVECTOR V, FXMVECTOR LengthMin, FXMVECTOR LengthMax);
XMVECTOR    XM_CALLCONV     XMVector3Reflect(FXMVECTOR Incident, FXMVECTOR Normal);
XMVECTOR    XM_CALLCONV     XMVector3Refract(FXMVECTOR Incident, FXMVECTOR Normal, float RefractionIndex);
XMVECTOR    XM_CALLCONV     XMVector3RefractV(FXMVECTOR Incident, FXMVECTOR Normal, FXMVECTOR RefractionIndex);
XMVECTOR    XM_CALLCONV     XMVector3Orthogonal(FXMVECTOR V);
XMVECTOR    XM_CALLCONV     XMVector3AngleBetweenNormalsEst(FXMVECTOR N1, FXMVECTOR N2);
XMVECTOR    XM_CALLCONV     XMVector3AngleBetweenNormals(FXMVECTOR N1, FXMVECTOR N2);
XMVECTOR    XM_CALLCONV     XMVector3AngleBetweenVectors(FXMVECTOR V1, FXMVECTOR V2);
XMVECTOR    XM_CALLCONV     XMVector3LinePointDistance(FXMVECTOR LinePoint1, FXMVECTOR LinePoint2, FXMVECTOR Point);
void        XM_CALLCONV     XMVector3ComponentsFromNormal(_Out_ XMVECTOR* pParallel, _Out_ XMVECTOR* pPerpendicular, _In_ FXMVECTOR V, _In_ FXMVECTOR Normal);
XMVECTOR    XM_CALLCONV     XMVector3Rotate(FXMVECTOR V, FXMVECTOR RotationQuaternion);
XMVECTOR    XM_CALLCONV     XMVector3InverseRotate(FXMVECTOR V, FXMVECTOR RotationQuaternion);
XMVECTOR    XM_CALLCONV     XMVector3Transform(FXMVECTOR V, FXMMATRIX M);
XMFLOAT4*   XM_CALLCONV     XMVector3TransformStream(_Out_writes_bytes_(sizeof(XMFLOAT4)+OutputStride*(VectorCount-1)) XMFLOAT4* pOutputStream,
                                                     _In_ size_t OutputStride,
                                                     _In_reads_bytes_(sizeof(XMFLOAT3)+InputStride*(VectorCount-1)) const XMFLOAT3* pInputStream,
                                                     _In_ size_t InputStride, _In_ size_t VectorCount, _In_ FXMMATRIX M);
XMVECTOR    XM_CALLCONV     XMVector3TransformCoord(FXMVECTOR V, FXMMATRIX M);
XMFLOAT3*   XM_CALLCONV     XMVector3TransformCoordStream(_Out_writes_bytes_(sizeof(XMFLOAT3)+OutputStride*(VectorCount-1)) XMFLOAT3* pOutputStream,
                                                          _In_ size_t OutputStride,
                                                          _In_reads_bytes_(sizeof(XMFLOAT3)+InputStride*(VectorCount-1)) const XMFLOAT3* pInputStream,
                                                          _In_ size_t InputStride, _In_ size_t VectorCount, _In_ FXMMATRIX M);
XMVECTOR    XM_CALLCONV     XMVector3TransformNormal(FXMVECTOR V, FXMMATRIX M);
XMFLOAT3*   XM_CALLCONV     XMVector3TransformNormalStream(_Out_writes_bytes_(sizeof(XMFLOAT3)+OutputStride*(VectorCount-1)) XMFLOAT3* pOutputStream,
                                                           _In_ size_t OutputStride,
                                                           _In_reads_bytes_(sizeof(XMFLOAT3)+InputStride*(VectorCount-1)) const XMFLOAT3* pInputStream,
                                                           _In_ size_t InputStride, _In_ size_t VectorCount, _In_ FXMMATRIX M);
XMVECTOR    XM_CALLCONV     XMVector3Project(FXMVECTOR V, float ViewportX, float ViewportY, float ViewportWidth, float ViewportHeight, float ViewportMinZ, float ViewportMaxZ,
                                             FXMMATRIX Projection, CXMMATRIX View, CXMMATRIX World);
XMFLOAT3*   XM_CALLCONV     XMVector3ProjectStream(_Out_writes_bytes_(sizeof(XMFLOAT3)+OutputStride*(VectorCount-1)) XMFLOAT3* pOutputStream,
                                                   _In_ size_t OutputStride,
                                                   _In_reads_bytes_(sizeof(XMFLOAT3)+InputStride*(VectorCount-1)) const XMFLOAT3* pInputStream,
                                                   _In_ size_t InputStride, _In_ size_t VectorCount,
                                                   _In_ float ViewportX, _In_ float ViewportY, _In_ float ViewportWidth, _In_ float ViewportHeight, _In_ float ViewportMinZ, _In_ float ViewportMaxZ,
                                                   _In_ FXMMATRIX Projection, _In_ CXMMATRIX View, _In_ CXMMATRIX World);
XMVECTOR    XM_CALLCONV     XMVector3Unproject(FXMVECTOR V, float ViewportX, float ViewportY, float ViewportWidth, float ViewportHeight, float ViewportMinZ, float ViewportMaxZ,
                                               FXMMATRIX Projection, CXMMATRIX View, CXMMATRIX World);
XMFLOAT3*   XM_CALLCONV     XMVector3UnprojectStream(_Out_writes_bytes_(sizeof(XMFLOAT3)+OutputStride*(VectorCount-1)) XMFLOAT3* pOutputStream,
                                                     _In_ size_t OutputStride,
                                                     _In_reads_bytes_(sizeof(XMFLOAT3)+InputStride*(VectorCount-1)) const XMFLOAT3* pInputStream,
                                                     _In_ size_t InputStride, _In_ size_t VectorCount,
                                                     _In_ float ViewportX, _In_ float ViewportY, _In_ float ViewportWidth, _In_ float ViewportHeight, _In_ float ViewportMinZ, _In_ float ViewportMaxZ,
                                                     _In_ FXMMATRIX Projection, _In_ CXMMATRIX View, _In_ CXMMATRIX World);

-----------------------------

본 블로그는 [DirectX11을 이용한 3D 게임 프로그래밍 입문]을 기반으로 공부를 하며,
DirectX12와 다른 부분을 수정해가며 정리를 하는 글입니다.

 

한글 자료가 필요하시다면, 부족하게나마 도움이 되었으면 합니다.

만약 더 자세한 내용이 필요하시다면, 공식 레퍼런스를 보시는 것을 추천합니다.

https://docs.microsoft.com/en-us/windows/win32/dxmath/ovw-xnamath-reference

 

DirectXMath programming reference - Win32 apps

DirectXMath programming reference In this article --> This section contains reference material for the DirectXMath Library. In this section DirectXMath DirectXMath programming guide -->

docs.microsoft.com

 

Linear Transform(선형 변환)

어떤 Vector에 대한 함수 중 다음 조건을 만족하고, 오직 그럴 때, 그 함수를 Linear Transform이라 부른다.

Linear Transform일 때에는 다음 또한 만족한다.

Standard Basis Vector(표준기저벡터)

현재 Coordinate System의 Axis들과 같은 방향인 Unit Vector.

Standard Basis Vector를 이용하면 일반 Vector도 다음과 같이 나타낼 수 있다.

이를 Linear Transform하면 그 Linearity가 성립하여 다음이 성립한다.

이는 이전에 한번 언급했던 Linear Combination이다.

따라서 하나의 Vector와 Matrix의 곱셈으로 표기할 수 있다.

이러한 Matrix ALinear Transform τMatrix Expression이라 부른다.

 

Scaling(비례)

물체의 크기를 바꾸는 효과를 내며, 선형변환이다.

Scaling Transform은 다음과 같이 정의된다.

Matrix Expression은 다음과 같다.

이 Matrix를 Scaling Matrix 라고 부른다.

 

Rotation(회전)

Vector v를 Axis n에 대해서 Θ 각도만큼 회전시키는 Transform이 있다.

이에 대한 Matrix Expression은 다음과 같다.

Rotation Matrix에는 흥미로운 속성이 있다.

Rotation Matrix의 각 Row Vector는 Unit size이고, Row Vector들은 서로 Orthogonal하다. 

따라서 Rotation Matrix는 Orthogonal Matrix이다.

 

Orthogonal Matrix는 Inverse Matrix이 자신의 Transposed Matrix와 같다는 속성이 있다.

이로 인해 Rotation Matrix의 Inverse Matrix는 다음과 같이 정의될 수 있다.

일반적으로 Inverse Matrix을 쉽고 효율적으로 계산할 수 있는 Orthogonal Matrix를 다루는 것이 바람직하다.

특히 Rotation Axis가 Standard Basis Vector인 경우에는 Rotation Matrix가 매우 간단해진다.

다음은 차례대로 x축, y축, z축에 대한 Rotation Matrix이다.

Homogeneous Coordinate(동차좌표)

3차원 Vector에 w 성분을 추가한 4-Element의 형태를 나타낸다.

추가된 성분 w는 서술하는 것이 Dot인지 Vector인지에 따라 그 값이 달라진다.

Vector인 경우에는 w 값은 0이, Dot인 경우에는 1이 된다.

나중에 보면 알겠지만, Dot에 대해서 w = 1인 경우, 정확한 이동이 가능하다. 

반대로 Vector에 대해서 w = 0인 경우, 이동 시 Vector가 변하지 않는다.

 

Rigid Body Transformation(강체 변환)

Transform 시 물체의 형태가 그대로 유지되는 Transformation.

 

Affine Transformation(아핀변환)

Linear Transformation에 Translation이 결합된 것.

Lintear Transformation으로 서술하지 못하는 Transformation을 서술하기 위해 사용된다.

이를 Matrix로 나타내면 아래와 같다.

여기에 w = 1인 HomogeneousCoordinate를 도입하면 아래와 같이 더 간결하게 표기할 수 있다.

위 식에서 사용된 4×4 Matrix를 Affine Transform의 Matrix Expression이라 부른다.

여기서 추가된 b는 하나의 Translation을 나타낸다는 점을 주목하자.

Vector에는 위치가 없으므로, 이 이동은 적용되지 말아야 한다.

하지만 Affine Transformation의 Linear Transformation 부분은 여전히 벡터에 적용되어야 한다.

Vector의 w = 0으로 설정함으로써, b에 의한 Translation은 적용되지 않는다.

 

그렇다면 Affine Transformation의 기하학적 의미는 무엇일까?

이는 Affine Transform을 Row Vector로 표시해보면 조금 더 쉽게 알아차릴 수 있다.

τ는 Rotation Transformation이므로 길이와 각도가 보존된다.

구체적으로, τ는 Standard Basis Vector i, j, k만 새로운 방향 τ(i), τ(j), τ(k)로회전한다.

Vector b는 단지 원점으로부터의 변위를 나타내는 Position Vector일 뿐이다.

이는 책상 위에 가지런히 올려져 있는 큐브를 손으로 집어 올린 모양을 생각하면 편하다.

또한 이런 원리는 Scaling이나 Skew(기울이기)에도 동일하게 적용된다.

그저 Standard Basis Vector가 왜곡되어 있을 뿐이다.

 

Identity Transformation(항등변환)

주어진 인수를 그대로 돌려주는 Linear Transformation.

이 Transformation의 Matrix Expression은 Unit Matrix이다.

이러한 맥락에서, Translation Transformation을 Affine Transformation으로 정의할 수 있다.

이를 Translation Matrix라고 부른다.

 

Change of Coordinate Transformation(좌표 변경 변환)

한 Coordinate System의 Coordinate를 다른 Coordinate System의 Coordinate로 Transform 하는 것을 말한다.

여기서 강조하고 싶은 것은 기하구조가 아니라 Coordinate System이 바뀐다는 점이다.

 

또한 Change of Coordinate Transformation은 결합 법칙이 성립된다.

이는 작게 보여도 생각보다 큰 성능 향상으로 이어지는 경우가 적지 않다.

 

Coordinate System F, G, H가 있다고 가정하자.

Change of Coordinate Transformation A는 Coordinate System F에서 G로, B는 G에서 H로 Transform 하는 Transformation이다.

이 때, C=AB일 때, C가 가지는 의미는 Coordinate System F에서 H로 Transform 하는 Transformation이다.

반대로 C의 Inverse Matrix는 Coordinate System H에서 F로 Transform 하는 Transformation이다.

마찬가지로 Inverse Matrix A와 B 역시 각각 G에서 F로, H에서 G로 Transform 하는 Transformation을 뜻한다.

 

여기서 다루는 Coordinate System은 모두 Reversible하다.

즉, 모든 Change of Coordinate Transformation Matrix는 Inverse Matrix가 존재한다.

 

또한 앞서 언급했던 모든 Rigid Body Transformation과 Change of Coordinate Transformation은 모두 동치이다.

생각해보면 당연하다. Change of Coordinate Transformation은 Coordinate System의 위치와 방향이 다 다르다.

때문에 한 Coordinate system에서 다른 Coordinate System으로의 Transformation에는 Coordinate들의 Rotation과 Translation이 필요하다.

이런 부분을 구해보면 결국 동일한 형태의 공식에 도달한다.

둘의 차이는 Transformation을 해석하는 방법의 차이 뿐이다.

 

----------------------------

 

이로써 책에서 언급한 이론적인 부분에 대한 정리는 모두 마쳤습니다.

다음에는 지난 2~3주간 설명한 부분들에 대한 DX12에서의 관련 함수들을 정리하고,
본격적인 DirectX12 Programming 입문과 함께 기본 코드의 분석을 병행하겠습니다.

해결해야 할 부분

  • 멀티플레이 시 Character를 찾지 못한다는 warning
    • 결국에는 Client에서 Controller가 Character를 찾지 못해서 생기는 문제.
      • Player Start Point가 하나였던걸로 기억하는데 이걸 여러개로 늘려서 테스트 해보려 한다.
    • Editor 테스트 뿐만 아니라 제대로 실행 시켰을 때도 같은 문제가 발생함.
  • Client에서 Interaction시 MovementMode가 변경 되었다가 몇 tick 후 원상복귀 됨
    • MovementMode를 write 하는 부분을 지워보았으나 여전히 값이 원상복귀됨.
    • 로직 상 문제라기 보다는 원래 구현상 있는 기능 중 무언가를 놓친 것 같다.
    • MovementComponent 코드를 읽어보면서 예상되는 부분을 찾아보자.
      • defaultground 어쩌고 부분에서 초기화가 될 수도 있을 것 같다. 
      • 하지만 정확한 것은 아니니 좀 더 정리를 해보자.

 

진도가 정말 느립니다.

posses 관련해서 찾아봤는데 명확한 해결 방법이 보이지 않습니다.

시간이 정말정말 오래 걸릴 것 같습니다.

'개발일지 > Treasure Hunter' 카테고리의 다른 글

20.05.25 개발일지  (0) 2020.05.25
20.05.23 개발일지  (2) 2020.05.23
20.05.20 개발일지  (0) 2020.05.20
20.05.18 개발일지  (0) 2020.05.18
20.05.16 개발일지  (0) 2020.05.16

오늘은 저번에 작성했던대로 Character의 Tick에 있던 정지 시 달리기 속도 초기화를 하는 작업을 MovementComponent의 OnMovementUpdated로 옮겼습니다.

처음에는 Character에 선언된 변수를 접근할 방법이 없어서 이 작업이 불가능 한줄 알았습니다.

하지만 MovementComponent의 MovementMode 값이 변하는 문제를 해결하기 위해 코드를 뜯어보다가 MovementComponent가 OwningCharacter라는 변수를 가지고 있는 것을 확인했습니다.

테스트 해 본 결과, MovementComponent가 Attach한 Character 객체였습니다.

이를 통해 Character가 선언한 변수나 함수에 접근이 가능해졌고,

OnMovementStop 함수를 선언해 기능을 이전했습니다.

 

이 외에는 크게 문제 해결을 하지 못했습니다.

길어도 일주일이면 갈피를 찾을 줄 알았는데 생각보다 정리가 잘 되지 않습니다.

그래서 당분간 우선적으로 수정 할 문제들과 시도한 것, 시도할 것을 개발일지로 적으려 합니다.

문젲가 모두 해결될 때까지 누적으로 위 사항들을 작성해나가겠습니다.

 

해결해야 할 부분

  • 멀티플레이 시 Character를 찾지 못한다는 warning
  • Client에서 Interaction시 MovementMode가 변경 되었다가 몇 tick 후 원상복귀 됨
    • MovementMode를 write 하는 부분을 지워보았으나 여전히 값이 원상복귀됨.
    • 로직 상 문제라기 보다는 원래 구현상 있는 기능 중 무언가를 놓친 것 같다.
    • MovementComponent 코드를 읽어보면서 예상되는 부분을 찾아보자.

'개발일지 > Treasure Hunter' 카테고리의 다른 글

20.05.23 개발일지  (2) 2020.05.23
20.05.21 개발일지  (0) 2020.05.21
20.05.18 개발일지  (0) 2020.05.18
20.05.16 개발일지  (0) 2020.05.16
20.05.14 개발일지  (0) 2020.05.14

https://www.acmicpc.net/contest/view/116

 

2015 ACM-ICPC 연습

 

www.acmicpc.net

오늘은 저번에 풀다 만 Virus 문제를 풀고, Party 문제를 해석하였습니다.

 

Virus 문제에서 고민을 했던 순회, 고립 등의 문제는 발생하지 않았습니다.

조건 상으로도 발생하지 않기도 했지만, 애당초 그런 입력을 주지 않는 것 같습니다.

코드를 제출 한 결과 50% 부분에서 시간초과가 났습니다.

대부분 이 부분에서 문제가 생기는 것 같은데, 몇 가지 시도를 하다가 우선은 보류하였습니다.

Vector를 사용하는 것이 문제가 될거라 생각하지는 않습니다.

동적할당으로 바꾸면 문제 해결에 도움은 될것이지만, 근본적인 알고리즘이 문제일 것이라 생각합니다.

 

Party는 아싸 컴공 학생들을 간호과 학생과 미팅시켜주는 문제입니다.

컴공과 학생과 간호과 학생을 불러 파티를 개최합니다.

파티 후 간호과 학생들이 선호하는 컴공과 학생 중 한명과 데이트를 합니다.

이 때 최소 몇번의 파티를 개최하면 모든 컴공과 학생들이 최소 1번은 데이트를 하는지 구하는 문제입니다.

단, 파티를 아무리 해도 모든 학생이 데이트를 하는 것이 불가능 하다면 이를 따로 출력해야 합니다.

 

문제 푸는 방식을 구상하는 것은 다음 주에 할 예정입니다.

'개발일지 > Algorithm' 카테고리의 다른 글

20.06.02 2015 ACM-ICPC 연습  (0) 2020.06.02
20.05.26 - 2015 ACM-ICPC 연습  (0) 2020.05.26
20.05.12 - 2015 ACM-ICPC 연습  (0) 2020.05.12
20.05.05 - 2015 ACM-ICPC 연습  (0) 2020.05.05
알고리즘 일지입니다.  (0) 2020.05.01

몇 주간의 개발 진행 기간을 거쳐 일주일간 발버둥 쳤으나 결국 정체기가 왔습니다.

 

오늘은 Multiplay 시작 시 발생하는 warning과 Climb 아무때나 시도되는 현상 수정,

그리고 Multiplay 상에서의 Climb를 살펴보았습니다.

 

우선 warning부분입니다.

로그를 자세히 보니, 상단에 Error가 발생하였습니다.

LogPlayerController: Error: EnableInput can only be specified on a PlayerController for itself 

이런 오류인데, EnableInput 호출 시 발생하는 문제입니다.

어디선가 무언가가 꼬인 것이라는 느낌이 있는데 아직 해결할 겨를이 안나기도 하고,

테스트에 문제가 없다고 판단하여 우선은 보류하고자 합니다.

 

Climb 문제는 Interaction 조건이 MovementMode로만 판단이 되고 있어서

실제로 벽이 있는지 없는지는 상관 없이 벽타기가 가능했습니다.

이를 해결하면서, 점프 중에 Climb가 되지 않는 현상도 수정했습니다.

 

마지막으로 Multiplay 중 Client의 Climb가 불가능한 현상입니다.

결론적으로 MovementType은 코드가 일부 누락이 되어 이를 추가해 수정하였지만.

MovementMode는 상호작용 시 값이 변했음에도 그 직후 값이 원상복귀가 되고 있습니다.

만약 Replicated라면 편안하게 RPC 함수를 만들었을테지만, 그렇지도 않아 해결방안에 고민이 듭니다.

 

다음에는 우선 Character의 Tick에 구현되어 있는 속도 조절 코드를 제거할 생각입니다.

MovementComponent에 OnMovementUpdate 함수에 추가를 해보고자 합니다.

그 이후에는 군데군데 건드려보면서 Multiplay상에서 Client의 Climb가 가능하도록 할 예정입니다.

'개발일지 > Treasure Hunter' 카테고리의 다른 글

20.05.21 개발일지  (0) 2020.05.21
20.05.20 개발일지  (0) 2020.05.20
20.05.16 개발일지  (0) 2020.05.16
20.05.14 개발일지  (0) 2020.05.14
20.05.13 개발일지  (0) 2020.05.13

Climbing 관련 기능이 점점 더 미궁에 빠져들고 있습니다.

 

우선 영상 촬영을 위해 Character의 Layered Motion 부분을 먼저 수정하였습니다.

역시 예상대로 Montage 변수명을 수정하면서 BP에서 연결이 끊겨 있었습니다.

이 부분을 연결해주자 대체로 잘 돌아갔습니다.

 

다만 Melee Attack 부분에 Print Log로 인한 crash가 발생하여 Print Log를 삭제하였습니다.

요즘들어 이런 문제가 자주 발생합니다.

Print Log에서 잘못된 메모리를 건드려 Crash가 나거나, 

잘 실행 되다가 Crash가 나는데 제가 작성한 코드 부분에서 발생하지 않았거나.

디버그를 할 수가 없어 상당히 난감합니다.

 

두번째로 Climb 부분의 Animation Trigger 변경을 위해 우선 Rope 부분만 변경을 해보았습니다.

하지만 정상적으로 작동하지 않았습니다.

게다가 Multiplay 테스트를 하면서 Host 쪽 Character를 움직일 때마다 warning이 발생하였습니다.

그 warning이 Climb Animation과 관련이 있는지 없는지 확인이 되지 않았습니다.

 

버그도 몇가지 발견했습니다.

첫번째로, Client를 두개 이상 띄울 때, 간혹 Session은 탐색은 했으나 Inactive 되어 접속을 못하는 현상을 발견했습니다.

또한 BlockTrap에 Character가 들락날락 하는 것만으로 벽이 움직이는 현상도 발견했습니다.

 

오늘 너무 심란하고 힘들었습니다.

문제가 해결되지 않을 뿐더러 정리도 잘 안되고, 그 때문에 진행도 더뎌지고 의욕을 많이 잃었습니다.

무엇보다 동기 부여가 많이 약해진것을 느꼈습니다.

그래서 당장 마무리 할 수 있는 기능들을 우선적으로 마무리를 하고, 영상을 촬영하여 포트폴리오에 추가했습니다.

 

내일은 개발을 집중적으로 하기 보다는 천천히 정리를 해보는 시간을 가지려 합니다.

무언가 해결이 되면 좋지만, 그렇지 못하다고 해서 스트레스를 받았다가는 이후 일에 더 지장이 생길 것 같아서입니다.

잘 추스려서 월요일부터 다시 열심히 개발하겠습니다.

'개발일지 > Treasure Hunter' 카테고리의 다른 글

20.05.20 개발일지  (0) 2020.05.20
20.05.18 개발일지  (0) 2020.05.18
20.05.14 개발일지  (0) 2020.05.14
20.05.13 개발일지  (0) 2020.05.13
20.05.11 개발일지  (0) 2020.05.11

+ Recent posts