diff --git a/TargetSystem/Content/Maps/TargetSystemTestMap.umap b/TargetSystem/Content/Maps/TargetSystemTestMap.umap index b2513b1..fc477f7 100644 Binary files a/TargetSystem/Content/Maps/TargetSystemTestMap.umap and b/TargetSystem/Content/Maps/TargetSystemTestMap.umap differ diff --git a/TargetSystem/Content/Maps/TargetSystemTest_SplitScreen_Map.umap b/TargetSystem/Content/Maps/TargetSystemTest_SplitScreen_Map.umap new file mode 100644 index 0000000..5433b6d Binary files /dev/null and b/TargetSystem/Content/Maps/TargetSystemTest_SplitScreen_Map.umap differ diff --git a/TargetSystem/Source/TargetSystem/Private/TargetSystemComponent.cpp b/TargetSystem/Source/TargetSystem/Private/TargetSystemComponent.cpp index b6518d4..d029f47 100644 --- a/TargetSystem/Source/TargetSystem/Private/TargetSystemComponent.cpp +++ b/TargetSystem/Source/TargetSystem/Private/TargetSystemComponent.cpp @@ -33,50 +33,54 @@ void UTargetSystemComponent::BeginPlay() return; } - OwnerPlayerController = UGameplayStatics::GetPlayerController(GetWorld(), 0); - if (!OwnerPlayerController) + OwnerPawn = Cast(OwnerActor); + if (!ensure(OwnerPawn)) { - TS_LOG(Error, TEXT("[%s] TargetSystemComponent: Cannot get PlayerController reference ..."), *OwnerActor->GetName()); + TS_LOG(Error, TEXT("[%s] TargetSystemComponent: Component is meant to be added to Pawn only ..."), *GetName()); return; } + + SetupLocalPlayerController(); } void UTargetSystemComponent::TickComponent(const float DeltaTime, const ELevelTick TickType, FActorComponentTickFunction* ThisTickFunction) { Super::TickComponent(DeltaTime, TickType, ThisTickFunction); - if (bTargetLocked && LockedOnTargetActor) + if (!bTargetLocked || !LockedOnTargetActor) { - if (!TargetIsTargetable(LockedOnTargetActor)) - { - TargetLockOff(); - return; - } + return; + } - SetControlRotationOnTarget(LockedOnTargetActor); + if (!TargetIsTargetable(LockedOnTargetActor)) + { + TargetLockOff(); + return; + } + + SetControlRotationOnTarget(LockedOnTargetActor); + + // Target Locked Off based on Distance + if (GetDistanceFromCharacter(LockedOnTargetActor) > MinimumDistanceToEnable) + { + TargetLockOff(); + } - // Target Locked Off based on Distance - if (GetDistanceFromCharacter(LockedOnTargetActor) > MinimumDistanceToEnable) + if (ShouldBreakLineOfSight() && !bIsBreakingLineOfSight) + { + if (BreakLineOfSightDelay <= 0) { TargetLockOff(); } - - if (ShouldBreakLineOfSight() && !bIsBreakingLineOfSight) + else { - if (BreakLineOfSightDelay <= 0) - { - TargetLockOff(); - } - else - { - bIsBreakingLineOfSight = true; - GetWorld()->GetTimerManager().SetTimer( - LineOfSightBreakTimerHandle, - this, - &UTargetSystemComponent::BreakLineOfSight, - BreakLineOfSightDelay - ); - } + bIsBreakingLineOfSight = true; + GetWorld()->GetTimerManager().SetTimer( + LineOfSightBreakTimerHandle, + this, + &UTargetSystemComponent::BreakLineOfSight, + BreakLineOfSightDelay + ); } } } @@ -309,33 +313,44 @@ bool UTargetSystemComponent::ShouldSwitchTargetActor(const float AxisValue) void UTargetSystemComponent::TargetLockOn(AActor* TargetToLockOn) { - if (TargetToLockOn) + if (!IsValid(TargetToLockOn)) { - bTargetLocked = true; - if (bShouldDrawLockedOnWidget) - { - CreateAndAttachTargetLockedOnWidgetComponent(TargetToLockOn); - } + return; + } - if (bShouldControlRotation) - { - ControlRotation(true); - } + // Recast PlayerController in case it wasn't already setup on Begin Play (local split screen) + SetupLocalPlayerController(); - if (bAdjustPitchBasedOnDistanceToTarget || bIgnoreLookInput) + bTargetLocked = true; + if (bShouldDrawLockedOnWidget) + { + CreateAndAttachTargetLockedOnWidgetComponent(TargetToLockOn); + } + + if (bShouldControlRotation) + { + ControlRotation(true); + } + + if (bAdjustPitchBasedOnDistanceToTarget || bIgnoreLookInput) + { + if (IsValid(OwnerPlayerController)) { OwnerPlayerController->SetIgnoreLookInput(true); } + } - if (OnTargetLockedOn.IsBound()) - { - OnTargetLockedOn.Broadcast(TargetToLockOn); - } + if (OnTargetLockedOn.IsBound()) + { + OnTargetLockedOn.Broadcast(TargetToLockOn); } } void UTargetSystemComponent::TargetLockOff() { + // Recast PlayerController in case it wasn't already setup on Begin Play (local split screen) + SetupLocalPlayerController(); + bTargetLocked = false; if (TargetLockedOnWidgetComponent) { @@ -349,7 +364,10 @@ void UTargetSystemComponent::TargetLockOff() ControlRotation(false); } - OwnerPlayerController->ResetIgnoreLookInput(); + if (IsValid(OwnerPlayerController)) + { + OwnerPlayerController->ResetIgnoreLookInput(); + } if (OnTargetLockedOff.IsBound()) { @@ -374,6 +392,11 @@ void UTargetSystemComponent::CreateAndAttachTargetLockedOnWidgetComponent(AActor UMeshComponent* MeshComponent = TargetActor->FindComponentByClass(); USceneComponent* ParentComponent = MeshComponent && LockedOnWidgetParentSocket != NAME_None ? MeshComponent : TargetActor->GetRootComponent(); + if (IsValid(OwnerPlayerController)) + { + TargetLockedOnWidgetComponent->SetOwnerPlayer(OwnerPlayerController->GetLocalPlayer()); + } + TargetLockedOnWidgetComponent->ComponentTags.Add(FName("TargetSystem.LockOnWidget")); TargetLockedOnWidgetComponent->SetWidgetSpace(EWidgetSpace::Screen); TargetLockedOnWidgetComponent->SetupAttachment(ParentComponent, LockedOnWidgetParentSocket); @@ -410,6 +433,17 @@ bool UTargetSystemComponent::TargetIsTargetable(const AActor* Actor) return true; } +void UTargetSystemComponent::SetupLocalPlayerController() +{ + if (!IsValid(OwnerPawn)) + { + TS_LOG(Error, TEXT("[%s] TargetSystemComponent: Component is meant to be added to Pawn only ..."), *GetName()); + return; + } + + OwnerPlayerController = Cast(OwnerPawn->GetController()); +} + AActor* UTargetSystemComponent::FindNearestTarget(TArray Actors) const { TArray ActorsHit; @@ -481,8 +515,14 @@ bool UTargetSystemComponent::LineTrace(FHitResult& HitResult, const AActor* Othe ); } -FRotator UTargetSystemComponent::GetControlRotationOnTarget(AActor* OtherActor) const +FRotator UTargetSystemComponent::GetControlRotationOnTarget(const AActor* OtherActor) const { + if (!IsValid(OwnerPlayerController)) + { + TS_LOG(Warning, TEXT("UTargetSystemComponent::GetControlRotationOnTarget - OwnerPlayerController is not valid ...")) + return FRotator::ZeroRotator; + } + const FRotator ControlRotation = OwnerPlayerController->GetControlRotation(); const FVector CharacterLocation = OwnerActor->GetActorLocation(); @@ -518,7 +558,7 @@ FRotator UTargetSystemComponent::GetControlRotationOnTarget(AActor* OtherActor) void UTargetSystemComponent::SetControlRotationOnTarget(AActor* TargetActor) const { - if (!OwnerPlayerController) + if (!IsValid(OwnerPlayerController)) { return; } @@ -570,13 +610,14 @@ void UTargetSystemComponent::BreakLineOfSight() void UTargetSystemComponent::ControlRotation(const bool ShouldControlRotation) const { - APawn* Pawn = Cast(OwnerActor); - if (Pawn) + if (!IsValid(OwnerPawn)) { - Pawn->bUseControllerRotationYaw = ShouldControlRotation; + return; } - UCharacterMovementComponent* CharacterMovementComponent = OwnerActor->FindComponentByClass(); + OwnerPawn->bUseControllerRotationYaw = ShouldControlRotation; + + UCharacterMovementComponent* CharacterMovementComponent = OwnerPawn->FindComponentByClass(); if (CharacterMovementComponent) { CharacterMovementComponent->bOrientRotationToMovement = !ShouldControlRotation; @@ -585,7 +626,7 @@ void UTargetSystemComponent::ControlRotation(const bool ShouldControlRotation) c bool UTargetSystemComponent::IsInViewport(const AActor* TargetActor) const { - if (!OwnerPlayerController) + if (!IsValid(OwnerPlayerController)) { return true; } diff --git a/TargetSystem/Source/TargetSystem/Public/TargetSystemComponent.h b/TargetSystem/Source/TargetSystem/Public/TargetSystemComponent.h index 65ae1cd..940e71b 100644 --- a/TargetSystem/Source/TargetSystem/Public/TargetSystemComponent.h +++ b/TargetSystem/Source/TargetSystem/Public/TargetSystemComponent.h @@ -176,6 +176,9 @@ class TARGETSYSTEM_API UTargetSystemComponent : public UActorComponent UPROPERTY() AActor* OwnerActor; + UPROPERTY() + APawn* OwnerPawn; + UPROPERTY() APlayerController* OwnerPlayerController; @@ -196,7 +199,7 @@ class TARGETSYSTEM_API UTargetSystemComponent : public UActorComponent bool bDesireToSwitch = false; float StartRotatingStack = 0.0f; - /** Actors search / trace */ + //~ Actors search / trace TArray GetAllActorsOfClass(TSubclassOf ActorClass) const; TArray FindTargetsInRange(TArray ActorsToLook, float RangeMin, float RangeMax) const; @@ -214,9 +217,9 @@ class TARGETSYSTEM_API UTargetSystemComponent : public UActorComponent float GetDistanceFromCharacter(const AActor* OtherActor) const; - /** Actor rotation */ + //~ Actor rotation - FRotator GetControlRotationOnTarget(AActor* OtherActor) const; + FRotator GetControlRotationOnTarget(const AActor* OtherActor) const; void SetControlRotationOnTarget(AActor* TargetActor) const; void ControlRotation(bool ShouldControlRotation) const; @@ -225,11 +228,11 @@ class TARGETSYSTEM_API UTargetSystemComponent : public UActorComponent static FRotator FindLookAtRotation(const FVector Start, const FVector Target); - /** Widget */ + //~ Widget void CreateAndAttachTargetLockedOnWidgetComponent(AActor* TargetActor); - /** Targeting */ + //~ Targeting void TargetLockOn(AActor* TargetToLockOn); void ResetIsSwitchingTarget(); @@ -237,6 +240,13 @@ class TARGETSYSTEM_API UTargetSystemComponent : public UActorComponent static bool TargetIsTargetable(const AActor* Actor); + /** + * Sets up cached Owner PlayerController from Owner Pawn. + * + * For local split screen, Pawn's Controller may not have been setup already when this component begins play. + */ + void SetupLocalPlayerController(); + protected: // Called when the game starts virtual void BeginPlay() override;