From d941552a6698e1db0e9016f2818c559daafc4cd1 Mon Sep 17 00:00:00 2001 From: Ji-Rath Date: Thu, 14 Jul 2022 14:24:50 -0500 Subject: [PATCH 01/37] Add RTSFormationsPlugin --- .../Content/BP_FormationGameMode.uasset | 3 + .../Content/BP_FormationUnit.uasset | 3 + .../Content/BP_SpawnEntity.uasset | 3 + .../RTSFormations/Content/BP_SpawnUnit.uasset | 3 + .../Content/DA_AgentFormation.uasset | 3 + .../Content/RTSFormationsExample.umap | 3 + Plugins/RTSFormations/RTSFormations.uplugin | 46 ++++ Plugins/RTSFormations/Resources/Icon128.png | 3 + .../RTSFormations/Private/RTSAgentTraits.cpp | 20 ++ .../Private/RTSFormationProcessors.cpp | 239 ++++++++++++++++++ .../Private/RTSFormationProcessors.h | 72 ++++++ .../Private/RTSFormationSubsystem.cpp | 79 ++++++ .../Private/RTSFormationSubsystem.h | 79 ++++++ .../RTSFormations/Private/RTSFormations.cpp | 20 ++ .../RTSFormations/Public/RTSAgentTraits.h | 49 ++++ .../RTSFormations/Public/RTSFormations.h | 15 ++ .../RTSFormations/RTSFormations.Build.cs | 53 ++++ 17 files changed, 693 insertions(+) create mode 100644 Plugins/RTSFormations/Content/BP_FormationGameMode.uasset create mode 100644 Plugins/RTSFormations/Content/BP_FormationUnit.uasset create mode 100644 Plugins/RTSFormations/Content/BP_SpawnEntity.uasset create mode 100644 Plugins/RTSFormations/Content/BP_SpawnUnit.uasset create mode 100644 Plugins/RTSFormations/Content/DA_AgentFormation.uasset create mode 100644 Plugins/RTSFormations/Content/RTSFormationsExample.umap create mode 100644 Plugins/RTSFormations/RTSFormations.uplugin create mode 100644 Plugins/RTSFormations/Resources/Icon128.png create mode 100644 Plugins/RTSFormations/Source/RTSFormations/Private/RTSAgentTraits.cpp create mode 100644 Plugins/RTSFormations/Source/RTSFormations/Private/RTSFormationProcessors.cpp create mode 100644 Plugins/RTSFormations/Source/RTSFormations/Private/RTSFormationProcessors.h create mode 100644 Plugins/RTSFormations/Source/RTSFormations/Private/RTSFormationSubsystem.cpp create mode 100644 Plugins/RTSFormations/Source/RTSFormations/Private/RTSFormationSubsystem.h create mode 100644 Plugins/RTSFormations/Source/RTSFormations/Private/RTSFormations.cpp create mode 100644 Plugins/RTSFormations/Source/RTSFormations/Public/RTSAgentTraits.h create mode 100644 Plugins/RTSFormations/Source/RTSFormations/Public/RTSFormations.h create mode 100644 Plugins/RTSFormations/Source/RTSFormations/RTSFormations.Build.cs diff --git a/Plugins/RTSFormations/Content/BP_FormationGameMode.uasset b/Plugins/RTSFormations/Content/BP_FormationGameMode.uasset new file mode 100644 index 00000000..3da8cd4b --- /dev/null +++ b/Plugins/RTSFormations/Content/BP_FormationGameMode.uasset @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:23930ce006e48f259eb128cec23236a56e977e9a9748277d15961c374cc495a8 +size 62555 diff --git a/Plugins/RTSFormations/Content/BP_FormationUnit.uasset b/Plugins/RTSFormations/Content/BP_FormationUnit.uasset new file mode 100644 index 00000000..b342a99a --- /dev/null +++ b/Plugins/RTSFormations/Content/BP_FormationUnit.uasset @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:dd24092b2f239e96f004695cc7346e0c03209cf930df5c4a065c0b3a01d1a98c +size 43629 diff --git a/Plugins/RTSFormations/Content/BP_SpawnEntity.uasset b/Plugins/RTSFormations/Content/BP_SpawnEntity.uasset new file mode 100644 index 00000000..ab22b363 --- /dev/null +++ b/Plugins/RTSFormations/Content/BP_SpawnEntity.uasset @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:41062c587661c36432e1b8f328e4b751229a8a507a3efc1ba728f2e13a391a8b +size 76296 diff --git a/Plugins/RTSFormations/Content/BP_SpawnUnit.uasset b/Plugins/RTSFormations/Content/BP_SpawnUnit.uasset new file mode 100644 index 00000000..90e01661 --- /dev/null +++ b/Plugins/RTSFormations/Content/BP_SpawnUnit.uasset @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:f7841fc6958941d4efcc7ba24a20058153b3099da39505dae1a772269def8361 +size 54872 diff --git a/Plugins/RTSFormations/Content/DA_AgentFormation.uasset b/Plugins/RTSFormations/Content/DA_AgentFormation.uasset new file mode 100644 index 00000000..a964a91e --- /dev/null +++ b/Plugins/RTSFormations/Content/DA_AgentFormation.uasset @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:5659130328bc80c748affa3d92beadb630059401ff8d52c586e8d9911e6ad668 +size 6627 diff --git a/Plugins/RTSFormations/Content/RTSFormationsExample.umap b/Plugins/RTSFormations/Content/RTSFormationsExample.umap new file mode 100644 index 00000000..721b6a0f --- /dev/null +++ b/Plugins/RTSFormations/Content/RTSFormationsExample.umap @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:7ee708c1b5079bb312343918ad48d202a40f0cdad9f990e2b9ee509f2a3b1b7e +size 43618 diff --git a/Plugins/RTSFormations/RTSFormations.uplugin b/Plugins/RTSFormations/RTSFormations.uplugin new file mode 100644 index 00000000..36b7eb8b --- /dev/null +++ b/Plugins/RTSFormations/RTSFormations.uplugin @@ -0,0 +1,46 @@ +{ + "FileVersion": 3, + "Version": 1, + "VersionName": "1.0", + "FriendlyName": "RTSFormations", + "Description": "", + "Category": "Other", + "CreatedBy": "JiRath", + "CreatedByURL": "", + "DocsURL": "", + "MarketplaceURL": "", + "SupportURL": "", + "CanContainContent": true, + "IsBetaVersion": true, + "IsExperimentalVersion": false, + "Installed": false, + "Modules": [ + { + "Name": "RTSFormations", + "Type": "Runtime", + "LoadingPhase": "Default" + } + ], + "Plugins": [ + { + "Name": "MassGameplay", + "Enabled": true + }, + { + "Name": "MassEntity", + "Enabled": true + }, + { + "Name": "MassAI", + "Enabled": true + }, + { + "Name": "StructUtils", + "Enabled": true + }, + { + "Name": "MassCommunitySample", + "Enabled": true + } + ] +} \ No newline at end of file diff --git a/Plugins/RTSFormations/Resources/Icon128.png b/Plugins/RTSFormations/Resources/Icon128.png new file mode 100644 index 00000000..26245f6a --- /dev/null +++ b/Plugins/RTSFormations/Resources/Icon128.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:f7239efaeefbd82de33ebe18518e50de075ea4188a468a9e4991396433d2275f +size 12699 diff --git a/Plugins/RTSFormations/Source/RTSFormations/Private/RTSAgentTraits.cpp b/Plugins/RTSFormations/Source/RTSFormations/Private/RTSAgentTraits.cpp new file mode 100644 index 00000000..abaf4e6e --- /dev/null +++ b/Plugins/RTSFormations/Source/RTSFormations/Private/RTSAgentTraits.cpp @@ -0,0 +1,20 @@ +// Fill out your copyright notice in the Description page of Project Settings. + + +#include "RTSAgentTraits.h" + +#include "MassEntityTemplateRegistry.h" +#include "MassNavigationFragments.h" +#include "MassObserverRegistry.h" + +void URTSFormationAgentTrait::BuildTemplate(FMassEntityTemplateBuildContext& BuildContext, UWorld& World) const +{ + UMassEntitySubsystem* EntitySubsystem = UWorld::GetSubsystem(&World); + check(EntitySubsystem); + + BuildContext.AddFragment(); + + uint32 MySharedFragmentHash = UE::StructUtils::GetStructCrc32(FConstStructView::Make(FormationSettings)); + FSharedStruct MySharedFragment = EntitySubsystem->GetOrCreateSharedFragment(MySharedFragmentHash, FormationSettings); + BuildContext.AddSharedFragment(MySharedFragment); +} diff --git a/Plugins/RTSFormations/Source/RTSFormations/Private/RTSFormationProcessors.cpp b/Plugins/RTSFormations/Source/RTSFormations/Private/RTSFormationProcessors.cpp new file mode 100644 index 00000000..5a98adda --- /dev/null +++ b/Plugins/RTSFormations/Source/RTSFormations/Private/RTSFormationProcessors.cpp @@ -0,0 +1,239 @@ +#include "RTSFormationProcessors.h" + +#include "MassCommonFragments.h" +#include "MassMovementFragments.h" +#include "MassNavigationFragments.h" +#include "MassNavigationTypes.h" +#include "MassSignalSubsystem.h" +#include "RTSAgentTraits.h" +#include "RTSFormationSubsystem.h" + +//----------------------------------------------------------------------// +// URTSFormationInitializer +//----------------------------------------------------------------------// +URTSFormationInitializer::URTSFormationInitializer() +{ + ObservedType = FRTSFormationAgent::StaticStruct(); + Operation = EMassObservedOperation::Add; +} + +void URTSFormationInitializer::ConfigureQueries() +{ + EntityQuery.AddRequirement(EMassFragmentAccess::ReadWrite); +} + +void URTSFormationInitializer::Initialize(UObject& Owner) +{ + Super::Initialize(Owner); + + SignalSubsystem = UWorld::GetSubsystem(Owner.GetWorld()); + FormationSubsystem = UWorld::GetSubsystem(Owner.GetWorld()); +} + +void URTSFormationInitializer::Execute(UMassEntitySubsystem& EntitySubsystem, FMassExecutionContext& Context) +{ + // First query is to give all units an appropriate unit index. + EntityQuery.ParallelForEachEntityChunk(EntitySubsystem, Context, [this](FMassExecutionContext& Context) + { + TArrayView RTSFormationAgents = Context.GetMutableFragmentView(); + + // Signal affected units/entities at the end + TArray UnitSignals; + UnitSignals.Reserve(FormationSubsystem->Units.Num()); + + // Since we can have multiple units, reserving is only done in the formation subsystem + // This is because it might be possible that a batch of spawned entities should go to different units + for (int32 EntityIndex = 0; EntityIndex < Context.GetNumEntities(); ++EntityIndex) + { + FRTSFormationAgent& RTSFormationAgent = RTSFormationAgents[EntityIndex]; + + // If for some reason the unit hasnt been created, we should create it now + // Unfortunately, with the nature of an array, this might cause a crash if the unit index is not next in line, need to handle this somehow + if (!FormationSubsystem->Units.IsValidIndex(RTSFormationAgent.UnitIndex)) + FormationSubsystem->Units.AddDefaulted(1); + + RTSFormationAgent.EntityIndex = FormationSubsystem->Units[RTSFormationAgent.UnitIndex].Entities.Num(); + FormationSubsystem->Units[RTSFormationAgent.UnitIndex].Entities.Emplace(Context.GetEntity(EntityIndex)); + + UnitSignals.AddUnique(RTSFormationAgent.UnitIndex); + } + // Signal entities in the unit that their position is updated + // @todo only notify affected entities + for(const int& Unit : UnitSignals) + SignalSubsystem->SignalEntities(FormationUpdated, FormationSubsystem->Units[Unit].Entities); + }); +} + +//----------------------------------------------------------------------// +// URTSFormationDestroyer +//----------------------------------------------------------------------// +URTSFormationDestroyer::URTSFormationDestroyer() +{ + ObservedType = FRTSFormationAgent::StaticStruct(); + Operation = EMassObservedOperation::Remove; +} + +void URTSFormationDestroyer::ConfigureQueries() +{ + EntityQuery.AddRequirement(EMassFragmentAccess::ReadOnly); +} + +void URTSFormationDestroyer::Initialize(UObject& Owner) +{ + SignalSubsystem = UWorld::GetSubsystem(Owner.GetWorld()); + FormationSubsystem = UWorld::GetSubsystem(Owner.GetWorld()); +} + +void URTSFormationDestroyer::Execute(UMassEntitySubsystem& EntitySubsystem, FMassExecutionContext& Context) +{ + EntityQuery.ParallelForEachEntityChunk(EntitySubsystem, Context, [this, &EntitySubsystem](FMassExecutionContext& Context) + { + TConstArrayView FormationAgents = Context.GetFragmentView(); + + // Signal affected units/entities at the end + TArray UnitSignals; + UnitSignals.Reserve(FormationSubsystem->Units.Num()); + + for (int32 EntityIndex = 0; EntityIndex < Context.GetNumEntities(); ++EntityIndex) + { + const FRTSFormationAgent& FormationAgent = FormationAgents[EntityIndex]; + + // Remove entity from units array + if (FormationSubsystem->Units.IsValidIndex(FormationAgent.UnitIndex)) + { + const int32 ItemIndex = FormationSubsystem->Units[FormationAgent.UnitIndex].Entities.IndexOfByKey(Context.GetEntity(EntityIndex)); + if (ItemIndex != INDEX_NONE) + { + // Since we are caching the index, we need to fix the entity index that replaces the destroyed one + // Not sure if this is the 'correct' way to handle this, but it works for now + if (FRTSFormationAgent* ReplacementFormationAgent = EntitySubsystem.GetFragmentDataPtr(FormationSubsystem->Units[FormationAgent.UnitIndex].Entities.Last())) + ReplacementFormationAgent->EntityIndex = ItemIndex; + + FormationSubsystem->Units[FormationAgent.UnitIndex].Entities.RemoveAtSwap(ItemIndex, 1, true); + UnitSignals.Emplace(FormationAgent.UnitIndex); + } + } + } + + // Signal affected units/entities + for(const int& Unit : UnitSignals) + { + //@todo add a consistent way to reference units since the index isn't reliable + if (FormationSubsystem->Units[Unit].Entities.Num() == 0) + { + FormationSubsystem->Units.RemoveAtSwap(Unit); + continue; + } + + // Really the only time we should notify every entity in the unit is when the center point changes + // Every other time we just have to notify the entity that is replacing the destroyed one + FormationSubsystem->Units[Unit].Entities.Shrink(); + SignalSubsystem->SignalEntities(FormationUpdated, FormationSubsystem->Units[Unit].Entities); + } + }); +} + +//----------------------------------------------------------------------// +// URTSAgentMovement +//----------------------------------------------------------------------// +void URTSAgentMovement::ConfigureQueries() +{ + EntityQuery.AddRequirement(EMassFragmentAccess::ReadWrite); + EntityQuery.AddRequirement(EMassFragmentAccess::ReadOnly); + EntityQuery.AddRequirement(EMassFragmentAccess::ReadWrite); +} + +void URTSAgentMovement::Execute(UMassEntitySubsystem& EntitySubsystem, FMassExecutionContext& Context) +{ + EntityQuery.ParallelForEachEntityChunk(EntitySubsystem, Context, [this](FMassExecutionContext& Context) + { + TArrayView MoveTargetFragments = Context.GetMutableFragmentView(); + TConstArrayView TransformFragments = Context.GetFragmentView(); + TArrayView VelocityFragments = Context.GetMutableFragmentView(); + + for (int32 EntityIndex = 0; EntityIndex < Context.GetNumEntities(); ++EntityIndex) + { + FMassMoveTargetFragment& MoveTarget = MoveTargetFragments[EntityIndex]; + const FTransform& Transform = TransformFragments[EntityIndex].GetTransform(); + FVector& Velocity = VelocityFragments[EntityIndex].Value; + + // Update move target values + MoveTarget.DistanceToGoal = (MoveTarget.Center - Transform.GetLocation()).Length(); + MoveTarget.Forward = (MoveTarget.Center - Transform.GetLocation()).GetSafeNormal(); + + // Once we are close enough to our goal, create stand action + if (MoveTarget.DistanceToGoal <= MoveTarget.SlackRadius) + { + MoveTarget.CreateNewAction(EMassMovementAction::Stand, *GetWorld()); + Velocity = FVector::Zero(); + } + } + }); +} + +//----------------------------------------------------------------------// +// URTSFormationUpdate +//----------------------------------------------------------------------// +void URTSFormationUpdate::Initialize(UObject& Owner) +{ + Super::Initialize(Owner); + SubscribeToSignal(FormationUpdated); + + FormationSubsystem = UWorld::GetSubsystem(Owner.GetWorld()); +} + +void URTSFormationUpdate::ConfigureQueries() +{ + EntityQuery.AddRequirement(EMassFragmentAccess::ReadOnly); + EntityQuery.AddRequirement(EMassFragmentAccess::ReadWrite); + EntityQuery.AddRequirement(EMassFragmentAccess::ReadOnly); + EntityQuery.AddSharedRequirement(EMassFragmentAccess::ReadOnly); +} + +void URTSFormationUpdate::SignalEntities(UMassEntitySubsystem& EntitySubsystem, FMassExecutionContext& Context, + FMassSignalNameLookup& EntitySignals) +{ + // Query to calculate move target for entities based on unit index + EntityQuery.ParallelForEachEntityChunk(EntitySubsystem, Context, [this](FMassExecutionContext& Context) + { + TConstArrayView RTSFormationAgents = Context.GetFragmentView(); + TArrayView MoveTargetFragments = Context.GetMutableFragmentView(); + TConstArrayView TransformFragments = Context.GetFragmentView(); + const FRTSFormationSettings& RTSFormationSettings = Context.GetSharedFragment(); + + for (int32 EntityIndex = 0; EntityIndex < Context.GetNumEntities(); ++EntityIndex) + { + + const FRTSFormationAgent& RTSFormationAgent = RTSFormationAgents[EntityIndex]; + FMassMoveTargetFragment& MoveTarget = MoveTargetFragments[EntityIndex]; + const FTransform& Transform = TransformFragments[EntityIndex].GetTransform(); + + // Convert UnitIndex to X/Y coords + const int w = RTSFormationAgent.EntityIndex / RTSFormationSettings.FormationLength; + const int l = RTSFormationAgent.EntityIndex % RTSFormationSettings.FormationLength; + + // We want the formation to be 'centered' so we need to create an offset + const FVector CenterOffset = FVector((FormationSubsystem->Units[RTSFormationAgent.UnitIndex].Entities.Num()/RTSFormationSettings.FormationLength/2) * RTSFormationSettings.BufferDistance, (RTSFormationSettings.FormationLength/2) * RTSFormationSettings.BufferDistance, 0.f); + + // Create movement action + MoveTarget.CreateNewAction(EMassMovementAction::Move, *GetWorld()); + + // Set entity position based on index in formation + FVector EntityPosition = FVector(w,l,0.f); + EntityPosition *= RTSFormationSettings.BufferDistance; + EntityPosition -= CenterOffset; + + // Rotate unit by calculated angle + FVector RotateValue = EntityPosition.RotateAngleAxis(FormationSubsystem->Units[RTSFormationAgent.UnitIndex].Angle, FVector(0.f,0.f,FormationSubsystem->Units[RTSFormationAgent.UnitIndex].TurnDirection)); + + // Finally add the units position to the entity position + RotateValue += FormationSubsystem->Units[RTSFormationAgent.UnitIndex].UnitPosition; + + MoveTarget.Center = RotateValue; + MoveTarget.Forward = (Transform.GetLocation() - MoveTarget.Center).GetSafeNormal(); + MoveTarget.DistanceToGoal = (Transform.GetLocation() - MoveTarget.Center).Length(); + MoveTarget.SlackRadius = 10.f; + MoveTarget.IntentAtGoal = EMassMovementAction::Stand; + } + }); +} diff --git a/Plugins/RTSFormations/Source/RTSFormations/Private/RTSFormationProcessors.h b/Plugins/RTSFormations/Source/RTSFormations/Private/RTSFormationProcessors.h new file mode 100644 index 00000000..23a6ae1d --- /dev/null +++ b/Plugins/RTSFormations/Source/RTSFormations/Private/RTSFormationProcessors.h @@ -0,0 +1,72 @@ +#pragma once +#include "MassEntityQuery.h" +#include "MassObserverProcessor.h" +#include "MassProcessor.h" +#include "MassSignalProcessorBase.h" +#include "RTSFormationProcessors.generated.h" + +class URTSFormationSubsystem; +const FName FormationUpdated = FName(TEXT("FormationUpdated")); + +// Observer that runs when a unit is spawned. Its main purpose is to add entities to a unit array +// in the subsystem and cache the index for future use in URTSFormationUpdate +UCLASS() +class RTSFORMATIONS_API URTSFormationInitializer : public UMassObserverProcessor +{ + GENERATED_BODY() + + URTSFormationInitializer(); + virtual void ConfigureQueries() override; + virtual void Initialize(UObject& Owner) override; + virtual void Execute(UMassEntitySubsystem& EntitySubsystem, FMassExecutionContext& Context) override; + + FMassEntityQuery EntityQuery; + + TObjectPtr SignalSubsystem; + TObjectPtr FormationSubsystem; +}; + +// Observer that runs when an entity is destroyed. Cleans up the unit array and tells the last unit to take their place +UCLASS() +class RTSFORMATIONS_API URTSFormationDestroyer : public UMassObserverProcessor +{ + GENERATED_BODY() + + URTSFormationDestroyer(); + virtual void ConfigureQueries() override; + virtual void Initialize(UObject& Owner) override; + virtual void Execute(UMassEntitySubsystem& EntitySubsystem, FMassExecutionContext& Context) override; + + FMassEntityQuery EntityQuery; + + TObjectPtr SignalSubsystem; + TObjectPtr FormationSubsystem; +}; + + +// Simple movement processor to get agents from a to b +UCLASS() +class RTSFORMATIONS_API URTSAgentMovement : public UMassProcessor +{ + GENERATED_BODY() + + virtual void ConfigureQueries() override; + virtual void Execute(UMassEntitySubsystem& EntitySubsystem, FMassExecutionContext& Context) override; + + FMassEntityQuery EntityQuery; + + FMassEntityQuery FormationQuery; +}; + +// Main bulk of formation logic. Calculates position of entities and sends it to the FMassMoveTargetFragment. +UCLASS() +class RTSFORMATIONS_API URTSFormationUpdate : public UMassSignalProcessorBase +{ + GENERATED_BODY() + + virtual void Initialize(UObject& Owner) override; + virtual void ConfigureQueries() override; + virtual void SignalEntities(UMassEntitySubsystem& EntitySubsystem, FMassExecutionContext& Context, FMassSignalNameLookup& EntitySignals) override; + + TObjectPtr FormationSubsystem; +}; diff --git a/Plugins/RTSFormations/Source/RTSFormations/Private/RTSFormationSubsystem.cpp b/Plugins/RTSFormations/Source/RTSFormations/Private/RTSFormationSubsystem.cpp new file mode 100644 index 00000000..550612bd --- /dev/null +++ b/Plugins/RTSFormations/Source/RTSFormations/Private/RTSFormationSubsystem.cpp @@ -0,0 +1,79 @@ +// Fill out your copyright notice in the Description page of Project Settings. + + +#include "RTSFormationSubsystem.h" + +#include "MassAgentComponent.h" +#include "MassEntitySubsystem.h" +#include "MassSignalSubsystem.h" +#include "MassSpawnerSubsystem.h" +#include "RTSAgentTraits.h" +#include "RTSFormationProcessors.h" +#include "Kismet/GameplayStatics.h" + +void URTSFormationSubsystem::DestroyEntity(UMassAgentComponent* Entity) +{ + UMassEntitySubsystem* EntitySubsystem = GetWorld()->GetSubsystem(); + check(EntitySubsystem); + + EntitySubsystem->Defer().DestroyEntity(Entity->GetEntityHandle()); +} + +void URTSFormationSubsystem::SetUnitPosition(const FVector& NewPosition, int UnitIndex) +{ + if (!ensure(Units.IsValidIndex(UnitIndex))) { return; } + + DrawDebugDirectionalArrow(GetWorld(), NewPosition, NewPosition+((NewPosition-Units[UnitIndex].UnitPosition).GetSafeNormal()*250.f), 150.f, FColor::Red, false, 10.f, 0, 25.f); + + // Calculate turn direction and angle for entities in unit + Units[UnitIndex].TurnDirection = (NewPosition-Units[UnitIndex].UnitPosition).GetSafeNormal().Y > 0 ? 1.f : -1.f; + Units[UnitIndex].Angle = FMath::RadiansToDegrees(acosf(FVector::DotProduct(FVector::ForwardVector, (NewPosition-Units[UnitIndex].UnitPosition).GetSafeNormal()))); + Units[UnitIndex].Angle += 180.f; // Temporary fix to resolve unit facing the wrong direction + + Units[UnitIndex].UnitPosition = NewPosition; + + // Signal entities of a position update + if (Units[UnitIndex].Entities.Num() > 0) + GetWorld()->GetSubsystem()->SignalEntities(FormationUpdated, Units[UnitIndex].Entities); +} + +void URTSFormationSubsystem::SpawnEntitiesForUnit(int UnitIndex, const UMassEntityConfigAsset* EntityConfig, int Count) +{ + if (!ensure(Units.IsValidIndex(UnitIndex))) { return; } + + UMassEntitySubsystem* EntitySubsystem = GetWorld()->GetSubsystem(); + + // Reserve space for the new units, the space will be filled in a processor + Units[UnitIndex].Entities.Reserve(Units[UnitIndex].Entities.Num()+Count); + + TArray Entities; + const FMassEntityTemplate* EntityTemplate = EntityConfig->GetConfig().GetOrCreateEntityTemplate(*UGameplayStatics::GetPlayerPawn(this, 0), *EntityConfig); + + // We are doing a little bit of work here since we are setting the unit index manually + // Otherwise, using SpawnEntities would be perfectly fine + // @todo find if there is a better way to modify templates in code + TArray SpawnedEntities; + TSharedRef CreationContext = EntitySubsystem->BatchCreateEntities(EntityTemplate->GetArchetype(), Count, SpawnedEntities); + + // Set the template default values for the entities + TConstArrayView FragmentInstances = EntityTemplate->GetInitialFragmentValues(); + EntitySubsystem->BatchSetEntityFragmentsValues(CreationContext->GetChunkCollection(), FragmentInstances); + + // Set unit index for entities + FRTSFormationAgent FormationAgent; + FormationAgent.UnitIndex = UnitIndex; + + TArray Fragments; + Fragments.Add(FConstStructView::Make(FormationAgent)); + EntitySubsystem->BatchSetEntityFragmentsValues(CreationContext->GetChunkCollection(), Fragments); +} + +int URTSFormationSubsystem::SpawnNewUnit(const UMassEntityConfigAsset* EntityConfig, int Count, const FVector& Position) +{ + int UnitIndex = Units.Num(); + Units.AddDefaulted(1); + Units[UnitIndex].UnitPosition = Position; + + SpawnEntitiesForUnit(UnitIndex, EntityConfig, Count); + return UnitIndex; +} diff --git a/Plugins/RTSFormations/Source/RTSFormations/Private/RTSFormationSubsystem.h b/Plugins/RTSFormations/Source/RTSFormations/Private/RTSFormationSubsystem.h new file mode 100644 index 00000000..955a59f6 --- /dev/null +++ b/Plugins/RTSFormations/Source/RTSFormations/Private/RTSFormationSubsystem.h @@ -0,0 +1,79 @@ +// Fill out your copyright notice in the Description page of Project Settings. + +#pragma once + +#include "CoreMinimal.h" +#include "Common/Misc/MSBPFunctionLibrary.h" +#include "RTSFormationSubsystem.generated.h" + +UENUM() +enum FormationType +{ + Rectangle, + Circle +}; + +USTRUCT(BlueprintType) +struct FUnitInfo +{ + GENERATED_BODY() + +public: + // Entities in the unit + UPROPERTY() + TArray Entities; + + // The current unit position + UPROPERTY(BlueprintReadOnly) + FVector UnitPosition; + + // The direction to turn the unit when rotating + UPROPERTY() + float TurnDirection = 1.f; + + // The entity length of the 'front' of the unit + UPROPERTY() + float FormationLength = 0; + + // The type of formation - WIP + UPROPERTY() + TEnumAsByte Formation = Rectangle; + + // The angle of the unit + UPROPERTY() + float Angle = 0; + + FUnitInfo() {}; +}; + +class UMassAgentComponent; +struct FMassEntityHandle; +/** + * Subsystem that handles the bulk of data shared among entities for the formation system. Enables simple unit creation and entity spawning + */ +UCLASS() +class RTSFORMATIONS_API URTSFormationSubsystem : public UWorldSubsystem +{ + GENERATED_BODY() + +public: + // Stores the num of units in the formation + UPROPERTY(BlueprintReadOnly) + TArray Units; + + // Destroy a specified entity + UFUNCTION(BlueprintCallable) + void DestroyEntity(UMassAgentComponent* Entity); + + // Set the position of a unit + UFUNCTION(BlueprintCallable) + void SetUnitPosition(const FVector& NewPosition, int UnitIndex = 0); + + // Spawn entities for a unit + UFUNCTION(BlueprintCallable) + void SpawnEntitiesForUnit(int UnitIndex, const UMassEntityConfigAsset* EntityConfig, int Count); + + // Spawn a new unit + UFUNCTION(BlueprintCallable) + int SpawnNewUnit(const UMassEntityConfigAsset* EntityConfig, int Count, const FVector& Position); +}; diff --git a/Plugins/RTSFormations/Source/RTSFormations/Private/RTSFormations.cpp b/Plugins/RTSFormations/Source/RTSFormations/Private/RTSFormations.cpp new file mode 100644 index 00000000..65ff621e --- /dev/null +++ b/Plugins/RTSFormations/Source/RTSFormations/Private/RTSFormations.cpp @@ -0,0 +1,20 @@ +// Copyright Epic Games, Inc. All Rights Reserved. + +#include "RTSFormations.h" + +#define LOCTEXT_NAMESPACE "FRTSFormationsModule" + +void FRTSFormationsModule::StartupModule() +{ + // This code will execute after your module is loaded into memory; the exact timing is specified in the .uplugin file per-module +} + +void FRTSFormationsModule::ShutdownModule() +{ + // This function may be called during shutdown to clean up your module. For modules that support dynamic reloading, + // we call this function before unloading the module. +} + +#undef LOCTEXT_NAMESPACE + +IMPLEMENT_MODULE(FRTSFormationsModule, RTSFormations) \ No newline at end of file diff --git a/Plugins/RTSFormations/Source/RTSFormations/Public/RTSAgentTraits.h b/Plugins/RTSFormations/Source/RTSFormations/Public/RTSAgentTraits.h new file mode 100644 index 00000000..567065c9 --- /dev/null +++ b/Plugins/RTSFormations/Source/RTSFormations/Public/RTSAgentTraits.h @@ -0,0 +1,49 @@ +// Fill out your copyright notice in the Description page of Project Settings. + +#pragma once + +#include "CoreMinimal.h" +#include "MassEntityTraitBase.h" +#include "MassEntityTypes.h" +#include "RTSAgentTraits.generated.h" + +class URTSFormationSubsystem; + +// Store basic info about the unit +USTRUCT() +struct RTSFORMATIONS_API FRTSFormationAgent : public FMassFragment +{ + GENERATED_BODY() + + // The index of the entity in the formation + int EntityIndex = 0; + + // The unit that this entity is a part of + int UnitIndex = 0; +}; + +USTRUCT() +struct RTSFORMATIONS_API FRTSFormationSettings : public FMassSharedFragment +{ + GENERATED_BODY() + + // Distance between each unit + UPROPERTY(EditAnywhere, Category = "Formation") + float BufferDistance = 100.f; + + // Unit width of formation + UPROPERTY(EditAnywhere, Category = "Formation") + int FormationLength = 1; +}; + +// Provides entity with FRTSFormationAgent fragment to enable formations +UCLASS() +class RTSFORMATIONS_API URTSFormationAgentTrait : public UMassEntityTraitBase +{ + GENERATED_BODY() + + virtual void BuildTemplate(FMassEntityTemplateBuildContext& BuildContext, UWorld& World) const override; + + UPROPERTY(EditAnywhere) + FRTSFormationSettings FormationSettings; +}; diff --git a/Plugins/RTSFormations/Source/RTSFormations/Public/RTSFormations.h b/Plugins/RTSFormations/Source/RTSFormations/Public/RTSFormations.h new file mode 100644 index 00000000..afdb4e36 --- /dev/null +++ b/Plugins/RTSFormations/Source/RTSFormations/Public/RTSFormations.h @@ -0,0 +1,15 @@ +// Copyright Epic Games, Inc. All Rights Reserved. + +#pragma once + +#include "CoreMinimal.h" +#include "Modules/ModuleManager.h" + +class FRTSFormationsModule : public IModuleInterface +{ +public: + + /** IModuleInterface implementation */ + virtual void StartupModule() override; + virtual void ShutdownModule() override; +}; diff --git a/Plugins/RTSFormations/Source/RTSFormations/RTSFormations.Build.cs b/Plugins/RTSFormations/Source/RTSFormations/RTSFormations.Build.cs new file mode 100644 index 00000000..2a572eb0 --- /dev/null +++ b/Plugins/RTSFormations/Source/RTSFormations/RTSFormations.Build.cs @@ -0,0 +1,53 @@ +// Copyright Epic Games, Inc. All Rights Reserved. + +using UnrealBuildTool; + +public class RTSFormations : ModuleRules +{ + public RTSFormations(ReadOnlyTargetRules Target) : base(Target) + { + PCHUsage = ModuleRules.PCHUsageMode.UseExplicitOrSharedPCHs; + + PublicIncludePaths.AddRange( + new string[] { + // ... add public include paths required here ... + } + ); + + + PrivateIncludePaths.AddRange( + new string[] { + // ... add other private include paths required here ... + } + ); + + + PublicDependencyModuleNames.AddRange( + new string[] + { + "Core", "MassSpawner", "MassEntity", "MassCommon", "StructUtils", "MassSignals", "MassMovement", "MassCommunitySample" + // ... add other public dependencies that you statically link with here ... + } + ); + + + PrivateDependencyModuleNames.AddRange( + new string[] + { + "CoreUObject", + "Engine", + "Slate", + "SlateCore", "MassNavigation", "MassActors" + // ... add private dependencies that you statically link with here ... + } + ); + + + DynamicallyLoadedModuleNames.AddRange( + new string[] + { + // ... add any modules that your module loads dynamically here ... + } + ); + } +} From 2bb9800dd84030f3e4c102bf5c5fc9fecb3d5dd8 Mon Sep 17 00:00:00 2001 From: Ji-Rath Date: Thu, 14 Jul 2022 17:55:45 -0500 Subject: [PATCH 02/37] Some small discoveries about LOD Processors --- Plugins/RTSFormations/Content/BP_SpawnUnit.uasset | 4 ++-- Plugins/RTSFormations/Content/DA_AgentFormation.uasset | 4 ++-- Plugins/RTSFormations/Content/RTSFormationsExample.umap | 4 ++-- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/Plugins/RTSFormations/Content/BP_SpawnUnit.uasset b/Plugins/RTSFormations/Content/BP_SpawnUnit.uasset index 90e01661..4a8f5ba1 100644 --- a/Plugins/RTSFormations/Content/BP_SpawnUnit.uasset +++ b/Plugins/RTSFormations/Content/BP_SpawnUnit.uasset @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:f7841fc6958941d4efcc7ba24a20058153b3099da39505dae1a772269def8361 -size 54872 +oid sha256:c92fb2db4ff8d4230ca4c21fa84c0c8b6022b9cf4c207d63a1b7c12d660955f8 +size 59196 diff --git a/Plugins/RTSFormations/Content/DA_AgentFormation.uasset b/Plugins/RTSFormations/Content/DA_AgentFormation.uasset index a964a91e..b0f10940 100644 --- a/Plugins/RTSFormations/Content/DA_AgentFormation.uasset +++ b/Plugins/RTSFormations/Content/DA_AgentFormation.uasset @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:5659130328bc80c748affa3d92beadb630059401ff8d52c586e8d9911e6ad668 -size 6627 +oid sha256:8e02ccf4e16cf58d666eccb29abf537c37a21d320266d50a99b8a23649c807d3 +size 6648 diff --git a/Plugins/RTSFormations/Content/RTSFormationsExample.umap b/Plugins/RTSFormations/Content/RTSFormationsExample.umap index 721b6a0f..07599c21 100644 --- a/Plugins/RTSFormations/Content/RTSFormationsExample.umap +++ b/Plugins/RTSFormations/Content/RTSFormationsExample.umap @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:7ee708c1b5079bb312343918ad48d202a40f0cdad9f990e2b9ee509f2a3b1b7e -size 43618 +oid sha256:a0319efbd01048cd0fc333cdbdeaebd6f17b67f7b833328f17cdae461b8fdb92 +size 42358 From 8aba178661cd05642418172a0ed62e48ea868df6 Mon Sep 17 00:00:00 2001 From: Ji-Rath Date: Mon, 18 Jul 2022 08:31:36 -0500 Subject: [PATCH 03/37] Fix crash on endplay Adjust entity position in formation to try and make it more seamless - WIP --- .../Private/RTSFormationProcessors.cpp | 31 +++++++++++-------- .../Private/RTSFormationSubsystem.cpp | 12 ++++++- .../Private/RTSFormationSubsystem.h | 3 ++ 3 files changed, 32 insertions(+), 14 deletions(-) diff --git a/Plugins/RTSFormations/Source/RTSFormations/Private/RTSFormationProcessors.cpp b/Plugins/RTSFormations/Source/RTSFormations/Private/RTSFormationProcessors.cpp index 5a98adda..964bb129 100644 --- a/Plugins/RTSFormations/Source/RTSFormations/Private/RTSFormationProcessors.cpp +++ b/Plugins/RTSFormations/Source/RTSFormations/Private/RTSFormationProcessors.cpp @@ -118,17 +118,20 @@ void URTSFormationDestroyer::Execute(UMassEntitySubsystem& EntitySubsystem, FMas // Signal affected units/entities for(const int& Unit : UnitSignals) { - //@todo add a consistent way to reference units since the index isn't reliable - if (FormationSubsystem->Units[Unit].Entities.Num() == 0) + if (FormationSubsystem->Units.IsValidIndex(Unit)) { - FormationSubsystem->Units.RemoveAtSwap(Unit); - continue; - } + //@todo add a consistent way to reference units since the index isn't reliable + if (FormationSubsystem->Units[Unit].Entities.Num() == 0) + { + FormationSubsystem->Units.RemoveAtSwap(Unit); + continue; + } - // Really the only time we should notify every entity in the unit is when the center point changes - // Every other time we just have to notify the entity that is replacing the destroyed one - FormationSubsystem->Units[Unit].Entities.Shrink(); - SignalSubsystem->SignalEntities(FormationUpdated, FormationSubsystem->Units[Unit].Entities); + // Really the only time we should notify every entity in the unit is when the center point changes + // Every other time we just have to notify the entity that is replacing the destroyed one + FormationSubsystem->Units[Unit].Entities.Shrink(); + SignalSubsystem->SignalEntities(FormationUpdated, FormationSubsystem->Units[Unit].Entities); + } } }); } @@ -207,13 +210,15 @@ void URTSFormationUpdate::SignalEntities(UMassEntitySubsystem& EntitySubsystem, const FRTSFormationAgent& RTSFormationAgent = RTSFormationAgents[EntityIndex]; FMassMoveTargetFragment& MoveTarget = MoveTargetFragments[EntityIndex]; const FTransform& Transform = TransformFragments[EntityIndex].GetTransform(); - + + const int Index = FormationSubsystem->Units[RTSFormationAgent.UnitIndex].bReverseUnit ? FormationSubsystem->Units[RTSFormationAgent.UnitIndex].Entities.Num()-1 - RTSFormationAgent.EntityIndex : RTSFormationAgent.EntityIndex; // Convert UnitIndex to X/Y coords - const int w = RTSFormationAgent.EntityIndex / RTSFormationSettings.FormationLength; - const int l = RTSFormationAgent.EntityIndex % RTSFormationSettings.FormationLength; + const int w = Index / RTSFormationSettings.FormationLength; + const int l = Index % RTSFormationSettings.FormationLength; // We want the formation to be 'centered' so we need to create an offset - const FVector CenterOffset = FVector((FormationSubsystem->Units[RTSFormationAgent.UnitIndex].Entities.Num()/RTSFormationSettings.FormationLength/2) * RTSFormationSettings.BufferDistance, (RTSFormationSettings.FormationLength/2) * RTSFormationSettings.BufferDistance, 0.f); + const FVector CenterOffset = FVector(0.f, (RTSFormationSettings.FormationLength/2) * RTSFormationSettings.BufferDistance, 0.f); + //(FormationSubsystem->Units[RTSFormationAgent.UnitIndex].Entities.Num()/RTSFormationSettings.FormationLength/2) * RTSFormationSettings.BufferDistance // Create movement action MoveTarget.CreateNewAction(EMassMovementAction::Move, *GetWorld()); diff --git a/Plugins/RTSFormations/Source/RTSFormations/Private/RTSFormationSubsystem.cpp b/Plugins/RTSFormations/Source/RTSFormations/Private/RTSFormationSubsystem.cpp index 550612bd..6283f902 100644 --- a/Plugins/RTSFormations/Source/RTSFormations/Private/RTSFormationSubsystem.cpp +++ b/Plugins/RTSFormations/Source/RTSFormations/Private/RTSFormationSubsystem.cpp @@ -23,12 +23,22 @@ void URTSFormationSubsystem::SetUnitPosition(const FVector& NewPosition, int Uni { if (!ensure(Units.IsValidIndex(UnitIndex))) { return; } - DrawDebugDirectionalArrow(GetWorld(), NewPosition, NewPosition+((NewPosition-Units[UnitIndex].UnitPosition).GetSafeNormal()*250.f), 150.f, FColor::Red, false, 10.f, 0, 25.f); + DrawDebugDirectionalArrow(GetWorld(), NewPosition, NewPosition+((NewPosition-Units[UnitIndex].UnitPosition).GetSafeNormal()*250.f), 150.f, FColor::Red, false, 5.f, 0, 25.f); // Calculate turn direction and angle for entities in unit + float OldAngle = Units[UnitIndex].Angle * Units[UnitIndex].TurnDirection; Units[UnitIndex].TurnDirection = (NewPosition-Units[UnitIndex].UnitPosition).GetSafeNormal().Y > 0 ? 1.f : -1.f; Units[UnitIndex].Angle = FMath::RadiansToDegrees(acosf(FVector::DotProduct(FVector::ForwardVector, (NewPosition-Units[UnitIndex].UnitPosition).GetSafeNormal()))); + + // Basically whenever we can + if (abs(OldAngle-Units[UnitIndex].Angle*Units[UnitIndex].TurnDirection) > 90.f) + { + Units[UnitIndex].bReverseUnit = !Units[UnitIndex].bReverseUnit; + } + Units[UnitIndex].Angle += 180.f; // Temporary fix to resolve unit facing the wrong direction + + Units[UnitIndex].UnitPosition = NewPosition; diff --git a/Plugins/RTSFormations/Source/RTSFormations/Private/RTSFormationSubsystem.h b/Plugins/RTSFormations/Source/RTSFormations/Private/RTSFormationSubsystem.h index 955a59f6..9dc32508 100644 --- a/Plugins/RTSFormations/Source/RTSFormations/Private/RTSFormationSubsystem.h +++ b/Plugins/RTSFormations/Source/RTSFormations/Private/RTSFormationSubsystem.h @@ -43,6 +43,9 @@ struct FUnitInfo UPROPERTY() float Angle = 0; + UPROPERTY() + bool bReverseUnit = false; + FUnitInfo() {}; }; From d7998fc728087099c38bbf40be59c22bd8112b3a Mon Sep 17 00:00:00 2001 From: Ji-Rath Date: Tue, 19 Jul 2022 10:22:21 -0500 Subject: [PATCH 04/37] Second pass on unit formations. While it is more organized than previously, it can use more work. Entities now go to location and choose a destination based on distance. --- .../Private/RTSFormationProcessors.cpp | 23 ++--- .../Private/RTSFormationSubsystem.cpp | 95 +++++++++++++++++-- .../Private/RTSFormationSubsystem.h | 11 ++- .../RTSFormations/Public/RTSAgentTraits.h | 2 + 4 files changed, 108 insertions(+), 23 deletions(-) diff --git a/Plugins/RTSFormations/Source/RTSFormations/Private/RTSFormationProcessors.cpp b/Plugins/RTSFormations/Source/RTSFormations/Private/RTSFormationProcessors.cpp index 964bb129..c9e20731 100644 --- a/Plugins/RTSFormations/Source/RTSFormations/Private/RTSFormationProcessors.cpp +++ b/Plugins/RTSFormations/Source/RTSFormations/Private/RTSFormationProcessors.cpp @@ -210,31 +210,32 @@ void URTSFormationUpdate::SignalEntities(UMassEntitySubsystem& EntitySubsystem, const FRTSFormationAgent& RTSFormationAgent = RTSFormationAgents[EntityIndex]; FMassMoveTargetFragment& MoveTarget = MoveTargetFragments[EntityIndex]; const FTransform& Transform = TransformFragments[EntityIndex].GetTransform(); - - const int Index = FormationSubsystem->Units[RTSFormationAgent.UnitIndex].bReverseUnit ? FormationSubsystem->Units[RTSFormationAgent.UnitIndex].Entities.Num()-1 - RTSFormationAgent.EntityIndex : RTSFormationAgent.EntityIndex; + + //const int Index = FormationSubsystem->Units[RTSFormationAgent.UnitIndex].bReverseUnit ? FormationSubsystem->Units[RTSFormationAgent.UnitIndex].Entities.Num()-1 - RTSFormationAgent.EntityIndex : RTSFormationAgent.EntityIndex; + //const int Index = RTSFormationAgent.EntityIndex; // Convert UnitIndex to X/Y coords - const int w = Index / RTSFormationSettings.FormationLength; - const int l = Index % RTSFormationSettings.FormationLength; + //const int w = Index / RTSFormationSettings.FormationLength; + //const int l = 4-RTSFormationAgent.EntityIndex % RTSFormationSettings.FormationLength; // We want the formation to be 'centered' so we need to create an offset - const FVector CenterOffset = FVector(0.f, (RTSFormationSettings.FormationLength/2) * RTSFormationSettings.BufferDistance, 0.f); + //const FVector CenterOffset = FVector(0.f, (RTSFormationSettings.FormationLength/2) * RTSFormationSettings.BufferDistance, 0.f); //(FormationSubsystem->Units[RTSFormationAgent.UnitIndex].Entities.Num()/RTSFormationSettings.FormationLength/2) * RTSFormationSettings.BufferDistance // Create movement action MoveTarget.CreateNewAction(EMassMovementAction::Move, *GetWorld()); // Set entity position based on index in formation - FVector EntityPosition = FVector(w,l,0.f); - EntityPosition *= RTSFormationSettings.BufferDistance; - EntityPosition -= CenterOffset; + //FVector EntityPosition = FVector(w,l,0.f); + //EntityPosition *= RTSFormationSettings.BufferDistance; + //EntityPosition -= CenterOffset; // Rotate unit by calculated angle - FVector RotateValue = EntityPosition.RotateAngleAxis(FormationSubsystem->Units[RTSFormationAgent.UnitIndex].Angle, FVector(0.f,0.f,FormationSubsystem->Units[RTSFormationAgent.UnitIndex].TurnDirection)); + //FVector RotateValue = EntityPosition.RotateAngleAxis(FormationSubsystem->Units[RTSFormationAgent.UnitIndex].Angle, FVector(0.f,0.f,FormationSubsystem->Units[RTSFormationAgent.UnitIndex].TurnDirection)); // Finally add the units position to the entity position - RotateValue += FormationSubsystem->Units[RTSFormationAgent.UnitIndex].UnitPosition; + //RotateValue += FormationSubsystem->Units[RTSFormationAgent.UnitIndex].UnitPosition; - MoveTarget.Center = RotateValue; + MoveTarget.Center = RTSFormationAgent.Position; MoveTarget.Forward = (Transform.GetLocation() - MoveTarget.Center).GetSafeNormal(); MoveTarget.DistanceToGoal = (Transform.GetLocation() - MoveTarget.Center).Length(); MoveTarget.SlackRadius = 10.f; diff --git a/Plugins/RTSFormations/Source/RTSFormations/Private/RTSFormationSubsystem.cpp b/Plugins/RTSFormations/Source/RTSFormations/Private/RTSFormationSubsystem.cpp index 6283f902..a1e9e081 100644 --- a/Plugins/RTSFormations/Source/RTSFormations/Private/RTSFormationSubsystem.cpp +++ b/Plugins/RTSFormations/Source/RTSFormations/Private/RTSFormationSubsystem.cpp @@ -4,6 +4,7 @@ #include "RTSFormationSubsystem.h" #include "MassAgentComponent.h" +#include "MassCommonFragments.h" #include "MassEntitySubsystem.h" #include "MassSignalSubsystem.h" #include "MassSpawnerSubsystem.h" @@ -24,27 +25,105 @@ void URTSFormationSubsystem::SetUnitPosition(const FVector& NewPosition, int Uni if (!ensure(Units.IsValidIndex(UnitIndex))) { return; } DrawDebugDirectionalArrow(GetWorld(), NewPosition, NewPosition+((NewPosition-Units[UnitIndex].UnitPosition).GetSafeNormal()*250.f), 150.f, FColor::Red, false, 5.f, 0, 25.f); - + // Calculate turn direction and angle for entities in unit float OldAngle = Units[UnitIndex].Angle * Units[UnitIndex].TurnDirection; Units[UnitIndex].TurnDirection = (NewPosition-Units[UnitIndex].UnitPosition).GetSafeNormal().Y > 0 ? 1.f : -1.f; Units[UnitIndex].Angle = FMath::RadiansToDegrees(acosf(FVector::DotProduct(FVector::ForwardVector, (NewPosition-Units[UnitIndex].UnitPosition).GetSafeNormal()))); + Units[UnitIndex].Angle += 180.f; // Temporary fix to resolve unit facing the wrong direction + + // This might be stupid slow + UMassEntitySubsystem* EntitySubsystem = UWorld::GetSubsystem(GetWorld()); - // Basically whenever we can - if (abs(OldAngle-Units[UnitIndex].Angle*Units[UnitIndex].TurnDirection) > 90.f) + struct SortNewPos { - Units[UnitIndex].bReverseUnit = !Units[UnitIndex].bReverseUnit; - } + FVector Position; + int Index; + + bool operator==(const SortNewPos& Other) const + { + return Other.Position == this->Position; + } + }; - Units[UnitIndex].Angle += 180.f; // Temporary fix to resolve unit facing the wrong direction - + TArray NewPositions; + NewPositions.Reserve(Units[UnitIndex].Entities.Num()); + const FVector CenterOffset = FVector(0.f, (Units[UnitIndex].FormationLength/2) * Units[UnitIndex].BufferDistance, 0.f); + for(int l=0;l FVector::Dist(B.Position, UnitPosition); + }); + + Units[UnitIndex].Entities.Sort([&EntitySubsystem, &NewPositions, &NewPosition](const FMassEntityHandle& A, const FMassEntityHandle& B) + { + const FVector& LocA = EntitySubsystem->GetFragmentDataChecked(A).GetTransform().GetLocation(); + const FVector& LocB = EntitySubsystem->GetFragmentDataChecked(B).GetTransform().GetLocation(); + return FVector::Dist(LocA, NewPosition) < FVector::Dist(LocB, NewPosition); + }); + + // We should not be doing this but for the convenience of the current logic in place, this is the fastest option i think + for(int i=0;i NewPositionsGroup; + TArray EntityGroup; + EntityGroup.Reserve(7); + NewPositionsGroup.Reserve(7); + for(int j=0;j<7;++j) + { + int Index = j+(i/7*7); + // Return early if there are no more entities + if(!Units[UnitIndex].Entities.IsValidIndex(Index)) + break; + + EntityGroup.Emplace(Units[UnitIndex].Entities[Index]); + NewPositionsGroup.Emplace(NewPositions[Index]); + } + for(const FMassEntityHandle& Entity : EntityGroup) + { + const FVector& EntityPos = EntitySubsystem->GetFragmentDataPtr(Entity)->GetTransform().GetLocation(); + SortNewPos ClosestPos; + float ClosestDist = -1; + for(const SortNewPos& NewPos : NewPositionsGroup) + { + if(ClosestDist == -1 || FVector::Dist(NewPos.Position, EntityPos) < ClosestDist) + { + ClosestDist = FVector::Dist(NewPos.Position, EntityPos); + ClosestPos = NewPos; + } + } + EntitySubsystem->GetFragmentDataPtr(Entity)->Position = ClosestPos.Position; + NewPositionsGroup.Remove(ClosestPos); + } + } Units[UnitIndex].UnitPosition = NewPosition; // Signal entities of a position update if (Units[UnitIndex].Entities.Num() > 0) - GetWorld()->GetSubsystem()->SignalEntities(FormationUpdated, Units[UnitIndex].Entities); + { + for(int i=0;iGetSubsystem()->DelaySignalEntity(FormationUpdated, Units[UnitIndex].Entities[i], i/7*0.25); + } + } + //GetWorld()->GetSubsystem()->SignalEntities(FormationUpdated, Units[UnitIndex].Entities); } void URTSFormationSubsystem::SpawnEntitiesForUnit(int UnitIndex, const UMassEntityConfigAsset* EntityConfig, int Count) diff --git a/Plugins/RTSFormations/Source/RTSFormations/Private/RTSFormationSubsystem.h b/Plugins/RTSFormations/Source/RTSFormations/Private/RTSFormationSubsystem.h index 9dc32508..ee37235b 100644 --- a/Plugins/RTSFormations/Source/RTSFormations/Private/RTSFormationSubsystem.h +++ b/Plugins/RTSFormations/Source/RTSFormations/Private/RTSFormationSubsystem.h @@ -28,12 +28,15 @@ struct FUnitInfo FVector UnitPosition; // The direction to turn the unit when rotating - UPROPERTY() + UPROPERTY(BlueprintReadWrite) float TurnDirection = 1.f; // The entity length of the 'front' of the unit - UPROPERTY() - float FormationLength = 0; + UPROPERTY(BlueprintReadWrite) + float FormationLength = 7; + + UPROPERTY(BlueprintReadWrite) + float BufferDistance = 100.f; // The type of formation - WIP UPROPERTY() @@ -61,7 +64,7 @@ class RTSFORMATIONS_API URTSFormationSubsystem : public UWorldSubsystem public: // Stores the num of units in the formation - UPROPERTY(BlueprintReadOnly) + UPROPERTY(BlueprintReadWrite) TArray Units; // Destroy a specified entity diff --git a/Plugins/RTSFormations/Source/RTSFormations/Public/RTSAgentTraits.h b/Plugins/RTSFormations/Source/RTSFormations/Public/RTSAgentTraits.h index 567065c9..01e19709 100644 --- a/Plugins/RTSFormations/Source/RTSFormations/Public/RTSAgentTraits.h +++ b/Plugins/RTSFormations/Source/RTSFormations/Public/RTSAgentTraits.h @@ -20,6 +20,8 @@ struct RTSFORMATIONS_API FRTSFormationAgent : public FMassFragment // The unit that this entity is a part of int UnitIndex = 0; + + FVector Position; }; USTRUCT() From 2111c5cc3d02cd2b62925389168ed20185c15537 Mon Sep 17 00:00:00 2001 From: Ji-Rath Date: Wed, 20 Jul 2022 22:26:03 -0500 Subject: [PATCH 05/37] Begin optimizing new formation system --- .../Private/RTSFormationProcessors.cpp | 76 ++++--- .../Private/RTSFormationSubsystem.cpp | 197 +++++++++++------- .../Private/RTSFormationSubsystem.h | 62 +++++- .../RTSFormations/Public/RTSAgentTraits.h | 11 +- 4 files changed, 230 insertions(+), 116 deletions(-) diff --git a/Plugins/RTSFormations/Source/RTSFormations/Private/RTSFormationProcessors.cpp b/Plugins/RTSFormations/Source/RTSFormations/Private/RTSFormationProcessors.cpp index c9e20731..ef775290 100644 --- a/Plugins/RTSFormations/Source/RTSFormations/Private/RTSFormationProcessors.cpp +++ b/Plugins/RTSFormations/Source/RTSFormations/Private/RTSFormationProcessors.cpp @@ -141,9 +141,12 @@ void URTSFormationDestroyer::Execute(UMassEntitySubsystem& EntitySubsystem, FMas //----------------------------------------------------------------------// void URTSAgentMovement::ConfigureQueries() { + EntityQuery.AddRequirement(EMassFragmentAccess::ReadOnly); EntityQuery.AddRequirement(EMassFragmentAccess::ReadWrite); EntityQuery.AddRequirement(EMassFragmentAccess::ReadOnly); EntityQuery.AddRequirement(EMassFragmentAccess::ReadWrite); + EntityQuery.AddConstSharedRequirement(EMassFragmentPresence::All); + EntityQuery.AddSharedRequirement(EMassFragmentAccess::ReadOnly); } void URTSAgentMovement::Execute(UMassEntitySubsystem& EntitySubsystem, FMassExecutionContext& Context) @@ -153,12 +156,41 @@ void URTSAgentMovement::Execute(UMassEntitySubsystem& EntitySubsystem, FMassExec TArrayView MoveTargetFragments = Context.GetMutableFragmentView(); TConstArrayView TransformFragments = Context.GetFragmentView(); TArrayView VelocityFragments = Context.GetMutableFragmentView(); + TConstArrayView RTSFormationAgents = Context.GetFragmentView(); + + const FRTSFormationSettings& FormationSettings = Context.GetSharedFragment(); + const FMassMovementParameters& MovementParameters = Context.GetConstSharedFragment(); + + URTSFormationSubsystem* FormationSubsystem = UWorld::GetSubsystem(GetWorld()); for (int32 EntityIndex = 0; EntityIndex < Context.GetNumEntities(); ++EntityIndex) { FMassMoveTargetFragment& MoveTarget = MoveTargetFragments[EntityIndex]; const FTransform& Transform = TransformFragments[EntityIndex].GetTransform(); FVector& Velocity = VelocityFragments[EntityIndex].Value; + const FRTSFormationAgent& RTSFormationAgent = RTSFormationAgents[EntityIndex]; + + const FUnitInfo& Unit = FormationSubsystem->Units[RTSFormationAgent.UnitIndex]; + + // Convert UnitIndex to X/Y coords + const int w = RTSFormationAgent.EntityIndex / Unit.FormationLength; + const int l = RTSFormationAgent.EntityIndex % Unit.FormationLength; + + // We want the formation to be 'centered' so we need to create an offset + const FVector CenterOffset = FVector((Unit.Entities.Num()/Unit.FormationLength/2) * Unit.BufferDistance, (Unit.FormationLength/2) * Unit.BufferDistance, 0.f); + + // Set entity position based on index in formation + FVector EntityPosition = FVector(w,l,0.f); + EntityPosition *= Unit.BufferDistance; + EntityPosition -= CenterOffset; + + // Rotate unit by calculated angle + FVector RotateValue = EntityPosition.RotateAngleAxis(Unit.Angle, FVector(0.f,0.f,Unit.TurnDirection)); + + // Finally add the units position to the entity position + RotateValue += FormationSubsystem->Units[RTSFormationAgent.UnitIndex].InterpolatedDestination; + + MoveTarget.Center = RotateValue; // Update move target values MoveTarget.DistanceToGoal = (MoveTarget.Center - Transform.GetLocation()).Length(); @@ -167,8 +199,14 @@ void URTSAgentMovement::Execute(UMassEntitySubsystem& EntitySubsystem, FMassExec // Once we are close enough to our goal, create stand action if (MoveTarget.DistanceToGoal <= MoveTarget.SlackRadius) { - MoveTarget.CreateNewAction(EMassMovementAction::Stand, *GetWorld()); - Velocity = FVector::Zero(); + //MoveTarget.CreateNewAction(EMassMovementAction::Stand, *GetWorld()); + MoveTarget.DesiredSpeed = FMassInt16Real(MovementParameters.GenerateDesiredSpeed(FormationSettings.WalkMovement, Context.GetEntity(EntityIndex).Index)); + if (FMath::IsNearlyEqual(MoveTarget.DistanceToGoal, 0.f, 1.f) && MoveTarget.GetCurrentAction() == EMassMovementAction::Move) + { + // We've reached the end and should be standing still. + MoveTarget.CreateNewAction(EMassMovementAction::Stand, *GetWorld()); + Velocity = FVector::Zero(); + } } } }); @@ -181,8 +219,6 @@ void URTSFormationUpdate::Initialize(UObject& Owner) { Super::Initialize(Owner); SubscribeToSignal(FormationUpdated); - - FormationSubsystem = UWorld::GetSubsystem(Owner.GetWorld()); } void URTSFormationUpdate::ConfigureQueries() @@ -190,6 +226,7 @@ void URTSFormationUpdate::ConfigureQueries() EntityQuery.AddRequirement(EMassFragmentAccess::ReadOnly); EntityQuery.AddRequirement(EMassFragmentAccess::ReadWrite); EntityQuery.AddRequirement(EMassFragmentAccess::ReadOnly); + EntityQuery.AddConstSharedRequirement(EMassFragmentPresence::All); EntityQuery.AddSharedRequirement(EMassFragmentAccess::ReadOnly); } @@ -199,47 +236,24 @@ void URTSFormationUpdate::SignalEntities(UMassEntitySubsystem& EntitySubsystem, // Query to calculate move target for entities based on unit index EntityQuery.ParallelForEachEntityChunk(EntitySubsystem, Context, [this](FMassExecutionContext& Context) { - TConstArrayView RTSFormationAgents = Context.GetFragmentView(); TArrayView MoveTargetFragments = Context.GetMutableFragmentView(); TConstArrayView TransformFragments = Context.GetFragmentView(); - const FRTSFormationSettings& RTSFormationSettings = Context.GetSharedFragment(); + + const FRTSFormationSettings& FormationSettings = Context.GetSharedFragment(); + const FMassMovementParameters& MovementParameters = Context.GetConstSharedFragment(); for (int32 EntityIndex = 0; EntityIndex < Context.GetNumEntities(); ++EntityIndex) { - - const FRTSFormationAgent& RTSFormationAgent = RTSFormationAgents[EntityIndex]; FMassMoveTargetFragment& MoveTarget = MoveTargetFragments[EntityIndex]; const FTransform& Transform = TransformFragments[EntityIndex].GetTransform(); - //const int Index = FormationSubsystem->Units[RTSFormationAgent.UnitIndex].bReverseUnit ? FormationSubsystem->Units[RTSFormationAgent.UnitIndex].Entities.Num()-1 - RTSFormationAgent.EntityIndex : RTSFormationAgent.EntityIndex; - //const int Index = RTSFormationAgent.EntityIndex; - // Convert UnitIndex to X/Y coords - //const int w = Index / RTSFormationSettings.FormationLength; - //const int l = 4-RTSFormationAgent.EntityIndex % RTSFormationSettings.FormationLength; - - // We want the formation to be 'centered' so we need to create an offset - //const FVector CenterOffset = FVector(0.f, (RTSFormationSettings.FormationLength/2) * RTSFormationSettings.BufferDistance, 0.f); - //(FormationSubsystem->Units[RTSFormationAgent.UnitIndex].Entities.Num()/RTSFormationSettings.FormationLength/2) * RTSFormationSettings.BufferDistance - // Create movement action MoveTarget.CreateNewAction(EMassMovementAction::Move, *GetWorld()); - - // Set entity position based on index in formation - //FVector EntityPosition = FVector(w,l,0.f); - //EntityPosition *= RTSFormationSettings.BufferDistance; - //EntityPosition -= CenterOffset; - - // Rotate unit by calculated angle - //FVector RotateValue = EntityPosition.RotateAngleAxis(FormationSubsystem->Units[RTSFormationAgent.UnitIndex].Angle, FVector(0.f,0.f,FormationSubsystem->Units[RTSFormationAgent.UnitIndex].TurnDirection)); - - // Finally add the units position to the entity position - //RotateValue += FormationSubsystem->Units[RTSFormationAgent.UnitIndex].UnitPosition; - - MoveTarget.Center = RTSFormationAgent.Position; MoveTarget.Forward = (Transform.GetLocation() - MoveTarget.Center).GetSafeNormal(); MoveTarget.DistanceToGoal = (Transform.GetLocation() - MoveTarget.Center).Length(); MoveTarget.SlackRadius = 10.f; MoveTarget.IntentAtGoal = EMassMovementAction::Stand; + MoveTarget.DesiredSpeed = FMassInt16Real(MovementParameters.GenerateDesiredSpeed(FormationSettings.RunMovement, Context.GetEntity(EntityIndex).Index)); } }); } diff --git a/Plugins/RTSFormations/Source/RTSFormations/Private/RTSFormationSubsystem.cpp b/Plugins/RTSFormations/Source/RTSFormations/Private/RTSFormationSubsystem.cpp index a1e9e081..a6dda8dd 100644 --- a/Plugins/RTSFormations/Source/RTSFormations/Private/RTSFormationSubsystem.cpp +++ b/Plugins/RTSFormations/Source/RTSFormations/Private/RTSFormationSubsystem.cpp @@ -6,6 +6,7 @@ #include "MassAgentComponent.h" #include "MassCommonFragments.h" #include "MassEntitySubsystem.h" +#include "MassNavigationFragments.h" #include "MassSignalSubsystem.h" #include "MassSpawnerSubsystem.h" #include "RTSAgentTraits.h" @@ -20,110 +21,131 @@ void URTSFormationSubsystem::DestroyEntity(UMassAgentComponent* Entity) EntitySubsystem->Defer().DestroyEntity(Entity->GetEntityHandle()); } -void URTSFormationSubsystem::SetUnitPosition(const FVector& NewPosition, int UnitIndex) +void URTSFormationSubsystem::UpdateUnitPosition(const FVector& NewPosition, int UnitIndex) { + TRACE_CPUPROFILER_EVENT_SCOPE(TEXT("UpdateUnitPosition")) if (!ensure(Units.IsValidIndex(UnitIndex))) { return; } - DrawDebugDirectionalArrow(GetWorld(), NewPosition, NewPosition+((NewPosition-Units[UnitIndex].UnitPosition).GetSafeNormal()*250.f), 150.f, FColor::Red, false, 5.f, 0, 25.f); - - // Calculate turn direction and angle for entities in unit - float OldAngle = Units[UnitIndex].Angle * Units[UnitIndex].TurnDirection; - Units[UnitIndex].TurnDirection = (NewPosition-Units[UnitIndex].UnitPosition).GetSafeNormal().Y > 0 ? 1.f : -1.f; - Units[UnitIndex].Angle = FMath::RadiansToDegrees(acosf(FVector::DotProduct(FVector::ForwardVector, (NewPosition-Units[UnitIndex].UnitPosition).GetSafeNormal()))); - Units[UnitIndex].Angle += 180.f; // Temporary fix to resolve unit facing the wrong direction + FUnitInfo& Unit = Units[UnitIndex]; - // This might be stupid slow UMassEntitySubsystem* EntitySubsystem = UWorld::GetSubsystem(GetWorld()); + + TSet NewPositions; + NewPositions.Reserve(Unit.Entities.Num()); - struct SortNewPos - { - FVector Position; - int Index; + //Idea + // Rotate and set position to closest entity to destination (done) + // Then, from the farthest unit, calculate the closest destination + const FVector CenterOffset = FVector((Unit.Entities.Num()/Unit.FormationLength/2) * Unit.BufferDistance, (Unit.FormationLength/2) * Unit.BufferDistance, 0.f); - bool operator==(const SortNewPos& Other) const - { - return Other.Position == this->Position; - } - }; - - TArray NewPositions; - NewPositions.Reserve(Units[UnitIndex].Entities.Num()); - const FVector CenterOffset = FVector(0.f, (Units[UnitIndex].FormationLength/2) * Units[UnitIndex].BufferDistance, 0.f); - - for(int l=0;l FVector::Dist(B.Position, UnitPosition); - }); - - Units[UnitIndex].Entities.Sort([&EntitySubsystem, &NewPositions, &NewPosition](const FMassEntityHandle& A, const FMassEntityHandle& B) { - const FVector& LocA = EntitySubsystem->GetFragmentDataChecked(A).GetTransform().GetLocation(); - const FVector& LocB = EntitySubsystem->GetFragmentDataChecked(B).GetTransform().GetLocation(); - return FVector::Dist(LocA, NewPosition) < FVector::Dist(LocB, NewPosition); - }); + TRACE_CPUPROFILER_EVENT_SCOPE(TEXT("SortPositions")) + Unit.Entities.Sort([&EntitySubsystem, &NewPosition](const FMassEntityHandle& A, const FMassEntityHandle& B) + { + const FVector& LocA = EntitySubsystem->GetFragmentDataChecked(A).GetTransform().GetLocation(); + const FVector& LocB = EntitySubsystem->GetFragmentDataChecked(B).GetTransform().GetLocation(); + return FVector::Dist(LocA, NewPosition) < FVector::Dist(LocB, NewPosition); + }); + } // We should not be doing this but for the convenience of the current logic in place, this is the fastest option i think - for(int i=0;i IndexesTaken; + IndexesTaken.Reserve(Unit.Entities.Num()); + { - TArray NewPositionsGroup; - TArray EntityGroup; - EntityGroup.Reserve(7); - NewPositionsGroup.Reserve(7); - for(int j=0;j<7;++j) - { - int Index = j+(i/7*7); - // Return early if there are no more entities - if(!Units[UnitIndex].Entities.IsValidIndex(Index)) - break; - - EntityGroup.Emplace(Units[UnitIndex].Entities[Index]); - NewPositionsGroup.Emplace(NewPositions[Index]); - } - for(const FMassEntityHandle& Entity : EntityGroup) + TRACE_CPUPROFILER_EVENT_SCOPE(TEXT("SetEntityIndex")) + for(int i=0;iGetFragmentDataPtr(Entity)->GetTransform().GetLocation(); - SortNewPos ClosestPos; - float ClosestDist = -1; - for(const SortNewPos& NewPos : NewPositionsGroup) + const FVector& Location = EntitySubsystem->GetFragmentDataPtr(Unit.Entities[i])->GetTransform().GetLocation(); + float ClosestDistance = -1; + FSortNewPos ClosestPos = FSortNewPos(); { - if(ClosestDist == -1 || FVector::Dist(NewPos.Position, EntityPos) < ClosestDist) + TRACE_CPUPROFILER_EVENT_SCOPE(TEXT("DetermineClosestPosition")) + for(const FSortNewPos& NewPos : NewPositions) { - ClosestDist = FVector::Dist(NewPos.Position, EntityPos); - ClosestPos = NewPos; + const float Dist = FVector::Dist(Location, NewPos.Position); + if (ClosestDistance == -1 || Dist < ClosestDistance) + { + ClosestDistance = Dist; + ClosestPos = NewPos; + } } + } + + // Basically scoot up entities if there is space in the front + int Index = ClosestPos.Index; + while(!IndexesTaken.Contains(Index-Unit.FormationLength) && Index-Unit.FormationLength >= 0) + { + Index -= Unit.FormationLength; } - EntitySubsystem->GetFragmentDataPtr(Entity)->Position = ClosestPos.Position; - NewPositionsGroup.Remove(ClosestPos); + + IndexesTaken.Emplace(Index); + EntitySubsystem->GetFragmentDataPtr(Unit.Entities[i])->EntityIndex = Index; + NewPositions.Remove(ClosestPos); } } +} + +void URTSFormationSubsystem::SetUnitPosition(const FVector& NewPosition, int UnitIndex) +{ + DrawDebugDirectionalArrow(GetWorld(), NewPosition, NewPosition+((NewPosition-Units[UnitIndex].InterpolatedDestination).GetSafeNormal()*250.f), 150.f, FColor::Red, false, 5.f, 0, 25.f); + + // Calculate turn direction and angle for entities in unit + Units[UnitIndex].TurnDirection = (NewPosition-Units[UnitIndex].InterpolatedDestination).GetSafeNormal().Y > 0 ? 1.f : -1.f; + Units[UnitIndex].Angle = FMath::RadiansToDegrees(acosf(FVector::DotProduct((NewPosition-Units[UnitIndex].InterpolatedDestination).GetSafeNormal(),FVector::ForwardVector))); + Units[UnitIndex].ForwardVector = (NewPosition-Units[UnitIndex].InterpolatedDestination).GetSafeNormal(); + Units[UnitIndex].Angle += 180.f; // Temporary fix to resolve unit facing the wrong direction Units[UnitIndex].UnitPosition = NewPosition; - // Signal entities of a position update - if (Units[UnitIndex].Entities.Num() > 0) + // Instantly set the angle since we are not keeping complete formation right now + Units[UnitIndex].InterpolatedAngle = Units[UnitIndex].Angle; + UpdateUnitPosition(NewPosition, UnitIndex); + + CurrentIndex = 0; + + // Sort entities from front-to-back + UMassEntitySubsystem* EntitySubsystem = UWorld::GetSubsystem(GetWorld()); + Units[UnitIndex].Entities.Sort([&](const FMassEntityHandle& A, const FMassEntityHandle& B) { - for(int i=0;iGetFragmentDataChecked(A).EntityIndex; + const int LocB = EntitySubsystem->GetFragmentDataChecked(B).EntityIndex; + return LocA < LocB; + }); + + // Signal entities to begin moving + FTimerDelegate TimerDelegate; + TimerDelegate.BindLambda([&]() + { + // Signal entities of a position update + if (Units[0].Entities.Num() > 0) { - GetWorld()->GetSubsystem()->DelaySignalEntity(FormationUpdated, Units[UnitIndex].Entities[i], i/7*0.25); + CurrentIndex = FMath::Clamp(CurrentIndex+1, 0, Units[0].Entities.Num()-1); + GetWorld()->GetSubsystem()->SignalEntity(FormationUpdated, Units[0].Entities[CurrentIndex]); } - } - //GetWorld()->GetSubsystem()->SignalEntities(FormationUpdated, Units[UnitIndex].Entities); + }); + GetWorld()->GetTimerManager().ClearTimer(MoveHandle); + GetWorld()->GetTimerManager().SetTimer(MoveHandle, TimerDelegate, 0.05, true); } void URTSFormationSubsystem::SpawnEntitiesForUnit(int UnitIndex, const UMassEntityConfigAsset* EntityConfig, int Count) @@ -166,3 +188,28 @@ int URTSFormationSubsystem::SpawnNewUnit(const UMassEntityConfigAsset* EntityCon SpawnEntitiesForUnit(UnitIndex, EntityConfig, Count); return UnitIndex; } + +void URTSFormationSubsystem::Tick(float DeltaTime) +{ + for(int i=0;i Date: Thu, 21 Jul 2022 22:34:58 -0500 Subject: [PATCH 06/37] Add simple variable tick rate to rts movement processor --- .../Private/RTSFormationProcessors.cpp | 24 +++++++++++-------- .../RTSFormations/RTSFormations.Build.cs | 2 +- 2 files changed, 15 insertions(+), 11 deletions(-) diff --git a/Plugins/RTSFormations/Source/RTSFormations/Private/RTSFormationProcessors.cpp b/Plugins/RTSFormations/Source/RTSFormations/Private/RTSFormationProcessors.cpp index ef775290..bd168f4b 100644 --- a/Plugins/RTSFormations/Source/RTSFormations/Private/RTSFormationProcessors.cpp +++ b/Plugins/RTSFormations/Source/RTSFormations/Private/RTSFormationProcessors.cpp @@ -5,6 +5,7 @@ #include "MassNavigationFragments.h" #include "MassNavigationTypes.h" #include "MassSignalSubsystem.h" +#include "MassSimulationLOD.h" #include "RTSAgentTraits.h" #include "RTSFormationSubsystem.h" @@ -60,7 +61,7 @@ void URTSFormationInitializer::Execute(UMassEntitySubsystem& EntitySubsystem, FM // Signal entities in the unit that their position is updated // @todo only notify affected entities for(const int& Unit : UnitSignals) - SignalSubsystem->SignalEntities(FormationUpdated, FormationSubsystem->Units[Unit].Entities); + SignalSubsystem->SignalEntities(FormationUpdated, FormationSubsystem->Units[Unit].Entities.Array()); }); } @@ -101,16 +102,16 @@ void URTSFormationDestroyer::Execute(UMassEntitySubsystem& EntitySubsystem, FMas // Remove entity from units array if (FormationSubsystem->Units.IsValidIndex(FormationAgent.UnitIndex)) { - const int32 ItemIndex = FormationSubsystem->Units[FormationAgent.UnitIndex].Entities.IndexOfByKey(Context.GetEntity(EntityIndex)); - if (ItemIndex != INDEX_NONE) + const FMassEntityHandle* ItemIndex = FormationSubsystem->Units[FormationAgent.UnitIndex].Entities.Find(Context.GetEntity(EntityIndex)); + if (ItemIndex) { // Since we are caching the index, we need to fix the entity index that replaces the destroyed one // Not sure if this is the 'correct' way to handle this, but it works for now - if (FRTSFormationAgent* ReplacementFormationAgent = EntitySubsystem.GetFragmentDataPtr(FormationSubsystem->Units[FormationAgent.UnitIndex].Entities.Last())) - ReplacementFormationAgent->EntityIndex = ItemIndex; + if (FRTSFormationAgent* ReplacementFormationAgent = EntitySubsystem.GetFragmentDataPtr(FormationSubsystem->Units[FormationAgent.UnitIndex].Entities.Array().Last())) + ReplacementFormationAgent->EntityIndex = FormationAgent.EntityIndex; - FormationSubsystem->Units[FormationAgent.UnitIndex].Entities.RemoveAtSwap(ItemIndex, 1, true); - UnitSignals.Emplace(FormationAgent.UnitIndex); + FormationSubsystem->Units[FormationAgent.UnitIndex].Entities.Remove(*ItemIndex); + UnitSignals.AddUnique(FormationAgent.UnitIndex); } } } @@ -130,7 +131,7 @@ void URTSFormationDestroyer::Execute(UMassEntitySubsystem& EntitySubsystem, FMas // Really the only time we should notify every entity in the unit is when the center point changes // Every other time we just have to notify the entity that is replacing the destroyed one FormationSubsystem->Units[Unit].Entities.Shrink(); - SignalSubsystem->SignalEntities(FormationUpdated, FormationSubsystem->Units[Unit].Entities); + SignalSubsystem->SignalEntities(FormationUpdated, FormationSubsystem->Units[Unit].Entities.Array()); } } }); @@ -147,6 +148,8 @@ void URTSAgentMovement::ConfigureQueries() EntityQuery.AddRequirement(EMassFragmentAccess::ReadWrite); EntityQuery.AddConstSharedRequirement(EMassFragmentPresence::All); EntityQuery.AddSharedRequirement(EMassFragmentAccess::ReadOnly); + EntityQuery.AddChunkRequirement(EMassFragmentAccess::ReadOnly, EMassFragmentPresence::Optional); + EntityQuery.SetChunkFilter(&FMassSimulationVariableTickChunkFragment::ShouldTickChunkThisFrame); } void URTSAgentMovement::Execute(UMassEntitySubsystem& EntitySubsystem, FMassExecutionContext& Context) @@ -188,7 +191,7 @@ void URTSAgentMovement::Execute(UMassEntitySubsystem& EntitySubsystem, FMassExec FVector RotateValue = EntityPosition.RotateAngleAxis(Unit.Angle, FVector(0.f,0.f,Unit.TurnDirection)); // Finally add the units position to the entity position - RotateValue += FormationSubsystem->Units[RTSFormationAgent.UnitIndex].InterpolatedDestination; + RotateValue += Unit.InterpolatedDestination; MoveTarget.Center = RotateValue; @@ -201,7 +204,8 @@ void URTSAgentMovement::Execute(UMassEntitySubsystem& EntitySubsystem, FMassExec { //MoveTarget.CreateNewAction(EMassMovementAction::Stand, *GetWorld()); MoveTarget.DesiredSpeed = FMassInt16Real(MovementParameters.GenerateDesiredSpeed(FormationSettings.WalkMovement, Context.GetEntity(EntityIndex).Index)); - if (FMath::IsNearlyEqual(MoveTarget.DistanceToGoal, 0.f, 1.f) && MoveTarget.GetCurrentAction() == EMassMovementAction::Move) + + if (FMath::IsNearlyEqual(MoveTarget.DistanceToGoal, 0.f, 0.5f) && MoveTarget.GetCurrentAction() == EMassMovementAction::Move) { // We've reached the end and should be standing still. MoveTarget.CreateNewAction(EMassMovementAction::Stand, *GetWorld()); diff --git a/Plugins/RTSFormations/Source/RTSFormations/RTSFormations.Build.cs b/Plugins/RTSFormations/Source/RTSFormations/RTSFormations.Build.cs index 2a572eb0..6becf8b1 100644 --- a/Plugins/RTSFormations/Source/RTSFormations/RTSFormations.Build.cs +++ b/Plugins/RTSFormations/Source/RTSFormations/RTSFormations.Build.cs @@ -37,7 +37,7 @@ public RTSFormations(ReadOnlyTargetRules Target) : base(Target) "CoreUObject", "Engine", "Slate", - "SlateCore", "MassNavigation", "MassActors" + "SlateCore", "MassNavigation", "MassActors", "MassLOD" // ... add private dependencies that you statically link with here ... } ); From 562bbf8c86048e2b8bad9a77f3da14312254956f Mon Sep 17 00:00:00 2001 From: Ji-Rath Date: Thu, 21 Jul 2022 22:36:00 -0500 Subject: [PATCH 07/37] Performance refactoring Optimize and cleanup rts subsystem for large-scale use Still need to consider if its feasible to move some logic to a processor rather than the subsystem --- .../Private/RTSFormationSubsystem.cpp | 107 ++++++++---------- .../Private/RTSFormationSubsystem.h | 33 +----- 2 files changed, 51 insertions(+), 89 deletions(-) diff --git a/Plugins/RTSFormations/Source/RTSFormations/Private/RTSFormationSubsystem.cpp b/Plugins/RTSFormations/Source/RTSFormations/Private/RTSFormationSubsystem.cpp index a6dda8dd..fe17a427 100644 --- a/Plugins/RTSFormations/Source/RTSFormations/Private/RTSFormationSubsystem.cpp +++ b/Plugins/RTSFormations/Source/RTSFormations/Private/RTSFormationSubsystem.cpp @@ -30,7 +30,7 @@ void URTSFormationSubsystem::UpdateUnitPosition(const FVector& NewPosition, int UMassEntitySubsystem* EntitySubsystem = UWorld::GetSubsystem(GetWorld()); - TSet NewPositions; + TMap NewPositions; NewPositions.Reserve(Unit.Entities.Num()); //Idea @@ -40,99 +40,90 @@ void URTSFormationSubsystem::UpdateUnitPosition(const FVector& NewPosition, int { TRACE_CPUPROFILER_EVENT_SCOPE(TEXT("CalculateNewPositions")) - for(int l=0;lGetFragmentDataChecked(A).GetTransform().GetLocation(); const FVector& LocB = EntitySubsystem->GetFragmentDataChecked(B).GetTransform().GetLocation(); - return FVector::Dist(LocA, NewPosition) < FVector::Dist(LocB, NewPosition); + return FVector::DistSquared2D(LocA, FrontMidPosition) < FVector::DistSquared2D(LocB, FrontMidPosition); }); } + + NewPositions.ValueSort([&FrontMidPosition](const FVector& A, const FVector& B) + { + return FVector::DistSquared2D(A, FrontMidPosition) < FVector::DistSquared2D(B, FrontMidPosition); + }); + DrawDebugPoint(GetWorld(), FrontMidPosition, 20.f, FColor::Yellow, false, 10.f); // We should not be doing this but for the convenience of the current logic in place, this is the fastest option i think - // This overcomplicated logic is to make sure each 'group' of entities chooses the closest destination possible. // @todo performance and transfer to processor? TSet IndexesTaken; IndexesTaken.Reserve(Unit.Entities.Num()); + int i=0; { TRACE_CPUPROFILER_EVENT_SCOPE(TEXT("SetEntityIndex")) - for(int i=0;iGetFragmentDataPtr(Unit.Entities[i])->GetTransform().GetLocation(); - float ClosestDistance = -1; - FSortNewPos ClosestPos = FSortNewPos(); + // Get first index since it is sorted + TPair ClosestPos; + for(const TPair& NewPos : NewPositions) { - TRACE_CPUPROFILER_EVENT_SCOPE(TEXT("DetermineClosestPosition")) - for(const FSortNewPos& NewPos : NewPositions) - { - const float Dist = FVector::Dist(Location, NewPos.Position); - if (ClosestDistance == -1 || Dist < ClosestDistance) - { - ClosestDistance = Dist; - ClosestPos = NewPos; - } - } - } - - // Basically scoot up entities if there is space in the front - int Index = ClosestPos.Index; - while(!IndexesTaken.Contains(Index-Unit.FormationLength) && Index-Unit.FormationLength >= 0) - { - Index -= Unit.FormationLength; + ClosestPos = NewPos; + break; } + + // Basically scoot up entities if there is space in the front + int& Index = ClosestPos.Key; IndexesTaken.Emplace(Index); - EntitySubsystem->GetFragmentDataPtr(Unit.Entities[i])->EntityIndex = Index; - NewPositions.Remove(ClosestPos); + EntitySubsystem->GetFragmentDataPtr(Entity)->EntityIndex = Index; + + //DrawDebugString(GetWorld(), *NewPositions.Find(Index), FString::Printf(TEXT("%d"), Index), NULL, FColor::Black, 10.f); + NewPositions.Remove(Index); + i++; } } } void URTSFormationSubsystem::SetUnitPosition(const FVector& NewPosition, int UnitIndex) { - DrawDebugDirectionalArrow(GetWorld(), NewPosition, NewPosition+((NewPosition-Units[UnitIndex].InterpolatedDestination).GetSafeNormal()*250.f), 150.f, FColor::Red, false, 5.f, 0, 25.f); + UMassEntitySubsystem* EntitySubsystem = UWorld::GetSubsystem(GetWorld()); + DrawDebugDirectionalArrow(GetWorld(), NewPosition, NewPosition+((NewPosition-Units[UnitIndex].InterpolatedDestination).GetSafeNormal()*250.f), 150.f, FColor::Red, false, 5.f, 0, 25.f); + + FUnitInfo& Unit = Units[UnitIndex]; // Calculate turn direction and angle for entities in unit - Units[UnitIndex].TurnDirection = (NewPosition-Units[UnitIndex].InterpolatedDestination).GetSafeNormal().Y > 0 ? 1.f : -1.f; - Units[UnitIndex].Angle = FMath::RadiansToDegrees(acosf(FVector::DotProduct((NewPosition-Units[UnitIndex].InterpolatedDestination).GetSafeNormal(),FVector::ForwardVector))); - Units[UnitIndex].ForwardVector = (NewPosition-Units[UnitIndex].InterpolatedDestination).GetSafeNormal(); - Units[UnitIndex].Angle += 180.f; // Temporary fix to resolve unit facing the wrong direction + Unit.TurnDirection = (NewPosition-Units[UnitIndex].InterpolatedDestination).GetSafeNormal().Y > 0 ? 1.f : -1.f; + Unit.Angle = FMath::RadiansToDegrees(acosf(FVector::DotProduct((NewPosition-Units[UnitIndex].InterpolatedDestination).GetSafeNormal(),FVector::ForwardVector))); + Unit.ForwardVector = (NewPosition-Units[UnitIndex].InterpolatedDestination).GetSafeNormal(); + Unit.Angle += 180.f; // Temporary fix to resolve unit facing the wrong direction - Units[UnitIndex].UnitPosition = NewPosition; + Unit.UnitPosition = NewPosition; // Instantly set the angle since we are not keeping complete formation right now - Units[UnitIndex].InterpolatedAngle = Units[UnitIndex].Angle; - UpdateUnitPosition(NewPosition, UnitIndex); + Unit.InterpolatedAngle = Units[UnitIndex].Angle; + UpdateUnitPosition(Unit.InterpolatedDestination, UnitIndex); CurrentIndex = 0; - // Sort entities from front-to-back - UMassEntitySubsystem* EntitySubsystem = UWorld::GetSubsystem(GetWorld()); - Units[UnitIndex].Entities.Sort([&](const FMassEntityHandle& A, const FMassEntityHandle& B) - { - const int LocA = EntitySubsystem->GetFragmentDataChecked(A).EntityIndex; - const int LocB = EntitySubsystem->GetFragmentDataChecked(B).EntityIndex; - return LocA < LocB; - }); - // Signal entities to begin moving FTimerDelegate TimerDelegate; TimerDelegate.BindLambda([&]() @@ -141,7 +132,7 @@ void URTSFormationSubsystem::SetUnitPosition(const FVector& NewPosition, int Uni if (Units[0].Entities.Num() > 0) { CurrentIndex = FMath::Clamp(CurrentIndex+1, 0, Units[0].Entities.Num()-1); - GetWorld()->GetSubsystem()->SignalEntity(FormationUpdated, Units[0].Entities[CurrentIndex]); + GetWorld()->GetSubsystem()->SignalEntity(FormationUpdated, Units[0].Entities.Array()[CurrentIndex]); } }); GetWorld()->GetTimerManager().ClearTimer(MoveHandle); diff --git a/Plugins/RTSFormations/Source/RTSFormations/Private/RTSFormationSubsystem.h b/Plugins/RTSFormations/Source/RTSFormations/Private/RTSFormationSubsystem.h index 79925a58..4949d84a 100644 --- a/Plugins/RTSFormations/Source/RTSFormations/Private/RTSFormationSubsystem.h +++ b/Plugins/RTSFormations/Source/RTSFormations/Private/RTSFormationSubsystem.h @@ -13,35 +13,6 @@ enum FormationType Circle }; -USTRUCT() -struct FSortNewPos -{ - GENERATED_BODY() - - FVector Position; - int Index; - - bool operator==(const FSortNewPos& Other) const - { - return Other.Index == Index; - } - - FSortNewPos() - { - Index = 0; - Position = FVector::Zero(); - } -}; -#if UE_BUILD_DEBUG -uint32 GetTypeHash(const FSortNewPos& Thing); -#else // optimize by inlining in shipping and development builds -FORCEINLINE uint32 GetTypeHash(const FSortNewPos& Thing) -{ - uint32 Hash = FCrc::MemCrc32(&Thing, sizeof(FSortNewPos)); - return Hash; -} -#endif - USTRUCT(BlueprintType) struct FUnitInfo { @@ -50,7 +21,7 @@ struct FUnitInfo public: // Entities in the unit UPROPERTY() - TArray Entities; + TSet Entities; // The current unit position UPROPERTY(BlueprintReadOnly) @@ -62,7 +33,7 @@ struct FUnitInfo // The entity length of the 'front' of the unit UPROPERTY(BlueprintReadWrite) - int FormationLength = 7; + int FormationLength = 8; UPROPERTY(BlueprintReadWrite) float BufferDistance = 100.f; From 1af4c826c454c7c5cd1ab7634d52093918ccbb51 Mon Sep 17 00:00:00 2001 From: Ji-Rath Date: Fri, 22 Jul 2022 10:36:40 -0500 Subject: [PATCH 08/37] Fix single entity not moving when changing position occasionally Get units to move in formation...at least good enough Adjust tolerance for reaching formation position --- .../Private/RTSFormationProcessors.cpp | 2 +- .../Private/RTSFormationSubsystem.cpp | 56 ++++++++++++------- 2 files changed, 36 insertions(+), 22 deletions(-) diff --git a/Plugins/RTSFormations/Source/RTSFormations/Private/RTSFormationProcessors.cpp b/Plugins/RTSFormations/Source/RTSFormations/Private/RTSFormationProcessors.cpp index bd168f4b..86bb4370 100644 --- a/Plugins/RTSFormations/Source/RTSFormations/Private/RTSFormationProcessors.cpp +++ b/Plugins/RTSFormations/Source/RTSFormations/Private/RTSFormationProcessors.cpp @@ -205,7 +205,7 @@ void URTSAgentMovement::Execute(UMassEntitySubsystem& EntitySubsystem, FMassExec //MoveTarget.CreateNewAction(EMassMovementAction::Stand, *GetWorld()); MoveTarget.DesiredSpeed = FMassInt16Real(MovementParameters.GenerateDesiredSpeed(FormationSettings.WalkMovement, Context.GetEntity(EntityIndex).Index)); - if (FMath::IsNearlyEqual(MoveTarget.DistanceToGoal, 0.f, 0.5f) && MoveTarget.GetCurrentAction() == EMassMovementAction::Move) + if (FMath::IsNearlyEqual(MoveTarget.DistanceToGoal, 0.f, 1.f) && MoveTarget.GetCurrentAction() == EMassMovementAction::Move) { // We've reached the end and should be standing still. MoveTarget.CreateNewAction(EMassMovementAction::Stand, *GetWorld()); diff --git a/Plugins/RTSFormations/Source/RTSFormations/Private/RTSFormationSubsystem.cpp b/Plugins/RTSFormations/Source/RTSFormations/Private/RTSFormationSubsystem.cpp index fe17a427..aa54029a 100644 --- a/Plugins/RTSFormations/Source/RTSFormations/Private/RTSFormationSubsystem.cpp +++ b/Plugins/RTSFormations/Source/RTSFormations/Private/RTSFormationSubsystem.cpp @@ -6,7 +6,6 @@ #include "MassAgentComponent.h" #include "MassCommonFragments.h" #include "MassEntitySubsystem.h" -#include "MassNavigationFragments.h" #include "MassSignalSubsystem.h" #include "MassSpawnerSubsystem.h" #include "RTSAgentTraits.h" @@ -32,10 +31,7 @@ void URTSFormationSubsystem::UpdateUnitPosition(const FVector& NewPosition, int TMap NewPositions; NewPositions.Reserve(Unit.Entities.Num()); - - //Idea - // Rotate and set position to closest entity to destination (done) - // Then, from the farthest unit, calculate the closest destination + const FVector CenterOffset = FVector((Unit.Entities.Num()/Unit.FormationLength/2) * Unit.BufferDistance, (Unit.FormationLength/2) * Unit.BufferDistance, 0.f); { @@ -53,22 +49,26 @@ void URTSFormationSubsystem::UpdateUnitPosition(const FVector& NewPosition, int NewPositions.Emplace(i, Position); } } + FVector FrontCenter = FVector((Unit.Entities.Num()/Unit.FormationLength/2) * Unit.BufferDistance, 0.f, 0.f).RotateAngleAxis(Unit.Angle, FVector(0.f,0.f,Unit.TurnDirection)); + TArray NewArray; + NewArray.Reserve(NewPositions.Num()); + NewPositions.GenerateValueArray(NewArray); FVector FrontMidPosition = NewPosition - FrontCenter; + FVector FarCorner = NewArray[0]; { TRACE_CPUPROFILER_EVENT_SCOPE(TEXT("SortPositions")) - Unit.Entities.Sort([&EntitySubsystem, &FrontMidPosition](const FMassEntityHandle& A, const FMassEntityHandle& B) + Unit.Entities.Sort([&EntitySubsystem, &FarCorner](const FMassEntityHandle& A, const FMassEntityHandle& B) { // Find if theres a way to move this logic to a processor, most of the cost is coming from retrieving the location const FVector& LocA = EntitySubsystem->GetFragmentDataChecked(A).GetTransform().GetLocation(); const FVector& LocB = EntitySubsystem->GetFragmentDataChecked(B).GetTransform().GetLocation(); - return FVector::DistSquared2D(LocA, FrontMidPosition) < FVector::DistSquared2D(LocB, FrontMidPosition); + return FVector::DistSquared2D(LocA, FarCorner) > FVector::DistSquared2D(LocB, FarCorner); }); } - - NewPositions.ValueSort([&FrontMidPosition](const FVector& A, const FVector& B) + NewPositions.ValueSort([&FarCorner](const FVector& A, const FVector& B) { - return FVector::DistSquared2D(A, FrontMidPosition) < FVector::DistSquared2D(B, FrontMidPosition); + return FVector::DistSquared2D(A, FarCorner) > FVector::DistSquared2D(B, FarCorner); }); DrawDebugPoint(GetWorld(), FrontMidPosition, 20.f, FColor::Yellow, false, 10.f); @@ -76,18 +76,27 @@ void URTSFormationSubsystem::UpdateUnitPosition(const FVector& NewPosition, int // @todo performance and transfer to processor? TSet IndexesTaken; IndexesTaken.Reserve(Unit.Entities.Num()); - - int i=0; + { TRACE_CPUPROFILER_EVENT_SCOPE(TEXT("SetEntityIndex")) for(const FMassEntityHandle& Entity : Unit.Entities) { + const FVector& Location = EntitySubsystem->GetFragmentDataPtr(Entity)->GetTransform().GetLocation(); // Get first index since it is sorted TPair ClosestPos; + float ClosestDistance = -1; + int i=0; for(const TPair& NewPos : NewPositions) { - ClosestPos = NewPos; - break; + float Dist = FVector::DistSquared2D(NewPos.Value, Location); + if (ClosestDistance == -1 || Dist < ClosestDistance) + { + ClosestPos = NewPos; + ClosestDistance = Dist; + // While its not perfect, this adds a hard cap to how many positions to check + if (++i > Unit.FormationLength*2) + break; + } } // Basically scoot up entities if there is space in the front @@ -95,18 +104,22 @@ void URTSFormationSubsystem::UpdateUnitPosition(const FVector& NewPosition, int IndexesTaken.Emplace(Index); EntitySubsystem->GetFragmentDataPtr(Entity)->EntityIndex = Index; - - //DrawDebugString(GetWorld(), *NewPositions.Find(Index), FString::Printf(TEXT("%d"), Index), NULL, FColor::Black, 10.f); NewPositions.Remove(Index); - i++; } } + + // Final sort to ensure that entities are signaled from front to back + Unit.Entities.Sort([&EntitySubsystem, &FarCorner](const FMassEntityHandle& A, const FMassEntityHandle& B) + { + // Find if theres a way to move this logic to a processor, most of the cost is coming from retrieving the location + const FVector& LocA = EntitySubsystem->GetFragmentDataChecked(A).GetTransform().GetLocation(); + const FVector& LocB = EntitySubsystem->GetFragmentDataChecked(B).GetTransform().GetLocation(); + return FVector::DistSquared2D(LocA, FarCorner) < FVector::DistSquared2D(LocB, FarCorner); + }); } void URTSFormationSubsystem::SetUnitPosition(const FVector& NewPosition, int UnitIndex) { - UMassEntitySubsystem* EntitySubsystem = UWorld::GetSubsystem(GetWorld()); - DrawDebugDirectionalArrow(GetWorld(), NewPosition, NewPosition+((NewPosition-Units[UnitIndex].InterpolatedDestination).GetSafeNormal()*250.f), 150.f, FColor::Red, false, 5.f, 0, 25.f); FUnitInfo& Unit = Units[UnitIndex]; @@ -120,7 +133,7 @@ void URTSFormationSubsystem::SetUnitPosition(const FVector& NewPosition, int Uni // Instantly set the angle since we are not keeping complete formation right now Unit.InterpolatedAngle = Units[UnitIndex].Angle; - UpdateUnitPosition(Unit.InterpolatedDestination, UnitIndex); + UpdateUnitPosition(NewPosition, UnitIndex); CurrentIndex = 0; @@ -131,8 +144,9 @@ void URTSFormationSubsystem::SetUnitPosition(const FVector& NewPosition, int Uni // Signal entities of a position update if (Units[0].Entities.Num() > 0) { - CurrentIndex = FMath::Clamp(CurrentIndex+1, 0, Units[0].Entities.Num()-1); + CurrentIndex = FMath::Clamp(CurrentIndex, 0, Units[0].Entities.Num()-1); GetWorld()->GetSubsystem()->SignalEntity(FormationUpdated, Units[0].Entities.Array()[CurrentIndex]); + CurrentIndex++; } }); GetWorld()->GetTimerManager().ClearTimer(MoveHandle); From cf9bdb0ce89c3f23f0eb2e8af02fb63a676aa90d Mon Sep 17 00:00:00 2001 From: Ji-Rath Date: Fri, 22 Jul 2022 15:28:21 -0500 Subject: [PATCH 09/37] Fix entity creation/destruction not properly updating unit Add RTSUpdateEntityIndex to spread out logic cost when finding formation index Fix bug where entities would stop moving when travelling to new destination --- .../Private/RTSFormationProcessors.cpp | 84 +++++++-- .../Private/RTSFormationProcessors.h | 13 ++ .../Private/RTSFormationSubsystem.cpp | 164 ++++++++---------- .../Private/RTSFormationSubsystem.h | 18 +- 4 files changed, 169 insertions(+), 110 deletions(-) diff --git a/Plugins/RTSFormations/Source/RTSFormations/Private/RTSFormationProcessors.cpp b/Plugins/RTSFormations/Source/RTSFormations/Private/RTSFormationProcessors.cpp index 86bb4370..57e5c396 100644 --- a/Plugins/RTSFormations/Source/RTSFormations/Private/RTSFormationProcessors.cpp +++ b/Plugins/RTSFormations/Source/RTSFormations/Private/RTSFormationProcessors.cpp @@ -107,8 +107,8 @@ void URTSFormationDestroyer::Execute(UMassEntitySubsystem& EntitySubsystem, FMas { // Since we are caching the index, we need to fix the entity index that replaces the destroyed one // Not sure if this is the 'correct' way to handle this, but it works for now - if (FRTSFormationAgent* ReplacementFormationAgent = EntitySubsystem.GetFragmentDataPtr(FormationSubsystem->Units[FormationAgent.UnitIndex].Entities.Array().Last())) - ReplacementFormationAgent->EntityIndex = FormationAgent.EntityIndex; + //if (FRTSFormationAgent* ReplacementFormationAgent = EntitySubsystem.GetFragmentDataPtr(FormationSubsystem->Units[FormationAgent.UnitIndex].Entities.Array().Last())) + // ReplacementFormationAgent->EntityIndex = FormationAgent.EntityIndex; FormationSubsystem->Units[FormationAgent.UnitIndex].Entities.Remove(*ItemIndex); UnitSignals.AddUnique(FormationAgent.UnitIndex); @@ -131,7 +131,8 @@ void URTSFormationDestroyer::Execute(UMassEntitySubsystem& EntitySubsystem, FMas // Really the only time we should notify every entity in the unit is when the center point changes // Every other time we just have to notify the entity that is replacing the destroyed one FormationSubsystem->Units[Unit].Entities.Shrink(); - SignalSubsystem->SignalEntities(FormationUpdated, FormationSubsystem->Units[Unit].Entities.Array()); + FormationSubsystem->UpdateUnitPosition(FormationSubsystem->Units[Unit].UnitPosition, Unit); + //SignalSubsystem->SignalEntities(FormationUpdated, FormationSubsystem->Units[Unit].Entities.Array()); } } }); @@ -204,14 +205,7 @@ void URTSAgentMovement::Execute(UMassEntitySubsystem& EntitySubsystem, FMassExec { //MoveTarget.CreateNewAction(EMassMovementAction::Stand, *GetWorld()); MoveTarget.DesiredSpeed = FMassInt16Real(MovementParameters.GenerateDesiredSpeed(FormationSettings.WalkMovement, Context.GetEntity(EntityIndex).Index)); - - if (FMath::IsNearlyEqual(MoveTarget.DistanceToGoal, 0.f, 1.f) && MoveTarget.GetCurrentAction() == EMassMovementAction::Move) - { - // We've reached the end and should be standing still. - MoveTarget.CreateNewAction(EMassMovementAction::Stand, *GetWorld()); - Velocity = FVector::Zero(); - } - } + } } }); } @@ -261,3 +255,71 @@ void URTSFormationUpdate::SignalEntities(UMassEntitySubsystem& EntitySubsystem, } }); } + +//----------------------------------------------------------------------// +// URTSUpdateEntityIndex +//----------------------------------------------------------------------// +void URTSUpdateEntityIndex::Initialize(UObject& Owner) +{ + Super::Initialize(Owner); + SubscribeToSignal(UpdateIndex); + FormationSubsystem = UWorld::GetSubsystem(Owner.GetWorld()); +} + +void URTSUpdateEntityIndex::ConfigureQueries() +{ + EntityQuery.AddRequirement(EMassFragmentAccess::ReadOnly); + EntityQuery.AddRequirement(EMassFragmentAccess::ReadWrite); + EntityQuery.AddRequirement(EMassFragmentAccess::ReadOnly); +} + +void URTSUpdateEntityIndex::SignalEntities(UMassEntitySubsystem& EntitySubsystem, FMassExecutionContext& Context, + FMassSignalNameLookup& EntitySignals) +{ + // Update entity index so that they go to the closest possible position + // Entities are signaled in order of distance to destination, this allows the NewPosition array to be sorted once + // and cut down on iterations significantly + EntityQuery.ParallelForEachEntityChunk(EntitySubsystem, Context, [this](FMassExecutionContext& Context) + { + TArrayView MoveTargetFragments = Context.GetMutableFragmentView(); + TConstArrayView TransformFragments = Context.GetFragmentView(); + TArrayView FormationAgents = Context.GetMutableFragmentView(); + + for (int32 EntityIndex = 0; EntityIndex < Context.GetNumEntities(); ++EntityIndex) + { + FMassMoveTargetFragment& MoveTarget = MoveTargetFragments[EntityIndex]; + const FVector& Location = TransformFragments[EntityIndex].GetTransform().GetLocation(); + FRTSFormationAgent& FormationAgent = FormationAgents[EntityIndex]; + + // Get first index since it is sorted + TPair ClosestPos; + float ClosestDistance = -1; + int i=0; + for(const TPair& NewPos : FormationSubsystem->Units[FormationAgent.UnitIndex].NewPositions) + { + float Dist = FVector::DistSquared2D(NewPos.Value, Location); + if (ClosestDistance == -1 || Dist < ClosestDistance) + { + ClosestPos = NewPos; + ClosestDistance = Dist; + + // While its not perfect, this adds a hard cap to how many positions to check + if (++i > FormationSubsystem->Units[FormationAgent.UnitIndex].FormationLength*2) + break; + } + } + + // Basically scoot up entities if there is space in the front + int& Index = ClosestPos.Key; + + FormationAgent.EntityIndex = Index; + FormationSubsystem->Units[FormationAgent.UnitIndex].NewPositions.Remove(Index); + + // Call subsystem function to get entities to move + if (FormationSubsystem->Units[FormationAgent.UnitIndex].NewPositions.Num() == 0) + { + FormationSubsystem->MoveEntities(FormationAgent.UnitIndex); + } + } + }); +} diff --git a/Plugins/RTSFormations/Source/RTSFormations/Private/RTSFormationProcessors.h b/Plugins/RTSFormations/Source/RTSFormations/Private/RTSFormationProcessors.h index 23a6ae1d..9887d781 100644 --- a/Plugins/RTSFormations/Source/RTSFormations/Private/RTSFormationProcessors.h +++ b/Plugins/RTSFormations/Source/RTSFormations/Private/RTSFormationProcessors.h @@ -7,6 +7,7 @@ class URTSFormationSubsystem; const FName FormationUpdated = FName(TEXT("FormationUpdated")); +const FName UpdateIndex = FName(TEXT("UpdateIndex")); // Observer that runs when a unit is spawned. Its main purpose is to add entities to a unit array // in the subsystem and cache the index for future use in URTSFormationUpdate @@ -70,3 +71,15 @@ class RTSFORMATIONS_API URTSFormationUpdate : public UMassSignalProcessorBase TObjectPtr FormationSubsystem; }; + +UCLASS() +class RTSFORMATIONS_API URTSUpdateEntityIndex : public UMassSignalProcessorBase +{ + GENERATED_BODY() + + virtual void Initialize(UObject& Owner) override; + virtual void ConfigureQueries() override; + virtual void SignalEntities(UMassEntitySubsystem& EntitySubsystem, FMassExecutionContext& Context, FMassSignalNameLookup& EntitySignals) override; + + TObjectPtr FormationSubsystem; +}; diff --git a/Plugins/RTSFormations/Source/RTSFormations/Private/RTSFormationSubsystem.cpp b/Plugins/RTSFormations/Source/RTSFormations/Private/RTSFormationSubsystem.cpp index aa54029a..5c2ad13c 100644 --- a/Plugins/RTSFormations/Source/RTSFormations/Private/RTSFormationSubsystem.cpp +++ b/Plugins/RTSFormations/Source/RTSFormations/Private/RTSFormationSubsystem.cpp @@ -6,6 +6,8 @@ #include "MassAgentComponent.h" #include "MassCommonFragments.h" #include "MassEntitySubsystem.h" +#include "MassMovementFragments.h" +#include "MassNavigationFragments.h" #include "MassSignalSubsystem.h" #include "MassSpawnerSubsystem.h" #include "RTSAgentTraits.h" @@ -25,97 +27,87 @@ void URTSFormationSubsystem::UpdateUnitPosition(const FVector& NewPosition, int TRACE_CPUPROFILER_EVENT_SCOPE(TEXT("UpdateUnitPosition")) if (!ensure(Units.IsValidIndex(UnitIndex))) { return; } + // Convenience variables FUnitInfo& Unit = Units[UnitIndex]; - UMassEntitySubsystem* EntitySubsystem = UWorld::GetSubsystem(GetWorld()); - - TMap NewPositions; + TMap& NewPositions = Unit.NewPositions; + + // Empty NewPositions Map to make room for new calculations + NewPositions.Empty(); NewPositions.Reserve(Unit.Entities.Num()); - + + // Calculate entity positions for new destination const FVector CenterOffset = FVector((Unit.Entities.Num()/Unit.FormationLength/2) * Unit.BufferDistance, (Unit.FormationLength/2) * Unit.BufferDistance, 0.f); + for(int i=0;i NewArray; + NewArray.Reserve(NewPositions.Num()); + NewPositions.GenerateValueArray(NewArray); + Unit.FarCorner = NewArray[0]; } - FVector FrontCenter = FVector((Unit.Entities.Num()/Unit.FormationLength/2) * Unit.BufferDistance, 0.f, 0.f).RotateAngleAxis(Unit.Angle, FVector(0.f,0.f,Unit.TurnDirection)); - TArray NewArray; - NewArray.Reserve(NewPositions.Num()); - NewPositions.GenerateValueArray(NewArray); - FVector FrontMidPosition = NewPosition - FrontCenter; - FVector FarCorner = NewArray[0]; + + Unit.Entities.Sort([&EntitySubsystem, &Unit](const FMassEntityHandle& A, const FMassEntityHandle& B) { - TRACE_CPUPROFILER_EVENT_SCOPE(TEXT("SortPositions")) - Unit.Entities.Sort([&EntitySubsystem, &FarCorner](const FMassEntityHandle& A, const FMassEntityHandle& B) - { - // Find if theres a way to move this logic to a processor, most of the cost is coming from retrieving the location - const FVector& LocA = EntitySubsystem->GetFragmentDataChecked(A).GetTransform().GetLocation(); - const FVector& LocB = EntitySubsystem->GetFragmentDataChecked(B).GetTransform().GetLocation(); - return FVector::DistSquared2D(LocA, FarCorner) > FVector::DistSquared2D(LocB, FarCorner); - }); - } - NewPositions.ValueSort([&FarCorner](const FVector& A, const FVector& B) + //@todo Find if theres a way to move this logic to a processor, most of the cost is coming from retrieving the location + const FVector& LocA = EntitySubsystem->GetFragmentDataChecked(A).GetTransform().GetLocation(); + const FVector& LocB = EntitySubsystem->GetFragmentDataChecked(B).GetTransform().GetLocation(); + return FVector::DistSquared2D(LocA, Unit.FarCorner) > FVector::DistSquared2D(LocB, Unit.FarCorner); + }); + + NewPositions.ValueSort([&Unit](const FVector& A, const FVector& B) { - return FVector::DistSquared2D(A, FarCorner) > FVector::DistSquared2D(B, FarCorner); + return FVector::DistSquared2D(A, Unit.FarCorner) > FVector::DistSquared2D(B, Unit.FarCorner); }); - DrawDebugPoint(GetWorld(), FrontMidPosition, 20.f, FColor::Yellow, false, 10.f); - // We should not be doing this but for the convenience of the current logic in place, this is the fastest option i think - // @todo performance and transfer to processor? - TSet IndexesTaken; - IndexesTaken.Reserve(Unit.Entities.Num()); - + // Update the entities new index gradually using signals. This allows the bulk of processing to be spread out between frames + FMassExecutionContext Context = EntitySubsystem->CreateExecutionContext(GetWorld()->GetDeltaSeconds()); + TArray Entities = Unit.Entities.Array(); + for(int i=0;iGetFragmentDataPtr(Entity)->GetTransform().GetLocation(); - // Get first index since it is sorted - TPair ClosestPos; - float ClosestDistance = -1; - int i=0; - for(const TPair& NewPos : NewPositions) - { - float Dist = FVector::DistSquared2D(NewPos.Value, Location); - if (ClosestDistance == -1 || Dist < ClosestDistance) - { - ClosestPos = NewPos; - ClosestDistance = Dist; - // While its not perfect, this adds a hard cap to how many positions to check - if (++i > Unit.FormationLength*2) - break; - } - } - - // Basically scoot up entities if there is space in the front - int& Index = ClosestPos.Key; - - IndexesTaken.Emplace(Index); - EntitySubsystem->GetFragmentDataPtr(Entity)->EntityIndex = Index; - NewPositions.Remove(Index); - } - } + GetWorld()->GetSubsystem()->DelaySignalEntity(UpdateIndex, Entities[i], 0.01*(i/Unit.FormationLength)); + } + +} +void URTSFormationSubsystem::MoveEntities(int UnitIndex) +{ + FUnitInfo& Unit = Units[UnitIndex]; + UMassEntitySubsystem* EntitySubsystem = UWorld::GetSubsystem(GetWorld()); + // Final sort to ensure that entities are signaled from front to back - Unit.Entities.Sort([&EntitySubsystem, &FarCorner](const FMassEntityHandle& A, const FMassEntityHandle& B) + Unit.Entities.Sort([&EntitySubsystem, &Unit](const FMassEntityHandle& A, const FMassEntityHandle& B) { // Find if theres a way to move this logic to a processor, most of the cost is coming from retrieving the location const FVector& LocA = EntitySubsystem->GetFragmentDataChecked(A).GetTransform().GetLocation(); const FVector& LocB = EntitySubsystem->GetFragmentDataChecked(B).GetTransform().GetLocation(); - return FVector::DistSquared2D(LocA, FarCorner) < FVector::DistSquared2D(LocB, FarCorner); + return FVector::DistSquared2D(LocA, Unit.FarCorner) < FVector::DistSquared2D(LocB, Unit.FarCorner); }); + + CurrentIndex = 0; + + // Signal entities to begin moving + FMassExecutionContext Context = EntitySubsystem->CreateExecutionContext(GetWorld()->GetDeltaSeconds()); + TArray Entities = Unit.Entities.Array(); + for(int i=0;iGetSubsystem()->DelaySignalEntity(FormationUpdated, Entities[i], 0.1*(i/Unit.FormationLength)); + } } void URTSFormationSubsystem::SetUnitPosition(const FVector& NewPosition, int UnitIndex) @@ -126,31 +118,21 @@ void URTSFormationSubsystem::SetUnitPosition(const FVector& NewPosition, int Uni // Calculate turn direction and angle for entities in unit Unit.TurnDirection = (NewPosition-Units[UnitIndex].InterpolatedDestination).GetSafeNormal().Y > 0 ? 1.f : -1.f; Unit.Angle = FMath::RadiansToDegrees(acosf(FVector::DotProduct((NewPosition-Units[UnitIndex].InterpolatedDestination).GetSafeNormal(),FVector::ForwardVector))); - Unit.ForwardVector = (NewPosition-Units[UnitIndex].InterpolatedDestination).GetSafeNormal(); - Unit.Angle += 180.f; // Temporary fix to resolve unit facing the wrong direction + Unit.Angle += 180.f; // Resolve unit facing the wrong direction + + UMassEntitySubsystem* EntitySubsystem = GetWorld()->GetSubsystem(); + for(const FMassEntityHandle& Entity : Unit.Entities) + { + EntitySubsystem->GetFragmentDataPtr(Entity)->CreateNewAction(EMassMovementAction::Stand, *GetWorld()); + EntitySubsystem->GetFragmentDataPtr(Entity)->Value = FVector::Zero(); + } Unit.UnitPosition = NewPosition; - // Instantly set the angle since we are not keeping complete formation right now + // Instantly set the angle since entity indexes will change Unit.InterpolatedAngle = Units[UnitIndex].Angle; + UpdateUnitPosition(NewPosition, UnitIndex); - - CurrentIndex = 0; - - // Signal entities to begin moving - FTimerDelegate TimerDelegate; - TimerDelegate.BindLambda([&]() - { - // Signal entities of a position update - if (Units[0].Entities.Num() > 0) - { - CurrentIndex = FMath::Clamp(CurrentIndex, 0, Units[0].Entities.Num()-1); - GetWorld()->GetSubsystem()->SignalEntity(FormationUpdated, Units[0].Entities.Array()[CurrentIndex]); - CurrentIndex++; - } - }); - GetWorld()->GetTimerManager().ClearTimer(MoveHandle); - GetWorld()->GetTimerManager().SetTimer(MoveHandle, TimerDelegate, 0.05, true); } void URTSFormationSubsystem::SpawnEntitiesForUnit(int UnitIndex, const UMassEntityConfigAsset* EntityConfig, int Count) diff --git a/Plugins/RTSFormations/Source/RTSFormations/Private/RTSFormationSubsystem.h b/Plugins/RTSFormations/Source/RTSFormations/Private/RTSFormationSubsystem.h index 4949d84a..1434561f 100644 --- a/Plugins/RTSFormations/Source/RTSFormations/Private/RTSFormationSubsystem.h +++ b/Plugins/RTSFormations/Source/RTSFormations/Private/RTSFormationSubsystem.h @@ -23,12 +23,15 @@ struct FUnitInfo UPROPERTY() TSet Entities; + UPROPERTY() + TMap NewPositions; + // The current unit position - UPROPERTY(BlueprintReadOnly) + UPROPERTY() FVector UnitPosition; // The direction to turn the unit when rotating - UPROPERTY(BlueprintReadWrite) + UPROPERTY() float TurnDirection = 1.f; // The entity length of the 'front' of the unit @@ -39,18 +42,14 @@ struct FUnitInfo float BufferDistance = 100.f; // The type of formation - WIP - UPROPERTY() + UPROPERTY(BlueprintReadWrite) TEnumAsByte Formation = Rectangle; // The angle of the unit UPROPERTY() float Angle = 0; - UPROPERTY() - bool bReverseUnit = false; - - UPROPERTY() - FVector ForwardVector; + FVector FarCorner; // Interpolated movement UPROPERTY() @@ -90,6 +89,9 @@ class RTSFORMATIONS_API URTSFormationSubsystem : public UTickableWorldSubsystem UFUNCTION(BlueprintCallable) void SetUnitPosition(const FVector& NewPosition, int UnitIndex = 0); + + UFUNCTION() + void MoveEntities(int UnitIndex); // Spawn entities for a unit UFUNCTION(BlueprintCallable) From eaadfd2343ac4895d2dfcb72ca74978e6e49dcbc Mon Sep 17 00:00:00 2001 From: Ji-Rath Date: Fri, 22 Jul 2022 21:58:40 -0500 Subject: [PATCH 10/37] Implement entity offset so that unit position doesnt have to be recalculated every processor tick. This also allows me to implement different formations in one spot. --- .../Private/RTSFormationProcessors.cpp | 21 ++----------------- .../Private/RTSFormationSubsystem.cpp | 1 + .../RTSFormations/Public/RTSAgentTraits.h | 2 ++ 3 files changed, 5 insertions(+), 19 deletions(-) diff --git a/Plugins/RTSFormations/Source/RTSFormations/Private/RTSFormationProcessors.cpp b/Plugins/RTSFormations/Source/RTSFormations/Private/RTSFormationProcessors.cpp index 57e5c396..1378ca20 100644 --- a/Plugins/RTSFormations/Source/RTSFormations/Private/RTSFormationProcessors.cpp +++ b/Plugins/RTSFormations/Source/RTSFormations/Private/RTSFormationProcessors.cpp @@ -176,25 +176,7 @@ void URTSAgentMovement::Execute(UMassEntitySubsystem& EntitySubsystem, FMassExec const FUnitInfo& Unit = FormationSubsystem->Units[RTSFormationAgent.UnitIndex]; - // Convert UnitIndex to X/Y coords - const int w = RTSFormationAgent.EntityIndex / Unit.FormationLength; - const int l = RTSFormationAgent.EntityIndex % Unit.FormationLength; - - // We want the formation to be 'centered' so we need to create an offset - const FVector CenterOffset = FVector((Unit.Entities.Num()/Unit.FormationLength/2) * Unit.BufferDistance, (Unit.FormationLength/2) * Unit.BufferDistance, 0.f); - - // Set entity position based on index in formation - FVector EntityPosition = FVector(w,l,0.f); - EntityPosition *= Unit.BufferDistance; - EntityPosition -= CenterOffset; - - // Rotate unit by calculated angle - FVector RotateValue = EntityPosition.RotateAngleAxis(Unit.Angle, FVector(0.f,0.f,Unit.TurnDirection)); - - // Finally add the units position to the entity position - RotateValue += Unit.InterpolatedDestination; - - MoveTarget.Center = RotateValue; + MoveTarget.Center = Unit.InterpolatedDestination - RTSFormationAgent.Offset; // Update move target values MoveTarget.DistanceToGoal = (MoveTarget.Center - Transform.GetLocation()).Length(); @@ -313,6 +295,7 @@ void URTSUpdateEntityIndex::SignalEntities(UMassEntitySubsystem& EntitySubsystem int& Index = ClosestPos.Key; FormationAgent.EntityIndex = Index; + FormationAgent.Offset = FormationSubsystem->Units[FormationAgent.UnitIndex].UnitPosition - ClosestPos.Value; FormationSubsystem->Units[FormationAgent.UnitIndex].NewPositions.Remove(Index); // Call subsystem function to get entities to move diff --git a/Plugins/RTSFormations/Source/RTSFormations/Private/RTSFormationSubsystem.cpp b/Plugins/RTSFormations/Source/RTSFormations/Private/RTSFormationSubsystem.cpp index 5c2ad13c..78a43d76 100644 --- a/Plugins/RTSFormations/Source/RTSFormations/Private/RTSFormationSubsystem.cpp +++ b/Plugins/RTSFormations/Source/RTSFormations/Private/RTSFormationSubsystem.cpp @@ -37,6 +37,7 @@ void URTSFormationSubsystem::UpdateUnitPosition(const FVector& NewPosition, int NewPositions.Reserve(Unit.Entities.Num()); // Calculate entity positions for new destination + // This is the logic that can change formation types const FVector CenterOffset = FVector((Unit.Entities.Num()/Unit.FormationLength/2) * Unit.BufferDistance, (Unit.FormationLength/2) * Unit.BufferDistance, 0.f); for(int i=0;i Date: Sat, 23 Jul 2022 10:15:25 -0500 Subject: [PATCH 11/37] Refactor position logic for units to support hollow formation --- .../Private/RTSFormationSubsystem.cpp | 36 +++++++++++++++++-- 1 file changed, 34 insertions(+), 2 deletions(-) diff --git a/Plugins/RTSFormations/Source/RTSFormations/Private/RTSFormationSubsystem.cpp b/Plugins/RTSFormations/Source/RTSFormations/Private/RTSFormationSubsystem.cpp index 78a43d76..0a81523a 100644 --- a/Plugins/RTSFormations/Source/RTSFormations/Private/RTSFormationSubsystem.cpp +++ b/Plugins/RTSFormations/Source/RTSFormations/Private/RTSFormationSubsystem.cpp @@ -39,10 +39,41 @@ void URTSFormationSubsystem::UpdateUnitPosition(const FVector& NewPosition, int // Calculate entity positions for new destination // This is the logic that can change formation types const FVector CenterOffset = FVector((Unit.Entities.Num()/Unit.FormationLength/2) * Unit.BufferDistance, (Unit.FormationLength/2) * Unit.BufferDistance, 0.f); + int PlacedUnits = 0; + int PosIndex = 0; + while (PlacedUnits < Unit.Entities.Num()) + { + int w = PosIndex / Unit.FormationLength; + int l = PosIndex % Unit.FormationLength; + + // Hollow formation logic (2 layers) + int Switch = Unit.Entities.Num() - Unit.FormationLength*2; + if (w != 0 && w != 1 && !(PlacedUnits >= Switch) + && l != 0 && l != 1 && l != Unit.FormationLength-1 && l != Unit.FormationLength-2) + { + PosIndex++; + continue; + } + + + PlacedUnits++; + FVector Position = FVector(w,l,0.f); + Position *= Unit.BufferDistance; + Position -= CenterOffset; + Position = Position.RotateAngleAxis(Unit.Angle, FVector(0.f,0.f,Unit.TurnDirection)); + Position += NewPosition; + NewPositions.Emplace(PosIndex, Position); + + //DrawDebugPoint(GetWorld(), Position, 20.f, FColor::Red, false, 10.f); + + PosIndex++; + } + /* for(int i=0;i Date: Mon, 8 Aug 2022 10:38:58 -0500 Subject: [PATCH 12/37] Implement basic circle formation --- .../RTSFormations/Private/RTSFormationSubsystem.cpp | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/Plugins/RTSFormations/Source/RTSFormations/Private/RTSFormationSubsystem.cpp b/Plugins/RTSFormations/Source/RTSFormations/Private/RTSFormationSubsystem.cpp index 0a81523a..abf9eed1 100644 --- a/Plugins/RTSFormations/Source/RTSFormations/Private/RTSFormationSubsystem.cpp +++ b/Plugins/RTSFormations/Source/RTSFormations/Private/RTSFormationSubsystem.cpp @@ -43,10 +43,11 @@ void URTSFormationSubsystem::UpdateUnitPosition(const FVector& NewPosition, int int PosIndex = 0; while (PlacedUnits < Unit.Entities.Num()) { - int w = PosIndex / Unit.FormationLength; - int l = PosIndex % Unit.FormationLength; + float w = PosIndex / Unit.FormationLength; + float l = PosIndex % Unit.FormationLength; // Hollow formation logic (2 layers) + /* int Switch = Unit.Entities.Num() - Unit.FormationLength*2; if (w != 0 && w != 1 && !(PlacedUnits >= Switch) && l != 0 && l != 1 && l != Unit.FormationLength-1 && l != Unit.FormationLength-2) @@ -54,7 +55,13 @@ void URTSFormationSubsystem::UpdateUnitPosition(const FVector& NewPosition, int PosIndex++; continue; } - + */ + int Rings = 2; + int AmountPerRing = Unit.Entities.Num() / Rings; + float Angle = PosIndex * PI * (2) / AmountPerRing; + float Radius = Unit.FormationLength + (PosIndex / (AmountPerRing) * 1.5f); + w = FMath::Cos(Angle) * Radius; + l = FMath::Sin(Angle) * Radius; PlacedUnits++; FVector Position = FVector(w,l,0.f); From e27e63e43872452df1190a30cfdde3340fab6484 Mon Sep 17 00:00:00 2001 From: Ji-Rath Date: Mon, 8 Aug 2022 11:53:44 -0500 Subject: [PATCH 13/37] Add formation presets --- .../RTSFormations/Content/DA_Circle.uasset | 3 + .../RTSFormations/Content/DA_Hollow.uasset | 3 + .../RTSFormations/Content/DA_Rectangle.uasset | 3 + Plugins/RTSFormations/Content/DA_Spear.uasset | 3 + .../Private/FormationPresets.cpp | 5 ++ .../Private/RTSFormationSubsystem.cpp | 61 ++++++++++--------- .../RTSFormations/Public/FormationPresets.h | 43 +++++++++++++ 7 files changed, 92 insertions(+), 29 deletions(-) create mode 100644 Plugins/RTSFormations/Content/DA_Circle.uasset create mode 100644 Plugins/RTSFormations/Content/DA_Hollow.uasset create mode 100644 Plugins/RTSFormations/Content/DA_Rectangle.uasset create mode 100644 Plugins/RTSFormations/Content/DA_Spear.uasset create mode 100644 Plugins/RTSFormations/Source/RTSFormations/Private/FormationPresets.cpp create mode 100644 Plugins/RTSFormations/Source/RTSFormations/Public/FormationPresets.h diff --git a/Plugins/RTSFormations/Content/DA_Circle.uasset b/Plugins/RTSFormations/Content/DA_Circle.uasset new file mode 100644 index 00000000..0e2dfd19 --- /dev/null +++ b/Plugins/RTSFormations/Content/DA_Circle.uasset @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:d3493bd947c27702c45138ad61ec2f70fc803e16880ad1ae256b13c919fbb9c0 +size 1416 diff --git a/Plugins/RTSFormations/Content/DA_Hollow.uasset b/Plugins/RTSFormations/Content/DA_Hollow.uasset new file mode 100644 index 00000000..d16427e5 --- /dev/null +++ b/Plugins/RTSFormations/Content/DA_Hollow.uasset @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:1b88599e52b11f395e5fba5aac7fa98c5ba964d104aab752b50b96decc8a6bb6 +size 1289 diff --git a/Plugins/RTSFormations/Content/DA_Rectangle.uasset b/Plugins/RTSFormations/Content/DA_Rectangle.uasset new file mode 100644 index 00000000..c97c88fd --- /dev/null +++ b/Plugins/RTSFormations/Content/DA_Rectangle.uasset @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:2705763c96d6b4dbfc7207ae09be3b6f635e95a956050d3c0de1981dd3cf69ee +size 1238 diff --git a/Plugins/RTSFormations/Content/DA_Spear.uasset b/Plugins/RTSFormations/Content/DA_Spear.uasset new file mode 100644 index 00000000..afd3f59b --- /dev/null +++ b/Plugins/RTSFormations/Content/DA_Spear.uasset @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:fa7c7baa2af92d662046635ff5aa2292c1a1684cab7ee3e103577030a7c9d30a +size 1369 diff --git a/Plugins/RTSFormations/Source/RTSFormations/Private/FormationPresets.cpp b/Plugins/RTSFormations/Source/RTSFormations/Private/FormationPresets.cpp new file mode 100644 index 00000000..377aceb6 --- /dev/null +++ b/Plugins/RTSFormations/Source/RTSFormations/Private/FormationPresets.cpp @@ -0,0 +1,5 @@ +// Fill out your copyright notice in the Description page of Project Settings. + + +#include "FormationPresets.h" + diff --git a/Plugins/RTSFormations/Source/RTSFormations/Private/RTSFormationSubsystem.cpp b/Plugins/RTSFormations/Source/RTSFormations/Private/RTSFormationSubsystem.cpp index abf9eed1..01433dde 100644 --- a/Plugins/RTSFormations/Source/RTSFormations/Private/RTSFormationSubsystem.cpp +++ b/Plugins/RTSFormations/Source/RTSFormations/Private/RTSFormationSubsystem.cpp @@ -47,21 +47,26 @@ void URTSFormationSubsystem::UpdateUnitPosition(const FVector& NewPosition, int float l = PosIndex % Unit.FormationLength; // Hollow formation logic (2 layers) - /* - int Switch = Unit.Entities.Num() - Unit.FormationLength*2; - if (w != 0 && w != 1 && !(PlacedUnits >= Switch) - && l != 0 && l != 1 && l != Unit.FormationLength-1 && l != Unit.FormationLength-2) + if (Unit.bHollow) { - PosIndex++; - continue; + int Switch = Unit.Entities.Num() - Unit.FormationLength*2; + if (w != 0 && w != 1 && !(PlacedUnits >= Switch) + && l != 0 && l != 1 && l != Unit.FormationLength-1 && l != Unit.FormationLength-2) + { + PosIndex++; + continue; + } + } + + // Circle formation + if (Unit.Formation == Circle) + { + int AmountPerRing = Unit.Entities.Num() / Unit.Rings; + float Angle = PosIndex * PI * 2 / AmountPerRing; + float Radius = Unit.FormationLength + (PosIndex / AmountPerRing * 1.5f); + w = FMath::Cos(Angle) * Radius; + l = FMath::Sin(Angle) * Radius; } - */ - int Rings = 2; - int AmountPerRing = Unit.Entities.Num() / Rings; - float Angle = PosIndex * PI * (2) / AmountPerRing; - float Radius = Unit.FormationLength + (PosIndex / (AmountPerRing) * 1.5f); - w = FMath::Cos(Angle) * Radius; - l = FMath::Sin(Angle) * Radius; PlacedUnits++; FVector Position = FVector(w,l,0.f); @@ -72,24 +77,8 @@ void URTSFormationSubsystem::UpdateUnitPosition(const FVector& NewPosition, int NewPositions.Emplace(PosIndex, Position); //DrawDebugPoint(GetWorld(), Position, 20.f, FColor::Red, false, 10.f); - PosIndex++; } - /* - for(int i=0;iFormationLength; + Unit.BufferDistance = FormationAsset->BufferDistance; + Unit.Formation = FormationAsset->Formation; + Unit.Rings = FormationAsset->Rings; + Unit.bHollow = FormationAsset->bHollow; + + GetWorld()->GetSubsystem()->SignalEntities(FormationUpdated, Unit.Entities.Array()); +} + void URTSFormationSubsystem::Tick(float DeltaTime) { for(int i=0;i Formation = Rectangle; + + // The formation length of the unit + UPROPERTY(EditAnywhere, BlueprintReadWrite) + int FormationLength = 8; + + // Distance between units + UPROPERTY(EditAnywhere, BlueprintReadWrite) + float BufferDistance = 100.f; + + // Amount of rings for the circle formation + UPROPERTY(EditAnywhere, BlueprintReadWrite) + int Rings = 2; + + UPROPERTY(EditAnywhere, BlueprintReadWrite) + bool bHollow = false; +}; From c28ff6f5897bb579826751e2780a55977e5c3053 Mon Sep 17 00:00:00 2001 From: Ji-Rath Date: Mon, 8 Aug 2022 11:54:28 -0500 Subject: [PATCH 14/37] Add circle formation variables Move enum formation to FormationPresets to avoid circular dependency, should be moved to its own class in the future --- .../Private/RTSFormationSubsystem.h | 19 ++++++++++++------- 1 file changed, 12 insertions(+), 7 deletions(-) diff --git a/Plugins/RTSFormations/Source/RTSFormations/Private/RTSFormationSubsystem.h b/Plugins/RTSFormations/Source/RTSFormations/Private/RTSFormationSubsystem.h index 1434561f..f65b92c2 100644 --- a/Plugins/RTSFormations/Source/RTSFormations/Private/RTSFormationSubsystem.h +++ b/Plugins/RTSFormations/Source/RTSFormations/Private/RTSFormationSubsystem.h @@ -3,16 +3,10 @@ #pragma once #include "CoreMinimal.h" +#include "FormationPresets.h" #include "Common/Misc/MSBPFunctionLibrary.h" #include "RTSFormationSubsystem.generated.h" -UENUM() -enum FormationType -{ - Rectangle, - Circle -}; - USTRUCT(BlueprintType) struct FUnitInfo { @@ -44,6 +38,14 @@ struct FUnitInfo // The type of formation - WIP UPROPERTY(BlueprintReadWrite) TEnumAsByte Formation = Rectangle; + + // Amount of rings for the circle formation + UPROPERTY(EditAnywhere, BlueprintReadWrite) + int Rings = 2; + + UPROPERTY(EditAnywhere, BlueprintReadWrite) + bool bHollow = false; + // The angle of the unit UPROPERTY() @@ -101,6 +103,9 @@ class RTSFORMATIONS_API URTSFormationSubsystem : public UTickableWorldSubsystem UFUNCTION(BlueprintCallable) int SpawnNewUnit(const UMassEntityConfigAsset* EntityConfig, int Count, const FVector& Position); + UFUNCTION(BlueprintCallable) + void SetFormationPreset(int UnitIndex, UFormationPresets* FormationAsset); + virtual void Tick(float DeltaTime) override; virtual bool IsTickable() const override; virtual TStatId GetStatId() const override; From f3853a2cb3762304c53cfcd5f2a0fe51457900eb Mon Sep 17 00:00:00 2001 From: Ji-Rath Date: Tue, 16 Aug 2022 11:41:35 -0500 Subject: [PATCH 15/37] Attempt to resolve incorrect order while smooth rotating unit Implement smooth rotation of unit when the angle is not extreme --- .../Private/RTSFormationProcessors.cpp | 12 ++- .../Private/RTSFormationSubsystem.cpp | 79 +++++++++++++++---- .../Private/RTSFormationSubsystem.h | 6 ++ 3 files changed, 78 insertions(+), 19 deletions(-) diff --git a/Plugins/RTSFormations/Source/RTSFormations/Private/RTSFormationProcessors.cpp b/Plugins/RTSFormations/Source/RTSFormations/Private/RTSFormationProcessors.cpp index 1378ca20..d9955c5b 100644 --- a/Plugins/RTSFormations/Source/RTSFormations/Private/RTSFormationProcessors.cpp +++ b/Plugins/RTSFormations/Source/RTSFormations/Private/RTSFormationProcessors.cpp @@ -8,6 +8,7 @@ #include "MassSimulationLOD.h" #include "RTSAgentTraits.h" #include "RTSFormationSubsystem.h" +#include "Engine/World.h" //----------------------------------------------------------------------// // URTSFormationInitializer @@ -175,8 +176,11 @@ void URTSAgentMovement::Execute(UMassEntitySubsystem& EntitySubsystem, FMassExec const FRTSFormationAgent& RTSFormationAgent = RTSFormationAgents[EntityIndex]; const FUnitInfo& Unit = FormationSubsystem->Units[RTSFormationAgent.UnitIndex]; - - MoveTarget.Center = Unit.InterpolatedDestination - RTSFormationAgent.Offset; + + FVector Offset = RTSFormationAgent.Offset; + if (Unit.bBlendAngle && Unit.Formation != Circle) + Offset = RTSFormationAgent.Offset.RotateAngleAxis(Unit.InterpolatedAngle, FVector(0.f,0.f,Unit.TurnDirection)); + MoveTarget.Center = Unit.InterpolatedDestination - Offset; // Update move target values MoveTarget.DistanceToGoal = (MoveTarget.Center - Transform.GetLocation()).Length(); @@ -279,7 +283,7 @@ void URTSUpdateEntityIndex::SignalEntities(UMassEntitySubsystem& EntitySubsystem int i=0; for(const TPair& NewPos : FormationSubsystem->Units[FormationAgent.UnitIndex].NewPositions) { - float Dist = FVector::DistSquared2D(NewPos.Value, Location); + float Dist = FVector::DistSquared2D(NewPos.Value, FormationAgent.Offset); if (ClosestDistance == -1 || Dist < ClosestDistance) { ClosestPos = NewPos; @@ -295,7 +299,7 @@ void URTSUpdateEntityIndex::SignalEntities(UMassEntitySubsystem& EntitySubsystem int& Index = ClosestPos.Key; FormationAgent.EntityIndex = Index; - FormationAgent.Offset = FormationSubsystem->Units[FormationAgent.UnitIndex].UnitPosition - ClosestPos.Value; + FormationAgent.Offset = ClosestPos.Value; FormationSubsystem->Units[FormationAgent.UnitIndex].NewPositions.Remove(Index); // Call subsystem function to get entities to move diff --git a/Plugins/RTSFormations/Source/RTSFormations/Private/RTSFormationSubsystem.cpp b/Plugins/RTSFormations/Source/RTSFormations/Private/RTSFormationSubsystem.cpp index 01433dde..cfde69ba 100644 --- a/Plugins/RTSFormations/Source/RTSFormations/Private/RTSFormationSubsystem.cpp +++ b/Plugins/RTSFormations/Source/RTSFormations/Private/RTSFormationSubsystem.cpp @@ -3,6 +3,7 @@ #include "RTSFormationSubsystem.h" +#include "DrawDebugHelpers.h" #include "MassAgentComponent.h" #include "MassCommonFragments.h" #include "MassEntitySubsystem.h" @@ -12,6 +13,7 @@ #include "MassSpawnerSubsystem.h" #include "RTSAgentTraits.h" #include "RTSFormationProcessors.h" +#include "Engine/World.h" #include "Kismet/GameplayStatics.h" void URTSFormationSubsystem::DestroyEntity(UMassAgentComponent* Entity) @@ -47,7 +49,7 @@ void URTSFormationSubsystem::UpdateUnitPosition(const FVector& NewPosition, int float l = PosIndex % Unit.FormationLength; // Hollow formation logic (2 layers) - if (Unit.bHollow) + if (Unit.bHollow && Unit.Formation == Rectangle) { int Switch = Unit.Entities.Num() - Unit.FormationLength*2; if (w != 0 && w != 1 && !(PlacedUnits >= Switch) @@ -71,17 +73,25 @@ void URTSFormationSubsystem::UpdateUnitPosition(const FVector& NewPosition, int PlacedUnits++; FVector Position = FVector(w,l,0.f); Position *= Unit.BufferDistance; - Position -= CenterOffset; - Position = Position.RotateAngleAxis(Unit.Angle, FVector(0.f,0.f,Unit.TurnDirection)); - Position += NewPosition; + if (Unit.Formation == Rectangle) + Position -= CenterOffset; + if (!Unit.bBlendAngle) + Position = Position.RotateAngleAxis(Unit.InterpolatedAngle, FVector(0.f,0.f,Unit.TurnDirection)); + //Position += NewPosition; NewPositions.Emplace(PosIndex, Position); - //DrawDebugPoint(GetWorld(), Position, 20.f, FColor::Red, false, 10.f); + DrawDebugPoint(GetWorld(), Position, 20.f, FColor::Red, false, 10.f); PosIndex++; } // The position to order entities/positions is based on the furthest destination location Unit.FarCorner = NewPosition; + + NewPositions.ValueSort([&Unit, &NewPosition](const FVector& A, const FVector& B) + { + return FVector::DistSquared2D(A+NewPosition, Unit.InterpolatedDestination) < FVector::DistSquared2D(B+NewPosition, Unit.InterpolatedDestination); + }); + if (NewPositions.Num()) { TArray NewArray; @@ -89,13 +99,13 @@ void URTSFormationSubsystem::UpdateUnitPosition(const FVector& NewPosition, int NewPositions.GenerateValueArray(NewArray); Unit.FarCorner = NewArray[0]; } - + DrawDebugPoint(GetWorld(), Unit.FarCorner, 30.f, FColor::Green, false, 10.f); Unit.Entities.Sort([&EntitySubsystem, &Unit](const FMassEntityHandle& A, const FMassEntityHandle& B) { //@todo Find if theres a way to move this logic to a processor, most of the cost is coming from retrieving the location - const FVector& LocA = EntitySubsystem->GetFragmentDataChecked(A).GetTransform().GetLocation(); - const FVector& LocB = EntitySubsystem->GetFragmentDataChecked(B).GetTransform().GetLocation(); + const FVector& LocA = EntitySubsystem->GetFragmentDataChecked(A).Offset; + const FVector& LocB = EntitySubsystem->GetFragmentDataChecked(B).Offset; return FVector::DistSquared2D(LocA, Unit.FarCorner) > FVector::DistSquared2D(LocB, Unit.FarCorner); }); @@ -110,8 +120,7 @@ void URTSFormationSubsystem::UpdateUnitPosition(const FVector& NewPosition, int for(int i=0;iGetSubsystem()->DelaySignalEntity(UpdateIndex, Entities[i], 0.01*(i/Unit.FormationLength)); - } - + } } void URTSFormationSubsystem::MoveEntities(int UnitIndex) @@ -123,8 +132,8 @@ void URTSFormationSubsystem::MoveEntities(int UnitIndex) Unit.Entities.Sort([&EntitySubsystem, &Unit](const FMassEntityHandle& A, const FMassEntityHandle& B) { // Find if theres a way to move this logic to a processor, most of the cost is coming from retrieving the location - const FVector& LocA = EntitySubsystem->GetFragmentDataChecked(A).GetTransform().GetLocation(); - const FVector& LocB = EntitySubsystem->GetFragmentDataChecked(B).GetTransform().GetLocation(); + const FVector& LocA = EntitySubsystem->GetFragmentDataChecked(A).Offset; + const FVector& LocB = EntitySubsystem->GetFragmentDataChecked(B).Offset; return FVector::DistSquared2D(LocA, Unit.FarCorner) < FVector::DistSquared2D(LocB, Unit.FarCorner); }); @@ -146,8 +155,9 @@ void URTSFormationSubsystem::SetUnitPosition(const FVector& NewPosition, int Uni FUnitInfo& Unit = Units[UnitIndex]; // Calculate turn direction and angle for entities in unit Unit.TurnDirection = (NewPosition-Units[UnitIndex].InterpolatedDestination).GetSafeNormal().Y > 0 ? 1.f : -1.f; + FVector OldDir = Unit.ForwardDir; + Unit.ForwardDir = (NewPosition-Units[UnitIndex].InterpolatedDestination).GetSafeNormal(); Unit.Angle = FMath::RadiansToDegrees(acosf(FVector::DotProduct((NewPosition-Units[UnitIndex].InterpolatedDestination).GetSafeNormal(),FVector::ForwardVector))); - Unit.Angle += 180.f; // Resolve unit facing the wrong direction UMassEntitySubsystem* EntitySubsystem = GetWorld()->GetSubsystem(); for(const FMassEntityHandle& Entity : Unit.Entities) @@ -159,7 +169,10 @@ void URTSFormationSubsystem::SetUnitPosition(const FVector& NewPosition, int Uni Unit.UnitPosition = NewPosition; // Instantly set the angle since entity indexes will change - Unit.InterpolatedAngle = Units[UnitIndex].Angle; + Unit.InterpolatedAngle = OldDir.Dot(Unit.ForwardDir) > 0.25 ? Unit.InterpolatedAngle : Unit.Angle; + Unit.bBlendAngle = OldDir.Dot(Unit.ForwardDir) > 0.25; + UE_LOG(LogTemp, Error, TEXT("Dot: %f"), OldDir.Dot(Unit.ForwardDir)); + UpdateUnitPosition(NewPosition, UnitIndex); } @@ -224,7 +237,11 @@ void URTSFormationSubsystem::Tick(float DeltaTime) for(int i=0;i 180) + { + // We need to add on to one of the values. + if (end > start) + { + // We'll add it on to start... + start += 360; + } + else + { + // Add it on to end. + end += 360; + } + } + + // Interpolate it. + float value = (start + ((end - start) * amount)); + + // Wrap it.. + float rangeZero = 360; + + if (value >= 0 && value <= 360) + return value; + + return fmod(value,rangeZero); +} diff --git a/Plugins/RTSFormations/Source/RTSFormations/Private/RTSFormationSubsystem.h b/Plugins/RTSFormations/Source/RTSFormations/Private/RTSFormationSubsystem.h index f65b92c2..1a6d1c81 100644 --- a/Plugins/RTSFormations/Source/RTSFormations/Private/RTSFormationSubsystem.h +++ b/Plugins/RTSFormations/Source/RTSFormations/Private/RTSFormationSubsystem.h @@ -62,6 +62,10 @@ struct FUnitInfo UPROPERTY() float InterpolationSpeed = 5.f; + + bool bBlendAngle = false; + + FVector ForwardDir; FUnitInfo() {}; }; @@ -112,6 +116,8 @@ class RTSFORMATIONS_API URTSFormationSubsystem : public UTickableWorldSubsystem virtual void OnWorldBeginPlay(UWorld& InWorld) override; + static float LerpDegrees(float start, float end, float amount); + UPROPERTY() FTimerHandle MoveHandle; From ed66d869e1fa4e62f3a49f5f1a1cf3aa98d0790d Mon Sep 17 00:00:00 2001 From: Ji-Rath Date: Tue, 23 Aug 2022 12:44:58 -0500 Subject: [PATCH 16/37] Refactor formation rotation logic to use FRotator since the logic for interpolation is already built-in Temporarily remove optimizations for debugging Add specific logic for smooth rotations at close angles while recalculating units index when making large turns Remove LerpDegrees as it is no longer used --- .../Private/RTSFormationProcessors.cpp | 13 ++-- .../Private/RTSFormationSubsystem.cpp | 76 ++++++------------- .../Private/RTSFormationSubsystem.h | 11 ++- 3 files changed, 39 insertions(+), 61 deletions(-) diff --git a/Plugins/RTSFormations/Source/RTSFormations/Private/RTSFormationProcessors.cpp b/Plugins/RTSFormations/Source/RTSFormations/Private/RTSFormationProcessors.cpp index d9955c5b..d312c595 100644 --- a/Plugins/RTSFormations/Source/RTSFormations/Private/RTSFormationProcessors.cpp +++ b/Plugins/RTSFormations/Source/RTSFormations/Private/RTSFormationProcessors.cpp @@ -178,9 +178,12 @@ void URTSAgentMovement::Execute(UMassEntitySubsystem& EntitySubsystem, FMassExec const FUnitInfo& Unit = FormationSubsystem->Units[RTSFormationAgent.UnitIndex]; FVector Offset = RTSFormationAgent.Offset; - if (Unit.bBlendAngle && Unit.Formation != Circle) - Offset = RTSFormationAgent.Offset.RotateAngleAxis(Unit.InterpolatedAngle, FVector(0.f,0.f,Unit.TurnDirection)); - MoveTarget.Center = Unit.InterpolatedDestination - Offset; + if (Unit.bBlendAngle) + { + Offset = Offset.RotateAngleAxis(Unit.OldRotation.Yaw, FVector(0.f,0.f,-1.f)); + Offset = Offset.RotateAngleAxis(Unit.InterpRotation.Yaw, FVector(0.f,0.f,1.f)); + } + MoveTarget.Center = Unit.InterpolatedDestination + Offset; // Update move target values MoveTarget.DistanceToGoal = (MoveTarget.Center - Transform.GetLocation()).Length(); @@ -290,8 +293,8 @@ void URTSUpdateEntityIndex::SignalEntities(UMassEntitySubsystem& EntitySubsystem ClosestDistance = Dist; // While its not perfect, this adds a hard cap to how many positions to check - if (++i > FormationSubsystem->Units[FormationAgent.UnitIndex].FormationLength*2) - break; + //if (++i > FormationSubsystem->Units[FormationAgent.UnitIndex].FormationLength*2) + // break; } } diff --git a/Plugins/RTSFormations/Source/RTSFormations/Private/RTSFormationSubsystem.cpp b/Plugins/RTSFormations/Source/RTSFormations/Private/RTSFormationSubsystem.cpp index cfde69ba..d739fcfe 100644 --- a/Plugins/RTSFormations/Source/RTSFormations/Private/RTSFormationSubsystem.cpp +++ b/Plugins/RTSFormations/Source/RTSFormations/Private/RTSFormationSubsystem.cpp @@ -15,6 +15,7 @@ #include "RTSFormationProcessors.h" #include "Engine/World.h" #include "Kismet/GameplayStatics.h" +#include "Kismet/KismetMathLibrary.h" void URTSFormationSubsystem::DestroyEntity(UMassAgentComponent* Entity) { @@ -75,12 +76,11 @@ void URTSFormationSubsystem::UpdateUnitPosition(const FVector& NewPosition, int Position *= Unit.BufferDistance; if (Unit.Formation == Rectangle) Position -= CenterOffset; - if (!Unit.bBlendAngle) - Position = Position.RotateAngleAxis(Unit.InterpolatedAngle, FVector(0.f,0.f,Unit.TurnDirection)); - //Position += NewPosition; + + Position = Position.RotateAngleAxis(Unit.InterpRotation.Yaw+180.f, FVector(0.f,0.f,1.f)); + NewPositions.Emplace(PosIndex, Position); - - DrawDebugPoint(GetWorld(), Position, 20.f, FColor::Red, false, 10.f); + //DrawDebugPoint(GetWorld(), Position+Unit.InterpolatedDestination, 20.f, FColor::Yellow, false, 10.f); PosIndex++; } @@ -89,7 +89,7 @@ void URTSFormationSubsystem::UpdateUnitPosition(const FVector& NewPosition, int NewPositions.ValueSort([&Unit, &NewPosition](const FVector& A, const FVector& B) { - return FVector::DistSquared2D(A+NewPosition, Unit.InterpolatedDestination) < FVector::DistSquared2D(B+NewPosition, Unit.InterpolatedDestination); + return FVector::DistSquared2D(A+Unit.InterpolatedDestination, NewPosition) > FVector::DistSquared2D(B+Unit.InterpolatedDestination, NewPosition); }); if (NewPositions.Num()) @@ -99,14 +99,14 @@ void URTSFormationSubsystem::UpdateUnitPosition(const FVector& NewPosition, int NewPositions.GenerateValueArray(NewArray); Unit.FarCorner = NewArray[0]; } - DrawDebugPoint(GetWorld(), Unit.FarCorner, 30.f, FColor::Green, false, 10.f); + //DrawDebugPoint(GetWorld(), Unit.FarCorner+Unit.InterpolatedDestination, 30.f, FColor::Green, false, 5.f); Unit.Entities.Sort([&EntitySubsystem, &Unit](const FMassEntityHandle& A, const FMassEntityHandle& B) { //@todo Find if theres a way to move this logic to a processor, most of the cost is coming from retrieving the location - const FVector& LocA = EntitySubsystem->GetFragmentDataChecked(A).Offset; - const FVector& LocB = EntitySubsystem->GetFragmentDataChecked(B).Offset; - return FVector::DistSquared2D(LocA, Unit.FarCorner) > FVector::DistSquared2D(LocB, Unit.FarCorner); + const FVector& LocA = EntitySubsystem->GetFragmentDataChecked(A).GetTransform().GetLocation(); + const FVector& LocB = EntitySubsystem->GetFragmentDataChecked(B).GetTransform().GetLocation(); + return FVector::DistSquared2D(LocA, Unit.FarCorner+Unit.InterpolatedDestination) > FVector::DistSquared2D(LocB, Unit.FarCorner+Unit.InterpolatedDestination); }); NewPositions.ValueSort([&Unit](const FVector& A, const FVector& B) @@ -119,8 +119,11 @@ void URTSFormationSubsystem::UpdateUnitPosition(const FVector& NewPosition, int TArray Entities = Unit.Entities.Array(); for(int i=0;iGetSubsystem()->DelaySignalEntity(UpdateIndex, Entities[i], 0.01*(i/Unit.FormationLength)); + //const FVector& Loc = EntitySubsystem->GetFragmentDataChecked(Entities[i]).GetTransform().GetLocation(); + //DrawDebugString(GetWorld(), Loc, FString::Printf(TEXT("%d"),i), NULL, FColor::Red, 5.f, false); + GetWorld()->GetSubsystem()->SignalEntity(UpdateIndex, Entities[i]); } + } void URTSFormationSubsystem::MoveEntities(int UnitIndex) @@ -134,7 +137,7 @@ void URTSFormationSubsystem::MoveEntities(int UnitIndex) // Find if theres a way to move this logic to a processor, most of the cost is coming from retrieving the location const FVector& LocA = EntitySubsystem->GetFragmentDataChecked(A).Offset; const FVector& LocB = EntitySubsystem->GetFragmentDataChecked(B).Offset; - return FVector::DistSquared2D(LocA, Unit.FarCorner) < FVector::DistSquared2D(LocB, Unit.FarCorner); + return FVector::DistSquared2D(LocA, Unit.FarCorner) > FVector::DistSquared2D(LocB, Unit.FarCorner); }); CurrentIndex = 0; @@ -158,7 +161,13 @@ void URTSFormationSubsystem::SetUnitPosition(const FVector& NewPosition, int Uni FVector OldDir = Unit.ForwardDir; Unit.ForwardDir = (NewPosition-Units[UnitIndex].InterpolatedDestination).GetSafeNormal(); Unit.Angle = FMath::RadiansToDegrees(acosf(FVector::DotProduct((NewPosition-Units[UnitIndex].InterpolatedDestination).GetSafeNormal(),FVector::ForwardVector))); - + Unit.OldRotation = Unit.Rotation; + Unit.Rotation = UKismetMathLibrary::MakeRotFromX(Unit.ForwardDir); + //UE_LOG(LogTemp, Error, TEXT("Dot: %f"), OldDir.Dot(Unit.ForwardDir)); + Unit.bBlendAngle = OldDir.Dot(Unit.ForwardDir) > 0.6; + Unit.InterpRotation = Unit.bBlendAngle ? Unit.InterpRotation : Unit.Rotation; + + UMassEntitySubsystem* EntitySubsystem = GetWorld()->GetSubsystem(); for(const FMassEntityHandle& Entity : Unit.Entities) { @@ -167,12 +176,6 @@ void URTSFormationSubsystem::SetUnitPosition(const FVector& NewPosition, int Uni } Unit.UnitPosition = NewPosition; - - // Instantly set the angle since entity indexes will change - Unit.InterpolatedAngle = OldDir.Dot(Unit.ForwardDir) > 0.25 ? Unit.InterpolatedAngle : Unit.Angle; - Unit.bBlendAngle = OldDir.Dot(Unit.ForwardDir) > 0.25; - UE_LOG(LogTemp, Error, TEXT("Dot: %f"), OldDir.Dot(Unit.ForwardDir)); - UpdateUnitPosition(NewPosition, UnitIndex); } @@ -239,7 +242,8 @@ void URTSFormationSubsystem::Tick(float DeltaTime) FUnitInfo& Unit = Units[i]; if (Unit.Formation != Circle) { - Unit.InterpolatedAngle = LerpDegrees(Unit.InterpolatedAngle, Unit.Angle, DeltaTime); + //Unit.InterpAngle = LerpDegrees(Unit.OldAngle, Unit.OldAngle+Unit.AngleDiff, DeltaTime/2); + Unit.InterpRotation = UKismetMathLibrary::RInterpTo(Unit.InterpRotation, Unit.Rotation, DeltaTime, 1.f); } Unit.InterpolatedDestination = FMath::VInterpConstantTo(Unit.InterpolatedDestination, Unit.UnitPosition, DeltaTime, 150.f); @@ -260,35 +264,3 @@ void URTSFormationSubsystem::OnWorldBeginPlay(UWorld& InWorld) { } - -float URTSFormationSubsystem::LerpDegrees(float start, float end, float amount) -{ - // Credit where credit is due! Definitely not complicated but rather out of convenience - // https://stackoverflow.com/a/2708740 - float difference = abs(end - start); - if (difference > 180) - { - // We need to add on to one of the values. - if (end > start) - { - // We'll add it on to start... - start += 360; - } - else - { - // Add it on to end. - end += 360; - } - } - - // Interpolate it. - float value = (start + ((end - start) * amount)); - - // Wrap it.. - float rangeZero = 360; - - if (value >= 0 && value <= 360) - return value; - - return fmod(value,rangeZero); -} diff --git a/Plugins/RTSFormations/Source/RTSFormations/Private/RTSFormationSubsystem.h b/Plugins/RTSFormations/Source/RTSFormations/Private/RTSFormationSubsystem.h index 1a6d1c81..76a7b70b 100644 --- a/Plugins/RTSFormations/Source/RTSFormations/Private/RTSFormationSubsystem.h +++ b/Plugins/RTSFormations/Source/RTSFormations/Private/RTSFormationSubsystem.h @@ -45,7 +45,6 @@ struct FUnitInfo UPROPERTY(EditAnywhere, BlueprintReadWrite) bool bHollow = false; - // The angle of the unit UPROPERTY() @@ -58,7 +57,13 @@ struct FUnitInfo FVector InterpolatedDestination; UPROPERTY() - float InterpolatedAngle = 0.f; + FRotator Rotation; + + UPROPERTY() + FRotator InterpRotation; + + UPROPERTY() + FRotator OldRotation; UPROPERTY() float InterpolationSpeed = 5.f; @@ -116,8 +121,6 @@ class RTSFORMATIONS_API URTSFormationSubsystem : public UTickableWorldSubsystem virtual void OnWorldBeginPlay(UWorld& InWorld) override; - static float LerpDegrees(float start, float end, float amount); - UPROPERTY() FTimerHandle MoveHandle; From 20a16277db0671530b249f4a7fed81b5a342205e Mon Sep 17 00:00:00 2001 From: Ji-Rath Date: Sat, 27 Aug 2022 11:18:18 -0500 Subject: [PATCH 17/37] Refactor UpdateUnitPosition and implement CalculateNewPositions() to better separate code chunks Implement simple comments to core functions in RTSFormationSubsystem for my own sanity --- .../Private/RTSFormationSubsystem.cpp | 49 ++++++++++--------- .../Private/RTSFormationSubsystem.h | 6 +-- 2 files changed, 29 insertions(+), 26 deletions(-) diff --git a/Plugins/RTSFormations/Source/RTSFormations/Private/RTSFormationSubsystem.cpp b/Plugins/RTSFormations/Source/RTSFormations/Private/RTSFormationSubsystem.cpp index d739fcfe..70d0f4e1 100644 --- a/Plugins/RTSFormations/Source/RTSFormations/Private/RTSFormationSubsystem.cpp +++ b/Plugins/RTSFormations/Source/RTSFormations/Private/RTSFormationSubsystem.cpp @@ -25,20 +25,12 @@ void URTSFormationSubsystem::DestroyEntity(UMassAgentComponent* Entity) EntitySubsystem->Defer().DestroyEntity(Entity->GetEntityHandle()); } -void URTSFormationSubsystem::UpdateUnitPosition(const FVector& NewPosition, int UnitIndex) +void URTSFormationSubsystem::CalculateNewPositions(FUnitInfo& Unit, TMap& NewPositions) { - TRACE_CPUPROFILER_EVENT_SCOPE(TEXT("UpdateUnitPosition")) - if (!ensure(Units.IsValidIndex(UnitIndex))) { return; } - - // Convenience variables - FUnitInfo& Unit = Units[UnitIndex]; - UMassEntitySubsystem* EntitySubsystem = UWorld::GetSubsystem(GetWorld()); - TMap& NewPositions = Unit.NewPositions; - // Empty NewPositions Map to make room for new calculations NewPositions.Empty(); NewPositions.Reserve(Unit.Entities.Num()); - + // Calculate entity positions for new destination // This is the logic that can change formation types const FVector CenterOffset = FVector((Unit.Entities.Num()/Unit.FormationLength/2) * Unit.BufferDistance, (Unit.FormationLength/2) * Unit.BufferDistance, 0.f); @@ -83,10 +75,23 @@ void URTSFormationSubsystem::UpdateUnitPosition(const FVector& NewPosition, int //DrawDebugPoint(GetWorld(), Position+Unit.InterpolatedDestination, 20.f, FColor::Yellow, false, 10.f); PosIndex++; } +} - // The position to order entities/positions is based on the furthest destination location - Unit.FarCorner = NewPosition; +void URTSFormationSubsystem::UpdateUnitPosition(const FVector& NewPosition, int UnitIndex) +{ + TRACE_CPUPROFILER_EVENT_SCOPE(TEXT("UpdateUnitPosition")) + if (!ensure(Units.IsValidIndex(UnitIndex))) { return; } + // Convenience variables + FUnitInfo& Unit = Units[UnitIndex]; + UMassEntitySubsystem* EntitySubsystem = UWorld::GetSubsystem(GetWorld()); + TMap& NewPositions = Unit.NewPositions; + + // Calculate new positions for entities and output to NewPositions + CalculateNewPositions(Unit, NewPositions); + + // Calculate far corner by finding the new position that is furthest from the unit destination + Unit.FarCorner = NewPosition; NewPositions.ValueSort([&Unit, &NewPosition](const FVector& A, const FVector& B) { return FVector::DistSquared2D(A+Unit.InterpolatedDestination, NewPosition) > FVector::DistSquared2D(B+Unit.InterpolatedDestination, NewPosition); @@ -100,7 +105,8 @@ void URTSFormationSubsystem::UpdateUnitPosition(const FVector& NewPosition, int Unit.FarCorner = NewArray[0]; } //DrawDebugPoint(GetWorld(), Unit.FarCorner+Unit.InterpolatedDestination, 30.f, FColor::Green, false, 5.f); - + + // Sort entities by distance to the far corner location Unit.Entities.Sort([&EntitySubsystem, &Unit](const FMassEntityHandle& A, const FMassEntityHandle& B) { //@todo Find if theres a way to move this logic to a processor, most of the cost is coming from retrieving the location @@ -108,22 +114,21 @@ void URTSFormationSubsystem::UpdateUnitPosition(const FVector& NewPosition, int const FVector& LocB = EntitySubsystem->GetFragmentDataChecked(B).GetTransform().GetLocation(); return FVector::DistSquared2D(LocA, Unit.FarCorner+Unit.InterpolatedDestination) > FVector::DistSquared2D(LocB, Unit.FarCorner+Unit.InterpolatedDestination); }); - + + // Sort new positions by distance to the far corner location NewPositions.ValueSort([&Unit](const FVector& A, const FVector& B) { return FVector::DistSquared2D(A, Unit.FarCorner) > FVector::DistSquared2D(B, Unit.FarCorner); }); - // Update the entities new index gradually using signals. This allows the bulk of processing to be spread out between frames + // Signal entities to update their position index FMassExecutionContext Context = EntitySubsystem->CreateExecutionContext(GetWorld()->GetDeltaSeconds()); TArray Entities = Unit.Entities.Array(); for(int i=0;iGetFragmentDataChecked(Entities[i]).GetTransform().GetLocation(); //DrawDebugString(GetWorld(), Loc, FString::Printf(TEXT("%d"),i), NULL, FColor::Red, 5.f, false); GetWorld()->GetSubsystem()->SignalEntity(UpdateIndex, Entities[i]); } - } void URTSFormationSubsystem::MoveEntities(int UnitIndex) @@ -158,16 +163,17 @@ void URTSFormationSubsystem::SetUnitPosition(const FVector& NewPosition, int Uni FUnitInfo& Unit = Units[UnitIndex]; // Calculate turn direction and angle for entities in unit Unit.TurnDirection = (NewPosition-Units[UnitIndex].InterpolatedDestination).GetSafeNormal().Y > 0 ? 1.f : -1.f; + FVector OldDir = Unit.ForwardDir; Unit.ForwardDir = (NewPosition-Units[UnitIndex].InterpolatedDestination).GetSafeNormal(); - Unit.Angle = FMath::RadiansToDegrees(acosf(FVector::DotProduct((NewPosition-Units[UnitIndex].InterpolatedDestination).GetSafeNormal(),FVector::ForwardVector))); + Unit.OldRotation = Unit.Rotation; Unit.Rotation = UKismetMathLibrary::MakeRotFromX(Unit.ForwardDir); - //UE_LOG(LogTemp, Error, TEXT("Dot: %f"), OldDir.Dot(Unit.ForwardDir)); + Unit.bBlendAngle = OldDir.Dot(Unit.ForwardDir) > 0.6; Unit.InterpRotation = Unit.bBlendAngle ? Unit.InterpRotation : Unit.Rotation; - - + + // Jank solution to stop entities from moving UMassEntitySubsystem* EntitySubsystem = GetWorld()->GetSubsystem(); for(const FMassEntityHandle& Entity : Unit.Entities) { @@ -242,7 +248,6 @@ void URTSFormationSubsystem::Tick(float DeltaTime) FUnitInfo& Unit = Units[i]; if (Unit.Formation != Circle) { - //Unit.InterpAngle = LerpDegrees(Unit.OldAngle, Unit.OldAngle+Unit.AngleDiff, DeltaTime/2); Unit.InterpRotation = UKismetMathLibrary::RInterpTo(Unit.InterpRotation, Unit.Rotation, DeltaTime, 1.f); } diff --git a/Plugins/RTSFormations/Source/RTSFormations/Private/RTSFormationSubsystem.h b/Plugins/RTSFormations/Source/RTSFormations/Private/RTSFormationSubsystem.h index 76a7b70b..0c8edd81 100644 --- a/Plugins/RTSFormations/Source/RTSFormations/Private/RTSFormationSubsystem.h +++ b/Plugins/RTSFormations/Source/RTSFormations/Private/RTSFormationSubsystem.h @@ -46,10 +46,6 @@ struct FUnitInfo UPROPERTY(EditAnywhere, BlueprintReadWrite) bool bHollow = false; - // The angle of the unit - UPROPERTY() - float Angle = 0; - FVector FarCorner; // Interpolated movement @@ -115,6 +111,8 @@ class RTSFORMATIONS_API URTSFormationSubsystem : public UTickableWorldSubsystem UFUNCTION(BlueprintCallable) void SetFormationPreset(int UnitIndex, UFormationPresets* FormationAsset); + void CalculateNewPositions(FUnitInfo& Unit, TMap& NewPositions); + virtual void Tick(float DeltaTime) override; virtual bool IsTickable() const override; virtual TStatId GetStatId() const override; From a9d3b1294dd2831968b3f6ccaec50b4499abe083 Mon Sep 17 00:00:00 2001 From: Ji-Rath Date: Mon, 29 Aug 2022 16:28:19 -0500 Subject: [PATCH 18/37] Cleanup RTSFormationSubsystem Modify turn rotation on units so they turn about half as fast - should be a variable in the future --- .../RTSFormations/Private/RTSFormationSubsystem.cpp | 11 ++++++----- .../RTSFormations/Private/RTSFormationSubsystem.h | 5 ++++- 2 files changed, 10 insertions(+), 6 deletions(-) diff --git a/Plugins/RTSFormations/Source/RTSFormations/Private/RTSFormationSubsystem.cpp b/Plugins/RTSFormations/Source/RTSFormations/Private/RTSFormationSubsystem.cpp index 70d0f4e1..8f762781 100644 --- a/Plugins/RTSFormations/Source/RTSFormations/Private/RTSFormationSubsystem.cpp +++ b/Plugins/RTSFormations/Source/RTSFormations/Private/RTSFormationSubsystem.cpp @@ -161,16 +161,17 @@ void URTSFormationSubsystem::SetUnitPosition(const FVector& NewPosition, int Uni DrawDebugDirectionalArrow(GetWorld(), NewPosition, NewPosition+((NewPosition-Units[UnitIndex].InterpolatedDestination).GetSafeNormal()*250.f), 150.f, FColor::Red, false, 5.f, 0, 25.f); FUnitInfo& Unit = Units[UnitIndex]; - // Calculate turn direction and angle for entities in unit - Unit.TurnDirection = (NewPosition-Units[UnitIndex].InterpolatedDestination).GetSafeNormal().Y > 0 ? 1.f : -1.f; - + FVector OldDir = Unit.ForwardDir; Unit.ForwardDir = (NewPosition-Units[UnitIndex].InterpolatedDestination).GetSafeNormal(); + // Calculate turn direction and angle for entities in unit + Unit.TurnDirection = Unit.ForwardDir.Y > 0 ? 1.f : -1.f; + Unit.OldRotation = Unit.Rotation; Unit.Rotation = UKismetMathLibrary::MakeRotFromX(Unit.ForwardDir); - Unit.bBlendAngle = OldDir.Dot(Unit.ForwardDir) > 0.6; + Unit.bBlendAngle = OldDir.Dot(Unit.ForwardDir) > 0.4; Unit.InterpRotation = Unit.bBlendAngle ? Unit.InterpRotation : Unit.Rotation; // Jank solution to stop entities from moving @@ -248,7 +249,7 @@ void URTSFormationSubsystem::Tick(float DeltaTime) FUnitInfo& Unit = Units[i]; if (Unit.Formation != Circle) { - Unit.InterpRotation = UKismetMathLibrary::RInterpTo(Unit.InterpRotation, Unit.Rotation, DeltaTime, 1.f); + Unit.InterpRotation = UKismetMathLibrary::RInterpTo(Unit.InterpRotation, Unit.Rotation, DeltaTime, 0.5f); } Unit.InterpolatedDestination = FMath::VInterpConstantTo(Unit.InterpolatedDestination, Unit.UnitPosition, DeltaTime, 150.f); diff --git a/Plugins/RTSFormations/Source/RTSFormations/Private/RTSFormationSubsystem.h b/Plugins/RTSFormations/Source/RTSFormations/Private/RTSFormationSubsystem.h index 0c8edd81..53463eba 100644 --- a/Plugins/RTSFormations/Source/RTSFormations/Private/RTSFormationSubsystem.h +++ b/Plugins/RTSFormations/Source/RTSFormations/Private/RTSFormationSubsystem.h @@ -46,6 +46,7 @@ struct FUnitInfo UPROPERTY(EditAnywhere, BlueprintReadWrite) bool bHollow = false; + UPROPERTY() FVector FarCorner; // Interpolated movement @@ -63,9 +64,11 @@ struct FUnitInfo UPROPERTY() float InterpolationSpeed = 5.f; - + + UPROPERTY() bool bBlendAngle = false; + UPROPERTY() FVector ForwardDir; FUnitInfo() {}; From 5eb1eda9061e43ca7fe56d6797100530990d558b Mon Sep 17 00:00:00 2001 From: Ji-Rath Date: Mon, 29 Aug 2022 16:29:12 -0500 Subject: [PATCH 19/37] Extend the Gameplay Debuggger to enable RTS Unit debugging Add GameplayDebuggerCategory_RTSAgents Add GameplayDebugger to dependency list --- .../GameplayDebuggerCategory_RTSAgents.cpp | 61 +++++++++++++++++++ .../RTSFormations/Private/RTSFormations.cpp | 18 ++++++ .../GameplayDebuggerCategory_RTSAgents.h | 37 +++++++++++ .../RTSFormations/RTSFormations.Build.cs | 2 +- 4 files changed, 117 insertions(+), 1 deletion(-) create mode 100644 Plugins/RTSFormations/Source/RTSFormations/Private/GameplayDebuggerCategory_RTSAgents.cpp create mode 100644 Plugins/RTSFormations/Source/RTSFormations/Public/GameplayDebuggerCategory_RTSAgents.h diff --git a/Plugins/RTSFormations/Source/RTSFormations/Private/GameplayDebuggerCategory_RTSAgents.cpp b/Plugins/RTSFormations/Source/RTSFormations/Private/GameplayDebuggerCategory_RTSAgents.cpp new file mode 100644 index 00000000..c2ecb2f1 --- /dev/null +++ b/Plugins/RTSFormations/Source/RTSFormations/Private/GameplayDebuggerCategory_RTSAgents.cpp @@ -0,0 +1,61 @@ +// Fill out your copyright notice in the Description page of Project Settings. + + +#include "GameplayDebuggerCategory_RTSAgents.h" + +#include "DrawDebugHelpers.h" +#include "MassNavigationFragments.h" +#include "Engine/World.h" + +#if WITH_GAMEPLAY_DEBUGGER + +#include "GameFramework/PlayerController.h" + +FGameplayDebuggerCategory_RTSAgents::FGameplayDebuggerCategory_RTSAgents() +{ + bShowOnlyWithDebugActor = false; + SetDataPackReplication(&DataPack); +} + +void FGameplayDebuggerCategory_RTSAgents::CollectData(APlayerController* OwnerPC, AActor* DebugActor) +{ + if (OwnerPC) + { + //DataPack.ActorName = OwnerPC->GetPawn()->GetName(); + URTSFormationSubsystem* FormationSubsystem = UWorld::GetSubsystem(OwnerPC->GetWorld()); + UMassEntitySubsystem* EntitySubsystem = UWorld::GetSubsystem(OwnerPC->GetWorld()); + DataPack.NumUnits = FormationSubsystem->Units.Num(); + + DataPack.Positions.Empty(); + DataPack.Positions.Reserve(FormationSubsystem->Units[0].Entities.Num()); + + for(auto& Entity : FormationSubsystem->Units[0].Entities) + { + const FVector& Pos = EntitySubsystem->GetFragmentDataChecked(Entity).Center; + DataPack.Positions.Add(Pos); + } + } +} + +void FGameplayDebuggerCategory_RTSAgents::DrawData(APlayerController* OwnerPC, FGameplayDebuggerCanvasContext& CanvasContext) +{ + CanvasContext.Printf(TEXT("{yellow}Units: {white}%d"), DataPack.NumUnits); + CanvasContext.Printf(TEXT("{yellow}Entities in Unit 0: {white}%d"), DataPack.Positions.Num()); + for(FVector& Pos : DataPack.Positions) + { + DrawDebugCanvasWireSphere(CanvasContext.Canvas.Get(), Pos, FColor::Yellow, 20.f, 5); + } +} + +TSharedRef FGameplayDebuggerCategory_RTSAgents::MakeInstance() +{ + return MakeShareable(new FGameplayDebuggerCategory_RTSAgents()); +} + +void FGameplayDebuggerCategory_RTSAgents::FRepData::Serialize(FArchive& Ar) +{ + Ar << Positions; + Ar << NumUnits; +} + +#endif // WITH_GAMEPLAY_DEBUGGER diff --git a/Plugins/RTSFormations/Source/RTSFormations/Private/RTSFormations.cpp b/Plugins/RTSFormations/Source/RTSFormations/Private/RTSFormations.cpp index 65ff621e..8f7c53f3 100644 --- a/Plugins/RTSFormations/Source/RTSFormations/Private/RTSFormations.cpp +++ b/Plugins/RTSFormations/Source/RTSFormations/Private/RTSFormations.cpp @@ -2,17 +2,35 @@ #include "RTSFormations.h" +#if WITH_GAMEPLAY_DEBUGGER +#include "GameplayDebugger.h" +#include "GameplayDebuggerCategory_RTSAgents.h" +#endif // WITH_GAMEPLAY_DEBUGGER + #define LOCTEXT_NAMESPACE "FRTSFormationsModule" void FRTSFormationsModule::StartupModule() { // This code will execute after your module is loaded into memory; the exact timing is specified in the .uplugin file per-module +#if WITH_GAMEPLAY_DEBUGGER + IGameplayDebugger& GameplayDebuggerModule = IGameplayDebugger::Get(); + GameplayDebuggerModule.RegisterCategory("RTSAgents", IGameplayDebugger::FOnGetCategory::CreateStatic(&FGameplayDebuggerCategory_RTSAgents::MakeInstance), EGameplayDebuggerCategoryState::EnabledInGameAndSimulate, 8); + GameplayDebuggerModule.NotifyCategoriesChanged(); +#endif } void FRTSFormationsModule::ShutdownModule() { // This function may be called during shutdown to clean up your module. For modules that support dynamic reloading, // we call this function before unloading the module. +#if WITH_GAMEPLAY_DEBUGGER + //If the gameplay debugger is available, unregister the category + if (IGameplayDebugger::IsAvailable()) + { + IGameplayDebugger& GameplayDebuggerModule = IGameplayDebugger::Get(); + GameplayDebuggerModule.UnregisterCategory("RTSAgents"); + } +#endif } #undef LOCTEXT_NAMESPACE diff --git a/Plugins/RTSFormations/Source/RTSFormations/Public/GameplayDebuggerCategory_RTSAgents.h b/Plugins/RTSFormations/Source/RTSFormations/Public/GameplayDebuggerCategory_RTSAgents.h new file mode 100644 index 00000000..f27d2aa6 --- /dev/null +++ b/Plugins/RTSFormations/Source/RTSFormations/Public/GameplayDebuggerCategory_RTSAgents.h @@ -0,0 +1,37 @@ +// Fill out your copyright notice in the Description page of Project Settings. + +#pragma once + +#if WITH_GAMEPLAY_DEBUGGER + +#include "CoreMinimal.h" +#include "GameplayDebuggerCategory.h" +#include "RTSFormationSubsystem.h" + +class APlayerController; +class AActor; + +class FGameplayDebuggerCategory_RTSAgents : public FGameplayDebuggerCategory +{ +public: + FGameplayDebuggerCategory_RTSAgents(); + virtual void CollectData(APlayerController* OwnerPC, AActor* DebugActor) override; + virtual void DrawData(APlayerController* OwnerPC, FGameplayDebuggerCanvasContext& CanvasContext) override; + + static TSharedRef MakeInstance(); + +protected: + struct FRepData + { + // Put all data you want to display here + TArray Positions; + + int NumUnits = 0; + + void Serialize(FArchive& Ar); + }; + + FRepData DataPack; +}; + +#endif // WITH_GAMEPLAY_DEBUGGER diff --git a/Plugins/RTSFormations/Source/RTSFormations/RTSFormations.Build.cs b/Plugins/RTSFormations/Source/RTSFormations/RTSFormations.Build.cs index 6becf8b1..9e0a96d6 100644 --- a/Plugins/RTSFormations/Source/RTSFormations/RTSFormations.Build.cs +++ b/Plugins/RTSFormations/Source/RTSFormations/RTSFormations.Build.cs @@ -37,7 +37,7 @@ public RTSFormations(ReadOnlyTargetRules Target) : base(Target) "CoreUObject", "Engine", "Slate", - "SlateCore", "MassNavigation", "MassActors", "MassLOD" + "SlateCore", "MassNavigation", "MassActors", "MassLOD", "GameplayDebugger" // ... add private dependencies that you statically link with here ... } ); From ec3ab602abb83e42d1b4ccc6dd779d2a073b5cdf Mon Sep 17 00:00:00 2001 From: Ji-Rath Date: Thu, 1 Sep 2022 21:55:33 -0500 Subject: [PATCH 20/37] Add RTSAgentSubsystem for agent position tracking and communication to mass processors --- .../Private/RTSAgentSubsystem.cpp | 33 +++++++++++++++++++ .../RTSFormations/Private/RTSAgentSubsystem.h | 27 +++++++++++++++ 2 files changed, 60 insertions(+) create mode 100644 Plugins/RTSFormations/Source/RTSFormations/Private/RTSAgentSubsystem.cpp create mode 100644 Plugins/RTSFormations/Source/RTSFormations/Private/RTSAgentSubsystem.h diff --git a/Plugins/RTSFormations/Source/RTSFormations/Private/RTSAgentSubsystem.cpp b/Plugins/RTSFormations/Source/RTSFormations/Private/RTSAgentSubsystem.cpp new file mode 100644 index 00000000..fc19413c --- /dev/null +++ b/Plugins/RTSFormations/Source/RTSFormations/Private/RTSAgentSubsystem.cpp @@ -0,0 +1,33 @@ +// Fill out your copyright notice in the Description page of Project Settings. + + +#include "RTSAgentSubsystem.h" + +#include "LaunchEntityProcessor.h" +#include "MassCommandBuffer.h" +#include "MassEntitySubsystem.h" +#include "Engine/World.h" + +void URTSAgentSubsystem::LaunchEntities(const FVector& Location, float Radius) const +{ + TRACE_CPUPROFILER_EVENT_SCOPE(TEXT("FindNearbyEntities")); + + UMassEntitySubsystem* EntitySubsystem = GetWorld()->GetSubsystem(); + + // Query items in radius + TArray Entities; + const FBox Bounds(Location - FVector(Radius, Radius, 0.f), Location + FVector(Radius, Radius, 0.f)); + AgentHashGrid.Query(Bounds,Entities); + + if (EntitySubsystem) + { + FLaunchEntityFragment LaunchEntityFragment; + LaunchEntityFragment.Origin = Location; + + for(const FMassEntityHandle& Entity : Entities) + { + EntitySubsystem->Defer().PushCommand(FCommandAddFragmentInstance(Entity, + FConstStructView::Make(LaunchEntityFragment))); + } + } +} diff --git a/Plugins/RTSFormations/Source/RTSFormations/Private/RTSAgentSubsystem.h b/Plugins/RTSFormations/Source/RTSFormations/Private/RTSAgentSubsystem.h new file mode 100644 index 00000000..c058c92f --- /dev/null +++ b/Plugins/RTSFormations/Source/RTSFormations/Private/RTSAgentSubsystem.h @@ -0,0 +1,27 @@ +// Fill out your copyright notice in the Description page of Project Settings. + +#pragma once + +#include "CoreMinimal.h" +#include "Subsystems/WorldSubsystem.h" +#include "HierarchicalHashGrid2D.h" +#include "MassEntityTypes.h" +#include "RTSAgentSubsystem.generated.h" + + +typedef THierarchicalHashGrid2D<2, 4, FMassEntityHandle> RTSAgentHashGrid2D; + +/** + * + */ +UCLASS() +class RTSFORMATIONS_API URTSAgentSubsystem : public UWorldSubsystem +{ + GENERATED_BODY() + +public: + RTSAgentHashGrid2D AgentHashGrid = RTSAgentHashGrid2D(); + + UFUNCTION(BlueprintCallable, BlueprintPure = false) + void LaunchEntities(const FVector& Location, float Radius) const; +}; From a316107de300d0a05bd061773289d108de8e534e Mon Sep 17 00:00:00 2001 From: Ji-Rath Date: Thu, 1 Sep 2022 21:55:55 -0500 Subject: [PATCH 21/37] Add LaunchEntityProcessor to handle launching entities --- .../Private/LaunchEntityProcessor.cpp | 101 ++++++++++++++++++ .../Public/LaunchEntityProcessor.h | 50 +++++++++ 2 files changed, 151 insertions(+) create mode 100644 Plugins/RTSFormations/Source/RTSFormations/Private/LaunchEntityProcessor.cpp create mode 100644 Plugins/RTSFormations/Source/RTSFormations/Public/LaunchEntityProcessor.h diff --git a/Plugins/RTSFormations/Source/RTSFormations/Private/LaunchEntityProcessor.cpp b/Plugins/RTSFormations/Source/RTSFormations/Private/LaunchEntityProcessor.cpp new file mode 100644 index 00000000..b8179209 --- /dev/null +++ b/Plugins/RTSFormations/Source/RTSFormations/Private/LaunchEntityProcessor.cpp @@ -0,0 +1,101 @@ +// Fill out your copyright notice in the Description page of Project Settings. +#include "LaunchEntityProcessor.h" + +#include "DrawDebugHelpers.h" +#include "MassCommonFragments.h" +#include "MassMovementFragments.h" +#include "MassNavigationFragments.h" +#include "Engine/World.h" + +//----------------------------------------------------------------------// +// ULaunchEntityProcessor +//----------------------------------------------------------------------// +ULaunchEntityProcessor::ULaunchEntityProcessor() +{ + ObservedType = FLaunchEntityFragment::StaticStruct(); + Operation = EMassObservedOperation::Add; +} + +void ULaunchEntityProcessor::ConfigureQueries() +{ + EntityQuery.AddRequirement(EMassFragmentAccess::ReadOnly); + EntityQuery.AddRequirement(EMassFragmentAccess::ReadWrite); + EntityQuery.AddRequirement(EMassFragmentAccess::ReadWrite); + EntityQuery.AddRequirement(EMassFragmentAccess::ReadOnly); + +} + +void ULaunchEntityProcessor::Initialize(UObject& Owner) +{ + SignalSubsystem = UWorld::GetSubsystem(Owner.GetWorld()); + FormationSubsystem = UWorld::GetSubsystem(Owner.GetWorld()); +} + +void ULaunchEntityProcessor::Execute(UMassEntitySubsystem& EntitySubsystem, FMassExecutionContext& Context) +{ + EntityQuery.ParallelForEachEntityChunk(EntitySubsystem, Context, [this, &EntitySubsystem](FMassExecutionContext& Context) + { + TConstArrayView LaunchEntityFragments = Context.GetFragmentView(); + TArrayView MoveTargetFragments = Context.GetMutableFragmentView(); + TArrayView ForceFragments = Context.GetMutableFragmentView(); + TConstArrayView TransformFragments = Context.GetFragmentView(); + + for (int32 EntityIndex = 0; EntityIndex < Context.GetNumEntities(); ++EntityIndex) + { + const FLaunchEntityFragment& LaunchEntityFragment = LaunchEntityFragments[EntityIndex]; + FMassMoveTargetFragment& MoveTargetFragment = MoveTargetFragments[EntityIndex]; + FMassForceFragment& ForceFragment = ForceFragments[EntityIndex]; + const FTransformFragment& TransformFragment = TransformFragments[EntityIndex]; + + MoveTargetFragment.CreateNewAction(EMassMovementAction::Animate, *GetWorld()); + ForceFragment.Value = (TransformFragment.GetTransform().GetTranslation()-LaunchEntityFragment.Origin).GetSafeNormal()*LaunchEntityFragment.Magnitude; + UE_LOG(LogTemp, Log, TEXT("Initialize Force: %s"), *ForceFragment.Value.ToString()) + } + }); +} + +//----------------------------------------------------------------------// +// UMoveForceProcessor +//----------------------------------------------------------------------// + +void UMoveForceProcessor::ConfigureQueries() +{ + EntityQuery.AddRequirement(EMassFragmentAccess::ReadOnly); + EntityQuery.AddRequirement(EMassFragmentAccess::ReadWrite); + EntityQuery.AddRequirement(EMassFragmentAccess::ReadWrite); + EntityQuery.AddRequirement(EMassFragmentAccess::ReadWrite); + +} + +void UMoveForceProcessor::Execute(UMassEntitySubsystem& EntitySubsystem, FMassExecutionContext& Context) +{ + EntityQuery.ParallelForEachEntityChunk(EntitySubsystem, Context, [this, &EntitySubsystem](FMassExecutionContext& Context) + { + TConstArrayView LaunchEntityFragments = Context.GetFragmentView(); + TArrayView MoveTargetFragments = Context.GetMutableFragmentView(); + TArrayView ForceFragments = Context.GetMutableFragmentView(); + TArrayView TransformFragments = Context.GetMutableFragmentView(); + + for (int32 EntityIndex = 0; EntityIndex < Context.GetNumEntities(); ++EntityIndex) + { + const FLaunchEntityFragment& LaunchEntityFragment = LaunchEntityFragments[EntityIndex]; + FMassMoveTargetFragment& MoveTargetFragment = MoveTargetFragments[EntityIndex]; + FMassForceFragment& ForceFragment = ForceFragments[EntityIndex]; + FTransformFragment& TransformFragment = TransformFragments[EntityIndex]; + + if(ForceFragment.Value.Length() > 0) + { + TransformFragment.GetMutableTransform().AddToTranslation(ForceFragment.Value); + //ForceFragment.Value -= FVector(0.1f)*Context.GetDeltaTimeSeconds(); + //UE_LOG(LogTemp, Log, TEXT("Force: %f"), ForceFragment.Value.Length()); + DrawDebugSphere(GetWorld(), TransformFragment.GetTransform().GetLocation(), 40.f, 5, FColor::Red); + } + else + { + EntitySubsystem.Defer().RemoveFragment(Context.GetEntity(EntityIndex)); + ForceFragment.Value = FVector::ZeroVector; + MoveTargetFragment.CreateNewAction(EMassMovementAction::Stand, *GetWorld()); + } + } + }); +} \ No newline at end of file diff --git a/Plugins/RTSFormations/Source/RTSFormations/Public/LaunchEntityProcessor.h b/Plugins/RTSFormations/Source/RTSFormations/Public/LaunchEntityProcessor.h new file mode 100644 index 00000000..eb96e95e --- /dev/null +++ b/Plugins/RTSFormations/Source/RTSFormations/Public/LaunchEntityProcessor.h @@ -0,0 +1,50 @@ + +#pragma once + +#include "MassEntityQuery.h" +#include "MassEntityTypes.h" +#include "MassObserverProcessor.h" +#include "MassSignalSubsystem.h" +#include "RTSFormationSubsystem.h" +#include "LaunchEntityProcessor.generated.h" + +// Observer that runs when an entity is destroyed. Cleans up the unit array and tells the last unit to take their place +UCLASS() +class RTSFORMATIONS_API ULaunchEntityProcessor : public UMassObserverProcessor +{ + GENERATED_BODY() + + ULaunchEntityProcessor(); + virtual void ConfigureQueries() override; + virtual void Initialize(UObject& Owner) override; + virtual void Execute(UMassEntitySubsystem& EntitySubsystem, FMassExecutionContext& Context) override; + + FMassEntityQuery EntityQuery; + + TObjectPtr SignalSubsystem; + TObjectPtr FormationSubsystem; +}; + +// Observer that runs when an entity is destroyed. Cleans up the unit array and tells the last unit to take their place +UCLASS() +class RTSFORMATIONS_API UMoveForceProcessor : public UMassProcessor +{ + GENERATED_BODY() + + virtual void ConfigureQueries() override; + virtual void Execute(UMassEntitySubsystem& EntitySubsystem, FMassExecutionContext& Context) override; + + FMassEntityQuery EntityQuery; +}; + +USTRUCT() +struct RTSFORMATIONS_API FLaunchEntityFragment : public FMassFragment +{ + GENERATED_BODY() +public: + UPROPERTY() + FVector Origin; + + UPROPERTY() + float Magnitude; +}; \ No newline at end of file From e95a2fcb463f376fbba51954bf02653c1627da7e Mon Sep 17 00:00:00 2001 From: Ji-Rath Date: Thu, 1 Sep 2022 21:56:35 -0500 Subject: [PATCH 22/37] Begin implementing HashGrid to entities for querying positions --- .../Source/RTSFormations/Private/RTSAgentTraits.cpp | 1 + .../RTSFormations/Source/RTSFormations/Public/RTSAgentTraits.h | 3 +++ 2 files changed, 4 insertions(+) diff --git a/Plugins/RTSFormations/Source/RTSFormations/Private/RTSAgentTraits.cpp b/Plugins/RTSFormations/Source/RTSFormations/Private/RTSAgentTraits.cpp index abaf4e6e..e8acbfad 100644 --- a/Plugins/RTSFormations/Source/RTSFormations/Private/RTSAgentTraits.cpp +++ b/Plugins/RTSFormations/Source/RTSFormations/Private/RTSAgentTraits.cpp @@ -6,6 +6,7 @@ #include "MassEntityTemplateRegistry.h" #include "MassNavigationFragments.h" #include "MassObserverRegistry.h" +#include "Engine/World.h" void URTSFormationAgentTrait::BuildTemplate(FMassEntityTemplateBuildContext& BuildContext, UWorld& World) const { diff --git a/Plugins/RTSFormations/Source/RTSFormations/Public/RTSAgentTraits.h b/Plugins/RTSFormations/Source/RTSFormations/Public/RTSAgentTraits.h index 6e0dbed0..555fedcd 100644 --- a/Plugins/RTSFormations/Source/RTSFormations/Public/RTSAgentTraits.h +++ b/Plugins/RTSFormations/Source/RTSFormations/Public/RTSAgentTraits.h @@ -6,6 +6,7 @@ #include "MassEntityTraitBase.h" #include "MassEntityTypes.h" #include "MassMovementTypes.h" +#include "RTSAgentSubsystem.h" #include "RTSAgentTraits.generated.h" class URTSFormationSubsystem; @@ -23,6 +24,8 @@ struct RTSFORMATIONS_API FRTSFormationAgent : public FMassFragment int UnitIndex = 0; FVector Offset; + + RTSAgentHashGrid2D::FCellLocation CellLoc; }; USTRUCT() From e401d35de97bd0646a96f75a0a1525a5912eae2c Mon Sep 17 00:00:00 2001 From: Ji-Rath Date: Thu, 1 Sep 2022 21:56:58 -0500 Subject: [PATCH 23/37] Implement basic hashgrid processors to update and set location of entities --- .../Private/RTSFormationProcessors.cpp | 76 +++++++++++++++++++ .../Private/RTSFormationProcessors.h | 29 +++++++ 2 files changed, 105 insertions(+) diff --git a/Plugins/RTSFormations/Source/RTSFormations/Private/RTSFormationProcessors.cpp b/Plugins/RTSFormations/Source/RTSFormations/Private/RTSFormationProcessors.cpp index d312c595..c093a462 100644 --- a/Plugins/RTSFormations/Source/RTSFormations/Private/RTSFormationProcessors.cpp +++ b/Plugins/RTSFormations/Source/RTSFormations/Private/RTSFormationProcessors.cpp @@ -1,11 +1,13 @@ #include "RTSFormationProcessors.h" +#include "LaunchEntityProcessor.h" #include "MassCommonFragments.h" #include "MassMovementFragments.h" #include "MassNavigationFragments.h" #include "MassNavigationTypes.h" #include "MassSignalSubsystem.h" #include "MassSimulationLOD.h" +#include "RTSAgentSubsystem.h" #include "RTSAgentTraits.h" #include "RTSFormationSubsystem.h" #include "Engine/World.h" @@ -144,6 +146,7 @@ void URTSFormationDestroyer::Execute(UMassEntitySubsystem& EntitySubsystem, FMas //----------------------------------------------------------------------// void URTSAgentMovement::ConfigureQueries() { + EntityQuery.AddRequirement(EMassFragmentAccess::None, EMassFragmentPresence::None); EntityQuery.AddRequirement(EMassFragmentAccess::ReadOnly); EntityQuery.AddRequirement(EMassFragmentAccess::ReadWrite); EntityQuery.AddRequirement(EMassFragmentAccess::ReadOnly); @@ -313,3 +316,76 @@ void URTSUpdateEntityIndex::SignalEntities(UMassEntitySubsystem& EntitySubsystem } }); } + +//----------------------------------------------------------------------// +// URTSUpdateHashPosition +//----------------------------------------------------------------------// +void URTSUpdateHashPosition::ConfigureQueries() +{ + EntityQuery.AddRequirement(EMassFragmentAccess::ReadOnly); + EntityQuery.AddRequirement(EMassFragmentAccess::ReadWrite); +} + +void URTSUpdateHashPosition::Execute(UMassEntitySubsystem& EntitySubsystem, FMassExecutionContext& Context) +{ + EntityQuery.ParallelForEachEntityChunk(EntitySubsystem, Context, [this](FMassExecutionContext& Context) + { + TConstArrayView TransformFragments = Context.GetFragmentView(); + TArrayView FormationAgents = Context.GetMutableFragmentView(); + + for (int32 EntityIndex = 0; EntityIndex < Context.GetNumEntities(); ++EntityIndex) + { + FRTSFormationAgent& RTSAgent = FormationAgents[EntityIndex]; + const FVector& Location = TransformFragments[EntityIndex].GetTransform().GetLocation(); + + float Radius = 25.f; + const FBox NewBounds(Location - FVector(Radius, Radius, 0.f), Location + FVector(Radius, Radius, 0.f)); + RTSAgent.CellLoc = AgentSubsystem->AgentHashGrid.Move(Context.GetEntity(EntityIndex), RTSAgent.CellLoc, NewBounds); + } + }); +} + +void URTSUpdateHashPosition::Initialize(UObject& Owner) +{ + AgentSubsystem = UWorld::GetSubsystem(Owner.GetWorld()); +} + +//----------------------------------------------------------------------// +// URTSInitializeHashPosition +//----------------------------------------------------------------------// +URTSInitializeHashPosition::URTSInitializeHashPosition() +{ + ObservedType = FRTSFormationAgent::StaticStruct(); + Operation = EMassObservedOperation::Add; +} + +void URTSInitializeHashPosition::ConfigureQueries() +{ + EntityQuery.AddRequirement(EMassFragmentAccess::ReadOnly); + EntityQuery.AddRequirement(EMassFragmentAccess::ReadWrite); + EntityQuery.AddRequirement(EMassFragmentAccess::ReadOnly); +} + +void URTSInitializeHashPosition::Execute(UMassEntitySubsystem& EntitySubsystem, FMassExecutionContext& Context) +{ + EntityQuery.ParallelForEachEntityChunk(EntitySubsystem, Context, [this](FMassExecutionContext& Context) + { + TConstArrayView TransformFragments = Context.GetFragmentView(); + TArrayView FormationAgents = Context.GetMutableFragmentView(); + + for (int32 EntityIndex = 0; EntityIndex < Context.GetNumEntities(); ++EntityIndex) + { + FRTSFormationAgent& RTSAgent = FormationAgents[EntityIndex]; + const FVector& Location = TransformFragments[EntityIndex].GetTransform().GetLocation(); + + float Radius = 25.f; + const FBox NewBounds(Location - FVector(Radius, Radius, 0.f), Location + FVector(Radius, Radius, 0.f)); + RTSAgent.CellLoc = AgentSubsystem->AgentHashGrid.Add(Context.GetEntity(EntityIndex), NewBounds); + } + }); +} + +void URTSInitializeHashPosition::Initialize(UObject& Owner) +{ + AgentSubsystem = UWorld::GetSubsystem(Owner.GetWorld()); +} diff --git a/Plugins/RTSFormations/Source/RTSFormations/Private/RTSFormationProcessors.h b/Plugins/RTSFormations/Source/RTSFormations/Private/RTSFormationProcessors.h index 9887d781..477b9a32 100644 --- a/Plugins/RTSFormations/Source/RTSFormations/Private/RTSFormationProcessors.h +++ b/Plugins/RTSFormations/Source/RTSFormations/Private/RTSFormationProcessors.h @@ -83,3 +83,32 @@ class RTSFORMATIONS_API URTSUpdateEntityIndex : public UMassSignalProcessorBase TObjectPtr FormationSubsystem; }; + +// Simple movement processor to get agents from a to b +UCLASS() +class RTSFORMATIONS_API URTSUpdateHashPosition : public UMassProcessor +{ + GENERATED_BODY() + + virtual void ConfigureQueries() override; + virtual void Execute(UMassEntitySubsystem& EntitySubsystem, FMassExecutionContext& Context) override; + virtual void Initialize(UObject& Owner) override; + + FMassEntityQuery EntityQuery; + TObjectPtr AgentSubsystem; +}; + +// Simple movement processor to get agents from a to b +UCLASS() +class RTSFORMATIONS_API URTSInitializeHashPosition : public UMassObserverProcessor +{ + GENERATED_BODY() + + URTSInitializeHashPosition(); + virtual void ConfigureQueries() override; + virtual void Execute(UMassEntitySubsystem& EntitySubsystem, FMassExecutionContext& Context) override; + virtual void Initialize(UObject& Owner) override; + + FMassEntityQuery EntityQuery; + TObjectPtr AgentSubsystem; +}; From 4e3c93c7c21f165646eb696ad938a4673e2a8e92 Mon Sep 17 00:00:00 2001 From: Ji-Rath Date: Thu, 8 Sep 2022 11:11:24 -0500 Subject: [PATCH 24/37] Clean up Signaling entities and ensure that there are entities to signal before calling Fix units not updating their position correctly when setting a new preset or being spawned in-game --- .../Private/RTSFormationSubsystem.cpp | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/Plugins/RTSFormations/Source/RTSFormations/Private/RTSFormationSubsystem.cpp b/Plugins/RTSFormations/Source/RTSFormations/Private/RTSFormationSubsystem.cpp index 8f762781..5b94c786 100644 --- a/Plugins/RTSFormations/Source/RTSFormations/Private/RTSFormationSubsystem.cpp +++ b/Plugins/RTSFormations/Source/RTSFormations/Private/RTSFormationSubsystem.cpp @@ -123,11 +123,10 @@ void URTSFormationSubsystem::UpdateUnitPosition(const FVector& NewPosition, int // Signal entities to update their position index FMassExecutionContext Context = EntitySubsystem->CreateExecutionContext(GetWorld()->GetDeltaSeconds()); - TArray Entities = Unit.Entities.Array(); - for(int i=0;iGetSubsystem()->SignalEntity(UpdateIndex, Entities[i]); + TArray Entities = Unit.Entities.Array(); + GetWorld()->GetSubsystem()->SignalEntities(UpdateIndex, Entities); } } @@ -178,8 +177,11 @@ void URTSFormationSubsystem::SetUnitPosition(const FVector& NewPosition, int Uni UMassEntitySubsystem* EntitySubsystem = GetWorld()->GetSubsystem(); for(const FMassEntityHandle& Entity : Unit.Entities) { - EntitySubsystem->GetFragmentDataPtr(Entity)->CreateNewAction(EMassMovementAction::Stand, *GetWorld()); - EntitySubsystem->GetFragmentDataPtr(Entity)->Value = FVector::Zero(); + if (EntitySubsystem->GetFragmentDataPtr(Entity)) + { + EntitySubsystem->GetFragmentDataPtr(Entity)->CreateNewAction(EMassMovementAction::Stand, *GetWorld()); + EntitySubsystem->GetFragmentDataPtr(Entity)->Value = FVector::Zero(); + } } Unit.UnitPosition = NewPosition; @@ -239,7 +241,7 @@ void URTSFormationSubsystem::SetFormationPreset(int UnitIndex, UFormationPresets Unit.Rings = FormationAsset->Rings; Unit.bHollow = FormationAsset->bHollow; - GetWorld()->GetSubsystem()->SignalEntities(FormationUpdated, Unit.Entities.Array()); + SetUnitPosition(Units[UnitIndex].UnitPosition, UnitIndex); } void URTSFormationSubsystem::Tick(float DeltaTime) From 8f5b90b8875fa8372888989e07da54dd4168b6c8 Mon Sep 17 00:00:00 2001 From: Ji-Rath Date: Thu, 8 Sep 2022 11:12:12 -0500 Subject: [PATCH 25/37] Fix entities not updating their index due to legacy code Move Hash grid processors to their own file --- .../Private/RTSFormationProcessors.cpp | 88 ++----------------- .../Private/RTSFormationProcessors.h | 30 +------ 2 files changed, 6 insertions(+), 112 deletions(-) diff --git a/Plugins/RTSFormations/Source/RTSFormations/Private/RTSFormationProcessors.cpp b/Plugins/RTSFormations/Source/RTSFormations/Private/RTSFormationProcessors.cpp index c093a462..281e26d8 100644 --- a/Plugins/RTSFormations/Source/RTSFormations/Private/RTSFormationProcessors.cpp +++ b/Plugins/RTSFormations/Source/RTSFormations/Private/RTSFormationProcessors.cpp @@ -7,7 +7,6 @@ #include "MassNavigationTypes.h" #include "MassSignalSubsystem.h" #include "MassSimulationLOD.h" -#include "RTSAgentSubsystem.h" #include "RTSAgentTraits.h" #include "RTSFormationSubsystem.h" #include "Engine/World.h" @@ -64,7 +63,7 @@ void URTSFormationInitializer::Execute(UMassEntitySubsystem& EntitySubsystem, FM // Signal entities in the unit that their position is updated // @todo only notify affected entities for(const int& Unit : UnitSignals) - SignalSubsystem->SignalEntities(FormationUpdated, FormationSubsystem->Units[Unit].Entities.Array()); + FormationSubsystem->SetUnitPosition(FormationSubsystem->Units[Unit].UnitPosition, Unit); }); } @@ -110,8 +109,6 @@ void URTSFormationDestroyer::Execute(UMassEntitySubsystem& EntitySubsystem, FMas { // Since we are caching the index, we need to fix the entity index that replaces the destroyed one // Not sure if this is the 'correct' way to handle this, but it works for now - //if (FRTSFormationAgent* ReplacementFormationAgent = EntitySubsystem.GetFragmentDataPtr(FormationSubsystem->Units[FormationAgent.UnitIndex].Entities.Array().Last())) - // ReplacementFormationAgent->EntityIndex = FormationAgent.EntityIndex; FormationSubsystem->Units[FormationAgent.UnitIndex].Entities.Remove(*ItemIndex); UnitSignals.AddUnique(FormationAgent.UnitIndex); @@ -135,7 +132,6 @@ void URTSFormationDestroyer::Execute(UMassEntitySubsystem& EntitySubsystem, FMas // Every other time we just have to notify the entity that is replacing the destroyed one FormationSubsystem->Units[Unit].Entities.Shrink(); FormationSubsystem->UpdateUnitPosition(FormationSubsystem->Units[Unit].UnitPosition, Unit); - //SignalSubsystem->SignalEntities(FormationUpdated, FormationSubsystem->Units[Unit].Entities.Array()); } } }); @@ -180,6 +176,9 @@ void URTSAgentMovement::Execute(UMassEntitySubsystem& EntitySubsystem, FMassExec const FUnitInfo& Unit = FormationSubsystem->Units[RTSFormationAgent.UnitIndex]; + if(MoveTarget.GetCurrentAction() == EMassMovementAction::Stand) + MoveTarget.CreateNewAction(EMassMovementAction::Move, *GetWorld()); + FVector Offset = RTSFormationAgent.Offset; if (Unit.bBlendAngle) { @@ -192,7 +191,7 @@ void URTSAgentMovement::Execute(UMassEntitySubsystem& EntitySubsystem, FMassExec MoveTarget.DistanceToGoal = (MoveTarget.Center - Transform.GetLocation()).Length(); MoveTarget.Forward = (MoveTarget.Center - Transform.GetLocation()).GetSafeNormal(); - // Once we are close enough to our goal, create stand action + // Once we are close enough to our goal, begin walking if (MoveTarget.DistanceToGoal <= MoveTarget.SlackRadius) { //MoveTarget.CreateNewAction(EMassMovementAction::Stand, *GetWorld()); @@ -261,8 +260,6 @@ void URTSUpdateEntityIndex::Initialize(UObject& Owner) void URTSUpdateEntityIndex::ConfigureQueries() { EntityQuery.AddRequirement(EMassFragmentAccess::ReadOnly); - EntityQuery.AddRequirement(EMassFragmentAccess::ReadWrite); - EntityQuery.AddRequirement(EMassFragmentAccess::ReadOnly); } void URTSUpdateEntityIndex::SignalEntities(UMassEntitySubsystem& EntitySubsystem, FMassExecutionContext& Context, @@ -273,14 +270,10 @@ void URTSUpdateEntityIndex::SignalEntities(UMassEntitySubsystem& EntitySubsystem // and cut down on iterations significantly EntityQuery.ParallelForEachEntityChunk(EntitySubsystem, Context, [this](FMassExecutionContext& Context) { - TArrayView MoveTargetFragments = Context.GetMutableFragmentView(); - TConstArrayView TransformFragments = Context.GetFragmentView(); TArrayView FormationAgents = Context.GetMutableFragmentView(); for (int32 EntityIndex = 0; EntityIndex < Context.GetNumEntities(); ++EntityIndex) { - FMassMoveTargetFragment& MoveTarget = MoveTargetFragments[EntityIndex]; - const FVector& Location = TransformFragments[EntityIndex].GetTransform().GetLocation(); FRTSFormationAgent& FormationAgent = FormationAgents[EntityIndex]; // Get first index since it is sorted @@ -317,75 +310,4 @@ void URTSUpdateEntityIndex::SignalEntities(UMassEntitySubsystem& EntitySubsystem }); } -//----------------------------------------------------------------------// -// URTSUpdateHashPosition -//----------------------------------------------------------------------// -void URTSUpdateHashPosition::ConfigureQueries() -{ - EntityQuery.AddRequirement(EMassFragmentAccess::ReadOnly); - EntityQuery.AddRequirement(EMassFragmentAccess::ReadWrite); -} - -void URTSUpdateHashPosition::Execute(UMassEntitySubsystem& EntitySubsystem, FMassExecutionContext& Context) -{ - EntityQuery.ParallelForEachEntityChunk(EntitySubsystem, Context, [this](FMassExecutionContext& Context) - { - TConstArrayView TransformFragments = Context.GetFragmentView(); - TArrayView FormationAgents = Context.GetMutableFragmentView(); - - for (int32 EntityIndex = 0; EntityIndex < Context.GetNumEntities(); ++EntityIndex) - { - FRTSFormationAgent& RTSAgent = FormationAgents[EntityIndex]; - const FVector& Location = TransformFragments[EntityIndex].GetTransform().GetLocation(); - - float Radius = 25.f; - const FBox NewBounds(Location - FVector(Radius, Radius, 0.f), Location + FVector(Radius, Radius, 0.f)); - RTSAgent.CellLoc = AgentSubsystem->AgentHashGrid.Move(Context.GetEntity(EntityIndex), RTSAgent.CellLoc, NewBounds); - } - }); -} - -void URTSUpdateHashPosition::Initialize(UObject& Owner) -{ - AgentSubsystem = UWorld::GetSubsystem(Owner.GetWorld()); -} - -//----------------------------------------------------------------------// -// URTSInitializeHashPosition -//----------------------------------------------------------------------// -URTSInitializeHashPosition::URTSInitializeHashPosition() -{ - ObservedType = FRTSFormationAgent::StaticStruct(); - Operation = EMassObservedOperation::Add; -} - -void URTSInitializeHashPosition::ConfigureQueries() -{ - EntityQuery.AddRequirement(EMassFragmentAccess::ReadOnly); - EntityQuery.AddRequirement(EMassFragmentAccess::ReadWrite); - EntityQuery.AddRequirement(EMassFragmentAccess::ReadOnly); -} - -void URTSInitializeHashPosition::Execute(UMassEntitySubsystem& EntitySubsystem, FMassExecutionContext& Context) -{ - EntityQuery.ParallelForEachEntityChunk(EntitySubsystem, Context, [this](FMassExecutionContext& Context) - { - TConstArrayView TransformFragments = Context.GetFragmentView(); - TArrayView FormationAgents = Context.GetMutableFragmentView(); - - for (int32 EntityIndex = 0; EntityIndex < Context.GetNumEntities(); ++EntityIndex) - { - FRTSFormationAgent& RTSAgent = FormationAgents[EntityIndex]; - const FVector& Location = TransformFragments[EntityIndex].GetTransform().GetLocation(); - - float Radius = 25.f; - const FBox NewBounds(Location - FVector(Radius, Radius, 0.f), Location + FVector(Radius, Radius, 0.f)); - RTSAgent.CellLoc = AgentSubsystem->AgentHashGrid.Add(Context.GetEntity(EntityIndex), NewBounds); - } - }); -} -void URTSInitializeHashPosition::Initialize(UObject& Owner) -{ - AgentSubsystem = UWorld::GetSubsystem(Owner.GetWorld()); -} diff --git a/Plugins/RTSFormations/Source/RTSFormations/Private/RTSFormationProcessors.h b/Plugins/RTSFormations/Source/RTSFormations/Private/RTSFormationProcessors.h index 477b9a32..731aa308 100644 --- a/Plugins/RTSFormations/Source/RTSFormations/Private/RTSFormationProcessors.h +++ b/Plugins/RTSFormations/Source/RTSFormations/Private/RTSFormationProcessors.h @@ -72,6 +72,7 @@ class RTSFORMATIONS_API URTSFormationUpdate : public UMassSignalProcessorBase TObjectPtr FormationSubsystem; }; +// Signal Processor that updates the agents index in the unit based on values in the FormationSubsystem Unit Array UCLASS() class RTSFORMATIONS_API URTSUpdateEntityIndex : public UMassSignalProcessorBase { @@ -83,32 +84,3 @@ class RTSFORMATIONS_API URTSUpdateEntityIndex : public UMassSignalProcessorBase TObjectPtr FormationSubsystem; }; - -// Simple movement processor to get agents from a to b -UCLASS() -class RTSFORMATIONS_API URTSUpdateHashPosition : public UMassProcessor -{ - GENERATED_BODY() - - virtual void ConfigureQueries() override; - virtual void Execute(UMassEntitySubsystem& EntitySubsystem, FMassExecutionContext& Context) override; - virtual void Initialize(UObject& Owner) override; - - FMassEntityQuery EntityQuery; - TObjectPtr AgentSubsystem; -}; - -// Simple movement processor to get agents from a to b -UCLASS() -class RTSFORMATIONS_API URTSInitializeHashPosition : public UMassObserverProcessor -{ - GENERATED_BODY() - - URTSInitializeHashPosition(); - virtual void ConfigureQueries() override; - virtual void Execute(UMassEntitySubsystem& EntitySubsystem, FMassExecutionContext& Context) override; - virtual void Initialize(UObject& Owner) override; - - FMassEntityQuery EntityQuery; - TObjectPtr AgentSubsystem; -}; From c931510b567cae7a8ef7c848ceb211ea2c463141 Mon Sep 17 00:00:00 2001 From: Ji-Rath Date: Thu, 8 Sep 2022 11:12:28 -0500 Subject: [PATCH 26/37] Add RTSAgentProcessors to handle hash grid positions of RTS agents --- .../Private/RTSAgentProcessors.cpp | 120 ++++++++++++++++++ .../Private/RTSAgentProcessors.h | 52 ++++++++ 2 files changed, 172 insertions(+) create mode 100644 Plugins/RTSFormations/Source/RTSFormations/Private/RTSAgentProcessors.cpp create mode 100644 Plugins/RTSFormations/Source/RTSFormations/Private/RTSAgentProcessors.h diff --git a/Plugins/RTSFormations/Source/RTSFormations/Private/RTSAgentProcessors.cpp b/Plugins/RTSFormations/Source/RTSFormations/Private/RTSAgentProcessors.cpp new file mode 100644 index 00000000..1fbd4da7 --- /dev/null +++ b/Plugins/RTSFormations/Source/RTSFormations/Private/RTSAgentProcessors.cpp @@ -0,0 +1,120 @@ +// Fill out your copyright notice in the Description page of Project Settings. + + +#include "RTSAgentProcessors.h" +#include "MassCommonFragments.h" +#include "MassEntitySubsystem.h" +#include "RTSAgentTraits.h" +#include "Engine/World.h" + +//----------------------------------------------------------------------// +// URTSUpdateHashPosition +//----------------------------------------------------------------------// +void URTSUpdateHashPosition::ConfigureQueries() +{ + EntityQuery.AddRequirement(EMassFragmentAccess::ReadOnly); + EntityQuery.AddRequirement(EMassFragmentAccess::ReadWrite); + EntityQuery.AddRequirement(EMassFragmentAccess::ReadOnly); +} + +void URTSUpdateHashPosition::Execute(UMassEntitySubsystem& EntitySubsystem, FMassExecutionContext& Context) +{ + EntityQuery.ParallelForEachEntityChunk(EntitySubsystem, Context, [this](FMassExecutionContext& Context) + { + TConstArrayView TransformFragments = Context.GetFragmentView(); + TArrayView FormationAgents = Context.GetMutableFragmentView(); + TConstArrayView RadiusFragments = Context.GetFragmentView(); + + for (int32 EntityIndex = 0; EntityIndex < Context.GetNumEntities(); ++EntityIndex) + { + FRTSFormationAgent& RTSAgent = FormationAgents[EntityIndex]; + const FVector& Location = TransformFragments[EntityIndex].GetTransform().GetLocation(); + const float Radius = RadiusFragments[EntityIndex].Radius; + + const FBox NewBounds(Location - FVector(Radius, Radius, 0.f), Location + FVector(Radius, Radius, 0.f)); + RTSAgent.CellLoc = AgentSubsystem->AgentHashGrid.Move(Context.GetEntity(EntityIndex), RTSAgent.CellLoc, NewBounds); + } + }); +} + +void URTSUpdateHashPosition::Initialize(UObject& Owner) +{ + AgentSubsystem = UWorld::GetSubsystem(Owner.GetWorld()); +} + +//----------------------------------------------------------------------// +// URTSInitializeHashPosition +//----------------------------------------------------------------------// +URTSInitializeHashPosition::URTSInitializeHashPosition() +{ + ObservedType = FRTSFormationAgent::StaticStruct(); + Operation = EMassObservedOperation::Add; +} + +void URTSInitializeHashPosition::ConfigureQueries() +{ + EntityQuery.AddRequirement(EMassFragmentAccess::ReadOnly); + EntityQuery.AddRequirement(EMassFragmentAccess::ReadOnly); + EntityQuery.AddRequirement(EMassFragmentAccess::ReadOnly); +} + +void URTSInitializeHashPosition::Execute(UMassEntitySubsystem& EntitySubsystem, FMassExecutionContext& Context) +{ + EntityQuery.ParallelForEachEntityChunk(EntitySubsystem, Context, [this](FMassExecutionContext& Context) + { + TConstArrayView TransformFragments = Context.GetFragmentView(); + TArrayView FormationAgents = Context.GetMutableFragmentView(); + TConstArrayView RadiusFragments = Context.GetFragmentView(); + + for (int32 EntityIndex = 0; EntityIndex < Context.GetNumEntities(); ++EntityIndex) + { + FRTSFormationAgent& RTSAgent = FormationAgents[EntityIndex]; + const FVector& Location = TransformFragments[EntityIndex].GetTransform().GetLocation(); + const float Radius = RadiusFragments[EntityIndex].Radius; + + const FBox NewBounds(Location - FVector(Radius, Radius, 0.f), Location + FVector(Radius, Radius, 0.f)); + RTSAgent.CellLoc = AgentSubsystem->AgentHashGrid.Add(Context.GetEntity(EntityIndex), NewBounds); + } + }); +} + +void URTSInitializeHashPosition::Initialize(UObject& Owner) +{ + AgentSubsystem = UWorld::GetSubsystem(Owner.GetWorld()); +} + +//----------------------------------------------------------------------// +// URTSRemoveHashPosition +//----------------------------------------------------------------------// + +URTSRemoveHashPosition::URTSRemoveHashPosition() +{ + ObservedType = FRTSFormationAgent::StaticStruct(); + Operation = EMassObservedOperation::Remove; +} + +void URTSRemoveHashPosition::ConfigureQueries() +{ + EntityQuery.AddRequirement(EMassFragmentAccess::ReadOnly); +} + +void URTSRemoveHashPosition::Execute(UMassEntitySubsystem& EntitySubsystem, FMassExecutionContext& Context) +{ + EntityQuery.ParallelForEachEntityChunk(EntitySubsystem, Context, [this](FMassExecutionContext& Context) + { + TConstArrayView FormationAgents = Context.GetFragmentView(); + + for (int32 EntityIndex = 0; EntityIndex < Context.GetNumEntities(); ++EntityIndex) + { + const FRTSFormationAgent& RTSAgent = FormationAgents[EntityIndex]; + + AgentSubsystem->AgentHashGrid.Remove(Context.GetEntity(EntityIndex), RTSAgent.CellLoc); + } + }); +} + +void URTSRemoveHashPosition::Initialize(UObject& Owner) +{ + Super::Initialize(Owner); + AgentSubsystem = UWorld::GetSubsystem(Owner.GetWorld()); +} \ No newline at end of file diff --git a/Plugins/RTSFormations/Source/RTSFormations/Private/RTSAgentProcessors.h b/Plugins/RTSFormations/Source/RTSFormations/Private/RTSAgentProcessors.h new file mode 100644 index 00000000..a920601e --- /dev/null +++ b/Plugins/RTSFormations/Source/RTSFormations/Private/RTSAgentProcessors.h @@ -0,0 +1,52 @@ +// Fill out your copyright notice in the Description page of Project Settings. + +#pragma once + +#include "CoreMinimal.h" +#include "MassObserverProcessor.h" +#include "MassProcessor.h" +#include "RTSAgentProcessors.generated.h" + +// Update hash grid position of entities +UCLASS() +class RTSFORMATIONS_API URTSUpdateHashPosition : public UMassProcessor +{ + GENERATED_BODY() + + virtual void ConfigureQueries() override; + virtual void Execute(UMassEntitySubsystem& EntitySubsystem, FMassExecutionContext& Context) override; + virtual void Initialize(UObject& Owner) override; + + FMassEntityQuery EntityQuery; + TObjectPtr AgentSubsystem; +}; + +// Initialize the hash grid position of RTS agents +UCLASS() +class RTSFORMATIONS_API URTSInitializeHashPosition : public UMassObserverProcessor +{ + GENERATED_BODY() + + URTSInitializeHashPosition(); + virtual void ConfigureQueries() override; + virtual void Execute(UMassEntitySubsystem& EntitySubsystem, FMassExecutionContext& Context) override; + virtual void Initialize(UObject& Owner) override; + + FMassEntityQuery EntityQuery; + TObjectPtr AgentSubsystem; +}; + +// Remove the entities from the hash grid when destroyed/no longer an RTSAgent +UCLASS() +class RTSFORMATIONS_API URTSRemoveHashPosition : public UMassObserverProcessor +{ + GENERATED_BODY() + + URTSRemoveHashPosition(); + virtual void ConfigureQueries() override; + virtual void Execute(UMassEntitySubsystem& EntitySubsystem, FMassExecutionContext& Context) override; + virtual void Initialize(UObject& Owner) override; + + FMassEntityQuery EntityQuery; + TObjectPtr AgentSubsystem; +}; From 4b1b2b9930d0d21b722883a6a1c49d2b00cfc446 Mon Sep 17 00:00:00 2001 From: Ji-Rath Date: Thu, 8 Sep 2022 11:13:11 -0500 Subject: [PATCH 27/37] Change LaunchEntityProcessor to a Signal Processor since I couldnt get an observer working for the life of me - should be further investigated --- .../Private/LaunchEntityProcessor.cpp | 53 ++++++++++--------- .../Public/LaunchEntityProcessor.h | 25 +++++---- 2 files changed, 44 insertions(+), 34 deletions(-) diff --git a/Plugins/RTSFormations/Source/RTSFormations/Private/LaunchEntityProcessor.cpp b/Plugins/RTSFormations/Source/RTSFormations/Private/LaunchEntityProcessor.cpp index b8179209..4ac526bf 100644 --- a/Plugins/RTSFormations/Source/RTSFormations/Private/LaunchEntityProcessor.cpp +++ b/Plugins/RTSFormations/Source/RTSFormations/Private/LaunchEntityProcessor.cpp @@ -5,51 +5,52 @@ #include "MassCommonFragments.h" #include "MassMovementFragments.h" #include "MassNavigationFragments.h" +#include "TimerManager.h" #include "Engine/World.h" //----------------------------------------------------------------------// // ULaunchEntityProcessor //----------------------------------------------------------------------// -ULaunchEntityProcessor::ULaunchEntityProcessor() -{ - ObservedType = FLaunchEntityFragment::StaticStruct(); - Operation = EMassObservedOperation::Add; -} void ULaunchEntityProcessor::ConfigureQueries() { - EntityQuery.AddRequirement(EMassFragmentAccess::ReadOnly); + EntityQuery.AddRequirement(EMassFragmentAccess::ReadWrite); EntityQuery.AddRequirement(EMassFragmentAccess::ReadWrite); - EntityQuery.AddRequirement(EMassFragmentAccess::ReadWrite); EntityQuery.AddRequirement(EMassFragmentAccess::ReadOnly); - + EntityQuery.AddTagRequirement(EMassFragmentPresence::None); } void ULaunchEntityProcessor::Initialize(UObject& Owner) { + Super::Initialize(Owner); SignalSubsystem = UWorld::GetSubsystem(Owner.GetWorld()); FormationSubsystem = UWorld::GetSubsystem(Owner.GetWorld()); + SubscribeToSignal(LaunchEntity); } -void ULaunchEntityProcessor::Execute(UMassEntitySubsystem& EntitySubsystem, FMassExecutionContext& Context) +void ULaunchEntityProcessor::SignalEntities(UMassEntitySubsystem& EntitySubsystem, FMassExecutionContext& Context, + FMassSignalNameLookup& EntitySignals) { EntityQuery.ParallelForEachEntityChunk(EntitySubsystem, Context, [this, &EntitySubsystem](FMassExecutionContext& Context) { - TConstArrayView LaunchEntityFragments = Context.GetFragmentView(); + TArrayView LaunchEntityFragments = Context.GetMutableFragmentView(); TArrayView MoveTargetFragments = Context.GetMutableFragmentView(); - TArrayView ForceFragments = Context.GetMutableFragmentView(); TConstArrayView TransformFragments = Context.GetFragmentView(); for (int32 EntityIndex = 0; EntityIndex < Context.GetNumEntities(); ++EntityIndex) { const FLaunchEntityFragment& LaunchEntityFragment = LaunchEntityFragments[EntityIndex]; FMassMoveTargetFragment& MoveTargetFragment = MoveTargetFragments[EntityIndex]; - FMassForceFragment& ForceFragment = ForceFragments[EntityIndex]; const FTransformFragment& TransformFragment = TransformFragments[EntityIndex]; - MoveTargetFragment.CreateNewAction(EMassMovementAction::Animate, *GetWorld()); - ForceFragment.Value = (TransformFragment.GetTransform().GetTranslation()-LaunchEntityFragment.Origin).GetSafeNormal()*LaunchEntityFragment.Magnitude; - UE_LOG(LogTemp, Log, TEXT("Initialize Force: %s"), *ForceFragment.Value.ToString()) + MoveTargetFragment.CreateNewAction(EMassMovementAction::Move, *GetWorld()); + MoveTargetFragment.Center = TransformFragment.GetTransform().GetLocation()+(TransformFragment.GetTransform().GetTranslation()-LaunchEntityFragment.Origin).GetSafeNormal()*LaunchEntityFragment.Magnitude; + MoveTargetFragment.Center.Z = 0.f; + MoveTargetFragment.Forward = (TransformFragment.GetTransform().GetTranslation()-LaunchEntityFragment.Origin).GetSafeNormal(); + MoveTargetFragment.DistanceToGoal = (TransformFragment.GetTransform().GetTranslation()-LaunchEntityFragment.Origin).Length(); + //DrawDebugPoint(GetWorld(), MoveTargetFragment.Center+(FVector::UpVector*200.f), 40.f, FColor::Green, false, 10.f); + //UE_LOG(LogTemp, Error, TEXT("MoveTarget: "), *MoveTargetFragment.Center.ToString()); + Context.Defer().AddTag(Context.GetEntity(EntityIndex)); } }); } @@ -64,6 +65,7 @@ void UMoveForceProcessor::ConfigureQueries() EntityQuery.AddRequirement(EMassFragmentAccess::ReadWrite); EntityQuery.AddRequirement(EMassFragmentAccess::ReadWrite); EntityQuery.AddRequirement(EMassFragmentAccess::ReadWrite); + EntityQuery.AddTagRequirement(EMassFragmentPresence::All); } @@ -82,20 +84,23 @@ void UMoveForceProcessor::Execute(UMassEntitySubsystem& EntitySubsystem, FMassEx FMassMoveTargetFragment& MoveTargetFragment = MoveTargetFragments[EntityIndex]; FMassForceFragment& ForceFragment = ForceFragments[EntityIndex]; FTransformFragment& TransformFragment = TransformFragments[EntityIndex]; + + MoveTargetFragment.DistanceToGoal = (TransformFragment.GetTransform().GetTranslation()-MoveTargetFragment.Center).Length(); - if(ForceFragment.Value.Length() > 0) + if(MoveTargetFragment.DistanceToGoal < 50.f) { - TransformFragment.GetMutableTransform().AddToTranslation(ForceFragment.Value); - //ForceFragment.Value -= FVector(0.1f)*Context.GetDeltaTimeSeconds(); - //UE_LOG(LogTemp, Log, TEXT("Force: %f"), ForceFragment.Value.Length()); - DrawDebugSphere(GetWorld(), TransformFragment.GetTransform().GetLocation(), 40.f, 5, FColor::Red); + if (MoveTargetFragment.GetCurrentAction() == EMassMovementAction::Move) + { + //Context.Defer().RemoveFragment(Context.GetEntity(EntityIndex)); + //Context.Defer().RemoveTag(Context.GetEntity(EntityIndex)); + Context.Defer().DestroyEntity(Context.GetEntity(EntityIndex)); + MoveTargetFragment.CreateNewAction(EMassMovementAction::Stand, *GetWorld()); + } } else { - EntitySubsystem.Defer().RemoveFragment(Context.GetEntity(EntityIndex)); - ForceFragment.Value = FVector::ZeroVector; - MoveTargetFragment.CreateNewAction(EMassMovementAction::Stand, *GetWorld()); + DrawDebugSphere(GetWorld(), TransformFragment.GetTransform().GetLocation(), 40.f, 5, FColor::Red); } } }); -} \ No newline at end of file +} diff --git a/Plugins/RTSFormations/Source/RTSFormations/Public/LaunchEntityProcessor.h b/Plugins/RTSFormations/Source/RTSFormations/Public/LaunchEntityProcessor.h index eb96e95e..4683d077 100644 --- a/Plugins/RTSFormations/Source/RTSFormations/Public/LaunchEntityProcessor.h +++ b/Plugins/RTSFormations/Source/RTSFormations/Public/LaunchEntityProcessor.h @@ -3,23 +3,22 @@ #include "MassEntityQuery.h" #include "MassEntityTypes.h" -#include "MassObserverProcessor.h" +#include "MassSignalProcessorBase.h" #include "MassSignalSubsystem.h" #include "RTSFormationSubsystem.h" #include "LaunchEntityProcessor.generated.h" +const FName LaunchEntity = FName(TEXT("LaunchEntity")); + // Observer that runs when an entity is destroyed. Cleans up the unit array and tells the last unit to take their place UCLASS() -class RTSFORMATIONS_API ULaunchEntityProcessor : public UMassObserverProcessor +class RTSFORMATIONS_API ULaunchEntityProcessor : public UMassSignalProcessorBase { GENERATED_BODY() - - ULaunchEntityProcessor(); + virtual void ConfigureQueries() override; virtual void Initialize(UObject& Owner) override; - virtual void Execute(UMassEntitySubsystem& EntitySubsystem, FMassExecutionContext& Context) override; - - FMassEntityQuery EntityQuery; + virtual void SignalEntities(UMassEntitySubsystem& EntitySubsystem, FMassExecutionContext& Context, FMassSignalNameLookup& EntitySignals) override; TObjectPtr SignalSubsystem; TObjectPtr FormationSubsystem; @@ -41,10 +40,16 @@ USTRUCT() struct RTSFORMATIONS_API FLaunchEntityFragment : public FMassFragment { GENERATED_BODY() -public: + UPROPERTY() FVector Origin; UPROPERTY() - float Magnitude; -}; \ No newline at end of file + float Magnitude = 500.f; +}; + +USTRUCT() +struct RTSFORMATIONS_API FInitLaunchFragment : public FMassTag +{ + GENERATED_BODY() +}; From 971de222477afbf7594bf14e25e09d3ea1d42726 Mon Sep 17 00:00:00 2001 From: Ji-Rath Date: Thu, 8 Sep 2022 11:14:04 -0500 Subject: [PATCH 28/37] Add BP_RTSPlayer for birds-eye view of map Add BP_Pad for base class of interactables --- Plugins/RTSFormations/Content/BP_Pad.uasset | 3 +++ Plugins/RTSFormations/Content/BP_RTSPlayer.uasset | 3 +++ 2 files changed, 6 insertions(+) create mode 100644 Plugins/RTSFormations/Content/BP_Pad.uasset create mode 100644 Plugins/RTSFormations/Content/BP_RTSPlayer.uasset diff --git a/Plugins/RTSFormations/Content/BP_Pad.uasset b/Plugins/RTSFormations/Content/BP_Pad.uasset new file mode 100644 index 00000000..8dce72e9 --- /dev/null +++ b/Plugins/RTSFormations/Content/BP_Pad.uasset @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:2166daab8221723086dec734f7c0201987e67a0bc3b39b28dc01b77bad722de5 +size 71052 diff --git a/Plugins/RTSFormations/Content/BP_RTSPlayer.uasset b/Plugins/RTSFormations/Content/BP_RTSPlayer.uasset new file mode 100644 index 00000000..0cd35cda --- /dev/null +++ b/Plugins/RTSFormations/Content/BP_RTSPlayer.uasset @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:eb1c999c27a7c7fdaeae6c130a17cd81d7f84c2afc85fb780d86d5f369d9d358 +size 161881 From 7e65b5997def04a3d4a7692508f196940bbdcd8d Mon Sep 17 00:00:00 2001 From: Ji-Rath Date: Thu, 8 Sep 2022 11:14:35 -0500 Subject: [PATCH 29/37] Modify BP_SpawnUnit and BP_SpawnEntity to use new base class BP_Pad --- Plugins/RTSFormations/Content/BP_SpawnEntity.uasset | 4 ++-- Plugins/RTSFormations/Content/BP_SpawnUnit.uasset | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/Plugins/RTSFormations/Content/BP_SpawnEntity.uasset b/Plugins/RTSFormations/Content/BP_SpawnEntity.uasset index ab22b363..a880a194 100644 --- a/Plugins/RTSFormations/Content/BP_SpawnEntity.uasset +++ b/Plugins/RTSFormations/Content/BP_SpawnEntity.uasset @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:41062c587661c36432e1b8f328e4b751229a8a507a3efc1ba728f2e13a391a8b -size 76296 +oid sha256:ab064b19f9e18ab37e48845793da5cf162c6581b815c3447082ad5afd5e20d72 +size 38178 diff --git a/Plugins/RTSFormations/Content/BP_SpawnUnit.uasset b/Plugins/RTSFormations/Content/BP_SpawnUnit.uasset index 4a8f5ba1..098e7611 100644 --- a/Plugins/RTSFormations/Content/BP_SpawnUnit.uasset +++ b/Plugins/RTSFormations/Content/BP_SpawnUnit.uasset @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:c92fb2db4ff8d4230ca4c21fa84c0c8b6022b9cf4c207d63a1b7c12d660955f8 -size 59196 +oid sha256:a615fe94c2b9b2752e8a1f98c9de991d365310bef11a6b14c40f7293b336e461 +size 46708 From 0b34daaa1d114599f159f00c9dcbb7fda584b2e9 Mon Sep 17 00:00:00 2001 From: Ji-Rath Date: Thu, 8 Sep 2022 11:15:00 -0500 Subject: [PATCH 30/37] Cleanup BP_FormationGameMode and re-enable randomized position generating --- Plugins/RTSFormations/Content/BP_FormationGameMode.uasset | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Plugins/RTSFormations/Content/BP_FormationGameMode.uasset b/Plugins/RTSFormations/Content/BP_FormationGameMode.uasset index 3da8cd4b..30fd402a 100644 --- a/Plugins/RTSFormations/Content/BP_FormationGameMode.uasset +++ b/Plugins/RTSFormations/Content/BP_FormationGameMode.uasset @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:23930ce006e48f259eb128cec23236a56e977e9a9748277d15961c374cc495a8 -size 62555 +oid sha256:2d3a46b13e4eb2e4dec46d349baabcc6396ba79f41ff07abf6fd4d707103583d +size 64332 From dfc136790d1eceb12a327fcfc1e4a63cbb0a4172 Mon Sep 17 00:00:00 2001 From: Ji-Rath Date: Thu, 8 Sep 2022 11:15:41 -0500 Subject: [PATCH 31/37] Modify DA_AgentFormation so LODSimulation is no longer included by default, will need to determine whether its worth it to create an LODCollectorProcessor or leave it out for simplicity --- Plugins/RTSFormations/Content/DA_AgentFormation.uasset | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Plugins/RTSFormations/Content/DA_AgentFormation.uasset b/Plugins/RTSFormations/Content/DA_AgentFormation.uasset index b0f10940..9d60fa2c 100644 --- a/Plugins/RTSFormations/Content/DA_AgentFormation.uasset +++ b/Plugins/RTSFormations/Content/DA_AgentFormation.uasset @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:8e02ccf4e16cf58d666eccb29abf537c37a21d320266d50a99b8a23649c807d3 -size 6648 +oid sha256:c0eadd6c99610c2a4e380038085f1384eb1d6294f6709551d9d7df51a931d944 +size 7209 From f9eea6618436f81c80506f9acc04080eedcb2e59 Mon Sep 17 00:00:00 2001 From: Ji-Rath Date: Thu, 8 Sep 2022 11:16:20 -0500 Subject: [PATCH 32/37] Adjust LaunchEntities function to use QuerySmall and Signal processing due to roadblock with observers - needs further investigation --- .../Source/RTSFormations/Private/RTSAgentSubsystem.cpp | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/Plugins/RTSFormations/Source/RTSFormations/Private/RTSAgentSubsystem.cpp b/Plugins/RTSFormations/Source/RTSFormations/Private/RTSAgentSubsystem.cpp index fc19413c..d59d3932 100644 --- a/Plugins/RTSFormations/Source/RTSFormations/Private/RTSAgentSubsystem.cpp +++ b/Plugins/RTSFormations/Source/RTSFormations/Private/RTSAgentSubsystem.cpp @@ -10,24 +10,29 @@ void URTSAgentSubsystem::LaunchEntities(const FVector& Location, float Radius) const { - TRACE_CPUPROFILER_EVENT_SCOPE(TEXT("FindNearbyEntities")); + TRACE_CPUPROFILER_EVENT_SCOPE(TEXT("LaunchEntities")); UMassEntitySubsystem* EntitySubsystem = GetWorld()->GetSubsystem(); // Query items in radius TArray Entities; const FBox Bounds(Location - FVector(Radius, Radius, 0.f), Location + FVector(Radius, Radius, 0.f)); - AgentHashGrid.Query(Bounds,Entities); + AgentHashGrid.QuerySmall(Bounds,Entities); if (EntitySubsystem) { FLaunchEntityFragment LaunchEntityFragment; LaunchEntityFragment.Origin = Location; + LaunchEntityFragment.Magnitude = 500.f; + for(const FMassEntityHandle& Entity : Entities) { EntitySubsystem->Defer().PushCommand(FCommandAddFragmentInstance(Entity, FConstStructView::Make(LaunchEntityFragment))); } + // hacky fix since I couldnt get observer working for the life of me + if (Entities.Num()) + GetWorld()->GetSubsystem()->DelaySignalEntities(LaunchEntity, Entities,0.1f); } } From 5ec694b2e9b7cdb94485c430d06d84578d2c3da6 Mon Sep 17 00:00:00 2001 From: Ji-Rath Date: Thu, 8 Sep 2022 11:16:44 -0500 Subject: [PATCH 33/37] Update RTSFormationExample map so that updated gamemode and character are included in the plugin --- Plugins/RTSFormations/Content/RTSFormationsExample.umap | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Plugins/RTSFormations/Content/RTSFormationsExample.umap b/Plugins/RTSFormations/Content/RTSFormationsExample.umap index 07599c21..2b777deb 100644 --- a/Plugins/RTSFormations/Content/RTSFormationsExample.umap +++ b/Plugins/RTSFormations/Content/RTSFormationsExample.umap @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:a0319efbd01048cd0fc333cdbdeaebd6f17b67f7b833328f17cdae461b8fdb92 -size 42358 +oid sha256:44a71ff5c200b4f0a62d8169231041b5367b22f6a7dbfb848e130504a49af7b9 +size 43692 From 7256e5cfea24c9b200c38897e527d6b135e3f725 Mon Sep 17 00:00:00 2001 From: Ji-Rath Date: Thu, 8 Sep 2022 13:14:00 -0500 Subject: [PATCH 34/37] Fix header files being in wrong folder location --- .../Source/RTSFormations/{Private => Public}/RTSAgentProcessors.h | 0 .../Source/RTSFormations/{Private => Public}/RTSAgentSubsystem.h | 0 .../RTSFormations/{Private => Public}/RTSFormationProcessors.h | 0 .../RTSFormations/{Private => Public}/RTSFormationSubsystem.h | 0 4 files changed, 0 insertions(+), 0 deletions(-) rename Plugins/RTSFormations/Source/RTSFormations/{Private => Public}/RTSAgentProcessors.h (100%) rename Plugins/RTSFormations/Source/RTSFormations/{Private => Public}/RTSAgentSubsystem.h (100%) rename Plugins/RTSFormations/Source/RTSFormations/{Private => Public}/RTSFormationProcessors.h (100%) rename Plugins/RTSFormations/Source/RTSFormations/{Private => Public}/RTSFormationSubsystem.h (100%) diff --git a/Plugins/RTSFormations/Source/RTSFormations/Private/RTSAgentProcessors.h b/Plugins/RTSFormations/Source/RTSFormations/Public/RTSAgentProcessors.h similarity index 100% rename from Plugins/RTSFormations/Source/RTSFormations/Private/RTSAgentProcessors.h rename to Plugins/RTSFormations/Source/RTSFormations/Public/RTSAgentProcessors.h diff --git a/Plugins/RTSFormations/Source/RTSFormations/Private/RTSAgentSubsystem.h b/Plugins/RTSFormations/Source/RTSFormations/Public/RTSAgentSubsystem.h similarity index 100% rename from Plugins/RTSFormations/Source/RTSFormations/Private/RTSAgentSubsystem.h rename to Plugins/RTSFormations/Source/RTSFormations/Public/RTSAgentSubsystem.h diff --git a/Plugins/RTSFormations/Source/RTSFormations/Private/RTSFormationProcessors.h b/Plugins/RTSFormations/Source/RTSFormations/Public/RTSFormationProcessors.h similarity index 100% rename from Plugins/RTSFormations/Source/RTSFormations/Private/RTSFormationProcessors.h rename to Plugins/RTSFormations/Source/RTSFormations/Public/RTSFormationProcessors.h diff --git a/Plugins/RTSFormations/Source/RTSFormations/Private/RTSFormationSubsystem.h b/Plugins/RTSFormations/Source/RTSFormations/Public/RTSFormationSubsystem.h similarity index 100% rename from Plugins/RTSFormations/Source/RTSFormations/Private/RTSFormationSubsystem.h rename to Plugins/RTSFormations/Source/RTSFormations/Public/RTSFormationSubsystem.h From 60ac80f370b48a2358d0287f7780c4e292490f5d Mon Sep 17 00:00:00 2001 From: JiRath Date: Wed, 21 Aug 2024 18:05:28 -0500 Subject: [PATCH 35/37] Update to 5.4 Resolve some rare crashes --- .../Content/BP_FormationGameMode.uasset | 3 - .../Content/BP_SpawnEntity.uasset | 4 +- .../Content/DA_AgentFormation.uasset | 4 +- .../Content/RTSFormationsExample.umap | 4 +- .../GameplayDebuggerCategory_RTSAgents.cpp | 3 +- .../Private/LaunchEntityProcessor.cpp | 13 ++- .../Private/RTSAgentProcessors.cpp | 38 +++++-- .../Private/RTSAgentSubsystem.cpp | 4 +- .../RTSFormations/Private/RTSAgentTraits.cpp | 8 +- .../Private/RTSFormationProcessors.cpp | 104 ++++++++++-------- .../Private/RTSFormationSubsystem.cpp | 39 +++---- .../Public/LaunchEntityProcessor.h | 4 +- .../RTSFormations/Public/RTSAgentProcessors.h | 16 ++- .../RTSFormations/Public/RTSAgentSubsystem.h | 9 ++ .../RTSFormations/Public/RTSAgentTraits.h | 2 +- .../Public/RTSFormationProcessors.h | 13 +-- .../Public/RTSFormationSubsystem.h | 13 ++- 17 files changed, 168 insertions(+), 113 deletions(-) delete mode 100644 Plugins/RTSFormations/Content/BP_FormationGameMode.uasset diff --git a/Plugins/RTSFormations/Content/BP_FormationGameMode.uasset b/Plugins/RTSFormations/Content/BP_FormationGameMode.uasset deleted file mode 100644 index 30fd402a..00000000 --- a/Plugins/RTSFormations/Content/BP_FormationGameMode.uasset +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:2d3a46b13e4eb2e4dec46d349baabcc6396ba79f41ff07abf6fd4d707103583d -size 64332 diff --git a/Plugins/RTSFormations/Content/BP_SpawnEntity.uasset b/Plugins/RTSFormations/Content/BP_SpawnEntity.uasset index a880a194..6b6023c8 100644 --- a/Plugins/RTSFormations/Content/BP_SpawnEntity.uasset +++ b/Plugins/RTSFormations/Content/BP_SpawnEntity.uasset @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:ab064b19f9e18ab37e48845793da5cf162c6581b815c3447082ad5afd5e20d72 -size 38178 +oid sha256:b97bec3386038271aba2a295c6e7237016d705696ebd66fc02e0f4c816e39472 +size 44559 diff --git a/Plugins/RTSFormations/Content/DA_AgentFormation.uasset b/Plugins/RTSFormations/Content/DA_AgentFormation.uasset index 9d60fa2c..1026ea67 100644 --- a/Plugins/RTSFormations/Content/DA_AgentFormation.uasset +++ b/Plugins/RTSFormations/Content/DA_AgentFormation.uasset @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:c0eadd6c99610c2a4e380038085f1384eb1d6294f6709551d9d7df51a931d944 -size 7209 +oid sha256:f2b30fac8712cfdb870abe52c4ab4220edc10c236bd7aa0d78fc6d5b14546772 +size 5852 diff --git a/Plugins/RTSFormations/Content/RTSFormationsExample.umap b/Plugins/RTSFormations/Content/RTSFormationsExample.umap index 2b777deb..8b94ebd1 100644 --- a/Plugins/RTSFormations/Content/RTSFormationsExample.umap +++ b/Plugins/RTSFormations/Content/RTSFormationsExample.umap @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:44a71ff5c200b4f0a62d8169231041b5367b22f6a7dbfb848e130504a49af7b9 -size 43692 +oid sha256:5947043901a3264925b8ed1925b03346f3187e77be415eb46019a2b3ca5dfddc +size 49798 diff --git a/Plugins/RTSFormations/Source/RTSFormations/Private/GameplayDebuggerCategory_RTSAgents.cpp b/Plugins/RTSFormations/Source/RTSFormations/Private/GameplayDebuggerCategory_RTSAgents.cpp index c2ecb2f1..32b9e8da 100644 --- a/Plugins/RTSFormations/Source/RTSFormations/Private/GameplayDebuggerCategory_RTSAgents.cpp +++ b/Plugins/RTSFormations/Source/RTSFormations/Private/GameplayDebuggerCategory_RTSAgents.cpp @@ -4,6 +4,7 @@ #include "GameplayDebuggerCategory_RTSAgents.h" #include "DrawDebugHelpers.h" +#include "MassEntitySubsystem.h" #include "MassNavigationFragments.h" #include "Engine/World.h" @@ -31,7 +32,7 @@ void FGameplayDebuggerCategory_RTSAgents::CollectData(APlayerController* OwnerPC for(auto& Entity : FormationSubsystem->Units[0].Entities) { - const FVector& Pos = EntitySubsystem->GetFragmentDataChecked(Entity).Center; + const FVector& Pos = EntitySubsystem->GetEntityManager().GetFragmentDataChecked(Entity).Center; DataPack.Positions.Add(Pos); } } diff --git a/Plugins/RTSFormations/Source/RTSFormations/Private/LaunchEntityProcessor.cpp b/Plugins/RTSFormations/Source/RTSFormations/Private/LaunchEntityProcessor.cpp index 4ac526bf..881ee8f7 100644 --- a/Plugins/RTSFormations/Source/RTSFormations/Private/LaunchEntityProcessor.cpp +++ b/Plugins/RTSFormations/Source/RTSFormations/Private/LaunchEntityProcessor.cpp @@ -3,6 +3,7 @@ #include "DrawDebugHelpers.h" #include "MassCommonFragments.h" +#include "MassExecutionContext.h" #include "MassMovementFragments.h" #include "MassNavigationFragments.h" #include "TimerManager.h" @@ -25,13 +26,13 @@ void ULaunchEntityProcessor::Initialize(UObject& Owner) Super::Initialize(Owner); SignalSubsystem = UWorld::GetSubsystem(Owner.GetWorld()); FormationSubsystem = UWorld::GetSubsystem(Owner.GetWorld()); - SubscribeToSignal(LaunchEntity); + SubscribeToSignal(*SignalSubsystem, LaunchEntity); } -void ULaunchEntityProcessor::SignalEntities(UMassEntitySubsystem& EntitySubsystem, FMassExecutionContext& Context, - FMassSignalNameLookup& EntitySignals) +void ULaunchEntityProcessor::SignalEntities(FMassEntityManager& EntityManager, + FMassExecutionContext& Context, FMassSignalNameLookup& EntitySignals) { - EntityQuery.ParallelForEachEntityChunk(EntitySubsystem, Context, [this, &EntitySubsystem](FMassExecutionContext& Context) + EntityQuery.ParallelForEachEntityChunk(EntityManager, Context, [this, &EntityManager](FMassExecutionContext& Context) { TArrayView LaunchEntityFragments = Context.GetMutableFragmentView(); TArrayView MoveTargetFragments = Context.GetMutableFragmentView(); @@ -69,9 +70,9 @@ void UMoveForceProcessor::ConfigureQueries() } -void UMoveForceProcessor::Execute(UMassEntitySubsystem& EntitySubsystem, FMassExecutionContext& Context) +void UMoveForceProcessor::Execute(FMassEntityManager& EntityManager, FMassExecutionContext& Context) { - EntityQuery.ParallelForEachEntityChunk(EntitySubsystem, Context, [this, &EntitySubsystem](FMassExecutionContext& Context) + EntityQuery.ParallelForEachEntityChunk(EntityManager, Context, [this, &EntityManager](FMassExecutionContext& Context) { TConstArrayView LaunchEntityFragments = Context.GetFragmentView(); TArrayView MoveTargetFragments = Context.GetMutableFragmentView(); diff --git a/Plugins/RTSFormations/Source/RTSFormations/Private/RTSAgentProcessors.cpp b/Plugins/RTSFormations/Source/RTSFormations/Private/RTSAgentProcessors.cpp index 1fbd4da7..dad4bd87 100644 --- a/Plugins/RTSFormations/Source/RTSFormations/Private/RTSAgentProcessors.cpp +++ b/Plugins/RTSFormations/Source/RTSFormations/Private/RTSAgentProcessors.cpp @@ -4,6 +4,7 @@ #include "RTSAgentProcessors.h" #include "MassCommonFragments.h" #include "MassEntitySubsystem.h" +#include "MassExecutionContext.h" #include "RTSAgentTraits.h" #include "Engine/World.h" @@ -15,15 +16,19 @@ void URTSUpdateHashPosition::ConfigureQueries() EntityQuery.AddRequirement(EMassFragmentAccess::ReadOnly); EntityQuery.AddRequirement(EMassFragmentAccess::ReadWrite); EntityQuery.AddRequirement(EMassFragmentAccess::ReadOnly); + EntityQuery.AddTagRequirement(EMassFragmentPresence::All); + EntityQuery.AddSubsystemRequirement(EMassFragmentAccess::ReadWrite); + EntityQuery.RegisterWithProcessor(*this); } -void URTSUpdateHashPosition::Execute(UMassEntitySubsystem& EntitySubsystem, FMassExecutionContext& Context) +void URTSUpdateHashPosition::Execute(FMassEntityManager& EntityManager, FMassExecutionContext& Context) { - EntityQuery.ParallelForEachEntityChunk(EntitySubsystem, Context, [this](FMassExecutionContext& Context) + EntityQuery.ForEachEntityChunk(EntityManager, Context, [this](FMassExecutionContext& Context) { TConstArrayView TransformFragments = Context.GetFragmentView(); TArrayView FormationAgents = Context.GetMutableFragmentView(); TConstArrayView RadiusFragments = Context.GetFragmentView(); + auto& AgentSubsystem = Context.GetMutableSubsystemChecked(); for (int32 EntityIndex = 0; EntityIndex < Context.GetNumEntities(); ++EntityIndex) { @@ -32,14 +37,14 @@ void URTSUpdateHashPosition::Execute(UMassEntitySubsystem& EntitySubsystem, FMas const float Radius = RadiusFragments[EntityIndex].Radius; const FBox NewBounds(Location - FVector(Radius, Radius, 0.f), Location + FVector(Radius, Radius, 0.f)); - RTSAgent.CellLoc = AgentSubsystem->AgentHashGrid.Move(Context.GetEntity(EntityIndex), RTSAgent.CellLoc, NewBounds); + RTSAgent.CellLoc = AgentSubsystem.AgentHashGrid.Move(Context.GetEntity(EntityIndex), RTSAgent.CellLoc, NewBounds); } }); } void URTSUpdateHashPosition::Initialize(UObject& Owner) { - AgentSubsystem = UWorld::GetSubsystem(Owner.GetWorld()); + Super::Initialize(Owner); } //----------------------------------------------------------------------// @@ -56,15 +61,19 @@ void URTSInitializeHashPosition::ConfigureQueries() EntityQuery.AddRequirement(EMassFragmentAccess::ReadOnly); EntityQuery.AddRequirement(EMassFragmentAccess::ReadOnly); EntityQuery.AddRequirement(EMassFragmentAccess::ReadOnly); + EntityQuery.AddTagRequirement(EMassFragmentPresence::None); + EntityQuery.AddSubsystemRequirement(EMassFragmentAccess::ReadWrite); + EntityQuery.RegisterWithProcessor(*this); } -void URTSInitializeHashPosition::Execute(UMassEntitySubsystem& EntitySubsystem, FMassExecutionContext& Context) +void URTSInitializeHashPosition::Execute(FMassEntityManager& EntityManager, FMassExecutionContext& Context) { - EntityQuery.ParallelForEachEntityChunk(EntitySubsystem, Context, [this](FMassExecutionContext& Context) + EntityQuery.ForEachEntityChunk(EntityManager, Context, [this](FMassExecutionContext& Context) { TConstArrayView TransformFragments = Context.GetFragmentView(); TArrayView FormationAgents = Context.GetMutableFragmentView(); TConstArrayView RadiusFragments = Context.GetFragmentView(); + auto& AgentSubsystem = Context.GetMutableSubsystemChecked(); for (int32 EntityIndex = 0; EntityIndex < Context.GetNumEntities(); ++EntityIndex) { @@ -73,14 +82,17 @@ void URTSInitializeHashPosition::Execute(UMassEntitySubsystem& EntitySubsystem, const float Radius = RadiusFragments[EntityIndex].Radius; const FBox NewBounds(Location - FVector(Radius, Radius, 0.f), Location + FVector(Radius, Radius, 0.f)); - RTSAgent.CellLoc = AgentSubsystem->AgentHashGrid.Add(Context.GetEntity(EntityIndex), NewBounds); + UE_LOG(LogTemp, Log, TEXT("Agents: %d"), AgentSubsystem.AgentHashGrid.GetItems().Num()); + RTSAgent.CellLoc = AgentSubsystem.AgentHashGrid.Add(Context.GetEntity(EntityIndex), NewBounds); + + Context.Defer().AddTag(Context.GetEntity(EntityIndex)); } }); } void URTSInitializeHashPosition::Initialize(UObject& Owner) { - AgentSubsystem = UWorld::GetSubsystem(Owner.GetWorld()); + Super::Initialize(Owner); } //----------------------------------------------------------------------// @@ -96,19 +108,22 @@ URTSRemoveHashPosition::URTSRemoveHashPosition() void URTSRemoveHashPosition::ConfigureQueries() { EntityQuery.AddRequirement(EMassFragmentAccess::ReadOnly); + EntityQuery.AddSubsystemRequirement(EMassFragmentAccess::ReadWrite); + EntityQuery.RegisterWithProcessor(*this); } -void URTSRemoveHashPosition::Execute(UMassEntitySubsystem& EntitySubsystem, FMassExecutionContext& Context) +void URTSRemoveHashPosition::Execute(FMassEntityManager& EntityManager, FMassExecutionContext& Context) { - EntityQuery.ParallelForEachEntityChunk(EntitySubsystem, Context, [this](FMassExecutionContext& Context) + EntityQuery.ForEachEntityChunk(EntityManager, Context, [this](FMassExecutionContext& Context) { TConstArrayView FormationAgents = Context.GetFragmentView(); + auto& AgentSubsystem = Context.GetMutableSubsystemChecked(); for (int32 EntityIndex = 0; EntityIndex < Context.GetNumEntities(); ++EntityIndex) { const FRTSFormationAgent& RTSAgent = FormationAgents[EntityIndex]; - AgentSubsystem->AgentHashGrid.Remove(Context.GetEntity(EntityIndex), RTSAgent.CellLoc); + AgentSubsystem.AgentHashGrid.Remove(Context.GetEntity(EntityIndex), RTSAgent.CellLoc); } }); } @@ -116,5 +131,4 @@ void URTSRemoveHashPosition::Execute(UMassEntitySubsystem& EntitySubsystem, FMas void URTSRemoveHashPosition::Initialize(UObject& Owner) { Super::Initialize(Owner); - AgentSubsystem = UWorld::GetSubsystem(Owner.GetWorld()); } \ No newline at end of file diff --git a/Plugins/RTSFormations/Source/RTSFormations/Private/RTSAgentSubsystem.cpp b/Plugins/RTSFormations/Source/RTSFormations/Private/RTSAgentSubsystem.cpp index d59d3932..78bc8428 100644 --- a/Plugins/RTSFormations/Source/RTSFormations/Private/RTSAgentSubsystem.cpp +++ b/Plugins/RTSFormations/Source/RTSFormations/Private/RTSAgentSubsystem.cpp @@ -28,8 +28,8 @@ void URTSAgentSubsystem::LaunchEntities(const FVector& Location, float Radius) c for(const FMassEntityHandle& Entity : Entities) { - EntitySubsystem->Defer().PushCommand(FCommandAddFragmentInstance(Entity, - FConstStructView::Make(LaunchEntityFragment))); + EntitySubsystem->GetEntityManager().Defer().PushCommand(Entity, + LaunchEntityFragment); } // hacky fix since I couldnt get observer working for the life of me if (Entities.Num()) diff --git a/Plugins/RTSFormations/Source/RTSFormations/Private/RTSAgentTraits.cpp b/Plugins/RTSFormations/Source/RTSFormations/Private/RTSAgentTraits.cpp index e8acbfad..03d1ca2a 100644 --- a/Plugins/RTSFormations/Source/RTSFormations/Private/RTSAgentTraits.cpp +++ b/Plugins/RTSFormations/Source/RTSFormations/Private/RTSAgentTraits.cpp @@ -3,19 +3,21 @@ #include "RTSAgentTraits.h" +#include "MassEntitySubsystem.h" #include "MassEntityTemplateRegistry.h" #include "MassNavigationFragments.h" #include "MassObserverRegistry.h" #include "Engine/World.h" -void URTSFormationAgentTrait::BuildTemplate(FMassEntityTemplateBuildContext& BuildContext, UWorld& World) const +void URTSFormationAgentTrait::BuildTemplate(FMassEntityTemplateBuildContext& BuildContext, const UWorld& World) const { UMassEntitySubsystem* EntitySubsystem = UWorld::GetSubsystem(&World); + FMassEntityManager& EntityManager = UE::Mass::Utils::GetEntityManagerChecked(World); check(EntitySubsystem); BuildContext.AddFragment(); - + uint32 MySharedFragmentHash = UE::StructUtils::GetStructCrc32(FConstStructView::Make(FormationSettings)); - FSharedStruct MySharedFragment = EntitySubsystem->GetOrCreateSharedFragment(MySharedFragmentHash, FormationSettings); + auto MySharedFragment = EntityManager.GetOrCreateSharedFragmentByHash(MySharedFragmentHash); BuildContext.AddSharedFragment(MySharedFragment); } diff --git a/Plugins/RTSFormations/Source/RTSFormations/Private/RTSFormationProcessors.cpp b/Plugins/RTSFormations/Source/RTSFormations/Private/RTSFormationProcessors.cpp index 281e26d8..a79d1c8c 100644 --- a/Plugins/RTSFormations/Source/RTSFormations/Private/RTSFormationProcessors.cpp +++ b/Plugins/RTSFormations/Source/RTSFormations/Private/RTSFormationProcessors.cpp @@ -23,6 +23,7 @@ URTSFormationInitializer::URTSFormationInitializer() void URTSFormationInitializer::ConfigureQueries() { EntityQuery.AddRequirement(EMassFragmentAccess::ReadWrite); + EntityQuery.RegisterWithProcessor(*this); } void URTSFormationInitializer::Initialize(UObject& Owner) @@ -33,10 +34,10 @@ void URTSFormationInitializer::Initialize(UObject& Owner) FormationSubsystem = UWorld::GetSubsystem(Owner.GetWorld()); } -void URTSFormationInitializer::Execute(UMassEntitySubsystem& EntitySubsystem, FMassExecutionContext& Context) +void URTSFormationInitializer::Execute(FMassEntityManager& EntityManager, FMassExecutionContext& Context) { // First query is to give all units an appropriate unit index. - EntityQuery.ParallelForEachEntityChunk(EntitySubsystem, Context, [this](FMassExecutionContext& Context) + EntityQuery.ForEachEntityChunk(EntityManager, Context, [this](FMassExecutionContext& Context) { TArrayView RTSFormationAgents = Context.GetMutableFragmentView(); @@ -79,38 +80,41 @@ URTSFormationDestroyer::URTSFormationDestroyer() void URTSFormationDestroyer::ConfigureQueries() { EntityQuery.AddRequirement(EMassFragmentAccess::ReadOnly); + EntityQuery.AddSubsystemRequirement(EMassFragmentAccess::ReadWrite); + EntityQuery.AddSubsystemRequirement(EMassFragmentAccess::ReadWrite); + EntityQuery.RegisterWithProcessor(*this); } void URTSFormationDestroyer::Initialize(UObject& Owner) { - SignalSubsystem = UWorld::GetSubsystem(Owner.GetWorld()); - FormationSubsystem = UWorld::GetSubsystem(Owner.GetWorld()); + Super::Initialize(Owner); } -void URTSFormationDestroyer::Execute(UMassEntitySubsystem& EntitySubsystem, FMassExecutionContext& Context) +void URTSFormationDestroyer::Execute(FMassEntityManager& EntityManager, FMassExecutionContext& Context) { - EntityQuery.ParallelForEachEntityChunk(EntitySubsystem, Context, [this, &EntitySubsystem](FMassExecutionContext& Context) + EntityQuery.ForEachEntityChunk(EntityManager, Context, [this, &EntityManager](FMassExecutionContext& Context) { + auto& FormationSubsystem = Context.GetMutableSubsystemChecked(); TConstArrayView FormationAgents = Context.GetFragmentView(); // Signal affected units/entities at the end TArray UnitSignals; - UnitSignals.Reserve(FormationSubsystem->Units.Num()); + UnitSignals.Reserve(FormationSubsystem.Units.Num()); for (int32 EntityIndex = 0; EntityIndex < Context.GetNumEntities(); ++EntityIndex) { const FRTSFormationAgent& FormationAgent = FormationAgents[EntityIndex]; // Remove entity from units array - if (FormationSubsystem->Units.IsValidIndex(FormationAgent.UnitIndex)) + if (FormationSubsystem.Units.IsValidIndex(FormationAgent.UnitIndex)) { - const FMassEntityHandle* ItemIndex = FormationSubsystem->Units[FormationAgent.UnitIndex].Entities.Find(Context.GetEntity(EntityIndex)); + const FMassEntityHandle* ItemIndex = FormationSubsystem.Units[FormationAgent.UnitIndex].Entities.Find(Context.GetEntity(EntityIndex)); if (ItemIndex) { // Since we are caching the index, we need to fix the entity index that replaces the destroyed one // Not sure if this is the 'correct' way to handle this, but it works for now - FormationSubsystem->Units[FormationAgent.UnitIndex].Entities.Remove(*ItemIndex); + FormationSubsystem.Units[FormationAgent.UnitIndex].Entities.Remove(*ItemIndex); UnitSignals.AddUnique(FormationAgent.UnitIndex); } } @@ -119,19 +123,19 @@ void URTSFormationDestroyer::Execute(UMassEntitySubsystem& EntitySubsystem, FMas // Signal affected units/entities for(const int& Unit : UnitSignals) { - if (FormationSubsystem->Units.IsValidIndex(Unit)) + if (FormationSubsystem.Units.IsValidIndex(Unit)) { //@todo add a consistent way to reference units since the index isn't reliable - if (FormationSubsystem->Units[Unit].Entities.Num() == 0) + if (FormationSubsystem.Units[Unit].Entities.Num() == 0) { - FormationSubsystem->Units.RemoveAtSwap(Unit); + FormationSubsystem.Units.RemoveAtSwap(Unit); continue; } // Really the only time we should notify every entity in the unit is when the center point changes // Every other time we just have to notify the entity that is replacing the destroyed one - FormationSubsystem->Units[Unit].Entities.Shrink(); - FormationSubsystem->UpdateUnitPosition(FormationSubsystem->Units[Unit].UnitPosition, Unit); + FormationSubsystem.Units[Unit].Entities.Shrink(); + FormationSubsystem.UpdateUnitPosition(FormationSubsystem.Units[Unit].UnitPosition, Unit); } } }); @@ -146,35 +150,37 @@ void URTSAgentMovement::ConfigureQueries() EntityQuery.AddRequirement(EMassFragmentAccess::ReadOnly); EntityQuery.AddRequirement(EMassFragmentAccess::ReadWrite); EntityQuery.AddRequirement(EMassFragmentAccess::ReadOnly); - EntityQuery.AddRequirement(EMassFragmentAccess::ReadWrite); EntityQuery.AddConstSharedRequirement(EMassFragmentPresence::All); EntityQuery.AddSharedRequirement(EMassFragmentAccess::ReadOnly); + EntityQuery.AddChunkRequirement(EMassFragmentAccess::ReadOnly, EMassFragmentPresence::Optional); EntityQuery.SetChunkFilter(&FMassSimulationVariableTickChunkFragment::ShouldTickChunkThisFrame); + + EntityQuery.AddSubsystemRequirement(EMassFragmentAccess::ReadWrite); + EntityQuery.RegisterWithProcessor(*this); } -void URTSAgentMovement::Execute(UMassEntitySubsystem& EntitySubsystem, FMassExecutionContext& Context) +void URTSAgentMovement::Execute(FMassEntityManager& EntityManager, FMassExecutionContext& Context) { - EntityQuery.ParallelForEachEntityChunk(EntitySubsystem, Context, [this](FMassExecutionContext& Context) + EntityQuery.ForEachEntityChunk(EntityManager, Context, [this](FMassExecutionContext& Context) { TArrayView MoveTargetFragments = Context.GetMutableFragmentView(); TConstArrayView TransformFragments = Context.GetFragmentView(); - TArrayView VelocityFragments = Context.GetMutableFragmentView(); TConstArrayView RTSFormationAgents = Context.GetFragmentView(); const FRTSFormationSettings& FormationSettings = Context.GetSharedFragment(); const FMassMovementParameters& MovementParameters = Context.GetConstSharedFragment(); - URTSFormationSubsystem* FormationSubsystem = UWorld::GetSubsystem(GetWorld()); + auto& FormationSubsystem = Context.GetMutableSubsystemChecked(); for (int32 EntityIndex = 0; EntityIndex < Context.GetNumEntities(); ++EntityIndex) { FMassMoveTargetFragment& MoveTarget = MoveTargetFragments[EntityIndex]; const FTransform& Transform = TransformFragments[EntityIndex].GetTransform(); - FVector& Velocity = VelocityFragments[EntityIndex].Value; + const FRTSFormationAgent& RTSFormationAgent = RTSFormationAgents[EntityIndex]; - const FUnitInfo& Unit = FormationSubsystem->Units[RTSFormationAgent.UnitIndex]; + const FUnitInfo& Unit = FormationSubsystem.Units[RTSFormationAgent.UnitIndex]; if(MoveTarget.GetCurrentAction() == EMassMovementAction::Stand) MoveTarget.CreateNewAction(EMassMovementAction::Move, *GetWorld()); @@ -207,7 +213,8 @@ void URTSAgentMovement::Execute(UMassEntitySubsystem& EntitySubsystem, FMassExec void URTSFormationUpdate::Initialize(UObject& Owner) { Super::Initialize(Owner); - SubscribeToSignal(FormationUpdated); + auto SignalSubsystem = UWorld::GetSubsystem(Owner.GetWorld()); + SubscribeToSignal(*SignalSubsystem, FormationUpdated); } void URTSFormationUpdate::ConfigureQueries() @@ -217,13 +224,14 @@ void URTSFormationUpdate::ConfigureQueries() EntityQuery.AddRequirement(EMassFragmentAccess::ReadOnly); EntityQuery.AddConstSharedRequirement(EMassFragmentPresence::All); EntityQuery.AddSharedRequirement(EMassFragmentAccess::ReadOnly); + EntityQuery.RegisterWithProcessor(*this); } -void URTSFormationUpdate::SignalEntities(UMassEntitySubsystem& EntitySubsystem, FMassExecutionContext& Context, +void URTSFormationUpdate::SignalEntities(FMassEntityManager& EntityManager, FMassExecutionContext& Context, FMassSignalNameLookup& EntitySignals) { // Query to calculate move target for entities based on unit index - EntityQuery.ParallelForEachEntityChunk(EntitySubsystem, Context, [this](FMassExecutionContext& Context) + EntityQuery.ForEachEntityChunk(EntityManager, Context, [this](FMassExecutionContext& Context) { TArrayView MoveTargetFragments = Context.GetMutableFragmentView(); TConstArrayView TransformFragments = Context.GetFragmentView(); @@ -253,23 +261,26 @@ void URTSFormationUpdate::SignalEntities(UMassEntitySubsystem& EntitySubsystem, void URTSUpdateEntityIndex::Initialize(UObject& Owner) { Super::Initialize(Owner); - SubscribeToSignal(UpdateIndex); - FormationSubsystem = UWorld::GetSubsystem(Owner.GetWorld()); + auto SignalSubsystem = UWorld::GetSubsystem(Owner.GetWorld()); + SubscribeToSignal(*SignalSubsystem, UpdateIndex); } void URTSUpdateEntityIndex::ConfigureQueries() { EntityQuery.AddRequirement(EMassFragmentAccess::ReadOnly); + EntityQuery.AddSubsystemRequirement(EMassFragmentAccess::ReadWrite); + EntityQuery.RegisterWithProcessor(*this); } -void URTSUpdateEntityIndex::SignalEntities(UMassEntitySubsystem& EntitySubsystem, FMassExecutionContext& Context, +void URTSUpdateEntityIndex::SignalEntities(FMassEntityManager& EntityManager, FMassExecutionContext& Context, FMassSignalNameLookup& EntitySignals) { // Update entity index so that they go to the closest possible position // Entities are signaled in order of distance to destination, this allows the NewPosition array to be sorted once // and cut down on iterations significantly - EntityQuery.ParallelForEachEntityChunk(EntitySubsystem, Context, [this](FMassExecutionContext& Context) + EntityQuery.ForEachEntityChunk(EntityManager, Context, [this](FMassExecutionContext& Context) { + auto& FormationSubsystem = Context.GetMutableSubsystemChecked(); TArrayView FormationAgents = Context.GetMutableFragmentView(); for (int32 EntityIndex = 0; EntityIndex < Context.GetNumEntities(); ++EntityIndex) @@ -280,31 +291,38 @@ void URTSUpdateEntityIndex::SignalEntities(UMassEntitySubsystem& EntitySubsystem TPair ClosestPos; float ClosestDistance = -1; int i=0; - for(const TPair& NewPos : FormationSubsystem->Units[FormationAgent.UnitIndex].NewPositions) + { - float Dist = FVector::DistSquared2D(NewPos.Value, FormationAgent.Offset); - if (ClosestDistance == -1 || Dist < ClosestDistance) + SCOPED_NAMED_EVENT(STAT_RTS_FindClosestPoint, FColor::Green); + for(const TPair& NewPos : FormationSubsystem.Units[FormationAgent.UnitIndex].NewPositions) { - ClosestPos = NewPos; - ClosestDistance = Dist; + float Dist = FVector::DistSquared2D(NewPos.Value, FormationAgent.Offset); + if (ClosestDistance == -1 || Dist < ClosestDistance) + { + ClosestPos = NewPos; + ClosestDistance = Dist; - // While its not perfect, this adds a hard cap to how many positions to check - //if (++i > FormationSubsystem->Units[FormationAgent.UnitIndex].FormationLength*2) - // break; + // While its not perfect, this adds a hard cap to how many positions to check + //if (++i > FormationSubsystem->Units[FormationAgent.UnitIndex].FormationLength*2) + // break; + } } } // Basically scoot up entities if there is space in the front int& Index = ClosestPos.Key; - - FormationAgent.EntityIndex = Index; - FormationAgent.Offset = ClosestPos.Value; - FormationSubsystem->Units[FormationAgent.UnitIndex].NewPositions.Remove(Index); + + { + SCOPED_NAMED_EVENT(STAT_RTS_RemoveClaimedPosition, FColor::Green); + FormationAgent.EntityIndex = Index; + FormationAgent.Offset = ClosestPos.Value; + FormationSubsystem.Units[FormationAgent.UnitIndex].NewPositions.Remove(Index); + } // Call subsystem function to get entities to move - if (FormationSubsystem->Units[FormationAgent.UnitIndex].NewPositions.Num() == 0) + if (FormationSubsystem.Units[FormationAgent.UnitIndex].NewPositions.Num() == 0) { - FormationSubsystem->MoveEntities(FormationAgent.UnitIndex); + FormationSubsystem.MoveEntities(FormationAgent.UnitIndex); } } }); diff --git a/Plugins/RTSFormations/Source/RTSFormations/Private/RTSFormationSubsystem.cpp b/Plugins/RTSFormations/Source/RTSFormations/Private/RTSFormationSubsystem.cpp index 5b94c786..16a3e5fb 100644 --- a/Plugins/RTSFormations/Source/RTSFormations/Private/RTSFormationSubsystem.cpp +++ b/Plugins/RTSFormations/Source/RTSFormations/Private/RTSFormationSubsystem.cpp @@ -22,14 +22,13 @@ void URTSFormationSubsystem::DestroyEntity(UMassAgentComponent* Entity) UMassEntitySubsystem* EntitySubsystem = GetWorld()->GetSubsystem(); check(EntitySubsystem); - EntitySubsystem->Defer().DestroyEntity(Entity->GetEntityHandle()); + EntitySubsystem->GetEntityManager().Defer().DestroyEntity(Entity->GetEntityHandle()); } void URTSFormationSubsystem::CalculateNewPositions(FUnitInfo& Unit, TMap& NewPositions) { // Empty NewPositions Map to make room for new calculations - NewPositions.Empty(); - NewPositions.Reserve(Unit.Entities.Num()); + NewPositions.Empty(Unit.Entities.Num()); // Calculate entity positions for new destination // This is the logic that can change formation types @@ -71,7 +70,7 @@ void URTSFormationSubsystem::CalculateNewPositions(FUnitInfo& Unit, TMapGetFragmentDataChecked(A).GetTransform().GetLocation(); - const FVector& LocB = EntitySubsystem->GetFragmentDataChecked(B).GetTransform().GetLocation(); + const FVector& LocA = EntitySubsystem->GetEntityManager().GetFragmentDataChecked(A).GetTransform().GetLocation(); + const FVector& LocB = EntitySubsystem->GetEntityManager().GetFragmentDataChecked(B).GetTransform().GetLocation(); return FVector::DistSquared2D(LocA, Unit.FarCorner+Unit.InterpolatedDestination) > FVector::DistSquared2D(LocB, Unit.FarCorner+Unit.InterpolatedDestination); }); @@ -122,7 +121,6 @@ void URTSFormationSubsystem::UpdateUnitPosition(const FVector& NewPosition, int }); // Signal entities to update their position index - FMassExecutionContext Context = EntitySubsystem->CreateExecutionContext(GetWorld()->GetDeltaSeconds()); if (Unit.Entities.Num()) { TArray Entities = Unit.Entities.Array(); @@ -132,6 +130,8 @@ void URTSFormationSubsystem::UpdateUnitPosition(const FVector& NewPosition, int void URTSFormationSubsystem::MoveEntities(int UnitIndex) { + SCOPED_NAMED_EVENT(STAT_RTS_MoveEntities, FColor::Green); + FUnitInfo& Unit = Units[UnitIndex]; UMassEntitySubsystem* EntitySubsystem = UWorld::GetSubsystem(GetWorld()); @@ -139,15 +139,14 @@ void URTSFormationSubsystem::MoveEntities(int UnitIndex) Unit.Entities.Sort([&EntitySubsystem, &Unit](const FMassEntityHandle& A, const FMassEntityHandle& B) { // Find if theres a way to move this logic to a processor, most of the cost is coming from retrieving the location - const FVector& LocA = EntitySubsystem->GetFragmentDataChecked(A).Offset; - const FVector& LocB = EntitySubsystem->GetFragmentDataChecked(B).Offset; + const FVector& LocA = EntitySubsystem->GetEntityManager().GetFragmentDataChecked(A).Offset; + const FVector& LocB = EntitySubsystem->GetEntityManager().GetFragmentDataChecked(B).Offset; return FVector::DistSquared2D(LocA, Unit.FarCorner) > FVector::DistSquared2D(LocB, Unit.FarCorner); }); CurrentIndex = 0; // Signal entities to begin moving - FMassExecutionContext Context = EntitySubsystem->CreateExecutionContext(GetWorld()->GetDeltaSeconds()); TArray Entities = Unit.Entities.Array(); for(int i=0;iGetSubsystem(); for(const FMassEntityHandle& Entity : Unit.Entities) { - if (EntitySubsystem->GetFragmentDataPtr(Entity)) + if (EntitySubsystem->GetEntityManager().GetFragmentDataPtr(Entity)) { - EntitySubsystem->GetFragmentDataPtr(Entity)->CreateNewAction(EMassMovementAction::Stand, *GetWorld()); - EntitySubsystem->GetFragmentDataPtr(Entity)->Value = FVector::Zero(); + EntitySubsystem->GetEntityManager().GetFragmentDataPtr(Entity)->CreateNewAction(EMassMovementAction::Stand, *GetWorld()); + EntitySubsystem->GetEntityManager().GetFragmentDataPtr(Entity)->Value = FVector::Zero(); } } @@ -199,25 +200,25 @@ void URTSFormationSubsystem::SpawnEntitiesForUnit(int UnitIndex, const UMassEnti Units[UnitIndex].Entities.Reserve(Units[UnitIndex].Entities.Num()+Count); TArray Entities; - const FMassEntityTemplate* EntityTemplate = EntityConfig->GetConfig().GetOrCreateEntityTemplate(*UGameplayStatics::GetPlayerPawn(this, 0), *EntityConfig); + auto& EntityTemplate = EntityConfig->GetConfig().GetOrCreateEntityTemplate(*UGameplayStatics::GetPlayerPawn(this, 0)->GetWorld()); // We are doing a little bit of work here since we are setting the unit index manually // Otherwise, using SpawnEntities would be perfectly fine // @todo find if there is a better way to modify templates in code TArray SpawnedEntities; - TSharedRef CreationContext = EntitySubsystem->BatchCreateEntities(EntityTemplate->GetArchetype(), Count, SpawnedEntities); + auto CreationContext = EntitySubsystem->GetMutableEntityManager().BatchCreateEntities(EntityTemplate.GetArchetype(), EntityTemplate.GetSharedFragmentValues(), Count, SpawnedEntities); // Set the template default values for the entities - TConstArrayView FragmentInstances = EntityTemplate->GetInitialFragmentValues(); - EntitySubsystem->BatchSetEntityFragmentsValues(CreationContext->GetChunkCollection(), FragmentInstances); + TConstArrayView FragmentInstances = EntityTemplate.GetInitialFragmentValues(); + EntitySubsystem->GetEntityManager().BatchSetEntityFragmentsValues(CreationContext->GetEntityCollection(), FragmentInstances); // Set unit index for entities FRTSFormationAgent FormationAgent; FormationAgent.UnitIndex = UnitIndex; TArray Fragments; - Fragments.Add(FConstStructView::Make(FormationAgent)); - EntitySubsystem->BatchSetEntityFragmentsValues(CreationContext->GetChunkCollection(), Fragments); + Fragments.Add(FInstancedStruct::Make(FormationAgent)); + EntitySubsystem->GetEntityManager().BatchSetEntityFragmentsValues(CreationContext->GetEntityCollection(), Fragments); } int URTSFormationSubsystem::SpawnNewUnit(const UMassEntityConfigAsset* EntityConfig, int Count, const FVector& Position) diff --git a/Plugins/RTSFormations/Source/RTSFormations/Public/LaunchEntityProcessor.h b/Plugins/RTSFormations/Source/RTSFormations/Public/LaunchEntityProcessor.h index 4683d077..14aea34b 100644 --- a/Plugins/RTSFormations/Source/RTSFormations/Public/LaunchEntityProcessor.h +++ b/Plugins/RTSFormations/Source/RTSFormations/Public/LaunchEntityProcessor.h @@ -18,7 +18,7 @@ class RTSFORMATIONS_API ULaunchEntityProcessor : public UMassSignalProcessorBase virtual void ConfigureQueries() override; virtual void Initialize(UObject& Owner) override; - virtual void SignalEntities(UMassEntitySubsystem& EntitySubsystem, FMassExecutionContext& Context, FMassSignalNameLookup& EntitySignals) override; + virtual void SignalEntities(FMassEntityManager& EntityManager, FMassExecutionContext& Context, FMassSignalNameLookup& EntitySignals) override; TObjectPtr SignalSubsystem; TObjectPtr FormationSubsystem; @@ -31,7 +31,7 @@ class RTSFORMATIONS_API UMoveForceProcessor : public UMassProcessor GENERATED_BODY() virtual void ConfigureQueries() override; - virtual void Execute(UMassEntitySubsystem& EntitySubsystem, FMassExecutionContext& Context) override; + virtual void Execute(FMassEntityManager& EntityManager, FMassExecutionContext& Context) override; FMassEntityQuery EntityQuery; }; diff --git a/Plugins/RTSFormations/Source/RTSFormations/Public/RTSAgentProcessors.h b/Plugins/RTSFormations/Source/RTSFormations/Public/RTSAgentProcessors.h index a920601e..3238dd3e 100644 --- a/Plugins/RTSFormations/Source/RTSFormations/Public/RTSAgentProcessors.h +++ b/Plugins/RTSFormations/Source/RTSFormations/Public/RTSAgentProcessors.h @@ -7,6 +7,13 @@ #include "MassProcessor.h" #include "RTSAgentProcessors.generated.h" +// Defines that an entity is managed by the RTSHashGrid +USTRUCT() +struct FRTSAgentHashTag : public FMassTag +{ + GENERATED_BODY(); +}; + // Update hash grid position of entities UCLASS() class RTSFORMATIONS_API URTSUpdateHashPosition : public UMassProcessor @@ -14,11 +21,10 @@ class RTSFORMATIONS_API URTSUpdateHashPosition : public UMassProcessor GENERATED_BODY() virtual void ConfigureQueries() override; - virtual void Execute(UMassEntitySubsystem& EntitySubsystem, FMassExecutionContext& Context) override; + virtual void Execute(FMassEntityManager& EntityManager, FMassExecutionContext& Context) override; virtual void Initialize(UObject& Owner) override; FMassEntityQuery EntityQuery; - TObjectPtr AgentSubsystem; }; // Initialize the hash grid position of RTS agents @@ -29,11 +35,10 @@ class RTSFORMATIONS_API URTSInitializeHashPosition : public UMassObserverProcess URTSInitializeHashPosition(); virtual void ConfigureQueries() override; - virtual void Execute(UMassEntitySubsystem& EntitySubsystem, FMassExecutionContext& Context) override; + virtual void Execute(FMassEntityManager& EntityManager, FMassExecutionContext& Context) override; virtual void Initialize(UObject& Owner) override; FMassEntityQuery EntityQuery; - TObjectPtr AgentSubsystem; }; // Remove the entities from the hash grid when destroyed/no longer an RTSAgent @@ -44,9 +49,8 @@ class RTSFORMATIONS_API URTSRemoveHashPosition : public UMassObserverProcessor URTSRemoveHashPosition(); virtual void ConfigureQueries() override; - virtual void Execute(UMassEntitySubsystem& EntitySubsystem, FMassExecutionContext& Context) override; + virtual void Execute(FMassEntityManager& EntityManager, FMassExecutionContext& Context) override; virtual void Initialize(UObject& Owner) override; FMassEntityQuery EntityQuery; - TObjectPtr AgentSubsystem; }; diff --git a/Plugins/RTSFormations/Source/RTSFormations/Public/RTSAgentSubsystem.h b/Plugins/RTSFormations/Source/RTSFormations/Public/RTSAgentSubsystem.h index c058c92f..2d148a11 100644 --- a/Plugins/RTSFormations/Source/RTSFormations/Public/RTSAgentSubsystem.h +++ b/Plugins/RTSFormations/Source/RTSFormations/Public/RTSAgentSubsystem.h @@ -25,3 +25,12 @@ class RTSFORMATIONS_API URTSAgentSubsystem : public UWorldSubsystem UFUNCTION(BlueprintCallable, BlueprintPure = false) void LaunchEntities(const FVector& Location, float Radius) const; }; + +template<> +struct TMassExternalSubsystemTraits +{ + enum + { + GameThreadOnly = false + }; +}; diff --git a/Plugins/RTSFormations/Source/RTSFormations/Public/RTSAgentTraits.h b/Plugins/RTSFormations/Source/RTSFormations/Public/RTSAgentTraits.h index 555fedcd..1a9175a7 100644 --- a/Plugins/RTSFormations/Source/RTSFormations/Public/RTSAgentTraits.h +++ b/Plugins/RTSFormations/Source/RTSFormations/Public/RTSAgentTraits.h @@ -46,7 +46,7 @@ class RTSFORMATIONS_API URTSFormationAgentTrait : public UMassEntityTraitBase { GENERATED_BODY() - virtual void BuildTemplate(FMassEntityTemplateBuildContext& BuildContext, UWorld& World) const override; + virtual void BuildTemplate(FMassEntityTemplateBuildContext& BuildContext, const UWorld& World) const override; UPROPERTY(EditAnywhere) FRTSFormationSettings FormationSettings; diff --git a/Plugins/RTSFormations/Source/RTSFormations/Public/RTSFormationProcessors.h b/Plugins/RTSFormations/Source/RTSFormations/Public/RTSFormationProcessors.h index 731aa308..37407f99 100644 --- a/Plugins/RTSFormations/Source/RTSFormations/Public/RTSFormationProcessors.h +++ b/Plugins/RTSFormations/Source/RTSFormations/Public/RTSFormationProcessors.h @@ -19,7 +19,7 @@ class RTSFORMATIONS_API URTSFormationInitializer : public UMassObserverProcessor URTSFormationInitializer(); virtual void ConfigureQueries() override; virtual void Initialize(UObject& Owner) override; - virtual void Execute(UMassEntitySubsystem& EntitySubsystem, FMassExecutionContext& Context) override; + virtual void Execute(FMassEntityManager& EntityManager, FMassExecutionContext& Context) override; FMassEntityQuery EntityQuery; @@ -36,12 +36,9 @@ class RTSFORMATIONS_API URTSFormationDestroyer : public UMassObserverProcessor URTSFormationDestroyer(); virtual void ConfigureQueries() override; virtual void Initialize(UObject& Owner) override; - virtual void Execute(UMassEntitySubsystem& EntitySubsystem, FMassExecutionContext& Context) override; + virtual void Execute(FMassEntityManager& EntityManager, FMassExecutionContext& Context) override; FMassEntityQuery EntityQuery; - - TObjectPtr SignalSubsystem; - TObjectPtr FormationSubsystem; }; @@ -52,7 +49,7 @@ class RTSFORMATIONS_API URTSAgentMovement : public UMassProcessor GENERATED_BODY() virtual void ConfigureQueries() override; - virtual void Execute(UMassEntitySubsystem& EntitySubsystem, FMassExecutionContext& Context) override; + virtual void Execute(FMassEntityManager& EntityManager, FMassExecutionContext& Context) override; FMassEntityQuery EntityQuery; @@ -67,7 +64,7 @@ class RTSFORMATIONS_API URTSFormationUpdate : public UMassSignalProcessorBase virtual void Initialize(UObject& Owner) override; virtual void ConfigureQueries() override; - virtual void SignalEntities(UMassEntitySubsystem& EntitySubsystem, FMassExecutionContext& Context, FMassSignalNameLookup& EntitySignals) override; + virtual void SignalEntities(FMassEntityManager& EntityManager, FMassExecutionContext& Context, FMassSignalNameLookup& EntitySignals) override; TObjectPtr FormationSubsystem; }; @@ -80,7 +77,7 @@ class RTSFORMATIONS_API URTSUpdateEntityIndex : public UMassSignalProcessorBase virtual void Initialize(UObject& Owner) override; virtual void ConfigureQueries() override; - virtual void SignalEntities(UMassEntitySubsystem& EntitySubsystem, FMassExecutionContext& Context, FMassSignalNameLookup& EntitySignals) override; + virtual void SignalEntities(FMassEntityManager& EntityManager, FMassExecutionContext& Context, FMassSignalNameLookup& EntitySignals) override; TObjectPtr FormationSubsystem; }; diff --git a/Plugins/RTSFormations/Source/RTSFormations/Public/RTSFormationSubsystem.h b/Plugins/RTSFormations/Source/RTSFormations/Public/RTSFormationSubsystem.h index 53463eba..3e86a9b2 100644 --- a/Plugins/RTSFormations/Source/RTSFormations/Public/RTSFormationSubsystem.h +++ b/Plugins/RTSFormations/Source/RTSFormations/Public/RTSFormationSubsystem.h @@ -4,6 +4,7 @@ #include "CoreMinimal.h" #include "FormationPresets.h" +#include "MassSubsystemBase.h" #include "Common/Misc/MSBPFunctionLibrary.h" #include "RTSFormationSubsystem.generated.h" @@ -76,11 +77,12 @@ struct FUnitInfo class UMassAgentComponent; struct FMassEntityHandle; + /** * Subsystem that handles the bulk of data shared among entities for the formation system. Enables simple unit creation and entity spawning */ UCLASS() -class RTSFORMATIONS_API URTSFormationSubsystem : public UTickableWorldSubsystem +class RTSFORMATIONS_API URTSFormationSubsystem : public UMassTickableSubsystemBase { GENERATED_BODY() @@ -127,3 +129,12 @@ class RTSFORMATIONS_API URTSFormationSubsystem : public UTickableWorldSubsystem int CurrentIndex = 0; }; + +template<> +struct TMassExternalSubsystemTraits +{ + enum + { + GameThreadOnly = false + }; +}; \ No newline at end of file From 4688504d96295b65a98c67eb346e27eaf1d6a3b8 Mon Sep 17 00:00:00 2001 From: JiRath Date: Wed, 21 Aug 2024 21:05:36 -0500 Subject: [PATCH 36/37] Update how subsystems are retrieved so they are more inline with native methods --- .../Private/RTSFormationProcessors.cpp | 19 ++++++++++--------- .../Public/RTSFormationProcessors.h | 7 ------- 2 files changed, 10 insertions(+), 16 deletions(-) diff --git a/Plugins/RTSFormations/Source/RTSFormations/Private/RTSFormationProcessors.cpp b/Plugins/RTSFormations/Source/RTSFormations/Private/RTSFormationProcessors.cpp index a79d1c8c..b99e0f5f 100644 --- a/Plugins/RTSFormations/Source/RTSFormations/Private/RTSFormationProcessors.cpp +++ b/Plugins/RTSFormations/Source/RTSFormations/Private/RTSFormationProcessors.cpp @@ -23,15 +23,14 @@ URTSFormationInitializer::URTSFormationInitializer() void URTSFormationInitializer::ConfigureQueries() { EntityQuery.AddRequirement(EMassFragmentAccess::ReadWrite); + EntityQuery.AddSubsystemRequirement(EMassFragmentAccess::ReadWrite); + EntityQuery.AddSubsystemRequirement(EMassFragmentAccess::ReadWrite); EntityQuery.RegisterWithProcessor(*this); } void URTSFormationInitializer::Initialize(UObject& Owner) { Super::Initialize(Owner); - - SignalSubsystem = UWorld::GetSubsystem(Owner.GetWorld()); - FormationSubsystem = UWorld::GetSubsystem(Owner.GetWorld()); } void URTSFormationInitializer::Execute(FMassEntityManager& EntityManager, FMassExecutionContext& Context) @@ -40,10 +39,12 @@ void URTSFormationInitializer::Execute(FMassEntityManager& EntityManager, FMassE EntityQuery.ForEachEntityChunk(EntityManager, Context, [this](FMassExecutionContext& Context) { TArrayView RTSFormationAgents = Context.GetMutableFragmentView(); + auto& SignalSubsystem = Context.GetMutableSubsystemChecked(); + auto& FormationSubsystem = Context.GetMutableSubsystemChecked(); // Signal affected units/entities at the end TArray UnitSignals; - UnitSignals.Reserve(FormationSubsystem->Units.Num()); + UnitSignals.Reserve(FormationSubsystem.Units.Num()); // Since we can have multiple units, reserving is only done in the formation subsystem // This is because it might be possible that a batch of spawned entities should go to different units @@ -53,18 +54,18 @@ void URTSFormationInitializer::Execute(FMassEntityManager& EntityManager, FMassE // If for some reason the unit hasnt been created, we should create it now // Unfortunately, with the nature of an array, this might cause a crash if the unit index is not next in line, need to handle this somehow - if (!FormationSubsystem->Units.IsValidIndex(RTSFormationAgent.UnitIndex)) - FormationSubsystem->Units.AddDefaulted(1); + if (!FormationSubsystem.Units.IsValidIndex(RTSFormationAgent.UnitIndex)) + FormationSubsystem.Units.AddDefaulted(1); - RTSFormationAgent.EntityIndex = FormationSubsystem->Units[RTSFormationAgent.UnitIndex].Entities.Num(); - FormationSubsystem->Units[RTSFormationAgent.UnitIndex].Entities.Emplace(Context.GetEntity(EntityIndex)); + RTSFormationAgent.EntityIndex = FormationSubsystem.Units[RTSFormationAgent.UnitIndex].Entities.Num(); + FormationSubsystem.Units[RTSFormationAgent.UnitIndex].Entities.Emplace(Context.GetEntity(EntityIndex)); UnitSignals.AddUnique(RTSFormationAgent.UnitIndex); } // Signal entities in the unit that their position is updated // @todo only notify affected entities for(const int& Unit : UnitSignals) - FormationSubsystem->SetUnitPosition(FormationSubsystem->Units[Unit].UnitPosition, Unit); + FormationSubsystem.SetUnitPosition(FormationSubsystem.Units[Unit].UnitPosition, Unit); }); } diff --git a/Plugins/RTSFormations/Source/RTSFormations/Public/RTSFormationProcessors.h b/Plugins/RTSFormations/Source/RTSFormations/Public/RTSFormationProcessors.h index 37407f99..e941d31a 100644 --- a/Plugins/RTSFormations/Source/RTSFormations/Public/RTSFormationProcessors.h +++ b/Plugins/RTSFormations/Source/RTSFormations/Public/RTSFormationProcessors.h @@ -22,9 +22,6 @@ class RTSFORMATIONS_API URTSFormationInitializer : public UMassObserverProcessor virtual void Execute(FMassEntityManager& EntityManager, FMassExecutionContext& Context) override; FMassEntityQuery EntityQuery; - - TObjectPtr SignalSubsystem; - TObjectPtr FormationSubsystem; }; // Observer that runs when an entity is destroyed. Cleans up the unit array and tells the last unit to take their place @@ -65,8 +62,6 @@ class RTSFORMATIONS_API URTSFormationUpdate : public UMassSignalProcessorBase virtual void Initialize(UObject& Owner) override; virtual void ConfigureQueries() override; virtual void SignalEntities(FMassEntityManager& EntityManager, FMassExecutionContext& Context, FMassSignalNameLookup& EntitySignals) override; - - TObjectPtr FormationSubsystem; }; // Signal Processor that updates the agents index in the unit based on values in the FormationSubsystem Unit Array @@ -78,6 +73,4 @@ class RTSFORMATIONS_API URTSUpdateEntityIndex : public UMassSignalProcessorBase virtual void Initialize(UObject& Owner) override; virtual void ConfigureQueries() override; virtual void SignalEntities(FMassEntityManager& EntityManager, FMassExecutionContext& Context, FMassSignalNameLookup& EntitySignals) override; - - TObjectPtr FormationSubsystem; }; From fc1332b1fcbcacbdd9631411588e576505030106 Mon Sep 17 00:00:00 2001 From: JiRath Date: Wed, 21 Aug 2024 21:06:01 -0500 Subject: [PATCH 37/37] Push RTSGameMode to support simple top down view --- Plugins/RTSFormations/Content/BP_RTSGameMode.uasset | 3 +++ 1 file changed, 3 insertions(+) create mode 100644 Plugins/RTSFormations/Content/BP_RTSGameMode.uasset diff --git a/Plugins/RTSFormations/Content/BP_RTSGameMode.uasset b/Plugins/RTSFormations/Content/BP_RTSGameMode.uasset new file mode 100644 index 00000000..7214f221 --- /dev/null +++ b/Plugins/RTSFormations/Content/BP_RTSGameMode.uasset @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:449fe40fdb052f4ff882939de401fa425428019e0670343e4330c23974bd126d +size 20713