Example #1
0
bool APawn::IsBasedOnActor(const AActor* Other) const
{
	UPrimitiveComponent * MovementBase = GetMovementBase();
	AActor* MovementBaseActor = MovementBase ? MovementBase->GetOwner() : NULL;

	if (MovementBaseActor && MovementBaseActor == Other)
	{
		return true;
	}

	return Super::IsBasedOnActor(Other);
}
void ConvertQueryOverlap(const PxShape* PShape, const PxRigidActor* PActor, FOverlapResult& OutOverlap, const PxFilterData& QueryFilter)
{
	const bool bBlock = IsBlocking(PShape, QueryFilter);

	FBodyInstance* BodyInst = FPhysxUserData::Get<FBodyInstance>(PActor->userData);
	FDestructibleChunkInfo* ChunkInfo = FPhysxUserData::Get<FDestructibleChunkInfo>(PActor->userData);

	// Grab actor/component
	UPrimitiveComponent* OwnerComponent = nullptr;

	// Try body instance
	if (BodyInst)
	{
		OwnerComponent = BodyInst->OwnerComponent.Get(); // cache weak pointer object, avoid multiple derefs below.
		if (OwnerComponent)
		{
			OutOverlap.Actor = OwnerComponent->GetOwner();
			OutOverlap.Component = BodyInst->OwnerComponent; // Copying weak pointer is faster than assigning raw pointer.
			OutOverlap.ItemIndex = OwnerComponent->bMultiBodyOverlap ? BodyInst->InstanceBodyIndex : INDEX_NONE;
		}
	}
	
	if (!OwnerComponent)
	{
		// Try chunk info
		if (ChunkInfo)
		{
			OwnerComponent = ChunkInfo->OwningComponent.Get(); // cache weak pointer object, avoid multiple derefs below.
			if (OwnerComponent)
			{
				OutOverlap.Actor = OwnerComponent->GetOwner();
				OutOverlap.Component = ChunkInfo->OwningComponent; // Copying weak pointer is faster than assigning raw pointer.
				OutOverlap.ItemIndex = OwnerComponent->bMultiBodyOverlap ? UDestructibleComponent::ChunkIdxToBoneIdx(ChunkInfo->ChunkIndex) : INDEX_NONE;
			}
		}
	}	

	// Other info
	OutOverlap.bBlockingHit = bBlock;
}
ALODActor* FHierarchicalLODUtilities::GetParentLODActor(const AActor* InActor)
{
	ALODActor* ParentActor = nullptr;
	if (InActor)
	{
		TArray<UStaticMeshComponent*> ComponentArray;
		InActor->GetComponents<UStaticMeshComponent>(ComponentArray);
		for (auto Component : ComponentArray)
		{
			UPrimitiveComponent* ParentComponent = Component->GetLODParentPrimitive();
			if (ParentComponent)
			{
				ParentActor = CastChecked<ALODActor>(ParentComponent->GetOwner());
				break;
			}
		}
	}

	return ParentActor;
}
bool FPhysicsManipulationEdMode::StartTracking( FEditorViewportClient* InViewportClient, FViewport* InViewport )
{
	//UE_LOG(LogEditorPhysMode, Warning, TEXT("Start Tracking"));

	FVector GrabLocation(0,0,0);
	UPrimitiveComponent* ComponentToGrab = NULL;
	
	USelection* Selection = GEditor->GetSelectedActors();

	for (int32 i=0; i<Selection->Num(); ++i)
	{
		AActor* SelectedActor = Cast<AActor>(Selection->GetSelectedObject(i));

		if (SelectedActor != NULL)
		{
			UPrimitiveComponent* PC = Cast<UPrimitiveComponent>(SelectedActor->GetRootComponent());

			if (PC != NULL && PC->BodyInstance.bSimulatePhysics)
			{
				ComponentToGrab = PC;
				
				HandleTargetLocation = SelectedActor->GetActorLocation();
				HandleTargetRotation = SelectedActor->GetActorRotation();
				break;
			}

			if (ComponentToGrab != NULL) { break; }
		}
	}

	if (ComponentToGrab != NULL)
	{
		HandleComp->GrabComponent(ComponentToGrab, NAME_None, ComponentToGrab->GetOwner()->GetActorLocation(), true);
	}

	return FEdMode::StartTracking(InViewportClient, InViewport);
}
Example #5
0
bool APawn::IsNetRelevantFor(const AActor* RealViewer, const AActor* ViewTarget, const FVector& SrcLocation) const
{
	CA_SUPPRESS(6011);
	if (bAlwaysRelevant || RealViewer == Controller || IsOwnedBy(ViewTarget) || IsOwnedBy(RealViewer) || this == ViewTarget || ViewTarget == Instigator
		|| IsBasedOnActor(ViewTarget) || (ViewTarget && ViewTarget->IsBasedOnActor(this)))
	{
		return true;
	}
	else if( (bHidden || bOnlyRelevantToOwner) && (!GetRootComponent() || !GetRootComponent()->IsCollisionEnabled()) ) 
	{
		return false;
	}
	else
	{
		UPrimitiveComponent* MovementBase = GetMovementBase();
		AActor* BaseActor = MovementBase ? MovementBase->GetOwner() : NULL;
		if ( MovementBase && BaseActor && GetMovementComponent() && ((Cast<const USkeletalMeshComponent>(MovementBase)) || (BaseActor == GetOwner())) )
		{
			return BaseActor->IsNetRelevantFor(RealViewer, ViewTarget, SrcLocation);
		}
	}

	return ((SrcLocation - GetActorLocation()).SizeSquared() < NetCullDistanceSquared);
}
FVector AAIController::GetFocalPoint()
{
	FBasedPosition FinalFocus;
	// find focus with highest priority
	for( int32 Index = FocusInformation.Priorities.Num()-1; Index >= 0; --Index)
	{
		const FFocusKnowledge::FFocusItem& Focusitem = FocusInformation.Priorities[Index];
		if ( Focusitem.Actor.IsValid() )
		{
			const AActor* Focus = Focusitem.Actor.Get();
			UPrimitiveComponent* MyBase = GetPawn() ? GetPawn()->GetMovementBase() : NULL;
			const bool bRequestedFocusIsBased = MyBase && Cast<const APawn>(Focus) && (Cast<const APawn>(Focus)->GetMovementBase() == MyBase);
			FinalFocus.Set(bRequestedFocusIsBased && MyBase ? MyBase->GetOwner() : NULL, Focus->GetActorLocation());
			break;
		}
		else if( !(*Focusitem.Position).IsZero() )
		{
			FinalFocus = Focusitem.Position;
			break;
		}
	}

	return *FinalFocus;
}
/** Set info in the HitResult (Actor, Component, PhysMaterial, BoneName, Item) based on the supplied shape and face index */
static void SetHitResultFromShapeAndFaceIndex(const PxShape* PShape,  const PxRigidActor* PActor, const uint32 FaceIndex, FHitResult& OutResult, bool bReturnPhysMat)
{
	const FBodyInstance* BodyInst = FPhysxUserData::Get<FBodyInstance>(PActor->userData);
	FDestructibleChunkInfo* ChunkInfo = FPhysxUserData::Get<FDestructibleChunkInfo>(PShape->userData);
	UPrimitiveComponent* PrimComp = FPhysxUserData::Get<UPrimitiveComponent>(PShape->userData);

	if(BodyInst)
	{
		BodyInst = BodyInst->GetOriginalBodyInstance(PShape);
	}

	if (ChunkInfo == NULL)
	{
		ChunkInfo = FPhysxUserData::Get<FDestructibleChunkInfo>(PActor->userData);
	}

	UPrimitiveComponent* OwningComponent = ChunkInfo != NULL ? ChunkInfo->OwningComponent.Get() : NULL;
	if (OwningComponent == NULL && BodyInst != NULL)
	{
		OwningComponent = BodyInst->OwnerComponent.Get();
	}

	// If the shape has a different parent component, we take that one instead of the ChunkInfo. This can happen in some 
	// cases where APEX moves shapes internally to another actor ( ex. FormExtended structures )
	if (PrimComp != NULL && OwningComponent != PrimComp)
	{
		OwningComponent = PrimComp;
	}

	OutResult.PhysMaterial = NULL;

	bool bReturnBody = false;

	// Grab actor/component
	if( OwningComponent )
	{
		OutResult.Actor = OwningComponent->GetOwner();
		OutResult.Component = OwningComponent;

		if (bReturnPhysMat)
		{
			// This function returns the single material in all cases other than trimesh or heightfield
			if(PxMaterial* PxMat = PShape->getMaterialFromInternalFaceIndex(FaceIndex))
			{
				OutResult.PhysMaterial = FPhysxUserData::Get<UPhysicalMaterial>(PxMat->userData);
			}
		}

		bReturnBody = OwningComponent->bMultiBodyOverlap;
	}

	// For destructibles give the ChunkInfo-Index as Item
	if (bReturnBody && ChunkInfo)
	{
		OutResult.Item = ChunkInfo->ChunkIndex;

		UDestructibleComponent* DMComp = Cast<UDestructibleComponent>(OwningComponent);
		OutResult.BoneName = DMComp->GetBoneName(UDestructibleComponent::ChunkIdxToBoneIdx(ChunkInfo->ChunkIndex));
	}
	// If BodyInstance and not destructible, give BodyIndex as Item
	else if (BodyInst)
	{
		OutResult.Item = BodyInst->InstanceBodyIndex;

		const UBodySetup* BodySetup = BodyInst->BodySetup.Get();
		if (BodySetup)
		{
			OutResult.BoneName = BodySetup->BoneName;
		}
	}
	else
	{
		// invalid index
		OutResult.Item = INDEX_NONE;
		OutResult.BoneName = NAME_None;
	}

	OutResult.FaceIndex = INDEX_NONE;
}
void UGripMotionControllerComponent::TickGrip()
{
	if (GrippedActors.Num())
	{
		FTransform WorldTransform;
		FTransform InverseTransform = this->GetComponentTransform().Inverse();

		for (int i = GrippedActors.Num() - 1; i >= 0; --i)
		{
			if (GrippedActors[i].Actor || GrippedActors[i].Component)
			{
				// GetRelativeTransformReverse had some serious f*****g floating point errors associated with it that was f*****g everything up
				// Not sure whats wrong with the function but I might want to push a patch out eventually
				WorldTransform = GrippedActors[i].RelativeTransform.GetRelativeTransform(InverseTransform);

				UPrimitiveComponent *root = GrippedActors[i].Component;
				AActor *actor = GrippedActors[i].Actor;

				if (!root)
					root = Cast<UPrimitiveComponent>(GrippedActors[i].Actor->GetRootComponent());

				if (!root)
					continue;

				if (!actor)
					actor = root->GetOwner();

				if (!actor)
					continue;

				if (bIsServer)
				{
					// Handle collision intersection detection, this is used to intelligently manage some of the networking and late update features.
					switch (GrippedActors[i].GripCollisionType.GetValue())
					{
					case EGripCollisionType::InteractiveCollisionWithPhysics:
					case EGripCollisionType::InteractiveHybridCollisionWithSweep:
					{
						TArray<FOverlapResult> Hits;
						FComponentQueryParams Params(NAME_None, this->GetOwner());
						Params.bTraceAsyncScene = root->bCheckAsyncSceneOnMove;
						Params.AddIgnoredActor(actor);
						Params.AddIgnoredActors(root->MoveIgnoreActors);

						if(GetWorld()->ComponentOverlapMulti(Hits, root, root->GetComponentLocation(), root->GetComponentQuat(), Params))
						{
							GrippedActors[i].bColliding = true;
						}
						else
						{
							GrippedActors[i].bColliding = false;
						}
					}

					// Skip collision intersects with these types, they dont need it
					case EGripCollisionType::PhysicsOnly:
					case EGripCollisionType::SweepWithPhysics:
					case EGripCollisionType::InteractiveCollisionWithSweep:
					default:break;
					}
				}

				// Need to figure out best default behavior
				/*if (GrippedActors[i].bHasSecondaryAttachment && GrippedActors[i].SecondaryAttachment)
				{
				WorldTransform.SetRotation((WorldTransform.GetLocation() - GrippedActors[i].SecondaryAttachment->GetComponentLocation()).ToOrientationRotator().Quaternion());
				}*/

				if (GrippedActors[i].GripCollisionType == EGripCollisionType::InteractiveCollisionWithPhysics)
				{
					UpdatePhysicsHandleTransform(GrippedActors[i], WorldTransform);
				}
				else if (GrippedActors[i].GripCollisionType == EGripCollisionType::InteractiveCollisionWithSweep)
				{
					if (bIsServer)
					{
						FHitResult OutHit;

						// Need to use without teleport so that the physics velocity is updated for when the actor is released to throw
						root->SetWorldTransform(WorldTransform, true, &OutHit);

						if (OutHit.bBlockingHit)
						{
							GrippedActors[i].bColliding = true;

							if (!actor->bReplicateMovement)
								actor->SetReplicateMovement(true);
						}
						else
						{
							GrippedActors[i].bColliding = false;

							if (actor->bReplicateMovement)
								actor->SetReplicateMovement(false);
						}
					}
					else
					{
						if (!GrippedActors[i].bColliding)
						{
							root->SetWorldTransform(WorldTransform, true);
						}
					}
				}
				else if (GrippedActors[i].GripCollisionType == EGripCollisionType::InteractiveHybridCollisionWithSweep)
				{
					// Need to use without teleport so that the physics velocity is updated for when the actor is released to throw
					//if (bIsServer)
					//{
					FBPActorPhysicsHandleInformation * GripHandle = GetPhysicsGrip(GrippedActors[i]);
					if (!GrippedActors[i].bColliding)
					{
						// Make sure that there is no collision on course before turning off collision and snapping to controller
						if (bIsServer && actor && actor->bReplicateMovement)
						{
							FCollisionQueryParams QueryParams(NAME_None, false, this->GetOwner());
							FCollisionResponseParams ResponseParam;
							root->InitSweepCollisionParams(QueryParams, ResponseParam);
							QueryParams.AddIgnoredActor(actor);
							QueryParams.AddIgnoredActors(root->MoveIgnoreActors);

							if (GetWorld()->SweepTestByChannel(root->GetComponentLocation(), WorldTransform.GetLocation(), root->GetComponentQuat(), ECollisionChannel::ECC_Visibility, root->GetCollisionShape(), QueryParams, ResponseParam))
							{
								GrippedActors[i].bColliding = true;
							}
							else
							{
								GrippedActors[i].bColliding = false;
							}
						}
						
						// If still not colliding
						if (!GrippedActors[i].bColliding)
						{
							if (bIsServer && actor->bReplicateMovement) // So we don't call on on change event over and over locally
								actor->SetReplicateMovement(false);

							if (bIsServer && GripHandle)
							{
								DestroyPhysicsHandle(GrippedActors[i]);

								if(GrippedActors[i].Actor)
									GrippedActors[i].Actor->DisableComponentsSimulatePhysics();
								else
									root->SetSimulatePhysics(false);
							}

							root->SetWorldTransform(WorldTransform, false);
						}
						else
						{
							if (bIsServer && GrippedActors[i].bColliding && GripHandle)
								UpdatePhysicsHandleTransform(GrippedActors[i], WorldTransform);
						}

					}
					else if (GrippedActors[i].bColliding && !GripHandle)
					{
						if (bIsServer && !actor->bReplicateMovement)
							actor->SetReplicateMovement(true);

						root->SetSimulatePhysics(true);

						if (bIsServer)
							SetUpPhysicsHandle(GrippedActors[i]);
					}
					else
					{
						if (bIsServer && GrippedActors[i].bColliding && GripHandle)
							UpdatePhysicsHandleTransform(GrippedActors[i], WorldTransform);
					}
				}
				else if (GrippedActors[i].GripCollisionType == EGripCollisionType::SweepWithPhysics)
				{
					if (bIsServer)
					{
						
						FVector OriginalPosition(root->GetComponentLocation());
						FRotator OriginalOrientation(root->GetComponentRotation());

						FVector NewPosition(WorldTransform.GetTranslation());
						FRotator NewOrientation(WorldTransform.GetRotation());

						// Now sweep collision separately so we can get hits but not have the location altered
						if (bUseWithoutTracking || NewPosition != OriginalPosition || NewOrientation != OriginalOrientation)
						{
							FVector move = NewPosition - OriginalPosition;

							// ComponentSweepMulti does nothing if moving < KINDA_SMALL_NUMBER in distance, so it's important to not try to sweep distances smaller than that. 
							const float MinMovementDistSq = (FMath::Square(4.f*KINDA_SMALL_NUMBER));

							if (bUseWithoutTracking || move.SizeSquared() > MinMovementDistSq || NewOrientation != OriginalOrientation)
							{
								if (CheckComponentWithSweep(root, move, NewOrientation, false))
								{

								}
							}
						}

						// Move the actor, we are not offsetting by the hit result anyway
						root->SetWorldTransform(WorldTransform, false);
					}
					else
					{
						// Move the actor, we are not offsetting by the hit result anyway
						root->SetWorldTransform(WorldTransform, false);
					}
				}
				else if (GrippedActors[i].GripCollisionType == EGripCollisionType::PhysicsOnly)
				{
					// Move the actor, we are not offsetting by the hit result anyway
					root->SetWorldTransform(WorldTransform, false);
				}
			}
			else
			{
				if (bIsServer)
				{
					DestroyPhysicsHandle(GrippedActors[i]);

					GrippedActors.RemoveAt(i); // If it got garbage collected then just remove the pointer, won't happen with new uproperty use, but keeping it here anyway
				}
			}

		}
	}

}
void UGripMotionControllerComponent::NotifyGrip_Implementation(const FBPActorGripInformation &NewGrip)
{

	switch (NewGrip.GripCollisionType.GetValue())
	{
	case EGripCollisionType::InteractiveCollisionWithPhysics:
	{
		if (bIsServer)
		{
			if (NewGrip.Component && NewGrip.Component->GetOwner())
				NewGrip.Component->GetOwner()->SetReplicateMovement(false);
			else if (NewGrip.Actor)
				NewGrip.Actor->SetReplicateMovement(false);
		}
		
		SetUpPhysicsHandle(NewGrip);

	} break;

	// Skip collision intersects with these types, they dont need it
	case EGripCollisionType::PhysicsOnly:
	case EGripCollisionType::SweepWithPhysics:
	case EGripCollisionType::InteractiveHybridCollisionWithSweep:
	case EGripCollisionType::InteractiveCollisionWithSweep:
	default: 
	{
		if (NewGrip.Component)
			NewGrip.Component->SetSimulatePhysics(false);
		else if(NewGrip.Actor)
			NewGrip.Actor->DisableComponentsSimulatePhysics();

		if (bIsServer)
		{
			if (NewGrip.Component && NewGrip.Component->GetOwner())
				NewGrip.Component->GetOwner()->SetReplicateMovement(false);
			else if(NewGrip.Actor)
				NewGrip.Actor->SetReplicateMovement(false);
		}
	} break;

	}

	UPrimitiveComponent *root = NewGrip.Component;

	if (!root && NewGrip.Actor)
	{
		root = Cast<UPrimitiveComponent>(NewGrip.Actor->GetRootComponent());

		if (root)
			root->SetEnableGravity(false);

		this->IgnoreActorWhenMoving(NewGrip.Actor, true);
	}
	else if (root)
	{
		root->SetEnableGravity(false);

		if(root->GetOwner())
			this->IgnoreActorWhenMoving(root->GetOwner(), true);
	}
}
bool UGripMotionControllerComponent::CheckComponentWithSweep(UPrimitiveComponent * ComponentToCheck, FVector Move, FRotator newOrientation, bool bSkipSimulatingComponents/*,  bool &bHadBlockingHitOut*/)
{
	TArray<FHitResult> Hits;
	// WARNING: HitResult is only partially initialized in some paths. All data is valid only if bFilledHitResult is true.
	FHitResult BlockingHit(NoInit);
	BlockingHit.bBlockingHit = false;
	BlockingHit.Time = 1.f;
	bool bFilledHitResult = false;
	bool bMoved = false;
	bool bIncludesOverlapsAtEnd = false;
	bool bRotationOnly = false;

	UPrimitiveComponent *root = ComponentToCheck;

	if (!root || !root->IsQueryCollisionEnabled())
		return false;

	FVector start(root->GetComponentLocation());

	const bool bCollisionEnabled = root->IsQueryCollisionEnabled();

	if (bCollisionEnabled)
	{
#if !(UE_BUILD_SHIPPING || UE_BUILD_TEST)
		if (!root->IsRegistered())
		{
			UE_LOG(LogTemp, Warning, TEXT("MovedComponent %s not initialized in grip motion controller"), *root->GetFullName());
		}
#endif

		UWorld* const MyWorld = GetWorld();
		FComponentQueryParams Params(TEXT("sweep_params"), root->GetOwner());

		FCollisionResponseParams ResponseParam;
		root->InitSweepCollisionParams(Params, ResponseParam);

		bool const bHadBlockingHit = MyWorld->ComponentSweepMulti(Hits, root, start, start + Move, newOrientation, Params);

		if (bHadBlockingHit)
		{
			int32 BlockingHitIndex = INDEX_NONE;
			float BlockingHitNormalDotDelta = BIG_NUMBER;
			for (int32 HitIdx = 0; HitIdx < Hits.Num(); HitIdx++)
			{
				const FHitResult& TestHit = Hits[HitIdx];

				// Ignore the owning actor to the motion controller
				if (TestHit.Actor == this->GetOwner() || (bSkipSimulatingComponents && TestHit.Component->IsSimulatingPhysics()))
				{
					if (Hits.Num() == 1)
					{
						//bHadBlockingHitOut = false;
						return false;
					}
					else
						continue;
				}

				if (TestHit.bBlockingHit && TestHit.IsValidBlockingHit())
				{
					if (TestHit.Time == 0.f)
					{
						// We may have multiple initial hits, and want to choose the one with the normal most opposed to our movement.
						const float NormalDotDelta = (TestHit.ImpactNormal | Move);
						if (NormalDotDelta < BlockingHitNormalDotDelta)
						{
							BlockingHitNormalDotDelta = NormalDotDelta;
							BlockingHitIndex = HitIdx;
						}
					}
					else if (BlockingHitIndex == INDEX_NONE)
					{
						// First non-overlapping blocking hit should be used, if an overlapping hit was not.
						// This should be the only non-overlapping blocking hit, and last in the results.
						BlockingHitIndex = HitIdx;
						break;
					}
					//}
				}
			}

			// Update blocking hit, if there was a valid one.
			if (BlockingHitIndex >= 0)
			{
				BlockingHit = Hits[BlockingHitIndex];
				bFilledHitResult = true;
			}
		}
	}

	// Handle blocking hit notifications. Avoid if pending kill (which could happen after overlaps).
	if (BlockingHit.bBlockingHit && !root->IsPendingKill())
	{
		check(bFilledHitResult);
		if (root->IsDeferringMovementUpdates())
		{
			FScopedMovementUpdate* ScopedUpdate = root->GetCurrentScopedMovement();
			ScopedUpdate->AppendBlockingHitAfterMove(BlockingHit);
		}
		else
		{
			if(root->GetOwner())
				root->DispatchBlockingHit(*root->GetOwner(), BlockingHit);
		}

		return true;
	}

	return false;
}
void FGameplayDebuggerCategory_AI::CollectData(APlayerController* OwnerPC, AActor* DebugActor)
{
	APawn* MyPawn = Cast<APawn>(DebugActor);
	ACharacter* MyChar = Cast<ACharacter>(MyPawn);
	DataPack.PawnName = MyPawn ? MyPawn->GetHumanReadableName() : FString(TEXT("{red}No selected pawn."));
	DataPack.bIsUsingCharacter = (MyChar != nullptr);

	AAIController* MyController = MyPawn ? Cast<AAIController>(MyPawn->Controller) : nullptr;
	DataPack.bHasController = (MyController != nullptr);
	if (MyController)
	{
		if (MyController->IsPendingKill() == false)
		{
			DataPack.ControllerName = MyController->GetName();
		}
		else
		{
			DataPack.ControllerName = TEXT("Controller PENDING KILL");
		}
	}
	else
	{
		DataPack.ControllerName = TEXT("No Controller");
	}

	if (MyPawn && !MyPawn->IsPendingKill())
	{
		UCharacterMovementComponent* CharMovementComp = MyChar ? MyChar->GetCharacterMovement() : nullptr;
		if (CharMovementComp)
		{
			UPrimitiveComponent* FloorComponent = MyPawn->GetMovementBase();
			AActor* FloorActor = FloorComponent ? FloorComponent->GetOwner() : nullptr;
			DataPack.MovementBaseInfo = FloorComponent ? FString::Printf(TEXT("%s.%s"), *GetNameSafe(FloorActor), *FloorComponent->GetName()) : FString(TEXT("None"));
			DataPack.MovementModeInfo = CharMovementComp->GetMovementName();
		}

		UBehaviorTreeComponent* BehaviorComp = MyController ? Cast<UBehaviorTreeComponent>(MyController->BrainComponent) : nullptr;
		DataPack.bIsUsingBehaviorTree = (BehaviorComp != nullptr);
		if (BehaviorComp)
		{
			DataPack.CurrentAITask = BehaviorComp->DescribeActiveTasks();
			DataPack.CurrentAIState = BehaviorComp->IsRunning() ? TEXT("Running") : BehaviorComp->IsPaused() ? TEXT("Paused") : TEXT("Inactive");
			DataPack.CurrentAIAssets = BehaviorComp->DescribeActiveTrees();
		}

		UGameplayTasksComponent* TasksComponent = MyController ? MyController->GetGameplayTasksComponent() : nullptr;
		DataPack.bIsUsingGameplayTasks = (TasksComponent != nullptr);
		if (TasksComponent)
		{
			for (FConstGameplayTaskIterator It = TasksComponent->GetTickingTaskIterator(); It; ++It)
			{
				const UGameplayTask* TaskOb = *It;
				if (TaskOb)
				{
					DataPack.TickingTaskInfo += DescribeTaskHelper(*TaskOb);
					DataPack.NumTickingTasks++;
				}
			}

			for (FConstGameplayTaskIterator It = TasksComponent->GetPriorityQueueIterator(); It; ++It)
			{
				const UGameplayTask* TaskOb = *It;
				if (TaskOb)
				{
					DataPack.TaskQueueInfo += DescribeTaskHelper(*TaskOb);
					DataPack.NumTasksInQueue++;
				}
			}
		}

		DataPack.MontageInfo = MyChar ? GetNameSafe(MyChar->GetCurrentMontage()) : FString();

		UNavigationSystem* NavSys = UNavigationSystem::GetCurrent(MyPawn->GetWorld());
		const ANavigationData* NavData = MyController && NavSys ? NavSys->GetNavDataForProps(MyController->GetNavAgentPropertiesRef()) : nullptr;
		DataPack.NavDataInfo = NavData ? NavData->GetConfig().Name.ToString() : FString();

		CollectPathData(MyController);
	}
	else
	{
		PathDataPack.PathCorridor.Reset();
		PathDataPack.PathPoints.Reset();
	}
}