Files
Unreal_EmberWild/Source/Survival_projet/Private/CharacterPreviewActor.cpp
T
2026-08-11 15:23:10 +02:00

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);
}
}