Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
47 commits
Select commit Hold shift + click to select a range
db88f61
Additional clarification of a processor
stefan-zimecki Jun 2, 2022
c4f3a94
Mass Per Instance Material Data Demo & Sample
PreyK Jul 3, 2022
766f459
Merge pull request #32 from stefan-zimecki/patch-1
Megafunk Jul 5, 2022
41d160f
Merge pull request #36 from PreyK/main
Megafunk Jul 5, 2022
2ecf087
More specific blueprint log
Megafunk Jul 5, 2022
cfe7237
Add RTSFormation Plugin
Ji-Rath Jul 8, 2022
4f78e83
Add Stand action when reaching formation destination. (it seems like …
Ji-Rath Jul 8, 2022
135bbd1
Begin implementing ratio-based formation shapes
Ji-Rath Jul 8, 2022
a7486d6
Add additional dependency to build.cs
Ji-Rath Jul 8, 2022
b70596f
Add RTSFormationProcessors for processor handling
Ji-Rath Jul 10, 2022
6129cb5
Add plugin dependencies
Ji-Rath Jul 10, 2022
2081403
Add RTSFormationSubsystem for handling entity destruction and unit array
Ji-Rath Jul 10, 2022
126a4dd
Add mass dependencies for C++
Ji-Rath Jul 10, 2022
7622832
Remove debug message from DestroyEntity
Ji-Rath Jul 10, 2022
73e6ae3
Update Agent data asset
Ji-Rath Jul 10, 2022
3c5ecfe
Add BP_FormationUnit
Ji-Rath Jul 11, 2022
2cd193a
Update DataAsset and example map
Ji-Rath Jul 11, 2022
9d26b36
Update RTSFormationSubsystem.cpp
Ji-Rath Jul 11, 2022
3727dbb
Create BP_UpdateFormation.uasset
Ji-Rath Jul 11, 2022
a7bf8d3
Cache unit index in fragment
Ji-Rath Jul 11, 2022
6e4111d
Comment out destruction handling in subsystem since it should be hand…
Ji-Rath Jul 11, 2022
4de0e3c
Clean up comments
Ji-Rath Jul 11, 2022
4921c5c
Remove material from actor visualization
Ji-Rath Jul 11, 2022
7fad122
Remove debug visualization trait
Ji-Rath Jul 11, 2022
9dff30b
Update from main repo
Ji-Rath Jul 11, 2022
846830d
Add UnitPosition to RTSFormationSubsystem so that a unit can move to …
Ji-Rath Jul 11, 2022
b5bd4fe
Fix dependency since new mass sample files are contained in a plugin
Ji-Rath Jul 11, 2022
efc2e95
Implement UnitPosition to URTSFormationUpdate
Ji-Rath Jul 11, 2022
2c5bc74
Fix invalid interface in BP_FormationUnit.uasset
Ji-Rath Jul 11, 2022
33b4533
Add BP_FormationGameMode for automatic unit movement
Ji-Rath Jul 11, 2022
9423eaf
Update RTSFormationsExample.umap to use new game mode
Ji-Rath Jul 11, 2022
de2661a
Optimized signaling entities during initialization so that they dont …
Ji-Rath Jul 13, 2022
4e7704a
Implement multiple units
Ji-Rath Jul 13, 2022
1b1e15a
Continue implementing multiple units
Ji-Rath Jul 13, 2022
a900b99
Finally implement proper unit rotation
Ji-Rath Jul 14, 2022
8354253
Cleanup RTSFormationSubsystem with new rotation system
Ji-Rath Jul 14, 2022
9dfd0f7
Change game mode so it moves all current units in the world
Ji-Rath Jul 14, 2022
6f09afb
Add BP_SpawnEntity and BP_SpawnUnit primarily for debugging
Ji-Rath Jul 14, 2022
e21b12b
Rename BP_UpdateFormation.uasset
Ji-Rath Jul 14, 2022
01f4c48
Add additional comments
Ji-Rath Jul 14, 2022
fefd767
Add comments to RTSFormationSubsystem
Ji-Rath Jul 14, 2022
5ca9199
Re-add avoidance to DA_AgentFormation
Ji-Rath Jul 14, 2022
f593b8c
Add MassCommunitySample as dependency
Ji-Rath Jul 14, 2022
e81f29e
Move RTSFormationsPlugin to new branch
Ji-Rath Jul 14, 2022
17a0ded
Add LODExamplePlugin
Ji-Rath Jul 15, 2022
aea9c0e
Increase distance on spawned LOD entities
Ji-Rath Jul 16, 2022
f8ef1b2
Change execution order of example processor
Ji-Rath Jul 16, 2022
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions Content/MassSample/Maps/ISMPerInstanceData.umap
Git LFS file not shown
3 changes: 3 additions & 0 deletions Plugins/LODExample/Content/BP_ActorVisualization.uasset
Git LFS file not shown
3 changes: 3 additions & 0 deletions Plugins/LODExample/Content/DA_LODExample.uasset
Git LFS file not shown
3 changes: 3 additions & 0 deletions Plugins/LODExample/Content/EQ_LODExample.uasset
Git LFS file not shown
3 changes: 3 additions & 0 deletions Plugins/LODExample/Content/LODExampleMap.umap
Git LFS file not shown
24 changes: 24 additions & 0 deletions Plugins/LODExample/LODExample.uplugin
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
{
"FileVersion": 3,
"Version": 1,
"VersionName": "1.0",
"FriendlyName": "LODExample",
"Description": "Simple example showcasing LODs for Visualization and Processor logic",
"Category": "Other",
"CreatedBy": "JiRath",
"CreatedByURL": "https://github.com/Ji-Rath",
"DocsURL": "",
"MarketplaceURL": "",
"SupportURL": "",
"CanContainContent": true,
"IsBetaVersion": false,
"IsExperimentalVersion": false,
"Installed": false,
"Modules": [
{
"Name": "LODExample",
"Type": "Runtime",
"LoadingPhase": "Default"
}
]
}
3 changes: 3 additions & 0 deletions Plugins/LODExample/Resources/Icon128.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
53 changes: 53 additions & 0 deletions Plugins/LODExample/Source/LODExample/LODExample.Build.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
// Copyright Epic Games, Inc. All Rights Reserved.

using UnrealBuildTool;

public class LODExample : ModuleRules
{
public LODExample(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", "MassLOD", "MassEntity", "MassCommon"
// ... add other public dependencies that you statically link with here ...
}
);


PrivateDependencyModuleNames.AddRange(
new string[]
{
"CoreUObject",
"Engine",
"Slate",
"SlateCore",
// ... add private dependencies that you statically link with here ...
}
);


DynamicallyLoadedModuleNames.AddRange(
new string[]
{
// ... add any modules that your module loads dynamically here ...
}
);
}
}
20 changes: 20 additions & 0 deletions Plugins/LODExample/Source/LODExample/Private/LODExample.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
// Copyright Epic Games, Inc. All Rights Reserved.

#include "LODExample.h"

#define LOCTEXT_NAMESPACE "FLODExampleModule"

void FLODExampleModule::StartupModule()
{
// This code will execute after your module is loaded into memory; the exact timing is specified in the .uplugin file per-module
}

void FLODExampleModule::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(FLODExampleModule, LODExample)
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
// Fill out your copyright notice in the Description page of Project Settings.


#include "LODExampleProcessors.h"

#include "MassCommonFragments.h"
#include "MassLODFragments.h"
#include "MassSimulationLOD.h"

ULODCollectorExampleProcessor::ULODCollectorExampleProcessor()
{
bAutoRegisterWithProcessingPhases = true;
}

ULODExampleProcessor::ULODExampleProcessor()
{
ExecutionOrder.ExecuteInGroup = UE::Mass::ProcessorGroupNames::Tasks;
ExecutionOrder.ExecuteAfter.Add(UE::Mass::ProcessorGroupNames::LOD);
}

void ULODExampleProcessor::Initialize(UObject& Owner)
{
Super::Initialize(Owner);
}

void ULODExampleProcessor::ConfigureQueries()
{
EntityQueryBase.AddRequirement<FTransformFragment>(EMassFragmentAccess::ReadOnly);

// Dont perform logic if outside view
// @note CulledByFrustumTag appears to only be added when not affected by LOD Max Count
EntityQueryBase.AddTagRequirement<FMassVisibilityCulledByFrustumTag>(EMassFragmentPresence::None);

//Chunk fragments to tick at specified intervals
EntityQueryBase.AddChunkRequirement<FMassSimulationVariableTickChunkFragment>(EMassFragmentAccess::ReadOnly, EMassFragmentPresence::Optional);
EntityQueryBase.SetChunkFilter(&FMassSimulationVariableTickChunkFragment::ShouldTickChunkThisFrame);

// Add base queries to high/med/low entity queries
EntityQuery_High = EntityQueryBase;
EntityQuery_Medium = EntityQueryBase;
EntityQuery_Low = EntityQueryBase;

EntityQuery_High.AddTagRequirement<FMassHighLODTag>(EMassFragmentPresence::All); // Query for high LOD
EntityQuery_Medium.AddTagRequirement<FMassMediumLODTag>(EMassFragmentPresence::All); // Query for medium LOD
EntityQuery_Low.AddTagRequirement<FMassLowLODTag>(EMassFragmentPresence::All); // Query for low
}

void ULODExampleProcessor::Execute(UMassEntitySubsystem& EntitySubsystem, FMassExecutionContext& Context)
{
// High LOD logic
EntityQuery_High.ParallelForEachEntityChunk(EntitySubsystem, Context, [this](const FMassExecutionContext& Context)
{
TConstArrayView<FTransformFragment> Transforms = Context.GetFragmentView<FTransformFragment>();
for (int EntityIdx = 0; EntityIdx < Context.GetNumEntities(); EntityIdx++)
{
const FVector& Position = Transforms[EntityIdx].GetTransform().GetLocation();
DrawDebugPoint(GetWorld(), Position+(FVector::UpVector*500.f), 20.f, FColor::Green, false, 0.25f);
}
});

// Med LOD logic
EntityQuery_Medium.ParallelForEachEntityChunk(EntitySubsystem, Context, [this](const FMassExecutionContext& Context)
{
TConstArrayView<FTransformFragment> Transforms = Context.GetFragmentView<FTransformFragment>();
for (int EntityIdx = 0; EntityIdx < Context.GetNumEntities(); EntityIdx++)
{
const FVector& Position = Transforms[EntityIdx].GetTransform().GetLocation();
DrawDebugPoint(GetWorld(), Position+(FVector::UpVector*500.f), 20.f, FColor::Yellow, false, 0.25f);
}
});

// Low LOD logic
EntityQuery_Low.ParallelForEachEntityChunk(EntitySubsystem, Context, [this](const FMassExecutionContext& Context)
{
TConstArrayView<FTransformFragment> Transforms = Context.GetFragmentView<FTransformFragment>();
for (int EntityIdx = 0; EntityIdx < Context.GetNumEntities(); EntityIdx++)
{
const FVector& Position = Transforms[EntityIdx].GetTransform().GetLocation();
DrawDebugPoint(GetWorld(), Position+(FVector::UpVector*500.f), 20.f, FColor::Red, false, 0.25f);
}
});
}
15 changes: 15 additions & 0 deletions Plugins/LODExample/Source/LODExample/Public/LODExample.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
// Copyright Epic Games, Inc. All Rights Reserved.

#pragma once

#include "CoreMinimal.h"
#include "Modules/ModuleManager.h"

class FLODExampleModule : public IModuleInterface
{
public:

/** IModuleInterface implementation */
virtual void StartupModule() override;
virtual void ShutdownModule() override;
};
38 changes: 38 additions & 0 deletions Plugins/LODExample/Source/LODExample/Public/LODExampleProcessors.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
// Fill out your copyright notice in the Description page of Project Settings.

#pragma once

#include "CoreMinimal.h"
#include "MassLODCollectorProcessor.h"
#include "LODExampleProcessors.generated.h"

//MassSample already includes MS

/**
* Simple class to enable LODCollection for entities
* @see UMassCrowdLODCollectorProcessor
*/
UCLASS()
class LODEXAMPLE_API ULODCollectorExampleProcessor : public UMassLODCollectorProcessor
{
GENERATED_BODY()

ULODCollectorExampleProcessor();
};

UCLASS()
class LODEXAMPLE_API ULODExampleProcessor : public UMassProcessor
{
GENERATED_BODY()

ULODExampleProcessor();

virtual void Initialize(UObject& Owner) override;
virtual void ConfigureQueries() override;
virtual void Execute(UMassEntitySubsystem& EntitySubsystem, FMassExecutionContext& Context) override;

FMassEntityQuery EntityQueryBase;
FMassEntityQuery EntityQuery_High;
FMassEntityQuery EntityQuery_Medium;
FMassEntityQuery EntityQuery_Low;
};
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,12 @@ struct MASSCOMMUNITYSAMPLE_API FSampleColorFragment : public FMassFragment
FColor Color = FColor::Red;
};

USTRUCT()
struct FISMPerInstanceDataFragment : public FMassFragment
{
GENERATED_BODY()
float data = 0;
};

USTRUCT()
struct MASSCOMMUNITYSAMPLE_API FInterpLocationFragment : public FMassFragment
Expand Down
46 changes: 29 additions & 17 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -61,8 +61,11 @@ After installing the requirements from above, follow these steps:
> 4.8 [Observers](#mass-o)
> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;4.8.1 [Observers limitations](#mass-o-n)
> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;4.8.2 [Observing multiple Fragment/Tags](#mass-o-mft)
> 4.10 [Mulitthreading](#mass-mt)
> 5. [Mass common operations](#mass-cm)
> 4.10 [Multithreading](#mass-mt)
> 5. [Common Mass operations](#mass-cm)
> 5.1 [Spawning entities](#mass-cm-spae)
> 5.2 [Destroying entities](#mass-cm-dsae)
> 5.3 [Operating Entities](#mass-cm-opee)
> 6. [Mass Plugins and Modules](#mass-pm)
> 6.1 [MassEntity](#mass-pm-me)
> 6.2 [MassGameplay](#mass-pm-gp)
Expand Down Expand Up @@ -99,7 +102,7 @@ In Mass, some ECS terminology differs from the norm in order to not get confused
Typical Unreal Engine game code is expressed as actor objects that inherit from parent classes to change their data and functionality based on what they ***are***.
In an ECS, an entity is only composed of fragments that get manipulated by processors based on which ECS components they ***have***.

An entity is really just a small unique identifier that points to some fragments. A Processor defines a query that filters only for entities that have specific fragments. For example, a basic "movement" Processor could query for entities that have a transform and velocity component to add the velocity to their current transform position.
An entity is really just a small unique identifier that points to some fragments. A Processor defines a query that filters only for entities that have specific fragments, and then performs an operation on those fragments. For example, a basic "movement" Processor could query for entities that have a transform and velocity component to add the velocity to their current transform position.

Fragments are stored in memory as tightly packed arrays of other identical fragment arrangements called archetypes. Because of this, the aforementioned movement processor can be incredibly high performance because it does a simple operation on a small amount of data all at once. New functionality can easily be added by creating new fragments and processors.

Expand Down Expand Up @@ -136,6 +139,8 @@ Currently, the sample features the following:
### 4.1 Entities
Small unique identifiers that point to a combination of [fragments](#mass-fragments) and [tags](#mass-tags) in memory. Entities are mainly a simple integer ID. For example, entity 103 might point to a single projectile with transform, velocity, and damage data.

<!-- TODO: Document the different ways in which we can identify an entity in mass and their purpose? FMassEntityHandle, FMassEntity, FMassEntityView?? -->

<a name="mass-fragments"></a>
### 4.2 Fragments
Data-only `UScriptStructs` that entities can own and processors can query on. To create a fragment, inherit from [`FMassFragment`](https://docs.unrealengine.com/5.0/en-US/API/Plugins/MassEntity/FMassFragment/).
Expand Down Expand Up @@ -451,7 +456,7 @@ MyQuery.ForEachEntityChunk(EntitySubsystem, Context, [](FMassExecutionContext& C
}
});
```
<!-- REVIEWMEVORI: Maybe move to mass common operations!! Spawning/Destroying subsections, although I think that wouldn't hurt having this here, and then referencing it back in the common mass operation section -->
<!-- REVIEWMEVORI: Maybe move to common Mass operations!! Spawning/Destroying subsections, although I think that wouldn't hurt having this here, and then referencing it back in the common mass operation section -->
<a name="mass-queries-mq"></a>
#### 4.6.3 Mutating entities with `Defer()`

Expand Down Expand Up @@ -869,7 +874,7 @@ Out of the box Mass can spread out work to threads in two different ways:


<a name="mass-cm"></a>
## 5. Mass common operations
## 5. Common Mass operations
This section is designed to serve as a quick reference for how to perform common operations with Mass. As usual, we are open to ideas on how to organize this stuff!!

As a rule of thumb, most entity mutations (adding/removing components, spawning or removing entities) are generally done by deferring them from inside of processors.
Expand All @@ -881,7 +886,7 @@ As a rule of thumb, most entity mutations (adding/removing components, spawning

<!--FIXMEFUNK: When does changing values require deferrment if ever? need more concurrency info for that-->

<a name="mass-cm-sae"></a>
<a name="mass-cm-spae"></a>
## 5.1 Spawning entities

In this Section we are going to review different methods to spawn entities. First, we review the `Mass Spawner`, which is useful to spawn entities with predefined data. Then, we'll move to more complex spawning methods that enable us to have fine grained control over the spawning.
Expand Down Expand Up @@ -954,29 +959,35 @@ Currently, my best guess is to use `FBuildEntityFromFragmentInstances` and then
It is very important to remember that Observers are only triggered explicitely in certain functions out of the box. [Check out the list here.](#mass-o-n)


<!-- <a name="mass-cm-dae"></a>
## 5.2 Destroying an entity
<a name="mass-cm-dsae"></a>
## 5.2 Destroying entities

Destroying an entity is rather straightfoward -->
[TODO]

<!-- #### Deferred -->

<!-- #### Direct Call -->

<a name="mass-cm-oe"></a>
## 5.2 Outside Entities
<a name="mass-cm-opee"></a>
## 5.3 Operating Entities

In this Section we are going to explore the most relevant tools Mass offers to operate Entities. This covers all the get and set operations and structures to work with them (fragment, archetype, tags...).

In cases where we need to access entities outside of the current processing context (e.g. avoiding another crowd entity that this entity is close to) one can call all of the regular Mass Subsystem functions or deferred actions on them. This is not ideal for cache coherency but is nearly unavoidable in gameplay code.
**Note:** In cases where we need to operate with Entities outside the current processing context (e.g. avoidance between Entity crowds) it is possible to call all of the regular Mass Subsystem functions or deferred actions on them. This is not ideal for cache coherency but it is nearly unavoidable in gameplay code.

## 5.2.1 FMassEntityView
## 5.2.1 `FMassEntityView`

`FMassEntityView` is a struct that makes checking for and getting fragments on another entity easier. It is normally constructed with an `FMassEntityHandle` and a `UMassEntitySubsystem`. On construction, the Entity View caches the entity's current archetype data for use later, reducing repeated work needed when normally calling the subsystem to retrieve information about a it multiple times.
`FMassEntityView` is a struct that eases all kinds of Entity operations. It is composed by a `FMassEntityHandle` and a `UMassEntitySubsystem`. On construction, the `FMassEntityView` caches the Entity's archetype data, which will later reduce repeated work needed to retrieve information about the Entity.

It also has functions for retreiving or changing fragment data already on the entity.
Following next, we expose some of the relevant functions of `FMassEntityView`:

<!--TODO: List of relevant functions interesting for the user:-->
- [TODO]
- [TODO]
- [TODO]

In this example we check for if another entity is an enemy and retrieve specific fragment data if it is.
```c
In the following example, we check if `NearbyEntity` is an enemy, if it is, we damage it:
```c++
FMassEntityView EntityView(EntitySubsystem, NearbyEntity.Entity);

if (EntityView.HasTag<FEnemyMassTag>())
Expand All @@ -988,6 +999,7 @@ if (EntityView.HasTag<FEnemyMassTag>())
}
```
<!--FIXMEKARL: Show better example?-->
<!--FORKARL: I think the damage is cool, but please, include also some simple code damaging the entity :) -->


<a name="mass-pm"></a>
Expand Down