150 lines
4.8 KiB
C++
150 lines
4.8 KiB
C++
// Fill out your copyright notice in the Description page of Project Settings.
|
|
|
|
|
|
#include "CharacterPreviewActor.h"
|
|
|
|
#include "Camera/CameraComponent.h"
|
|
#include "CharacterAppearanceComponent.h"
|
|
#include "Components/SkeletalMeshComponent.h"
|
|
|
|
ACharacterPreviewActor::ACharacterPreviewActor()
|
|
{
|
|
// Allume par SetFraming et eteint des que la camera est arrivee. Un
|
|
// mannequin immobile dans un menu n'a aucune raison de couter une frame.
|
|
PrimaryActorTick.bCanEverTick = true;
|
|
PrimaryActorTick.bStartWithTickEnabled = false;
|
|
|
|
USceneComponent* SceneRoot = CreateDefaultSubobject<USceneComponent>(TEXT("Root"));
|
|
SetRootComponent(SceneRoot);
|
|
|
|
TurntablePivot = CreateDefaultSubobject<USceneComponent>(TEXT("TurntablePivot"));
|
|
TurntablePivot->SetupAttachment(SceneRoot);
|
|
|
|
BodyMesh = CreateDefaultSubobject<USkeletalMeshComponent>(TEXT("BodyMesh"));
|
|
BodyMesh->SetupAttachment(TurntablePivot);
|
|
// Aucune collision : c'est un decor de menu, rien ne doit le heurter ni le
|
|
// tracer.
|
|
BodyMesh->SetCollisionEnabled(ECollisionEnabled::NoCollision);
|
|
|
|
// Les reperes de cadrage sont filles de la RACINE et pas du pivot : ils ne
|
|
// doivent surtout pas tourner avec le personnage, sinon la camera suivrait
|
|
// le clic-glisser au lieu de le montrer.
|
|
FullBodyViewSpot = CreateDefaultSubobject<USceneComponent>(TEXT("FullBodyViewSpot"));
|
|
FullBodyViewSpot->SetupAttachment(SceneRoot);
|
|
FullBodyViewSpot->SetRelativeLocation(FVector(260.f, 60.f, 95.f));
|
|
FullBodyViewSpot->SetRelativeRotation(FRotator(0.f, 180.f, 0.f));
|
|
|
|
FaceViewSpot = CreateDefaultSubobject<USceneComponent>(TEXT("FaceViewSpot"));
|
|
FaceViewSpot->SetupAttachment(SceneRoot);
|
|
FaceViewSpot->SetRelativeLocation(FVector(90.f, 25.f, 165.f));
|
|
FaceViewSpot->SetRelativeRotation(FRotator(0.f, 180.f, 0.f));
|
|
|
|
PreviewCamera = CreateDefaultSubobject<UCameraComponent>(TEXT("PreviewCamera"));
|
|
PreviewCamera->SetupAttachment(SceneRoot);
|
|
|
|
AppearanceComponent = CreateDefaultSubobject<UCharacterAppearanceComponent>(TEXT("AppearanceComponent"));
|
|
}
|
|
|
|
void ACharacterPreviewActor::BeginPlay()
|
|
{
|
|
Super::BeginPlay();
|
|
|
|
// Le composant cherche le mesh d'un ACharacter par defaut ; ici il n'y en a
|
|
// pas, on lui designe le notre.
|
|
if (AppearanceComponent)
|
|
{
|
|
AppearanceComponent->SetTargetMesh(BodyMesh);
|
|
}
|
|
|
|
// Pose la camera sur le cadrage de depart sans interpolation : glisser
|
|
// depuis l'origine de l'acteur au premier affichage montrerait un travelling
|
|
// que personne n'a demande.
|
|
if (const USceneComponent* Spot = GetSpotForFraming(CurrentFraming))
|
|
{
|
|
PreviewCamera->SetRelativeLocationAndRotation(Spot->GetRelativeLocation(), Spot->GetRelativeRotation());
|
|
}
|
|
}
|
|
|
|
void ACharacterPreviewActor::Tick(float DeltaSeconds)
|
|
{
|
|
Super::Tick(DeltaSeconds);
|
|
|
|
UpdateFramingInterpolation(DeltaSeconds);
|
|
}
|
|
|
|
void ACharacterPreviewActor::ApplyAppearance(const FCharacterAppearance& InAppearance)
|
|
{
|
|
if (AppearanceComponent)
|
|
{
|
|
AppearanceComponent->ApplyAppearance(InAppearance);
|
|
}
|
|
}
|
|
|
|
void ACharacterPreviewActor::SetFraming(ECharacterPreviewFraming NewFraming)
|
|
{
|
|
if (CurrentFraming == NewFraming)
|
|
{
|
|
return;
|
|
}
|
|
|
|
CurrentFraming = NewFraming;
|
|
SetActorTickEnabled(true);
|
|
}
|
|
|
|
void ACharacterPreviewActor::AddPreviewYaw(float DeltaDegrees)
|
|
{
|
|
TurntableYaw = FRotator::NormalizeAxis(TurntableYaw + DeltaDegrees);
|
|
|
|
if (TurntablePivot)
|
|
{
|
|
TurntablePivot->SetRelativeRotation(FRotator(0.f, TurntableYaw, 0.f));
|
|
}
|
|
}
|
|
|
|
void ACharacterPreviewActor::ResetPreviewYaw()
|
|
{
|
|
TurntableYaw = 0.f;
|
|
|
|
if (TurntablePivot)
|
|
{
|
|
TurntablePivot->SetRelativeRotation(FRotator::ZeroRotator);
|
|
}
|
|
}
|
|
|
|
USceneComponent* ACharacterPreviewActor::GetSpotForFraming(ECharacterPreviewFraming InFraming) const
|
|
{
|
|
return (InFraming == ECharacterPreviewFraming::Face) ? FaceViewSpot : FullBodyViewSpot;
|
|
}
|
|
|
|
void ACharacterPreviewActor::UpdateFramingInterpolation(float DeltaSeconds)
|
|
{
|
|
const USceneComponent* Spot = GetSpotForFraming(CurrentFraming);
|
|
if (!Spot || !PreviewCamera)
|
|
{
|
|
SetActorTickEnabled(false);
|
|
return;
|
|
}
|
|
|
|
const FVector TargetLocation = Spot->GetRelativeLocation();
|
|
const FRotator TargetRotation = Spot->GetRelativeRotation();
|
|
|
|
const FVector NewLocation = FMath::VInterpTo(
|
|
PreviewCamera->GetRelativeLocation(), TargetLocation, DeltaSeconds, FramingInterpSpeed);
|
|
const FRotator NewRotation = FMath::RInterpTo(
|
|
PreviewCamera->GetRelativeRotation(), TargetRotation, DeltaSeconds, FramingInterpSpeed);
|
|
|
|
PreviewCamera->SetRelativeLocationAndRotation(NewLocation, NewRotation);
|
|
|
|
// Un interpolateur exponentiel n'atteint jamais sa cible exactement : sans
|
|
// ce seuil, le Tick ne s'eteindrait plus jamais. On termine le dernier
|
|
// millimetre d'un coup, il ne se voit pas.
|
|
const bool bLocationReached = NewLocation.Equals(TargetLocation, 0.5f);
|
|
const bool bRotationReached = NewRotation.Equals(TargetRotation, 0.5f);
|
|
|
|
if (bLocationReached && bRotationReached)
|
|
{
|
|
PreviewCamera->SetRelativeLocationAndRotation(TargetLocation, TargetRotation);
|
|
SetActorTickEnabled(false);
|
|
}
|
|
}
|