솔루나 시프트 관련해서 AddMovementInput()
함수를 사용했을 때 가장 큰 문제가 있었다. 아무리 Max Flying Speed를 올려도 최대속도가 안나온다는 것. 언리얼 함수 로직의 가속도 관련된 부분때문에 그럴거라고 추측했고, 제대로 알기 위해 Movement 관련 함수를 학습했다.
ApplyControlInputToVelocity()
함수는 플레이어의 입력을 기반으로 속도를 업데이트하고 최대 속도를 넘지 않게 하며, 가속과 감속을 적용해 속도를 부드럽게 변경하는 것을 알 수 있었고, 여기서 '가속'과 '최대 속도'에 포커싱해서 최대 속도가 적용되도록 설정해보았다.
함수 내부를 건들 수는 없으므로, 솔루나 시프트 단계에서 AddMovementInput()
호출 이후 호출될 SetMaxSpeed()
함수를 만들어주었고, 실 구현부는 이렇다.
void UTrapperPlayerMovementComponent::SetMaxSpeed()
{
const FVector ControlAcceleration = GetPendingInputVector().GetClampedToMaxSize(1.f);
const float AnalogInputModifier_ = (ControlAcceleration.SizeSquared() > 0.f ? ControlAcceleration.Size() : 0.f);
const float MaxPawnSpeed = GetMaxSpeed() * AnalogInputModifier;
if (AnalogInputModifier_ > 0.f)
{
// 무조건 최대 속도로 설정
Velocity = ControlAcceleration.GetSafeNormal() * MaxPawnSpeed;
}
}
ApplyControlInputToVelocity()
함수에 있는 로직을 그대로 사용해 캐릭터의 입력값과 Max Speed를 가져와주고, Velocity를 무조건 최대 속도로 설정하도록 만들어주었다.
언리얼 엔진의 Velocity 계산을 덮어쓰게 만드는거라 호출 시점이 중요할 것 같은데, 가속도를 계산하는 ApplyControlInputToVelocity()
가 호출되는 UFloatingPawnMovement
의 TickComponent
가 언제 호출되는지는 아직 찾지 못했다(나중에 다시 볼 예정..). 어쨌든 PerformMovement()
함수 내에서 OnMovementUpdated()
가 호출(솔루나 시프트, 강제로 최대 속도로 변경한)된 이후 변수에 값이 저장되므로 적용될 것이라고 생각하고 실행해보았다.
가속도 때문에 최대 속도가 나오지 않는 문제는 얼추 해결됐다. 이제 Max Flying Speed에 따라 속도가 변화하는걸 체감할 수 있다.
이제 다음으로 해야할 것은, 자성이동 캐릭터 컨트롤 상태를 추가하는 것! 이동 방향에 따라 캐릭터 메시가 회전되도록 해주어야 한다.
// TrapperPlayer.h
UENUM(BlueprintType)
enum class ECharacterControlType : uint8
{
Moving UMETA(DisplayName = "Moving"),
MovingAlt UMETA(DisplayName = "MovingAlt"),
Idle UMETA(DisplayName = "Idle"),
IdleAlt UMETA(DisplayName = "IdleAlt"),
Drawing UMETA(DisplayName = "Drawing"),
MagneticMoving UMETA(DisplayName = "MagneticMoving"),
};
캐릭터 컨트롤 타입에 MagneticMoving
을 설정해주고,
if (Movement->GetMagneticMovingState())
{
ControlState = ECharacterControlType::MagneticMoving;
}
CharacterControlTypeCheck()
함수의 최하단에 ControlState
를 설정해주는 코드를 추가해주었다.
case ECharacterControlType::MagneticMoving:
if (!bCanCameraTurn) bUseControllerRotationYaw = true;
GetCharacterMovement()->bOrientRotationToMovement = false;
GetCharacterMovement()->bUseControllerDesiredRotation = true;
GetCharacterMovement()->RotationRate = InitialRotationRate;
해당하는 컨트롤 옵션을 설정해주고, 자성 이동을 시작할 때 컨트롤 타입을 체크하도록 호출해주면 끝!
솔루나 시프트를 사용 가능한 시점에, Outline이 그려짐과 동시에 이펙트가 동시에 출력되는 기능을 구현했다.
// AMagneticPillar.h
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Effects")
class UParticleSystemComponent* ShineOutline;
// AMagneticPillar.cpp
AMagneticPillar::AMagneticPillar()
{
// 생략
ShineOutline = CreateDefaultSubobject<UParticleSystemComponent>(TEXT("Particle"));
ShineOutline->bAutoActivate = false;
ShineOutline->SetupAttachment(Box);
static ConstructorHelpers::FObjectFinder<UParticleSystem> ParticleAsset(TEXT("/Script/Engine.ParticleSystem'/Game/Blueprints/VFX/Particles/P_ky_laser01.P_ky_laser01'"));
if (ParticleAsset.Succeeded())
{
ShineOutline->SetTemplate(ParticleAsset.Object);
}
}
void AMagneticPillar::SetOutline(bool Value)
{
Mesh->SetRenderCustomDepth(Value);
if (ShineOutline)
{
ShineOutline->ToggleActive();
}
}
UParticleSystemComponent
를 생성해주고 Template에 Particle System을 넣어준 뒤, Outline이 활성화 되는 부분에서 함께 활성화되도록 바꿔주었다.
목표지점에 도착하거나 중간에 스페이스바로 솔루나 시프트를 캔슬할 때 점프모드로 들어가도록 변경해주었다. 그리고 기획자분이 도착했을 때 좀 더 빠른속도로 착지하길 원하셔서, 도착했을 때 중력을 두배로 적용하도록 만들어 주었다.
먼저 타겟 위치를 지정할 때 상단지점보다 살짝 낮게 Z축을 변경해 주었고, 도착했을 때 점프 함수를 호출하고 중력을 두배 적용하도록 했다.
virtual void ProcessLanded(const FHitResult& Hit, float remainingTime, int32 Iterations) override;
void UTrapperPlayerMovementComponent::ProcessLanded(const FHitResult& Hit, float remainingTime, int32 Iterations)
{
Super::ProcessLanded(Hit, remainingTime, Iterations);
GravityScale = DefaultGravityScale;
}
착지 시에 중력이 원래대로 돌아와야 하므로, 착지할 때 호출되는 ProcessLanded()
함수를 오버라이드 하여 중력값을 원래대로 되돌려주는 코드를 넣어주었다.