// Project Lab - NHTV Igad #include "UnrealProject.h" #include "CharacterCarousel.h" #include "CharacterBase.h" #include "DefaultGameInstance.h" ACharacterCarousel::ACharacterCarousel() { PrimaryActorTick.bCanEverTick = true; selectedCharacterIndex = 0; m_visualSelected = 0; selected = false; m_zoom = 0; selectedCharacterOffset = FVector(30, -120, 0); } void ACharacterCarousel::BeginPlay() { check(IsValid(characterBlueprint)); Super::BeginPlay(); // Fix the rotation FRotator rotation = GetActorRotation(); SetActorRotation(FRotator(0, rotation.Yaw, 0)); const FVector position = GetActorLocation(); const FVector forward = GetActorForwardVector(); const FVector right = GetActorRightVector(); UDefaultGameInstance* instance = Cast(GetGameInstance()); check(instance); UCharacterSettings* settings = instance->GetCharacterSettings(); check(settings); for (int32 i = 0; i < settings->characterSaves.Num(); i++) { FActorSpawnParameters spawnParams; spawnParams.SpawnCollisionHandlingOverride = ESpawnActorCollisionHandlingMethod::AlwaysSpawn; ACharacterBase* actor = GetWorld()->SpawnActor(characterBlueprint, GetActorLocation(), GetActorRotation(), spawnParams); m_characters.Add(actor); } } void ACharacterCarousel::EndPlay(const EEndPlayReason::Type EndPlayReason) { Super::EndPlay(EndPlayReason); for (int32 i = 0; i < m_characters.Num(); i++) { if (IsValid(m_characters[i])) m_characters[i]->Destroy(); } m_characters.SetNum(0); } inline float MoveTowardsTarget(float current, float target, float delta, float min = 0.0005f) { const int32 moveDir = target > current ? 1 : -1; float d = FMath::Abs((target - current) * delta); if (d < 0.0005f) d = 0.0005f; current += d * moveDir; // Clamp current = (moveDir == 1) ? (current > target ? target : current) : (current < target ? target : current); return current; } void ACharacterCarousel::Tick(float DeltaTime) { Super::Tick(DeltaTime); if (m_characters.Num() == 0) return; // Zoom towards the target const float zoomTarget = selected ? 1 : 0; m_zoom = MoveTowardsTarget(m_zoom, zoomTarget, DeltaTime * 10); const FVector position = GetActorLocation(); const FVector forward = GetActorForwardVector(); const FVector right = GetActorRightVector(); const float carouselRadius = 1200; const float carouselAngle = 10; const FVector pivot = position - forward * carouselRadius; // Clamp the selected index (may be altered from a blueprint) selectedCharacterIndex = selectedCharacterIndex < 0 ? 0 : selectedCharacterIndex >= m_characters.Num() ? m_characters.Num() - 1 : selectedCharacterIndex; // Animate the carousel const float target = float(selectedCharacterIndex); m_visualSelected = MoveTowardsTarget(m_visualSelected, target, DeltaTime * (selected ? 10 : 5)); // Spawn and set the characters const FRotator rotation = GetActorRotation(); const FVector arm = forward * carouselRadius; const FVector selectedOffset = rotation.RotateVector(selectedCharacterOffset) * m_zoom; for (int32 i = 0; i < m_characters.Num(); i++) { const int32 distance = i - selectedCharacterIndex; if (distance < -2 || distance > 2) { // Dont render characters off screen m_characters[i]->GetRootComponent()->SetVisibility(false, true); } else { m_characters[i]->GetRootComponent()->SetVisibility(true, true); // Set the position relative from the pivot FRotator rotator(0, -carouselAngle * (float(i) - m_visualSelected), 0); if (m_zoom > 0) rotator.Yaw *= (m_zoom + 1); FVector offset = rotator.RotateVector(arm); if (i == selectedCharacterIndex) offset += selectedOffset; m_characters[i]->SetActorLocation(pivot + offset); m_characters[i]->SetActorRotation(rotator + FRotator(0, rotation.Yaw, 0)); } } } void ACharacterCarousel::Reset() { selectedCharacterIndex = 0; m_visualSelected = 0; selected = false; m_zoom = 0; } void ACharacterCarousel::Next() { if(!selected && m_characters.Num() > 0 && selectedCharacterIndex < m_characters.Num() - 1) selectedCharacterIndex++; } void ACharacterCarousel::Previous() { if (!selected && selectedCharacterIndex > 0) selectedCharacterIndex--; } void ACharacterCarousel::ToggleSelect(bool enabled) { selected = enabled; } void ACharacterCarousel::CreateNewCharacter(const FString& name) { UDefaultGameInstance* instance = Cast(GetGameInstance()); check(instance); UCharacterSettings* settings = instance->GetCharacterSettings(); check(settings); // Create a scene actor ACharacterBase* actor = GetWorld()->SpawnActor(characterBlueprint, GetActorLocation(), GetActorRotation()); m_characters.Add(actor); // Select this character selected = false; selectedCharacterIndex = m_characters.Num() - 1; m_zoom = 0; // Add to save FCharacterSave save; save.name = name; settings->characterSaves.Add(save); instance->SaveSettings(); } void ACharacterCarousel::DeleteCharacter(int32 index) { UDefaultGameInstance* instance = Cast(GetGameInstance()); check(instance); UCharacterSettings* settings = instance->GetCharacterSettings(); check(settings); if (index < 0 || index >= settings->characterSaves.Num()) { JERROR("Invalid character index"); return; } // Destroy scene actor m_characters[index]->Destroy(); m_characters.RemoveAt(index); if (index == selectedCharacterIndex) selectedCharacterIndex--; // Unselect selected = false; m_zoom = 0; // Remove from save settings->characterSaves.RemoveAt(index); instance->SaveSettings(); } ACharacterBase* ACharacterCarousel::GetCharacterModel(int32 index) { if (index < 0 || index >= m_characters.Num()) { JERROR("Invalid character index"); return nullptr; } return m_characters[index]; } int32 ACharacterCarousel::GetCharacterNum() { return m_characters.Num(); } void ACharacterCarousel::SaveCharacterName(const FString& name, int32 index) { UDefaultGameInstance* instance = Cast(GetGameInstance()); check(instance); UCharacterSettings* settings = instance->GetCharacterSettings(); check(settings); if (index < 0 || index >= settings->characterSaves.Num()) { JERROR("Invalid character index"); return; } settings->characterSaves[index].name = name; instance->SaveSettings(); } FString ACharacterCarousel::GetCharacterName(int32 index) { UDefaultGameInstance* instance = Cast(GetGameInstance()); check(instance); UCharacterSettings* settings = instance->GetCharacterSettings(); check(settings); if (index < 0 || index >= settings->characterSaves.Num()) { JERROR("Invalid character index"); return FString(); } return settings->characterSaves[index].name; }