Brick프로젝트-7

칼든개구리·2025년 4월 14일
1

결과 화면에 승자는 서서 있고 패배자는 엎드린 채로 승자는 3초뒤에 움직일 수 있고 패배자는 저 몽타주를 재생하고 움직일 수 없도록 만들려고 했다.

#include "Character/GoResultTrigger.h"
#include "Components/BoxComponent.h"
#include "Kismet/GameplayStatics.h"
#include "GameFramework/PlayerController.h"
#include "EngineUtils.h"
#include "GameFramework/Pawn.h"

AGoResultTrigger::AGoResultTrigger()
{
    PrimaryActorTick.bCanEverTick = false;

    TriggerBox = CreateDefaultSubobject<UBoxComponent>(TEXT("TriggerBox"));
    RootComponent = TriggerBox;
    TriggerBox->SetBoxExtent(FVector(100.f));
    TriggerBox->SetCollisionProfileName(TEXT("Trigger"));

    TriggerCount = 0;
    RequiredPlayerCount = 4; 
    bReadyToCount = false;
}

void AGoResultTrigger::BeginPlay()
{
    Super::BeginPlay();

    TriggerBox->OnComponentBeginOverlap.AddDynamic(this, &AGoResultTrigger::OnOverlapBegin);

    TriggerBox->SetCollisionEnabled(ECollisionEnabled::NoCollision);

    FTimerHandle DelayHandle;
    GetWorld()->GetTimerManager().SetTimer(DelayHandle, FTimerDelegate::CreateLambda([this]()
        {
            int32 Count = 0;
            for (TActorIterator<APawn> It(GetWorld()); It; ++It)
            {
                if (*It && It->IsPlayerControlled())
                {
                    Count++;
                }
            }

            RequiredPlayerCount = Count;
            bReadyToCount = true;

            // ✅ 이제 트리거 작동 가능하게 다시 켜기
            TriggerBox->SetCollisionEnabled(ECollisionEnabled::QueryOnly);

            UE_LOG(LogTemp, Warning, TEXT("플레이어 수 확정: %d"), RequiredPlayerCount);

        }), 0.5f, false);
}



void AGoResultTrigger::OnOverlapBegin(UPrimitiveComponent* OverlappedComp, AActor* OtherActor,
    UPrimitiveComponent* OtherComp, int32 OtherBodyIndex,
    bool bFromSweep, const FHitResult& SweepResult)
{
    if (!bReadyToCount) return;

    if (OtherActor && OtherActor->IsA(APawn::StaticClass()))
    {
        APawn* OverlappingPawn = Cast<APawn>(OtherActor);

        if (OverlappingPawn && !OverlappedPlayers.Contains(OverlappingPawn))
        {
            OverlappedPlayers.Add(OverlappingPawn);
            TriggerCount++;

            UE_LOG(LogTemp, Warning, TEXT("트리거 진입: %s (%d/%d)"), *OtherActor->GetName(), TriggerCount, RequiredPlayerCount);

            if (HasAuthority() && TriggerCount >= RequiredPlayerCount)
            {
                UE_LOG(LogTemp, Warning, TEXT("모든 플레이어가 도착했습니다. 결과 씬으로 이동!"));
                GetWorld()->ServerTravel(TEXT("/Game/Maps/ResultLevel?listen"));
                return; 
            }

        }
    }
}

이게 결과창으로 향하는 트리거 코드이고

#include "Network_Structure/ResultGameMode.h"
#include "Network_Structure/BrickInGameState.h"
#include "Character/BrickCharacter.h"
#include "Network_Structure/BrickGamePlayerState.h"
#include "Kismet/GameplayStatics.h"
#include "EngineUtils.h"
#include "Camera/CameraActor.h"
#include "GameFramework/PlayerController.h"

AResultGameMode::AResultGameMode()
{
}

void AResultGameMode::BeginPlay()
{
    Super::BeginPlay();

    FString CurrentLevel = GetWorld()->GetMapName();
    if (!CurrentLevel.Contains(TEXT("ResultLevel"))) return;

    FTimerHandle DelayHandle;
    GetWorld()->GetTimerManager().SetTimer(DelayHandle, FTimerDelegate::CreateLambda([this]() {
        ABrickInGameState* GS = GetGameState<ABrickInGameState>();
        if (!GS) return;

        EGameTeam WinningTeam = GS->GetWinningTeam();

        TArray<AActor*> WinSpots;
        TArray<AActor*> LoseSpots;
        UGameplayStatics::GetAllActorsWithTag(this, FName("WinSpot"), WinSpots);
        UGameplayStatics::GetAllActorsWithTag(this, FName("LoseSpot"), LoseSpots);

        TArray<ABrickCharacter*> AllWinners;
        TArray<ABrickCharacter*> AllLosers;

        for (TActorIterator<ABrickCharacter> It(GetWorld()); It; ++It)
        {
            ABrickCharacter* Char = *It;
            if (!Char) continue;

            ABrickGamePlayerState* PS = Cast<ABrickGamePlayerState>(Char->GetPlayerState());
            if (!PS) continue;

            if (PS->GetTeam() == WinningTeam)
                AllWinners.Add(Char);
            else
                AllLosers.Add(Char);
        }

        auto SortByPlayerID = [](const ABrickCharacter& A, const ABrickCharacter& B) {
            auto PSA = Cast<ABrickGamePlayerState>(A.GetPlayerState());
            auto PSB = Cast<ABrickGamePlayerState>(B.GetPlayerState());
            return PSA && PSB && PSA->GetBrickPlayerID() < PSB->GetBrickPlayerID();
            };

        AllWinners.Sort(SortByPlayerID);
        AllLosers.Sort(SortByPlayerID);

        TArray<ABrickCharacter*> WinningChars;
        TArray<ABrickCharacter*> LosingChars;

        // 승리팀 배치
        for (int32 i = 0; i < AllWinners.Num(); ++i)
        {
            if (!WinSpots.IsValidIndex(i)) break;

            ABrickCharacter* Char = AllWinners[i];
            if (!Char) continue;

            if (Char->HasAuthority())
            {
                Char->SetActorLocation(WinSpots[i]->GetActorLocation());
                Char->SetActorRotation(WinSpots[i]->GetActorRotation());
                Char->MulticastFixMeshRotation(FRotator(0.f, 0.f, 0.f));
            }

            Char->GetMesh()->SetRelativeRotation(FRotator(0.f, 0.f, 0.f));
            Char->AttachCrown();
            Char->PlayVictoryMontage();
            Char->SetMovementEnabled(false);

            WinningChars.Add(Char);

            FTimerHandle TempHandle;
            GetWorld()->GetTimerManager().SetTimer(TempHandle, FTimerDelegate::CreateWeakLambda(Char, [Char]() {
                Char->SetMovementEnabled(true);
                }), 3.0f, false);
        }

        // 패배팀 배치
        for (int32 i = 0; i < AllLosers.Num(); ++i)
        {
            if (!LoseSpots.IsValidIndex(i)) break;

            ABrickCharacter* Char = AllLosers[i];
            if (!Char || !Char->HasAuthority()) continue;

            Char->SetActorLocation(LoseSpots[i]->GetActorLocation());
            Char->SetActorRotation(LoseSpots[i]->GetActorRotation());
            Char->GetMesh()->SetRelativeRotation(FRotator(0.f, 0.f, 0.f));
            Char->PlayDefeatMontage();
            Char->SetMovementEnabled(false);

            LosingChars.Add(Char);
        }

        // 로컬 컨트롤러 캐릭터만 회전 보정
        for (FConstPlayerControllerIterator It = GetWorld()->GetPlayerControllerIterator(); It; ++It)
        {
            if (APlayerController* PC = Cast<APlayerController>(It->Get()))
            {
                ABrickCharacter* MyChar = Cast<ABrickCharacter>(PC->GetPawn());
                if (!MyChar) continue;

                ABrickGamePlayerState* PS = Cast<ABrickGamePlayerState>(MyChar->GetPlayerState());
                if (!PS) continue;

                bool bIsWinner = (PS->GetTeam() == WinningTeam);
                int32 SpotIndex = bIsWinner ? AllWinners.Find(MyChar) : AllLosers.Find(MyChar);
                const TArray<AActor*>& SpotArray = bIsWinner ? WinSpots : LoseSpots;

                if (SpotArray.IsValidIndex(SpotIndex))
                {
                    MyChar->MulticastApplyFinalPose(SpotArray[SpotIndex]->GetActorRotation(), FRotator(0.f, 0.f, 0.f));
                }
            }
        }

        // 밀기 로직
        FTimerHandle PushCheckHandle;
        GetWorld()->GetTimerManager().SetTimer(PushCheckHandle, FTimerDelegate::CreateLambda([WinningChars, LosingChars]() {
            const float PushDistance = 100.f;
            const float PushStrength = 100.f;

            for (ABrickCharacter* Winner : WinningChars)
            {
                if (!Winner) continue;

                FVector WinnerLocation = Winner->GetActorLocation();
                FVector WinnerForward = Winner->GetActorForwardVector();

                for (ABrickCharacter* Loser : LosingChars)
                {
                    if (!Loser) continue;

                    FVector ToLoser = Loser->GetActorLocation() - WinnerLocation;
                    float Dist = ToLoser.Size();

                    if (Dist < PushDistance && FVector::DotProduct(WinnerForward, ToLoser.GetSafeNormal()) > 0.5f)
                    {
                        FVector PushVector = WinnerForward * PushStrength * 0.05f;
                        Loser->AddActorWorldOffset(PushVector, true);
                    }
                }
            }
            }), 0.05f, true);

        // 카메라 전환
        ACameraActor* ResultCamera = nullptr;
        for (TActorIterator<ACameraActor> CamIt(GetWorld()); CamIt; ++CamIt)
        {
            if (CamIt->ActorHasTag(FName("ResultCamera")))
            {
                ResultCamera = *CamIt;
                break;
            }
        }

        if (ResultCamera)
        {
            for (FConstPlayerControllerIterator It = GetWorld()->GetPlayerControllerIterator(); It; ++It)
            {
                if (APlayerController* PC = It->Get())
                {
                    PC->bAutoManageActiveCameraTarget = false;
                    PC->SetViewTargetWithBlend(ResultCamera, 1.0f);
                }
            }
        }
        }), 0.3f, false);
}

이건 게임 종료 씬에서 사용할 씬이다.

멀티 적용을 위해 플레이어코드에서도 수정이 필요했는데


void ABrickCharacter::PlayVictoryMontage()
{
	if (VictoryMontage && GetMesh() && GetMesh()->GetAnimInstance())
	{
		GetMesh()->GetAnimInstance()->Montage_Play(VictoryMontage);
	}
}

void ABrickCharacter::PlayDefeatMontage()
{
	if (DefeatMontage && GetMesh() && GetMesh()->GetAnimInstance())
	{
		UAnimInstance* AnimInst = GetMesh()->GetAnimInstance();

		SetMovementEnabled(false);
		bCanTurn = false;

		if (!AnimInst->Montage_IsPlaying(DefeatMontage))
		{
			AnimInst->Montage_Play(DefeatMontage, 1.0f);
			UE_LOG(LogTemp, Warning, TEXT("▶▶ DefeatMontage 재생 시작"));
		}
	}
}



void ABrickCharacter::SetMovementEnabled(bool bEnabled)
{
	bCanMove = bEnabled;

	if (UCharacterMovementComponent* MoveComp = GetCharacterMovement())
	{
		if (bEnabled)
		{
			MoveComp->SetMovementMode(MOVE_Walking);
		}
		else
		{
			MoveComp->DisableMovement(); 
		}
	}
}

bool ABrickCharacter::CanBeMoved() const
{
	return bCanMove;
}

void ABrickCharacter::MulticastFixMeshRotation_Implementation(FRotator NewRotation)
{
	if (GetMesh())
	{
		GetMesh()->SetRelativeRotation(NewRotation);
	}
}

void ABrickCharacter::MulticastApplyFinalPose_Implementation(FRotator ActorRot, FRotator MeshRot)
{
	UE_LOG(LogTemp, Warning, TEXT("MulticastApplyFinalPose CALLED on %s. Rot: %s"), *GetName(), *ActorRot.ToString());

	SetActorRotation(ActorRot);
	if (GetMesh())
	{
		GetMesh()->SetRelativeRotation(MeshRot);
	}
}

이겼을 때 틀어줄 몽타주와 졌을 때 몽타주를 할당하고 졌을 때 움직이지 못하도록 하는 코드를 추가해주고
멀티를 위해서 코드를 몇개 작성해주었다.

profile
메타쏭이

0개의 댓글