Systems Architecture Documentation

Systems Architecture is a framework that focuses on modular and scalable game logic.

It rewards using DOD (Data-Oriented-Design) over industry-wide OOP standard for writing game logic to reduce occurrence of common pitfals when doing OOP wrong, by avoiding it as much as possible and by that reducing comprehensive complexity to bare minimum.

In this documentation you will learn best practices for producing modular and scalable game systems that together make complex game features, yet still doesn't require countless hours of organization, production and iteration times in the process of making games.

Systems Architecture is refreshingly simple yet powerful tool to make Unreal Engine game developers Work Smart, Not Hard TM and we are gonna achieve that by reducing complexity of codebase and production process to bare minimum!

Ok, enought pretty words - let's dive into how Systems Architecture works and what problems does it solve.

Contents


Documentation built with Unreal-Doc v1.0.8 tool by PsichiX

Book

This book covers explanation of Systems Architecture, as well as provides learning materials such as guidelines with best practices to keep your project healthy and your sanity in check, it also provides tutorial showing step-by-step how to use Systems Architecture in your game.

Pages


Documentation built with Unreal-Doc v1.0.8 tool by PsichiX

Getting started

Before we jump into the meat of Systems Architecture, let me quickly show you a snippet showcasing how working with Systems looks like in a gist:

Click to unfold code snippet

// Actor componetns are treated as data/tags put on actors, that user reads from
// or writes to, using system queries.
UCLASS(BlueprintType, Blueprintable, Meta = (BlueprintSpawnableComponent))
class EXAMPLE_API UPlayerComponent : public USystemsActorComponent
{
	GENERATED_BODY()
};

UCLASS(BlueprintType, Blueprintable, Meta = (BlueprintSpawnableComponent))
class EXAMPLE_API UEnemyComponent : public USystemsActorComponent
{
	GENERATED_BODY()
};

// System are usually lambdas/functions that process chunks of systems world
// data (it can be querying actor components, managing resources or just
// processing some data - the point is we focus on processing game data, no
// matter its form).
void HuntSystem(USystemsWorld& Systems)
{
	const auto DeltaTime = Systems.GetWorld()->GetDeltaSeconds();

	// Systems Architecture adds an interface over game world that allows us to
	// treat it as database that we can query, also rather than putting logic
	// into each object, we rather perform batched data processing on a set of
	// requested components.
	for (auto& PlayerData : Systems.Query<UPlayerComponent>())
	{
		// Queries always yield tuple with actor that owns components
		// requested for query, in addition to following components.
		auto* PlayerActor = PlayerData.Get<0>();
		const auto PlayerPosition = PlayerActor->GetActorLocation();

		const auto Nearest = Systems
								 .Query<UEnemyComponent>()
								 // Mapping iterator is one of the most commonly used
								 // types of iterators that transform data from one form
								 // to another, usually to get exact information needed
								 // by next iterators in chain, or to just process it and
								 // collect the result.
								 .Map<TTuple<FVector, float>>(
									 [&](const auto& EnemyData)
									 {
										 const auto* EnemyActor = EnemyData.Get<0>();
										 const auto EnemyPosition = EnemyActor->GetActorLocation();
										 const auto Distance = FVector::Distance(PlayerPosition, EnemyPosition);

										 return MakeTuple(EnemyPosition, Distance);
									 })
								 // Finding the nearest enemy based on distance extracted
								 // in previous iteration step.
								 .ComparedBy(
									 [](const auto& A, const auto& B)
									 {
										 const auto DistanceA = A.Get<1>();
										 const auto DistanceB = B.Get<1>();

										 return DistanceA < DistanceB;
									 });

		if (Nearest.IsSet())
		{
			const auto TargetPosition = Nearest.GetValue().Get<0>();
			const auto Direction = (TargetPosition - PlayerPosition).GetSafeNormal();
			const auto Velocity = Direction * 100.0f * DeltaTime;

			PlayerActor->SetActorLocation(PlayerPosition + Velocity);
		}
	}
}

UCLASS()
class EXAMPLE_API UExampleGameInstance : public UGameInstance
{
	GENERATED_BODY()

private:
	virtual void Init() override;
};

void UExampleGameInstance::Init()
{
	Super::Init();

	auto* Subsystem = USystemsSubsystem::Get(GetWorld());
	if (IsValid(Subsystem))
	{
		Subsystem->AcquireSystemsWorld(FName(),
			[&](auto& Systems)
			{
				// Due to how queries are resolved, we need to register all
				// components we might want to query.
				Systems.RegisterComponent<UPlayerComponent>();
				Systems.RegisterComponent<UEnemyComponent>();

				// Persistent systems (ones that will run on every game tick)
				// have to be registered in systems world. There are many types
				// of systems, but commonly used ones are state-less
				// lambda/function systems. System labels
				// ("Hunt") are used for resolving systems dependency graph
				// (user can provide sets of systems that given one has to run
				// before and after).
				Systems.InstallLambdaSystem(HuntSystem, FInstallSystemOptions("Hunt"));
			});
	}
}

Table of contents

Systems Architecture is not an ECS!

This is very important to say upfront:

Although at first glance Systems Architecture API looks somewhat like ECS, it definitely is not one.

There are similarities in both, but ECS do more than treat game as database, and its design choices revolve around best performance possible for massive worlds and making improved productivity and reduction of complexity is a side-effect of that, while Systems Architecture doesn't change much to the Unreal Engine API, rather resuses its parts (like actors and actor components) and only adds an API to enable accessing and processing game world in database-like fashion, making Systems Architecture focusing mostly on ergonomics and reducement of overall complexity.

That doesn't mean Systems Architecture doesn't care about performance - it already makes its internals do as less work as possible, and more optimizations are gonna arrive in further development, but the grand goal is and will be always making Unreal Engine game developers life as easier as possible.

That being said, Systems Architecture took great amount of inspiration from ECS, mostly from Rust ECS-based libraries and game engines that proven over time that database-like DOD approach to game logic is not only viable, but also highly beneficial for faster and easier game development process:

As well as Rust lazy-iterators and channel APIs.

Goals

More Data-Oriented-Design approach to game logic

Most infromation you can find prasing DOD focuses too much on its benefits to game performance with huge worlds. I would safely say that this actually shouldn't be the main thing DOD should be recognized by, there is even more important benefit of thinking in terms of data and data processing rather than communication between objects (OOP) - avoiding common pitfalls when trying to model game systems around Real World Things TM.

All game is, is just a bunch of data in memory that gets transformed from one form to another.

When we realize that, we can better and easier solve problems by designing data and game logic that works best together and do as less work as possible, instead of adding lots of usually unnecessary abstraction layers just because we might have a false intuition that representing data and logic in human-readable real world representation somehow will make us understand it better and quicker, while after year of development on each game, we find ourselves struggling with adding, removing or just changing how certain parts of game logic works, slapping hacky solutions here and there just to meet deadlines (we can't remove deadlines but we can reduce complexity).

We all have been through that, and we all promise ourselves we are not gonna make same mistakes again in next project, we just need to "better design how our objects communicate", while making totally new and even more problematic mistakes along the way - we can all agree that this next project is never actually free from mistakes we did last time, they all share same source.

That being said DOD isn't a miracle cure that solves all our problems, it brings its own to the table.

But all these problems revolve around us still trying to slap our (false) intuition about objects communication into problems that do not require these in the first place, us not being able to easily switch our brains from thinking in terms of object communication into thinking in terms of data transformations.

Yes, this takes time to appreciate data, but it pays off eventually with us being able to solve problems right way, not creating workarounds here ant there just to follow tons of abstraction layers requirements.

I hope this book will do its best to speed up switching from OOP to more DOD approach, by showing how to simplify problems without using tons of design patterns.

Flat and deterministic flow of data

Events-based logic approach commonly used in Unreal Engine is great for triggering foreign actions (a.k.a. objects messaging) but has one big flaw - it's not scalable as project grows. This happens because user have very limited control over order of actions happening when using events, and it grows into even bigger problem as dependency graph between parts of event-based logic grows.

Dependency graph in events-based game logic is a set of hidden rules of what action requires what other action to run before and after this one, to make sure objects being communicated have valid and expected state at each point of action execution. Hidden, because these rules emerge over time without user noticing them.

Basically any situation where feature A to run properly, depends on feature B to have a certain data, but that data is provided by feature C and so on, and there is no clear way to determine the order of actions that prepare data for any other feature in all code points related to this problem.

Sometimes it gets even worse when features triggers events that receivers triggers their events and their receivers triggers their events and so on - you can see where i'm going with it, this quickly turns into The Fuck Cascade TM of tons of state recalculation along the way in a single point in time, just to keep all objects always have valid state that depends on other game features state. This is clearly unmanageable and requires a lot of refactoring to either remove dependencies between them or to defer operations in time. This is something user usually do not put much thought into, until a really weird bug happen in some place unrelated to the problem.

Ok, so what we can do about it?

Well, flatten dependency graph and put execution of game features in line as much as possible. With that we reduce cognitive complexity to bare minimum, and user can easily manage exact order of actions to make sure state is always valid and do not need to recalculate on each change causing cascade of instant changes in features that depend on it - TL;DR: user can see exact flow of data in one place and that makes it easy to reason about, no need for juggling around the whole codebase.

In case user still wants to send data with events, but keep deterministic events execution, Systems Architecture provides feature called channels which uses polling to get received messages and execute them at the exact moment system needs to, avoiding immediate message execution when broadcasting messages.

There is also another benefit we gain from flat dependency graph, that we will discuss next:

Orthogonal and compact game logic

Quote from Eric Steven Raymond in Art of UNIX programming:

Orthogonality is one of the most important properties that can help make even complex designs compact. In a purely orthogonal design, operations do not have side effects; each action (whether it's an API call, a macro invocation, or a language operation) changes just one thing without affecting others. There is one and only one way to change each property of whatever system you are controlling.

What intrests us game developers from this quote is that when we express game logic in a set of simple work units we can easier reduce (or even remove) dependencies between parts of game logic and that allows us to easily reason about them (small code) and add/change/remove them without much time spend on refactoring (next to none dependencies between game logic parts).

Orthogonal game systems are ones that do not require each other to work (like you remove one and your game still runs), but they together make up bigger feature.

How Systems Architecture helps to achieve that?

Well, it helps by forcing user to think about data transformations instead of object communication. When we do not have to deal with abstractions, what's left is data and how we process that data. That allows to focus on solving actual problem instead of trying to fit it into some design pattern.

Because systems are all about data transformations, and System Architecture provides an ergonomic way for performing these (such as queries over game world and iterators), each system by the nature of data it works on, will keep itself doing only the work it needs and not causing hidden cascades of changes in the world, making every change explicit and intentional.

Simple example would be: instead of having per unit movement that handles input, position change and physics at once, you create separate systems:

  • input system that collects player input and turns it into input data stored either in a global resource or in some actor component, it depends on what you want to do with this data later.

    You should always design your data around what it's gonna be used for, not the other way around!

  • velocity resolve system that reads input data available for given actor and converts these inputs into velocity change and stores it in velocity component.

  • position change system that reads velocity and applies it to unit position.

You might think: "ok, but this particular example seems to be an easy task that can be just executed all in one place" - and you couldn't be wrong more.

Look at the bigger picture - few months later you'll need to extend or replace physics, or you would need to add another physics resolvers that work on unit velocity and at that point this code gets bigger. You obviously split it into smaller methods, but now designers wants you to let them parameterize or even disable some of its parts at will - well, hello Refactor Tractor TM my old friend. If you split these into smaller batched work units, you gain both easy way to toggle each stage of movement and even be able to quickly change or even remove/replace with brand new ones, not to mention being able to quickly add parameterization for designers in matter of minutes to hour, not days.

Like i said before:

Keeping systems small makes it easy to reason about them and that is a one huge productivity booster that you'll love to still have after year of development on your game.

Modularity and reusability

Modularity, which means we can build complex features from smaller building blocks, gives us an ability to reuse or even share these modules with other developers (free or paid, on github or marketplace). In companies it is even more important to share and reuse certain modules between different teams and products, as demand for making more by doing less grows as company structure and portfolio grows.

Systems Architecture, by its design, allows to put generalized systems and components into separate modules, with none (or sometimes next to none) dependencies between other parts of the game. Are you making a simulation game with NPCs wandering around on the map in one project, then in next one you need to make them too? Put these in a module or just migrate them to another project. Code that doesn't compile? Fear no more, you have got decoupled systems at the moment you've started making them, there is next to none work needed after migration, just register systems into pipeline and slap these components on actors and run the game.

For indies and Unreal Engine plugin creators there are also great news!

Systems Architecture core design, when treated as platform/framework, enables Unreal Engine plugin developers to finally create an ecosystem of plugins trully compatible with each other. Ecosystem of compatible plugins that do not require a hussle of installation steps, makes plugins more accessible for users that want game features that Just Works TM and creates opportunity for more sales for plugin devs - this is a big win for both worlds, users and creators.

And as a bonus:

System Architecture is and will always remain free under permissive licenses, for anyone, accepting reasonable contributions that improve its ergonomics, quality and performance.

Conclusions

If you should take anything from this, remember:

When making games, you're basically just processing data and you don't need fancy distraction-abstractions for that.

  • Make it flat
  • Make it small
  • Make it compact and disconnected from other systems

Then you'll keep your sanity after year of development on a single game title.


Documentation built with Unreal-Doc v1.0.8 tool by PsichiX

Architecture

This section will explain in-depth how Systems Architecture exactly works and how to abuse it to solve your problems the right way.

Systems Architecture adds a database-like interface over game world to allow ergonomic queries for game world data processing.

  • Actor components should only store data

    Or no data, if they are considered tags, used as markers over actors. Tags such as UEnemyComponent that enables this actor to be queried in systems that works on enemies.

    And sometimes methods, but only these that do not have side effects reaching outside of this actor component scope - the point is: it's systems job to perform game logic, actor component methods should at most contain methods that recalculate their internal state, and these are operations called in more than one place.

    This rule gets invalidated for situations like actor component being a bridge to some Unreal Engine built-in components or third-party code to which handles are stored in actor components. Just make sure your own actor components do as less work as possible.

  • Systems do most if not all game logic

    This means whenever you want to put some commonly used piece of logic in actor component or (gods forbid) in actor, make a system for it.

    It will pay off months later by still having small orthogonal game systems with as less dependencies as possible, systems that can be easily toggled, changed or even removed/replaced - no more hours or days of careful code refactoring just because you need to change or remove some bit of game feature.

  • Your game world is just a database

    Systems Architecture gives you ergonomic tools to ease work on batched sets of actor components using game world queries.

    UCLASS()
    class BOIDS_API UBoidsMovementSystem : public USystem
    {
    	GENERATED_BODY()
    
    public:
    	virtual void Run(USystemsWorld& Systems) override;
    };
    
    void UBoidsMovementSystem::Run(USystemsWorld& Systems)
    {
    	Super::Run(Systems);
    
    	const auto* BoidsSettings = Systems.Resource<UBoidsSettings>();
    	if (IsValid(BoidsSettings) == false)
    	{
    		return;
    	}
    	const auto TimeScale = BoidsSettings->TimeScale;
    	const auto DeltaTime = Systems.GetWorld()->GetDeltaSeconds() * TimeScale;
    
    	for (auto& QueryItem : Systems.Query<UVelocityComponent, UBoidComponent>())
    	{
    		auto* Actor = QueryItem.Get<0>();
    		const auto* Velocity = QueryItem.Get<1>();
    		const auto Position = Actor->GetActorLocation();
    
    		Actor->SetActorLocation(Position + Velocity->Value * DeltaTime);
    	}
    }
    

    With queries you have all this system logic in one place - that greatly reduces comprehensive complexity, no more need to jump around whole codebase just to understand how system works, and we make ourselves keep systems small, doing only only the work they need and no other.

Pages


Documentation built with Unreal-Doc v1.0.8 tool by PsichiX

World

Table of contents

Explanation

USystemsWorld is a registry that stores systems, resources and actor components. The first thing you do is you setup your systems world by installing systems, resources and registering actor components that will be used in systems queries to process and do something useful with resources and actor components.

UCLASS()
class EXAMPLE_API UExampleGameInstance : public UGameInstance
{
	GENERATED_BODY()

private:
	virtual void Init() override;
};

void UExampleGameInstance::Init()
{
	Super::Init();

	auto* Subsystem = USystemsSubsystem::Get(GetWorld());
	if (IsValid(Subsystem))
	{
		Subsystem->AcquireSystemsWorld(FName(),
			[&](auto& Systems)
			{
				Systems.RegisterComponent<UShiaComponent>();

				Systems.InstallResource<UShiaSettings>();

				Systems.InstallLambdaSystem(JustDoItSystem, FInstallSystemOptions("JustDoIt"));
			});
	}
}

In example above we register systems world in game instance. We can of course create systems worlds at will without registering it into USystemsSubsystem, this global registry is only a more convenient way to access and automate systems world management globally.

Once per tick (when using subsystem approach) all systems gets to run in order they were installed (system installation options can provide additional hints about more precise order when working with dependencies between systems).

Queries

The whole point of Systems Architecture is to process game world in a database-like fashion, which means to perform same operations on a batched set of actor components, instead of putting that work into these objects. That makes us completely decouple data from behaviors, making it easier to add, remove and change them at will, even reuse without dealing with solving hidden dependency tree problems between seemingly unrelated parts of codebase.

	const auto Count = static_cast<int>(Systems.Query<UBoidComponent>().Count());
	const auto Difference = Count - EXPECTED_POPULATION_NUMBER;

	if (Difference > 0)
	{
		for (auto& QueryItem : Systems.Query<UBoidComponent>().Take(Difference))
		{
			auto* Actor = QueryItem.Get<0>();
			Actor->Destroy();
		}
	}

Queries are iterators that always yields a tuple of actor components and can be chained with other iterators to produce more elaborated, yet easy to reason about data transformations. For example when you do Systems.Query<A, B, C>, values yielded by query iterator will be of type TTuple<AActor*, A*, B*, C*>. it's also important to note that user should never cache query objects, rather consume queries in place.

See TQuery.

Archetypes

Internally all actor components are stored in buckets called archetypes. Given actor archetype is determined by its FArchetypeSignature, which is calculated from the set of components given actor owns. When actor registers its components, systems world searches through already existing archetypes and if it finds one, actor gets registered in that bucket, if not new archetype bucket gets created.

The point of storing actors in groups per archetype is to make queries iteration as fast as possible by iterating over only these actors that own all requested components in a linear fashion without need for skipping over actors that we don't need to work on.

Archetype signatures are basically bitsets of 256 bits capacity, each bit represents registered actor component. Whenever signature gets calculated for given query, it makes empty signature and enables bits for requested component, then it compares this signature with each archetype bucket signature and stores in query object a list of archetypes to iterate on.

Problems that it solves

Usually when working with game managers it's a common practice to cache managers in other managers to not perform costly searches in game world. This is unnecessary work and Systems Architecture solves that by storing resources in single systems world, easily accessible from any system without any need for caching any used resources in systems itself.

Another common problem is usually hierarchy of game logic gets growing in depth, like when game logic is performed by game objects themselves, they reference one another, growing hidden dependency tree and by that comprehensive complexity that requires sometimes hours to jump around whole codebase just to understand how data flows between certain objects. Systems Architecture forces user to make flat game logic with explicit order of execution, additionally making units of work as small and as precise as possible to make it easy to reason about how game data gets processed.


Documentation built with Unreal-Doc v1.0.8 tool by PsichiX

Systems

Table of contents

Explanation

Systems are small but precise units of work, work that's mainly about processing game world data using queries. These queries are processed using iterators, and that makes queries the whole point of systems ergonomics since they simplify both computational and comprehensive complexity and make that part of game logic really easy to reason about, which in turn makes adding, removing and changing game logic easy and quicker than with monolithic code or features highly dependent on each other, scattered around the codebase.

Types of systems

There are few types of systems, but the ones that you will mostly use are stateless (lambdas/functions) and stateful (classes) systems.

Stateless systems

These are the most common ones. Stateless systems are just lambdas/functions that expect reference to USystemsWorld to operate on it. Stateless systems do not require any internal state to operate.

void MovementSystem(USystemsWorld& Systems)
{
	const auto* Settings = Systems.Resource<USettings>();
	if (IsValid(Settings) == false)
	{
		return;
	}
	const auto TimeScale = Settings->TimeScale;
	const auto DeltaTime = Systems.GetWorld()->GetDeltaSeconds() * TimeScale;

	for (auto& QueryItem : Systems.Query<UVelocityComponent>())
	{
		auto* Actor = QueryItem.Get<0>();
		const auto* Velocity = QueryItem.Get<1>();
		const auto Position = Actor->GetActorLocation();

		Actor->SetActorLocation(Position + Velocity->Value * DeltaTime);
	}
}
void UExampleGameInstance::Init()
{
	Super::Init();

	auto* Subsystem = USystemsSubsystem::Get(GetWorld());
	if (IsValid(Subsystem))
	{
		Subsystem->AcquireSystemsWorld(FName(),
			[&](auto& Systems)
			{
				Systems.RegisterComponent<UVelocityComponent>();

				Systems.InstallLambdaSystem(MovementSystem, FInstallSystemOptions("Movement"));
			});
	}
}

Stateful systems

Stateful systems are useful in cases where system requires some internal state that cannot be shared with other systems via resources (for example internal cache that is used to track changes in the system across multiple game ticks).

UCLASS()
class EXAMPLE_API ULogBirdsNumberChangeSystem : public USystem
{
	GENERATED_BODY()

public:
	virtual void Run(USystemsWorld& Systems) override;

	UPROPERTY()
	uint32 LastCount = 0;
};

void ULogBirdsNumberChangeSystem::Run(USystemsWorld& Systems)
{
	Super::Run(Systems);

	if (Systems.ComponentsDidChanged<UBirdComponent>() == false)
	{
		return;
	}

	const auto Number = static_cast<int>(Systems.Query<UBirdComponent>().Count());
	const Difference = Number - this->LastCount;
	this->LastCount = Number;

	if (Difference > 0)
	{
		UE_LOG(LogTemp, Warning, TEXT("Added %i birds"), Difference);
	}
	else if (Difference < 0)
	{
		UE_LOG(LogTemp, Warning, TEXT("Removed %i birds"), -Difference);
	}
}
void UExampleGameInstance::Init()
{
	Super::Init();

	auto* Subsystem = USystemsSubsystem::Get(GetWorld());
	if (IsValid(Subsystem))
	{
		Subsystem->AcquireSystemsWorld(FName(),
			[&](auto& Systems)
			{
				Systems.RegisterComponent<UBirdComponent>();

				Systems.InstallSystem<ULogBirdsNumberChangeSystem>(FInstallSystemOptions("LogBirdsNumberChange"));
			});
	}
}

Problems that it solves

There is very common (bad) practice in the industry that we make a game prototype, then we build actual game on top of that prototype codebase, instead of treating it purely as a reference for an actual, properly implemented solutions in actual project - this can and will quickly lead to poisoned codebase months and years later. It is really hard to break that habit, mostly in professional game development because time budget and famous last words such as "we will refactor that later", but that "later" do not come as soon as we would like, making us having a weak foundation and we build new features on top of that weak foundation, basically playing Jenga with our codebase, codebase which parts of will fall later as the project grows, causing The Fuck Cascade TM. and frustration later on - we can do better than that.

Systems Architecture helps with that by forcing simple and modular codebase from the get go, making switching from prototype to actual implementation a matter of just swapping prototype systems with actual properly implemented systems, sometimes not even having to change them at all.


Documentation built with Unreal-Doc v1.0.8 tool by PsichiX

Components

Table of contents

Explanation

See USystemsActorComponent and USystemsSceneComponent.

Systems Architecture treats game world as database that user can query. That enables us to perform same operations on a batch of actors and their requested components in-place.

	const auto Count = static_cast<int>(Systems.Query<UBoidComponent>().Count());
	const auto Difference = Count - EXPECTED_POPULATION_NUMBER;

	if (Difference > 0)
	{
		for (auto& QueryItem : Systems.Query<UBoidComponent>().Take(Difference))
		{
			auto* Actor = QueryItem.Get<0>();
			Actor->Destroy();
		}
	}

And all you have to do is to add or remove components on your actors of interest to enable/disable them for certain systems, instead of juggling or even duplicating code around many classes, growing your hidden dependency graph. That simplifies the game logic and greatly improves iteration times on game systems.

Keeping data decoupled from game logic enables you to easily perform different logic on same set of data, avoiding duplication of both logic and data, making it easier to iterate on them.

Of course sometimes keeping some logic in components is reasonable - good practice is to add into actor components only these methods that do not produce any side effects outside this component scope, which means logic that only serves to recalculate its internal state.

Problems that it solves

When working with Unreal Engine, mostly as a beginner, a little bit less as an advanced user, you find yourself slapping a lot of code into actors. The general thought wandering around your mind at that time usually goes like this: "This code is something that only this actor does, so it's obvious that this code belongs to this actor class, duh" - at that time, while there is still not much work put in there you might be right, but in that case your intuition is wrong once again.

Across next months you slap bits of logic there and its complexity grows. It doesn't help that you've split it into several methods, all of that effort gets invalidated once you have to add a second, and a third actor that does similar things, and you suddenly find yourself redesigning proper decoupling or inheritance tree or even doing massive refactoring because this one problem touches many seemingly unrelated places, that were part of complex hidden dependency tree.

All of that could be avoided with splitting monolithic work per components, but we can do even better and completely decouple data from logic that we put in small dedicated systems.


Documentation built with Unreal-Doc v1.0.8 tool by PsichiX

Resources

Table of contents

Explanation

Resource in context of Systems Architecture is any global object that all systems should be able to access (though resources definitely aren't singletons! - they share their lifetime with owning systems world). Objects such as:

  • Configuration/settings data

    For example systems run criteria that tells the conditions under what certain systems can run. Or Data Assets / Data Tables passed from game instance to systems world.

  • Cached data shared between systems

    Do you have spatial partitioning system that builds RTree for world actors to speed up spatial queries, and other systems in need to find the closest actor to their location? You definitely put that data in a resource object and make other systems query and read that resource!

  • Game managers

    Common Unreal Engine game development pattern is to create work units called "managers" that manage certain state. For some solutions, especially when dealing with third-party plugins, we have to still have access into these managers in our systems - definitely put them in systems world resources.

UCLASS(BlueprintType)
class EXAMPLE_API USettings : public UDataAsset
{
	GENERATED_BODY()

public:
	UPROPERTY(EditAnywhere)
	float TimeScale = 1;
};

void UExampleGameInstance::Init()
{
	Super::Init();

	auto* Subsystem = USystemsSubsystem::Get(GetWorld());
	if (IsValid(Subsystem))
	{
		Subsystem->AcquireSystemsWorld(FName(),
			[&](auto& Systems)
			{
				Systems.RegisterComponent<UVelocityComponent>();

				// USettings asset set up in editor.
				Systems.InstallResourceRaw(this->Settings);

				Systems.InstallLambdaSystem(MovementSystem, FInstallSystemOptions("Movement"));
			});
	}
}

void MovementSystem(USystemsWorld& Systems)
{
	const auto* Settings = Systems.Resource<USettings>();
	if (IsValid(Settings) == false)
	{
		return;
	}
	const auto TimeScale = Settings->TimeScale;
	const auto DeltaTime = Systems.GetWorld()->GetDeltaSeconds() * TimeScale;

	for (auto& QueryItem : Systems.Query<UVelocityComponent>())
	{
		auto* Actor = QueryItem.Get<0>();
		const auto* Velocity = QueryItem.Get<1>();
		const auto Position = Actor->GetActorLocation();

		Actor->SetActorLocation(Position + Velocity->Value * DeltaTime);
	}
}

Proxy Resources

Along the journey you might find yourself in a need for having to use wrapper-like resources that themselves are not unique types but they wrap unique type resource - wrappers/containers for some inner data. For that scenario you will use concept called Proxy Resources, which allows you to install resource wrapper, which of type itself is not used, instead you provide its inner type used for queries.

First we define proxy resource:

UCLASS()
class EXAMPLE_API UInventoryWrapper : public UDataAsset
{
	GENERATED_BODY()

public:
	UPROPERTY()
	UInventory* GeneratedInventory = nullptr;
};

Then we install it in Systems World:

auto* Wrapper = NewObject<UInventoryWrapper>(Systems, UInventoryWrapper::StaticClass());
Systems.InstallProxyResource<UInventory>(Wrapper, [](auto* Wrapper) { return Wrapper->GeneratedInventory; });

And finally we access it:

Systems.ProxyResource<UInventory>()->AddItem(FItem{FItemType::Sword});

Problems that it solves

While in most cases all what you do is you process game world queries, sometimes you require to access some configuration data, or cache some data for use by many other systems, or you just need to interact with third-party game managers - these are useful use cases and Systems Architecture doesn't get rid of them, rather make their use more ergonomic, yet not giving them global lifetime like with dangerous singletons.


Documentation built with Unreal-Doc v1.0.8 tool by PsichiX

Subsystem

Table of contents

Explanation

For the sake of convenience Systems Architecture provides handy wrapper over USystemsWorld global instances as a USystemsSubsystem that is basically a registry of systems worlds accessible from any place in the code.

All user needs to do is to register and setup systems world using USystemsSubsystem::AcquireSystemsWorld when certain game phase starts (either game, level, menu, etc) before adding/removing/querying actor components, and unregister systems world using USystemsSubsystem::ReleaseSystemsWorld when given game phase ends.

Examples

The most common game phases where systems might exists are game instance and game mode.

  • You register systems world in game instance when there are systems are possible to run during entire game lifetime. This is the easiest and safest option.
  • You register systems world in game mode when there are systems that are possible to run during given game level lifetime. This is useful if we for example have completely separate pipelines set for each level or game mode.

Game instance

Since game instance has a lifetime of entire game run, we only care here about registering systems world to subsystem on UGameInstance::Init.

UCLASS()
class EXAMPLE_API UExampleGameInstance : public UGameInstance
{
	GENERATED_BODY()

private:
	virtual void Init() override;
};

void UExampleGameInstance::Init()
{
	Super::Init();

	auto* Subsystem = USystemsSubsystem::Get(GetWorld());
	if (IsValid(Subsystem))
	{
		Subsystem->AcquireSystemsWorld(FName(),
			[&](auto& Systems)
			{
				Systems.RegisterComponent<UShiaComponent>();

				Systems.InstallResource<UShiaSettings>();

				Systems.InstallLambdaSystem(JustDoItSystem, FInstallSystemOptions("JustDoIt"));
			});
	}
}

Game mode

When it comes to game mode, we should register systems world on AGameModeBase::InitGame (it runs before any actor BeginPlay - if we register it on BeginPlay, then some actors placed on level might try to register to yet non-existing systems world) and unregister it on AGameModeBase::EndPlay.

UCLASS()
class EXAMPLE_API AExampleGameMode : public AGameModeBase
{
	GENERATED_BODY()

private:
	virtual void InitGame(const FString& MapName, const FString& Options, FString& ErrorMessage) override;

	virtual void EndPlay(const EEndPlayReason::Type EndPlayReason) override;
};

void AExampleGameMode::InitGame(const FString& MapName, const FString& Options, FString& ErrorMessage)
{
	Super::InitGame(MapName, Options, ErrorMessage);

	auto* Subsystem = USystemsSubsystem::Get(GetWorld());
	if (IsValid(Subsystem))
	{
		Subsystem->AcquireSystemsWorld(FName(),
			[&](auto& Systems)
			{
				Systems.RegisterComponent<UShiaComponent>();

				Systems.InstallResource<UShiaSettings>();

				Systems.InstallLambdaSystem(JustDoItSystem, FInstallSystemOptions("JustDoIt"));
			});
	}
}

void AExampleGameMode::EndPlay(const EEndPlayReason::Type EndPlayReason)
{
	Super::EndPlay(EndPlayReason);

	auto* Subsystem = USystemsSubsystem::Get(GetWorld());
	if (IsValid(Subsystem))
	{
		Subsystem->ReleaseSystemsWorld(ThisClass::SYSTEMS_WORLD);
	}
}

Documentation built with Unreal-Doc v1.0.8 tool by PsichiX

Pipelines

Table of contents

Explanation and setup

See USystemsPipeline.

Systems pipeline is a data asset that describes configuration of a single systems world: its components, resources and systems that should be installed for new systems world. Given pipeline is then assigned to either ASystemsGameMode or ASystemsGameState to automatically create and destroy dedicated systems world.

Having systems pipeline defined in a data asset simplifies systems world setup and gives more flexibility to game designers so they can balance and test special cases and new game features without asking programmers to take their time and make changes to the setup.

Data Asset

  • World Id - ID under which this pipeline systems world will be registered globally. Usually there is gonna be single systems world so it's best to leave None here, but in rare cases there might be sometimes a need to for multiple systems worlds in the game at once so here user can specify that.

Components

See components architecture book page.

List of all component types that are gonna be recognized by this systems world.

Components

  • Development Build Only - Ensures this component will be registered in development builds only (and editor builds).
  • Use - Useful for quickly disabling given component from the pipeline without actually removing it, can be used for quick dirty tests.

Resources

See resources architecture book page.

Type resources

List of type resources to install.

Type resources are usually just a typical UObject-based game managers, 3rd-party library wrappers, data shared runtime data cache/storage and such.

Type Resources

  • Use Global Storage - Makes this resource transferable from one pipeline to another, to achieve shared resources. For example if main menu pipeline has User Info resource to fill its user name, game pipeline will obtain exactly the same resource instead of creating new one, to use previously filled user name in the game.
  • Development Build Only - Ensures this resource will be registered in development builds only (and editor builds).
  • Use - Useful for quickly disabling given resource from the pipeline without actually removing it, can be used for quick dirty tests.

Asset resources

List of asset resources to install.

Asset resources are purely data (either baked data sources or systems settings) that will be used by systems.

Asset Resources

  • Proxy - Marks this resource as proxy with custom inner resource unpacker USystemsPipelineProxyResource.
  • Development Build Only - Ensures this resource will be registered in development builds only (and editor builds).
  • Use - Useful for quickly disabling given resource from the pipeline without actually removing it, can be used for quick dirty tests.

Systems

See systems architecture book page.

Here we list all of the systems that are gonna be used by this pipeline's systems world.

Order of systems in the list represents order of their execution!

Systems

  • Template - Template USystem-based object that can be configured in-place.
  • Development Build Only - Ensures this system will be registered in development builds only (and editor builds).
  • Use - Useful for quickly disabling given system from the pipeline without actually removing it, can be used for quick dirty tests.

Note the difference between category of systems:

  • Startup - Runs only at systems world installation.
  • Persistent - Runs on every game frame.
  • Cleanup - Runs only at systems world uninstallation.

Commonly used template types:

Stateful systems

Stateful systems are ones that can have configuration properties put in them and/or can hold internal state (for example to cache some values between next system runs, change detection for example). User makes them by inheriting directly from USystem class.

Stateless systems

Stateless systems are basically lambdas/functions registered into FSystemsReflection.

Lambda Systems

The reason for lambda systems to be registered is that Unreal doesn't come with it's own global function reflection so we have to put these into a dedicated registry ourselves, so we can assign them in ULambdaSystem's Function Name property.

Every game or plugin module that exposes its lambda systems, has to register them in module StartupModule and ShutdownModule methods like this:

#include "Tutorial.h"

#include "Systems/Public/SystemsReflection.h"

#include "Tutorial/Systems/Persistent/TutorialGoToSystem.h"
#include "Tutorial/Systems/Persistent/TutorialMoveTowardsTargetSystem.h"
#include "Tutorial/Systems/Persistent/TutorialMovementSystem.h"
#include "Tutorial/Systems/Persistent/TutorialSelectActorsSystem.h"

#define LOCTEXT_NAMESPACE "FTutorialModule"
#define SYSTEMS_NAMESPACE "Tutorial"

void FTutorialModule::StartupModule()
{
	REGISTER_SYSTEM_FUNCTION(TutorialGoToSystem);
	REGISTER_SYSTEM_FUNCTION(TutorialMoveTowardsTargetSystem);
	REGISTER_SYSTEM_FUNCTION(TutorialMovementSystem);
	REGISTER_SYSTEM_FUNCTION(TutorialSelectActorsSystem);
}

void FTutorialModule::ShutdownModule()
{
	UNREGISTER_SYSTEM_FUNCTION(TutorialGoToSystem);
	UNREGISTER_SYSTEM_FUNCTION(TutorialMoveTowardsTargetSystem);
	UNREGISTER_SYSTEM_FUNCTION(TutorialMovementSystem);
	UNREGISTER_SYSTEM_FUNCTION(TutorialSelectActorsSystem);
}

#undef LOCTEXT_NAMESPACE
#undef SYSTEMS_NAMESPACE

IMPLEMENT_GAME_MODULE(FTutorialModule, Tutorial);

Blueprint systems

Blueprint systems are systems made for quick and dirty prototyping by technical designers and content creators, they serve purely as final reference for programmers to translate them into native C++ systems. They exists exactly for scenarios where technical designer or content creator has an idea for a game feature they want to quickly make and test its viability, without bothering programmers to put their hands into every aspect of it, making feature design phase much faster and removing every back-and-forth between programmers and idea owners.

Blueprint systems are made up of two objects:

  • Query blueprint with public properties that represents what components (and optionally actor) query expects to iterate on.
  • System blueprint with OnRun event that performs one or more queries on systems world.

Here is example of creating blueprint systems:

  1. Create new game object that inherits from UObject:

    Create new object

    And name it BP_ShakeSystemQuery.

  2. Create public properties of actor/scene component types (and optionally an actor) your system query wants to iterate on:

    Create query properties

  3. Create new game object that inherits from UScriptableSystem:

    Create new system

    And name it BP_ShakeSystem.

  4. Create System World Query node and assign BP_ShakeSystemQuery object class:

    System query A

    With this we tell tis query iterator it will yield BP_ShakeSystemQuery objects with requested components and actor assigned to its fields.

  5. Create Lazy Iterate node:

    System query B

    Since Query returns a lazy iterator object (you can read about lazy iterators in this architecture book page), this node is a For Each equivalent, to iterate over this iterator elements.

  6. Apply changes to actor offset component we got from yielded query object:

    System query C

Using pipeline data assets

While you can call USystemsPipeline::Install and USystemsPipeline::Uninstall directly to install and uninstall given pipeline systems world globally, it is useful to have that automated, and for that these are game objects that do this for you, based on pipeline data asset assigned to them in the editor:

Systems game mode

See ASystemsGameMode.

Consider using it to install pipeline that has to run singleplayer or multiplayer server game logic (since game modes are spawned only there).

  1. Create game mode that inerits from ASystemsGameMode:

    Systems game mode A

    And name it BP_TutorialGameMode.

  2. Assign USystemsPipeline data asset into Systems Pipeline property:

    Systems game mode B

Systems game state

See ASystemsGameState.

Consider using it to install pipeline that has to run multiplayer client game logic (internally it will enforce to install only on multiplayer client).

  1. Create game state that inerits from ASystemsGameState:

    Systems game state A

    And name it BP_TutorialGameState.

  2. Assign USystemsPipeline data asset into Systems Pipeline property:

    Systems game state B

  3. Additionally you assign this game state class in its sibling game mode:

    Systems game state C

    To make server game mode spawn this game state with its pipeline on clients.


Documentation built with Unreal-Doc v1.0.8 tool by PsichiX

Iterators

Table of contents

Explanation

Iterators, or more precisely lazy iterators, are an useful tool for more efficient and ergonomic processing of collections and procedural generators. Lazy iterators are lazy because on their own they do not yield any values until consumed.

// [0.0, 4.0, 16.0, 36.0, 64.0]
const TArray<float> Result = IterRange(0, 10)
								 .Filter([](const auto& Value) { return Value % 2 == 0; })
								 .Map<float>([](const auto& Value) { return static_cast<float>(Value * Value); })
								 .CollectArray();

This lazyness allows us to produce more elaborated processing over data without making unnecessary caching between iteration stages, and that also makes iteration code cleaner and easier to reason about.

Types of iterators

Creating custom iterators

While built-in iterators provided by SystemsArchitecture should be enough for common usecases, sometimes for more advanced solutions you might find yourself in a need of creating a custom iterator that cannot be solved using built-in ones.

Anatomy if iterators

Let's take a look at Repeat iterator:

#pragma once

#include "CoreMinimal.h"

// Make sure to include converters declaration header first.
#include "Systems/Public/Iterator/ConvertersDecl.h"

// Then converters, macros and size hint, order doesn't matter here.
#include "Systems/Public/Iterator/Converters.h"
#include "Systems/Public/Iterator/Macros.h"
#include "Systems/Public/Iterator/SizeHint.h"

// This iterator is generalized over type of values it yields.
template <typename T>
struct TIterRepeat
{
public:
	// We need to provide type aliases for iterators injected with macros will
	// properly understand this iterato when wrapping it internally.
	using Self = TIterRepeat<T>;
	using Item = typename T;

	TIterRepeat() : Value(TOptional<T>())
	{
	}

	TIterRepeat(T Data) : Value(TOptional<T>(Data))
	{
	}

	// Provide `Next` method that returns optional value of item type.
	TOptional<Item> Next()
	{
		return TOptional<T>(this->Value);
	}

	// Provide `Sizehint` method thar returns hint about estimated range of
	// items it can yield.
	IterSizeHint SizeHint() const
	{
		return IterSizeHint{0, TOptional<uint32>()};
	}

private:
	TOptional<T> Value = {};

public:
	// To make iterators chains we call special macro that injects all other
	// built-in iterators as this one methods.
	ITER_IMPL
};

// Provide handy construction function for ergonomics, just because of C++
// having easier times with types deduction on them.
template <typename T>
TIterRepeat<T> IterRepeat(T Value)
{
	return TIterRepeat<T>(Value);
}
  • Self type alias

    Iterators chaining is done by wrapping consecutive iterators in one another so when consumer calls Next, it internally calls Next of iterator it wraps, and so on. To make them easily injectable with macros, they use Self as an alias for their type.

  • Item type alias

    Type alias for type of value that this iterator yields. Not stores, not takes as an input - exactly one that it yields. This is again used for iterators wrapping purposes so converter iterators that wraps other iterators can identify value type of previous one in chain by typename ITERATOR::Item.

  • Next method

    Every iterator should implement TOptional<Item> Next() method. This method does the actual job of yielding a value.

    See TQuery::Next.

  • SizeHint method.

    Size hints are used by for example collector iterators to estimate the capacity of collection where yielded data will be stored. This at most removes reallocations when adding every next value into that collection, and at least reduces it to some reasonable number.

    Finite iterators will give lower bounds and upper bounds set, infinite iterators will give only lower bounds.

    See TQuery::SizeHint, IterSizeHint.

  • ITER_IMPL macro

    This macro injects other iterators as methods of this one. Without it user would be left with ugly iterators chains made by passing next stages as argument to iterator functions/constructors.

Iterator adapters

In some rare cases you might find yourself struggling to express your data processing with built-in iterators, or you must to make some optimizations that cannot be solved using provided ones. For this advanced usecase you can create custom iterator adapters that works basically the same way as converter iterators.

template <typename T>
struct TIterOddAdapter
{
public:
	template <typename I>
	TOptional<T> Next(I& Iter)
	{
		Iter.Next();
		return Iter.Next();
	}

	template <typename I>
	IterSizeHint SizeHint(const I& Iter) const
	{
		return Iter.SizeHint();
	}
};
// [1, 3, 5, 7, 9]
const TArray<int> Result = IterRange(0, 10).Adapt(TIterOddAdapter<int>()).CollectArray();

They differ from regular iterators in a way that they do not need type aliases and their Next and SizeHint methods require reference to previous stage iterator so they both consume and process its yielded values.


Documentation built with Unreal-Doc v1.0.8 tool by PsichiX

Channels

Table of contents

Explanation

See TSenderChannel, TReceiverChannel.

Shared channels are Single-Produce, Multiple-Consumer events that instead being executed at the time of sending message (pushing), they are executed at the time of receiving, which is an on-demand operation - user decides when to read messages received in event.

	auto Sender = TSenderChannel<int>();
	auto Receiver = Sender.Receiver(1);

	Sender.Send(42);
	while (const auto Value = Receiver.Receive())
	{
		UE_LOG(LogTemp, Warning, TEXT("Received value: %i"), Value);
	}

Internally channels work as a ring buffer behind thread-safe shared pointer in receiver channel, and sender channel only holds a weak pointer to list of registered receivers. When you want to subscribe to a channel, you create a receiver and store that receiver somewhere.

It's important to note that receiver is bound to a sender channel as long as it exists, so there is no need for intentional unbinds, it is all automated by its design.

After you have sender and receivers made, when you tell sender channel to send message, it goes through all weak pointers pointing to receiver channels, and write cloned data into each of their queues. Messages are stored, but not yet executed.

When you want to receive and execute a message, just make receiver read sequence of next messages in queue and do something useful with them. That's all, simple isn't it?

Problems that it solves

There is only one but big problem that it solves - making more deterministic and ordered data flow between parts of game logic. That way we avoid situations where objects communicating with each other are dependend on proper order of messages being sent and received but we can't ensure that order. We also avoid event cycles, where there is set of event points that suddenly gets connected in a way that introduces a cycle of passing messages, leading to game freezes/crashes due to stack overflow, or simply just situations where some data gets passed too early and invalidates dependent object state causing unnecessary state recalculations later by other objects trying to get some data out of it.


Documentation built with Unreal-Doc v1.0.8 tool by PsichiX

Learning materials

List of learning materials that showcase common and special case usage of Systems Architecture.

Pages


Documentation built with Unreal-Doc v1.0.8 tool by PsichiX

Tutorial

Here we will showcase step-by-step process of basic usage of Systems Architecture - both for C++ and Blueprint usage scenarios.

Pages


Documentation built with Unreal-Doc v1.0.8 tool by PsichiX

Installation

Until plugin gets accepted to Unreal Engine Marketplace, we need to install plugin from GitHub.

  1. Download plugin from GitHub Release page (core plugin is in Systems-Architecture.zip archive file).

  2. Make sure project folder structure contains Plugins folder in its root (next to Content and Source folders), then unzip its content into Plugins folder.

  3. Launch editor and confirm plugin is properly added by trying to create new data asset of USystemsPipeline type:

    Create pipeline data asset


Documentation built with Unreal-Doc v1.0.8 tool by PsichiX

Setup component

C++

  • Tutorial/Components/TutorialMovementComponent.h:

    #pragma once
    
    #include "CoreMinimal.h"
    
    #include "Systems/Public/SystemsComponent.h"
    
    #include "TutorialMovementComponent.generated.h"
    
    UCLASS(BlueprintType, Blueprintable, Meta = (BlueprintSpawnableComponent))
    class TUTORIAL_API UTutorialMovementComponent : public USystemsActorComponent
    {
        GENERATED_BODY()
    
    public:
        UPROPERTY(EditAnywhere, BlueprintReadWrite)
        FVector Value = FVector(0);
    };
    
  • Tutorial/Components/TutorialMovementComponent.cpp:

    #include "Tutorial/Components/TutorialMovementComponent.h"
    

Blueprint

  1. Create new game object that inherits from USystemsActorComponent:

    Create component

    And name it BP_TutorialMovement.

  2. Add Value property of Vector type:

    Add properties


Documentation built with Unreal-Doc v1.0.8 tool by PsichiX

Setup actor

  1. Create new game object that inherits from ASystemsActor or Actor (if you don't need automatic built-in engine components registration based on Systems tag) - same for pawns (ASystemsPawn or Pawn):

    Create actor

    And name it BP_Entity.

  2. Add BP_TutorialMovement component:

    Add components

    Add components

  3. Assign random unit vector to TutorialMovement::Value on BeginPlay event:

    Add components

    To initially pick a random direction for this actor further movement.


Documentation built with Unreal-Doc v1.0.8 tool by PsichiX

Setup system

C++

  • Tutorial/Systems/TutorialMovementSystem.h:

    #pragma once
    
    #include "CoreMinimal.h"
    
    class USystemsWorld;
    
    UFUNCTION()
    void TUTORIAL_API TutorialMovementSystem(USystemsWorld& Systems);
    
  • Tutorial/Systems/TutorialMovementSystem.cpp:

    #include "Tutorial/Systems/Persistent/TutorialMovementSystem.h"
    
    #include "Systems/Public/SystemsWorld.h"
    
    #include "Tutorial/Components/TutorialMovementComponent.h"
    
    void TutorialMovementSystem(USystemsWorld& Systems)
    {
        const auto DeltaTime = Systems.GetWorld()->GetDeltaSeconds();
    
        Systems.Query<UTutorialMovementComponent>().ForEach(
            [&](auto& QueryItem)
            {
                auto* Actor = QueryItem.Get<0>();
                auto* Movement = QueryItem.Get<1>();
                const auto Position = Actor->GetActorLocation();
                const auto Velocity = Movement->Value * DeltaTime;
    
                Actor->SetActorLocation(Position + Velocity);
            });
    }
    

Since this is a lambda system, we need to register it to FSystemsReflection. To do that you have to register it in your game module:

  • Tutorial/Tutorial.h:

    #pragma once
    
    #include "CoreMinimal.h"
    
    #include "Modules/ModuleManager.h"
    
    class FTutorialModule : public IModuleInterface
    {
    public:
        virtual void StartupModule() override;
    
        virtual void ShutdownModule() override;
    };
    
  • Tutorial/Tutorial.cpp:

    #include "Tutorial.h"
    
    #include "Systems/Public/SystemsReflection.h"
    
    #include "Tutorial/Systems/Persistent/TutorialMovementSystem.h"
    
    #define LOCTEXT_NAMESPACE "FTutorialModule"
    #define SYSTEMS_NAMESPACE "Tutorial"
    
    void FTutorialModule::StartupModule()
    {
        REGISTER_SYSTEM_FUNCTION(TutorialMovementSystem);
    }
    
    void FTutorialModule::ShutdownModule()
    {
        UNREGISTER_SYSTEM_FUNCTION(TutorialMovementSystem);
    }
    
    #undef LOCTEXT_NAMESPACE
    #undef SYSTEMS_NAMESPACE
    
    IMPLEMENT_GAME_MODULE(FTutorialModule, Tutorial);
    

Blueprint

  1. Create new game object that inherits from Object:

    Create query

    And name it BP_TutorialMovementQuery.

  2. Add BP_TutorialMovement component and actor proeprties to tell over what systems world elements this query wants to iterate over.

    Add query properties

    Here Actor is Actor and Movement is BP_TutorialMovement component.

  3. Create new game object that inherits from UScriptableSystem:

    Create system

    And name it BP_TutorialMovementSystem.

  4. Override On Run event:

    Override OnRun event

  5. Create Query node and assign BP_TutorialMovementQuery class to it:

    Add query node

  6. Create Lazy Iterate node:

    Add lazy iterate node

  7. Perform update on yielded object:

    Perform update

    Note that Movement::Value is multiplied by Delta Time provided by On Run event.


Documentation built with Unreal-Doc v1.0.8 tool by PsichiX

Setup game

  1. Create new data asset that inherits from USystemsPipeline:

    Create data asset

    And name it DA_TutorialPipeline.

  2. Add components:

    • C++

      Add C++ components

    • Blueprint

      Add Blueprint components

  3. Add persistent systems:

    • C++

      Add C++ systems

    • Blueprint

      Add Blueprint systems

  4. Create new game object that inherits from ASystemsGameMode:

    Create game mode

    And name it BP_TutorialGameMode.

  5. Assign BP_TutorialPipeline to Systems Pipeline property:

    Create game mode

  6. Assign BP_TutorialGameMode to level you want this pipeline to run on:

    Create game mode

  7. Put some of BP_Entity actors on level, run game in PIE and see systems performing!


Documentation built with Unreal-Doc v1.0.8 tool by PsichiX

C++ API Reference

Structs

Classes

Functions


Documentation built with Unreal-Doc v1.0.8 tool by PsichiX

Structs


Documentation built with Unreal-Doc v1.0.8 tool by PsichiX

Struct: FActorsIter

struct SYSTEMS_API FActorsIter;

Reflection-enabled

Specifiers:

  • BlueprintType

Query iterator over systems world actors.

See USystemsWorld::Actors.


Methods

  • Adapt

    public:
    template <typename ADAPTER>
    TIterAdapt<Self, ADAPTER> Adapt(
        ADAPTER Adapter
    );
    

    Injects custom iterator adapter into the chain of iteration.

    Useful only for really custom/advanced solutions that cannot be solved with regular iterators.

    Note

    ADAPTER should implement iterator adapter methods. Yielded values share same type wit iterator that wraps this adapter.

    Example

    // [1, 3, 5, 7, 9]
    const TArray<int> Result = IterRange(0, 10).Adapt(TIterOddAdapter<int>()).CollectArray();
    
    template <typename T>
    struct TIterOddAdapter
    {
    public:
    	template <typename I>
    	TOptional<T> Next(I& Iter)
    	{
    		Iter.Next();
    		return Iter.Next();
    	}
    
    	template <typename I>
    	IterSizeHint SizeHint(const I& Iter) const
    	{
    		return Iter.SizeHint();
    	}
    };
    

    Arguments

  • All

    public:
    template <typename FUNCTOR>
    bool All(
        FUNCTOR Func
    );
    

    Checks if all values yielded by this iterator passes predicate.

    Note

    FUNCTOR should comply to given function signature: bool(I::Item Value) where Value holds current value yielded by iterator.

    Example

    // false
    const bool Result = IterRange(0, 10).All([](const auto& Value) { return Value > 5; });
    

    Arguments

  • Any

    public:
    template <typename FUNCTOR>
    bool Any(
        FUNCTOR Func
    );
    

    Checks if any value yielded by this iterator passes predicate.

    Note

    FUNCTOR should comply to given function signature: bool(I::Item Value) where Value holds current value yielded by iterator.

    Example

    // true
    const bool Result = IterRange(0, 10).Any([](const auto& Value) { return Value > 5; });
    

    Arguments

  • Cast

    public:
    template <typename RESULT>
    TIterCast<typename RESULT, Self> Cast();
    

    Casts yielded values to another type.

    Commonly used as a shourtcut for mapping between types using target type constructor.

    Note

    RESULT is type that this iterator will yield after casting.

    Example

    // [0.0, 1.0, 2.0, 3.0, 4.0]
    const TArray<float> Result = IterRange(0, 5).Cast<float>().CollectArray();
    
  • Chain

    public:
    template <typename ITER>
    TIterChain<Self, ITER> Chain(
        ITER&& Iter
    );
    

    Appends another iterator at the end of this iterator.

    Useful for combining results of different iterators that yield same value type.

    Note

    ITER should implement iterator methods. Yielded values share same type with this iterato value type.

    Example

    // [0, 1, 2, 3, 4, -5, -4, -3, -2, -1]
    const TArray<int> Result = IterRange(0, 5).Chain(IterRange(-5, 0)).CollectArray();
    

    Arguments

  • ComparedBy

    public:
    template <typename FUNCTOR>
    TOptional<Item> ComparedBy(
        FUNCTOR Func
    );
    

    Finds iterator value that compared to other items gets greater "score".

    Headline is rather vague, but what it actually does is user can do finding min/max value with this iterator consumer.

    Note

    FUNCTOR should comply to given function signature: bool(I::Item A, I::Item B) where A holds current value yielded by iterator and B is the one that has best "score" so far.

    Example

    // 42
    const int Result = IterRange(0, 42).ComparedBy([](const auto A, const auto B) { return A > B; });
    

    Arguments

  • Count

    public:
    uint32 Count();
    

    Returns exact number of items that iterator can yield.

    Example

    // 10
    const uint32 Result = IterRange(0, 10).Count();
    
  • Enumerate

    public:
    TIterEnumerate<Self> Enumerate();
    

    Enumerates values in this iterator.

    Useful for reading index of element/iteration.

    Note

    Yielded values have type: TTuple<uint32, Item>, which means this iterator yields tuple o index-value pair.

    Example

    // [0, 1, 2, 3, 4]
    const TArray<int> Result =
    	IterRepeat(42).Enumerate().Map<int>([](const auto& Pair) { return Pair.Get<0>(); }).Take(5).CollectArray();
    
  • FActorsIter

    public:
    FActorsIter(
        USystemsWorld* Systems
    );
    

    Constructs query from systems.

    An equivalent of calling USystemsWorld::Actors


    Arguments

    • Systems

      USystemsWorld* Systems
      

      Pointer to systems world of which actors user wants to iterate on.

  • Filter

    public:
    template <typename FUNCTOR>
    TIterFilter<Self, typename FUNCTOR> Filter(
        FUNCTOR Func
    );
    

    Filters values in the iterator by predicate.

    Note

    FUNCTOR should comply to this function signature: bool(const I::Item& Value).

    Example

    // [0, 2, 4, 6, 8]
    const TArray<int> Result = IterRange(0, 10).Filter([](const auto& Value) { return Value % 2 == 0; }).CollectArray();
    

    Arguments

  • FilterMap

    public:
    template <typename RESULT, typename FUNCTOR>
    TIterFilterMap<typename RESULT, Self, typename FUNCTOR> FilterMap(
        FUNCTOR Func
    );
    

    Filters values in the iterator by predicate and maps them to another type.

    Note

    RESULT is a type of values yielded by this iterator that maps into. FUNCTOR should comply to this function signature: TOptional<RESULT>(const I::Item& Value) Returning some value means pasing it to next iterator, returning none means we omit thi value.

    Example

    // [0.0, 2.0, 4.0, 6.0, 8.0]
    const TArray<float> I = IterRange(0, 10)
    							.FilterMap<float>(
    								[](const auto& Value)
    								{
    									if (Value % 2 == 0)
    									{
    										return TOptional<float>(static_cast<float>(Value));
    									}
    									else
    									{
    										return TOptional<float>();
    									}
    								})
    							.CollectArray();
    

    Arguments

  • Find

    public:
    template <typename FUNCTOR>
    TOptional<Item> Find(
        FUNCTOR Func
    );
    

    Finds and returns value in this iterator that passes FUNCTOR predicate.

    Note

    FUNCTOR should comply to given function signature: bool(I::Item Value) where Value holds current value yielded by iterator.

    Example

    // 5
    const TOptional<int> Result = IterRange(0, 10).Find([](const auto& Value) { return Value >= 5; });
    

    Arguments

  • FindMap

    public:
    template <typename RESULT, typename FUNCTOR>
    TOptional<RESULT> FindMap(
        FUNCTOR Func
    );
    

    Finds and returns value in this iterator that passes FUNCTOR predicate, mapped to another type.

    Note

    FUNCTOR should comply to given function signature: TOptional<Item>(I::Item Value) where Value holds current value yielded by iterator. RESULT is the returned type and use should always return TOptional<RESULT> in the predicate where some value means "found" an none means "not found".

    Example

    // 5.0
    const TOptional<float> Result = IterRange(0, 10).FindMap<float>(
    	[](const auto& Value)
    	{
    		if (Value >= 5)
    		{
    			return TOptional<float>(static_cast<float>(Value));
    		}
    		else
    		{
    			return TOptional<float>();
    		}
    	});
    

    Arguments

  • First

    public:
    TOptional<Item> First();
    

    Returns first item in the iterator.

    This is an equivalent of calling FActorsIter::Next.

    Example

    // 5
    const int Result = IterRange(5, 10).First();
    
  • Flatten

    public:
    template <typename RESULT>
    TIterFlatten<typename RESULT, Self> Flatten();
    

    Flattens nested iterators.

    Imagine you have an iterator that yields another iterators, such as arrays of arrays.

    Note

    RESULT is a type of values yielded by this iterator - it should be the same as nested iterator value type (c++ won't accept that syntax, hence we have to provide a RESULT type and ensure it's the same), FUNCTOR should comply to this function signature: RESULT(const I::Item& Value).

    Example

    // [0, 1, 2, 3, 4, 0, 1, 2, 3, 4]
    const TArray<int> P =
    	IterGenerate<TIterRange<int>>([]() { return IterRange(0, 5); }).Take(2).Flatten<int>().CollectArray();
    
  • Fold

    public:
    template <typename FUNCTOR>
    Item Fold(
        Item Start,
        FUNCTOR Func
    );
    

    Folds iterator into single value.

    Folding basically means going through all iterator items and collapsing them into single value. Example of folding can be sum/accumulation, or min/max, or anything like that - although for ones mentioned there are existing optimized iterator consumers: FActorsIter::Sum and FActorsIter::ComparedBy.

    Note

    RESULT is the returned type, same as Start argument used as initial accumulator. FUNCTOR should comply to given function signature: Item(Item Accumulator, I::Item Value) where Accumulator argument holds result of previous iteration, and Value holds curren value yielded by iterator. Start argument holds value passed to first iteratio Accumulator.

    Example

    // 45
    const int Result = IterRange(0, 10).Fold(0, [](const auto& Accum, const auto& Value) { return Accum + Value; });
    

    Arguments

  • ForEach

    public:
    template <typename FUNCTOR>
    void ForEach(
        FUNCTOR Func
    );
    

    Consumes iterator and yields its values for user to process.

    This is equivalent of:

    auto Iter = IterRange(0, 9);
    while (const auto Value = Iter.Next())
    {
    	const auto Squared = Value * Value;
    	UE_LOG(LogTemp, Warning, TEXT("Squared value: %i"), Squared);
    }
    

    Note

    FUNCTOR should comply to given function signature: void(I::Item Value) where Value holds current value yielded by iterator.

    Example

    for (const auto Value : IterRange(0, 9))
    {
    	const auto Squared = Value * Value;
    	UE_LOG(LogTemp, Warning, TEXT("Squared value: %i"), Squared);
    }
    

    Arguments

  • Inspect

    public:
    template <typename FUNCTOR>
    TIterInspect<Self, typename FUNCTOR> Inspect(
        FUNCTOR Func
    );
    

    Inspects yielded values.

    Useful when debugging iterators to for example log what values are yielded at which iterator chain stage.

    Note

    FUNCTOR should comply to this function signature: void(const I::Item& Value).

    Example

    // [0, 2, 4, 6, 8]
    const TArray<int> Result = IterRange(0, 10)
    							   .Inspect(
    								   [](const auto& Value)
    								   {
    									   // Prints values: 0, 1, 2, 3, 4, 5, 6, 7, 8, 9.
    									   UE_LOG(LogTemp, Warning, TEXT("Inspected item before: %i"), Value);
    								   })
    							   .Filter([](const auto& Value) { return Value % 2 == 0; })
    							   .Inspect(
    								   [](const auto& Value)
    								   {
    									   // Prints values: 0, 2, 4, 6, 8.
    									   UE_LOG(LogTemp, Warning, TEXT("Inspected item after: %i"), Value);
    								   })
    							   .CollectArray();
    

    Arguments

  • Last

    public:
    TOptional<Item> Last();
    

    Returns last item in the iterator.

    Example

    // 4
    const int Result = IterRange(0, 4).Last();
    
  • Map

    public:
    template <typename RESULT, typename FUNCTOR>
    TIterMap<typename RESULT, Self, typename FUNCTOR> Map(
        FUNCTOR Func
    );
    

    Maps yielded values to another type.

    Commonly used for data transformations for use in later stages of iteration.

    Note

    RESULT is type that this iterator will yield after data transformations. FUNCTOR should comply to this function signature: RESULT(const I::Item& Value).

    Example

    // [0.0, 4.0, 16.0, 36.0, 64.0]
    const TArray<float> Result = IterRange(0, 10)
    								 .Filter([](const auto& Value) { return Value % 2 == 0; })
    								 .Map<float>([](const auto& Value) { return static_cast<float>(Value * Value); })
    								 .CollectArray();
    

    Arguments

  • Next

    public:
    TOptional<Item> Next();
    

    Yields pointers to next actor.

    See TQuery::Next.

  • Nth

    public:
    TOptional<Item> Nth(
        uint32 Index
    );
    

    Returns Nth item in the iterator.

    Example

    // 5
    const TOptional<int> Result = IterRange(0, 10).Nth(5);
    

    Arguments

  • SizeHint

    public:
    IterSizeHint SizeHint() const;
    

    Gets hint about minimum and optional maximum items count this iterator can yield.

    See TQuery::SizeHint.

  • Skip

    public:
    TIterSkip<Self> Skip(
        uint32 Count
    );
    

    Skips iteration by number of elements.

    It's worth noting that this is one-shot skip, not repeated one, which means if we yield iterator of 10 elements and we skip 2 iterations, then it will skip just 2 values and yield rest 8.

    Example

    // [3, 4, 5, 6, 7]
    const TArray<int> Result = IterRange(0, 10).Skip(3).Take(5).CollectArray();
    

    Arguments

  • Take

    public:
    TIterTake<Self> Take(
        uint32 Count
    );
    

    Limits iterator to at most number of iterations.

    If we create an iterator that yields 10 values and we tell it to take 5, then it will stop iterating after next 5 values (or less, depends if there is enough values left in iterator).

    Example

    // [3, 4, 5, 6, 7]
    const TArray<int> Result = IterRange(0, 10).Skip(3).Take(5).CollectArray();
    

    Arguments

  • Views

    public:
    template <uint32 COUNT>
    TIterViews<Self, COUNT> Views();
    

    Yields sequences of consecutive views over set of values.

    If we create an iterator that yields 5 integer values and we tell it to view 2 at the same time, then it will yield TArrayView with values: [0, 1], [1, 2], [2, 3], [3, 4].

    Example

    // [(0, 1), (1, 2), (2, 3), (3, 4)]
    const TArray<TTuple<int, int>> Result =
    	IterRange(0, 5)
    		.Views<2>()
    		.Map<TTuple<int, int>>([](const auto& View) { return MakeTuple(View[0], View[1]); })
    		.CollectArray();
    
  • Zip

    public:
    template <typename ITER>
    TIterZip<Self, ITER> Zip(
        ITER&& Iter
    );
    

    Combines two iterators to make one that yields pair of both iterator values at the same time.

    Example

    // [(0, -5), (1, -4), (2, -3), (3, -2), (4, -1)]
    const TArray<TTuple<int, int>> Result = IterRange(0, 5).Zip(IterRange(-5, 0)).CollectArray();
    

    Arguments

  • begin

    public:
    TStlIterator<Self> begin();
    

    Allows this iterator to be used in for-in loop.

    Example

    // 0
    // 1
    // 2
    // 3
    // 4
    for (auto Value : IterRange(0, 5))
    {
    	UE_LOG(LogTemp, Info, TEXT("%i"), Value);
    }
    
  • end

    public:
    TStlIterator<Self> end();
    

    Allows this iterator to be used in for-in loop.

    Example

    // 0
    // 1
    // 2
    // 3
    // 4
    for (auto Value : IterRange(0, 5))
    {
    	UE_LOG(LogTemp, Info, TEXT("%i"), Value);
    }
    

Documentation built with Unreal-Doc v1.0.8 tool by PsichiX

Struct: FArchetypeSignature

struct SYSTEMS_API FArchetypeSignature;

Reflection-enabled


Signature of actor components set.

More in depth about archetype signatures in this architecture book section.

Signatures are used internally by USystemsWorld to allow identification of to which buckets (archetypes) given set of components belongs to - that allows for querying iterators over only these actor components that contain all requested components and omiting ones that doesn't, which improves performance of querying systems world (which is the most frequently used operation in systems game architecture, hence the importance of making it fast).

Note

Although constructing signatures has no real value for common usecases while developing games this API can be useful for users that needs to perform more advanced operations.

Example

	// Instead of this:
	const auto A = Systems.Query<UShiaComponent>().Count();

	// You can do this:
	auto Signature = FArchetypeSignature();
	if (const auto Index = Systems.ComponentTypeIndex(UShiaComponent::StaticClass()))
	{
		Signature.EnableBit(Index.GetValue());
	}

	const auto B = Systems.CountRaw(Signature);

Methods

  • Clear

    public:
    void Clear();
    

    Clears components set.

  • ContainsAll

    public:
    bool ContainsAll(
        const FArchetypeSignature& Other
    ) const;
    

    Checks if this signature fully contain component set of another signature.

    Used by USystemsWorld::Query to filter archetypes that are valid for iteration.


    Arguments

    • Other

      const FArchetypeSignature& Other
      

      Other signature to compare with.

  • ContainsAny

    public:
    bool ContainsAny(
        const FArchetypeSignature& Other
    ) const;
    

    Checks if this signature at least partially contain component set of another signature.


    Arguments

    • Other

      const FArchetypeSignature& Other
      

      Other signature to compare with.

  • CountBits

    public:
    uint32 CountBits() const;
    

    Counts bits of enabled components in set.

  • Difference

    public:
    FArchetypeSignature Difference(
        const FArchetypeSignature& Other
    ) const;
    

    Returns signature with only component set bits that differs between this and another signature.


    Arguments

    • Other

      const FArchetypeSignature& Other
      

      Other signature to test with.

  • DisableBit

    public:
    void DisableBit(
        uint32 Index
    );
    

    Disable component bit at given index.


    Arguments

    • Index

      uint32 Index
      

      Index of component bit to disable.

  • EnableBit

    public:
    void EnableBit(
        uint32 Index
    );
    

    Enable component bit at given index.


    Arguments

    • Index

      uint32 Index
      

      Index of component bit to enable.

  • Exclude

    public:
    FArchetypeSignature Exclude(
        const FArchetypeSignature& Other
    ) const;
    

    Exclude components set bits from ones of another signature.


    Arguments

    • Other

      const FArchetypeSignature& Other
      

      Other signature to exclude from.

  • FArchetypeSignature

    public:
    FArchetypeSignature();
    

    Constructs signature of empty components set.

  • FArchetypeSignature

    public:
    FArchetypeSignature(
        const FArchetypeSignature& Other
    );
    

    Copies signature from another one.


    Arguments

    • Other

      const FArchetypeSignature& Other
      

      Other signature to copy components set from.

  • FArchetypeSignature

    public:
    FArchetypeSignature(
        uint32 Bit
    );
    

    Constructs signature with single component bit enabled.


    Arguments

    • Bit

      uint32 Bit
      

      Index of component bit to enable.

  • HasBit

    public:
    bool HasBit(
        uint32 Index
    ) const;
    

    Checks if component bit at given index is enabled.


    Arguments

    • Index

      uint32 Index
      

      Index of component bit to check.

  • Include

    public:
    FArchetypeSignature Include(
        const FArchetypeSignature& Other
    ) const;
    

    Include components set bits from ones of another signature.


    Arguments

    • Other

      const FArchetypeSignature& Other
      

      Other signature to include from.

  • IsEmpty

    public:
    bool IsEmpty() const;
    

    Checks if components set is empty.

  • Union

    public:
    FArchetypeSignature Union(
        const FArchetypeSignature& Other
    ) const;
    

    Returns signature with only component set bits enabled by both signatures.


    Arguments

    • Other

      const FArchetypeSignature& Other
      

      Other signature to test with.


Documentation built with Unreal-Doc v1.0.8 tool by PsichiX

Struct: FInstallSystemOptions

struct SYSTEMS_API FInstallSystemOptions;

Reflection-enabled

Specifiers:

  • BlueprintType

Set of options for installation of this system.

It uses builder pattern to simplify setting options.

Note that both FInstallSystemOptions::RunBefore and FInstallSystemOptions::RunAfter methods are accumulative, which means you can specify with them multiple systems required to run before and/or after this one.

See USystemsWorld::InstallSystem, USystemsWorld::InstallLambdaSystem, USystemsWorld::InstallSystemRaw

Example

Systems.InstallLambdaSystem(BoidsFaceDirectionSystem,
	FInstallSystemOptions("BoidsFaceDirection")
		.RunBefore("BoidsFaceDirection")
		.RunAfter("BoidsApplyImpulse")
		.MultiplayerRunOn(ESystemMultiplayerRunOn::ServerAndClient));

Methods

  • FInstallSystemOptions

    public:
    FInstallSystemOptions(
        FName Name
    );
    

    Create new options object.

    Example

    Systems.InstallLambdaSystem(BoidsFaceDirectionSystem,
    	FInstallSystemOptions("BoidsFaceDirection")
    		.RunBefore("BoidsFaceDirection")
    		.RunAfter("BoidsApplyImpulse")
    		.MultiplayerRunOn(ESystemMultiplayerRunOn::ServerAndClient));
    

    Arguments

    • Name

      FName Name
      

      Name of system being installed.

  • RunAfter

    public:
    FInstallSystemOptions& RunAfter(
        FName Name
    );
    

    Add named dependency system that installed system should run after.

    Example

    Systems.InstallLambdaSystem(BoidsFaceDirectionSystem,
    	FInstallSystemOptions("BoidsFaceDirection")
    		.RunBefore("BoidsFaceDirection")
    		.RunAfter("BoidsApplyImpulse")
    		.MultiplayerRunOn(ESystemMultiplayerRunOn::ServerAndClient));
    

    Arguments

    • Name

      FName Name
      

      Name of other system.

  • RunBefore

    public:
    FInstallSystemOptions& RunBefore(
        FName Name
    );
    

    Add named dependency system that installed system should run before.

    Example

    Systems.InstallLambdaSystem(BoidsFaceDirectionSystem,
    	FInstallSystemOptions("BoidsFaceDirection")
    		.RunBefore("BoidsFaceDirection")
    		.RunAfter("BoidsApplyImpulse")
    		.MultiplayerRunOn(ESystemMultiplayerRunOn::ServerAndClient));
    

    Arguments

    • Name

      FName Name
      

      Name of other system.


Documentation built with Unreal-Doc v1.0.8 tool by PsichiX

Struct: FMetronome

struct SYSTEMS_API FMetronome;

Reflection-enabled

Specifiers:

  • BlueprintType
  • Blueprintable

Metronome for ticking optimizations.

Useful for spreading tickable work across time. User should use it to limit number of heavy ticking operations that do not need to perform on every tick, rather can be executed with uniform random time offset for each instance of a group. Good usecase would be AI tasks.

Example

auto Metronome = FMetronome{};
Metronome.Progress(10.0);
if (Metronome.ConsumeTicked())
{
	UE_LOG(LogTemp, Warning, TEXT("Metronome ticked!"));
}

Properties

  • Accumulator

    public:
    float Accumulator;
    

    Reflection-enabled

    Specifiers:

    • EditAnywhere
    • Category = Systems|Metronome

    Tells the current time phase.

  • Limit

    public:
    float Limit;
    

    Reflection-enabled

    Specifiers:

    • EditAnywhere
    • Category = Systems|Metronome

    Tells the time limit that marks time of ticking.

  • bTicked

    public:
    bool bTicked;
    

    Reflection-enabled

    Specifiers:

    • EditAnywhere
    • Category = Systems|Metronome

    Tells if metronome has ticked.


Methods


Documentation built with Unreal-Doc v1.0.8 tool by PsichiX

Struct: FSystemsPipelineAssetResource

struct SYSTEMS_API FSystemsPipelineAssetResource
    : public FSystemsPipelineResource;

Reflection-enabled

Specifiers:

  • BlueprintType

Pipeline asset resource descriptor.


Properties

  • Proxy

    public:
    TObjectPtr<USystemsPipelineProxyResource> Proxy;
    

    Reflection-enabled

    Specifiers:

    • EditAnywhere
    • Export
    • Instanced
    • Category = Options

    Optionally mark this resource as proxy one for given type.

    Useful to unpack wrapper resources and get their inner content that match requested proxy resource type.


Documentation built with Unreal-Doc v1.0.8 tool by PsichiX

Struct: FSystemsPipelineComponent

struct SYSTEMS_API FSystemsPipelineComponent;

Reflection-enabled

Specifiers:

  • BlueprintType

Pipeline component descriptor.


Properties

  • bDevelopmentBuildOnly

    public:
    bool bDevelopmentBuildOnly;
    

    Reflection-enabled

    Specifiers:

    • EditAnywhere
    • Category = Options

    Tells if this component should be used in development builds only.

    Useful for debug or prototype game features.

  • bUse

    public:
    bool bUse;
    

    Reflection-enabled

    Specifiers:

    • EditAnywhere
    • Category = Options

    Tells if this component should be used.

    Useful for quick enable/disable of component without removing it from the pipeline (quick tests).


Documentation built with Unreal-Doc v1.0.8 tool by PsichiX

Struct: FSystemsPipelineResource

struct SYSTEMS_API FSystemsPipelineResource;

Reflection-enabled

Specifiers:

  • BlueprintType

Pipeline resource descriptor.


Properties

  • bDevelopmentBuildOnly

    public:
    bool bDevelopmentBuildOnly;
    

    Reflection-enabled

    Specifiers:

    • EditAnywhere
    • Category = Options

    Tells if this resource should be used in development builds only.

    Useful for debug or prototype game features.

  • bUse

    public:
    bool bUse;
    

    Reflection-enabled

    Specifiers:

    • EditAnywhere
    • Category = Options

    Tells if this resource should be used.

    Useful for quick enable/disable of resource without removing it from the pipeline (quick tests).


Documentation built with Unreal-Doc v1.0.8 tool by PsichiX

Struct: FSystemsPipelineSystem

struct SYSTEMS_API FSystemsPipelineSystem;

Reflection-enabled

Specifiers:

  • BlueprintType

Pipeline system descriptor.


Properties

  • Template

    public:
    TObjectPtr<USystem> Template;
    

    Reflection-enabled

    Specifiers:

    • EditAnywhere
    • Export
    • Instanced
    • Category = System

    Template of system to instantiate.

  • bDevelopmentBuildOnly

    public:
    bool bDevelopmentBuildOnly;
    

    Reflection-enabled

    Specifiers:

    • EditAnywhere
    • Category = Options

    Tells if this system should be used in development builds only.

    Useful for debug or prototype game features.

  • bUse

    public:
    bool bUse;
    

    Reflection-enabled

    Specifiers:

    • EditAnywhere
    • Category = Options

    Tells if this system should be used.

    Useful for quick enable/disable of this system without removing it from the pipeline (quick tests).


Documentation built with Unreal-Doc v1.0.8 tool by PsichiX

Struct: FSystemsPipelineTypeResource

struct SYSTEMS_API FSystemsPipelineTypeResource
    : public FSystemsPipelineResource;

Reflection-enabled

Specifiers:

  • BlueprintType

Pipeline type resource descriptor.


Properties

  • bUseGlobalStorage

    public:
    bool bUseGlobalStorage;
    

    Reflection-enabled

    Specifiers:

    • EditAnywhere
    • Category = Options

    Tells if this resource should be stored in global storage when pipeline gets uninstalled and restored from global storage when pipeline gets installed.

    Useful to transfer resources between persistent maps, like player username typed in main menu and used in game world, or after mission ends, score gets transferred to main menu for display.


Documentation built with Unreal-Doc v1.0.8 tool by PsichiX

Struct: IterSizeHint

struct IterSizeHint;

Hint about minimum and optionally maximum number of items iterator can yield.

See TQuery::SizeHint.

Example

template <typename I>
void IterCollectIntoArray(I&& Iterator, TArray<typename I::Item>& Result)
{
	const auto SizeHint = Iterator.SizeHint();
	const auto Capacity = SizeHint.Maximum.IsSet() ? SizeHint.Maximum.GetValue() : SizeHint.Minimum;
	Result.Reserve(Result.Num() + Capacity);
	while (auto QueryItem = Iterator.Next())
	{
		Result.Add(QueryItem.GetValue());
	}
}

Properties

  • Maximum

    public:
    TOptional<uint32> Maximum;
    

    Maximum number of items iterator can yield.

  • Minimum

    public:
    uint32 Minimum;
    

    Minimum number of items iterator can yield.


Documentation built with Unreal-Doc v1.0.8 tool by PsichiX

Struct: TQuery

template <class... T>
struct TQuery;

Systems world query iterator.

Allows to iterate over actors and their components that comply to requested types signature.

More about iterators in this architecture book page.

Note

User should rather construct queries with USystemsWorld::Query instead o constructing queries by hand.

Returned query iterator has always put actor as first item of item tuple and then follow requested components. So Systems->Query<A, B, C>() iterator will yield given tuple TTuple<AActor*, A*, B*, C*>

Example

	const auto Count = static_cast<int>(Systems.Query<UBoidComponent>().Count());
	const auto Difference = Count - EXPECTED_POPULATION_NUMBER;

	if (Difference > 0)
	{
		for (auto& QueryItem : Systems.Query<UBoidComponent>().Take(Difference))
		{
			auto* Actor = QueryItem.Get<0>();
			Actor->Destroy();
		}
	}

Methods

  • Adapt

    public:
    template <typename ADAPTER>
    TIterAdapt<Self, ADAPTER> Adapt(
        ADAPTER Adapter
    );
    

    Injects custom iterator adapter into the chain of iteration.

    Useful only for really custom/advanced solutions that cannot be solved with regular iterators.

    Note

    ADAPTER should implement iterator adapter methods. Yielded values share same type wit iterator that wraps this adapter.

    Example

    // [1, 3, 5, 7, 9]
    const TArray<int> Result = IterRange(0, 10).Adapt(TIterOddAdapter<int>()).CollectArray();
    
    template <typename T>
    struct TIterOddAdapter
    {
    public:
    	template <typename I>
    	TOptional<T> Next(I& Iter)
    	{
    		Iter.Next();
    		return Iter.Next();
    	}
    
    	template <typename I>
    	IterSizeHint SizeHint(const I& Iter) const
    	{
    		return Iter.SizeHint();
    	}
    };
    

    Arguments

  • All

    public:
    template <typename FUNCTOR>
    bool All(
        FUNCTOR Func
    );
    

    Checks if all values yielded by this iterator passes predicate.

    Note

    FUNCTOR should comply to given function signature: bool(I::Item Value) where Value holds current value yielded by iterator.

    Example

    // false
    const bool Result = IterRange(0, 10).All([](const auto& Value) { return Value > 5; });
    

    Arguments

  • Any

    public:
    template <typename FUNCTOR>
    bool Any(
        FUNCTOR Func
    );
    

    Checks if any value yielded by this iterator passes predicate.

    Note

    FUNCTOR should comply to given function signature: bool(I::Item Value) where Value holds current value yielded by iterator.

    Example

    // true
    const bool Result = IterRange(0, 10).Any([](const auto& Value) { return Value > 5; });
    

    Arguments

  • Cast

    public:
    template <typename RESULT>
    TIterCast<typename RESULT, Self> Cast();
    

    Casts yielded values to another type.

    Commonly used as a shourtcut for mapping between types using target type constructor.

    Note

    RESULT is type that this iterator will yield after casting.

    Example

    // [0.0, 1.0, 2.0, 3.0, 4.0]
    const TArray<float> Result = IterRange(0, 5).Cast<float>().CollectArray();
    
  • Chain

    public:
    template <typename ITER>
    TIterChain<Self, ITER> Chain(
        ITER&& Iter
    );
    

    Appends another iterator at the end of this iterator.

    Useful for combining results of different iterators that yield same value type.

    Note

    ITER should implement iterator methods. Yielded values share same type with this iterato value type.

    Example

    // [0, 1, 2, 3, 4, -5, -4, -3, -2, -1]
    const TArray<int> Result = IterRange(0, 5).Chain(IterRange(-5, 0)).CollectArray();
    

    Arguments

  • CollectArray

    public:
    TArray<Item> CollectArray();
    

    Consumes iterator and returns an array with its values.

    Example

    const TArray<int> Result = IterRange(0, 10).CollectArray();
    
  • CollectIntoArray

    public:
    void CollectIntoArray(
        TArray<Item>& Result
    );
    

    Consumes iterator and stores its values into provided array.

    Example

    TArray<int> Result();
    IterRange(0, 10).CollectIntoArray(Result);
    

    Arguments

  • CollectIntoSet

    public:
    void CollectIntoSet(
        TSet<Item>& Result
    );
    

    Consumes iterator and stores its values into provided set.

    Example

    TSet<int> Result();
    IterRange(0, 10).CollectIntoSet(Result);
    

    Arguments

  • CollectSet

    public:
    TSet<Item> CollectSet();
    

    Consumes iterator and returns a set with its values.

    Example

    const TArray<int> Result = IterRange(0, 10).CollectArray();
    
  • ComparedBy

    public:
    template <typename FUNCTOR>
    TOptional<Item> ComparedBy(
        FUNCTOR Func
    );
    

    Finds iterator value that compared to other items gets greater "score".

    Headline is rather vague, but what it actually does is user can do finding min/max value with this iterator consumer.

    Note

    FUNCTOR should comply to given function signature: bool(I::Item A, I::Item B) where A holds current value yielded by iterator and B is the one that has best "score" so far.

    Example

    // 42
    const int Result = IterRange(0, 42).ComparedBy([](const auto A, const auto B) { return A > B; });
    

    Arguments

  • Count

    public:
    uint32 Count();
    

    Returns exact number of items that iterator can yield.

    Example

    // 10
    const uint32 Result = IterRange(0, 10).Count();
    
  • Enumerate

    public:
    TIterEnumerate<Self> Enumerate();
    

    Enumerates values in this iterator.

    Useful for reading index of element/iteration.

    Note

    Yielded values have type: TTuple<uint32, Item>, which means this iterator yields tuple o index-value pair.

    Example

    // [0, 1, 2, 3, 4]
    const TArray<int> Result =
    	IterRepeat(42).Enumerate().Map<int>([](const auto& Pair) { return Pair.Get<0>(); }).Take(5).CollectArray();
    
  • Filter

    public:
    template <typename FUNCTOR>
    TIterFilter<Self, typename FUNCTOR> Filter(
        FUNCTOR Func
    );
    

    Filters values in the iterator by predicate.

    Note

    FUNCTOR should comply to this function signature: bool(const I::Item& Value).

    Example

    // [0, 2, 4, 6, 8]
    const TArray<int> Result = IterRange(0, 10).Filter([](const auto& Value) { return Value % 2 == 0; }).CollectArray();
    

    Arguments

  • FilterMap

    public:
    template <typename RESULT, typename FUNCTOR>
    TIterFilterMap<typename RESULT, Self, typename FUNCTOR> FilterMap(
        FUNCTOR Func
    );
    

    Filters values in the iterator by predicate and maps them to another type.

    Note

    RESULT is a type of values yielded by this iterator that maps into. FUNCTOR should comply to this function signature: TOptional<RESULT>(const I::Item& Value) Returning some value means pasing it to next iterator, returning none means we omit thi value.

    Example

    // [0.0, 2.0, 4.0, 6.0, 8.0]
    const TArray<float> I = IterRange(0, 10)
    							.FilterMap<float>(
    								[](const auto& Value)
    								{
    									if (Value % 2 == 0)
    									{
    										return TOptional<float>(static_cast<float>(Value));
    									}
    									else
    									{
    										return TOptional<float>();
    									}
    								})
    							.CollectArray();
    

    Arguments

  • Find

    public:
    template <typename FUNCTOR>
    TOptional<Item> Find(
        FUNCTOR Func
    );
    

    Finds and returns value in this iterator that passes FUNCTOR predicate.

    Note

    FUNCTOR should comply to given function signature: bool(I::Item Value) where Value holds current value yielded by iterator.

    Example

    // 5
    const TOptional<int> Result = IterRange(0, 10).Find([](const auto& Value) { return Value >= 5; });
    

    Arguments

  • FindMap

    public:
    template <typename RESULT, typename FUNCTOR>
    TOptional<RESULT> FindMap(
        FUNCTOR Func
    );
    

    Finds and returns value in this iterator that passes FUNCTOR predicate, mapped to another type.

    Note

    FUNCTOR should comply to given function signature: TOptional<Item>(I::Item Value) where Value holds current value yielded by iterator. RESULT is the returned type and use should always return TOptional<RESULT> in the predicate where some value means "found" an none means "not found".

    Example

    // 5.0
    const TOptional<float> Result = IterRange(0, 10).FindMap<float>(
    	[](const auto& Value)
    	{
    		if (Value >= 5)
    		{
    			return TOptional<float>(static_cast<float>(Value));
    		}
    		else
    		{
    			return TOptional<float>();
    		}
    	});
    

    Arguments

  • First

    public:
    TOptional<Item> First();
    

    Returns first item in the iterator.

    This is an equivalent of calling TQuery::Next.

    Example

    // 5
    const int Result = IterRange(5, 10).First();
    
  • Flatten

    public:
    template <typename RESULT>
    TIterFlatten<typename RESULT, Self> Flatten();
    

    Flattens nested iterators.

    Imagine you have an iterator that yields another iterators, such as arrays of arrays.

    Note

    RESULT is a type of values yielded by this iterator - it should be the same as nested iterator value type (c++ won't accept that syntax, hence we have to provide a RESULT type and ensure it's the same), FUNCTOR should comply to this function signature: RESULT(const I::Item& Value).

    Example

    // [0, 1, 2, 3, 4, 0, 1, 2, 3, 4]
    const TArray<int> P =
    	IterGenerate<TIterRange<int>>([]() { return IterRange(0, 5); }).Take(2).Flatten<int>().CollectArray();
    
  • Fold

    public:
    template <typename FUNCTOR>
    Item Fold(
        Item Start,
        FUNCTOR Func
    );
    

    Folds iterator into single value.

    Folding basically means going through all iterator items and collapsing them into single value. Example of folding can be sum/accumulation, or min/max, or anything like that - although for ones mentioned there are existing optimized iterator consumers: TQuery::Sum and TQuery::ComparedBy.

    Note

    RESULT is the returned type, same as Start argument used as initial accumulator. FUNCTOR should comply to given function signature: Item(Item Accumulator, I::Item Value) where Accumulator argument holds result of previous iteration, and Value holds curren value yielded by iterator. Start argument holds value passed to first iteratio Accumulator.

    Example

    // 45
    const int Result = IterRange(0, 10).Fold(0, [](const auto& Accum, const auto& Value) { return Accum + Value; });
    

    Arguments

  • ForEach

    public:
    template <typename FUNCTOR>
    void ForEach(
        FUNCTOR Func
    );
    

    Consumes iterator and yields its values for user to process.

    This is equivalent of:

    auto Iter = IterRange(0, 9);
    while (const auto Value = Iter.Next())
    {
    	const auto Squared = Value * Value;
    	UE_LOG(LogTemp, Warning, TEXT("Squared value: %i"), Squared);
    }
    

    Note

    FUNCTOR should comply to given function signature: void(I::Item Value) where Value holds current value yielded by iterator.

    Example

    for (const auto Value : IterRange(0, 9))
    {
    	const auto Squared = Value * Value;
    	UE_LOG(LogTemp, Warning, TEXT("Squared value: %i"), Squared);
    }
    

    Arguments

  • Inspect

    public:
    template <typename FUNCTOR>
    TIterInspect<Self, typename FUNCTOR> Inspect(
        FUNCTOR Func
    );
    

    Inspects yielded values.

    Useful when debugging iterators to for example log what values are yielded at which iterator chain stage.

    Note

    FUNCTOR should comply to this function signature: void(const I::Item& Value).

    Example

    // [0, 2, 4, 6, 8]
    const TArray<int> Result = IterRange(0, 10)
    							   .Inspect(
    								   [](const auto& Value)
    								   {
    									   // Prints values: 0, 1, 2, 3, 4, 5, 6, 7, 8, 9.
    									   UE_LOG(LogTemp, Warning, TEXT("Inspected item before: %i"), Value);
    								   })
    							   .Filter([](const auto& Value) { return Value % 2 == 0; })
    							   .Inspect(
    								   [](const auto& Value)
    								   {
    									   // Prints values: 0, 2, 4, 6, 8.
    									   UE_LOG(LogTemp, Warning, TEXT("Inspected item after: %i"), Value);
    								   })
    							   .CollectArray();
    

    Arguments

  • Last

    public:
    TOptional<Item> Last();
    

    Returns last item in the iterator.

    Example

    // 4
    const int Result = IterRange(0, 4).Last();
    
  • Map

    public:
    template <typename RESULT, typename FUNCTOR>
    TIterMap<typename RESULT, Self, typename FUNCTOR> Map(
        FUNCTOR Func
    );
    

    Maps yielded values to another type.

    Commonly used for data transformations for use in later stages of iteration.

    Note

    RESULT is type that this iterator will yield after data transformations. FUNCTOR should comply to this function signature: RESULT(const I::Item& Value).

    Example

    // [0.0, 4.0, 16.0, 36.0, 64.0]
    const TArray<float> Result = IterRange(0, 10)
    								 .Filter([](const auto& Value) { return Value % 2 == 0; })
    								 .Map<float>([](const auto& Value) { return static_cast<float>(Value * Value); })
    								 .CollectArray();
    

    Arguments

  • Next

    public:
    TOptional<Item> Next();
    

    Yields tuple to next actors component set.

    Usually user would rather want to use iterator methods for ergonomic iteration over the query, but in case user has valid reasons to handle iteration different way, this is the single point that performs iteration and yields an item.

    Example

    	auto Query = Systems.Query<UShiaComponent>();
    	while (auto& QueryItem = Query.Next())
    	{
    		// Note that we do not check if QueryItem optional value is set.
    		// `while` loop perform these checks for us, hence we use it
    		// instead of standard `for` loop.
    		auto* Actor = QueryItem.GetValue().Get<0>();
    		auto* Shia = QueryItem.GetValue().Get<1>();
    
    		Shia->JustDoIt(Actor);
    	}
    
  • Nth

    public:
    TOptional<Item> Nth(
        uint32 Index
    );
    

    Returns Nth item in the iterator.

    Example

    // 5
    const TOptional<int> Result = IterRange(0, 10).Nth(5);
    

    Arguments

  • SizeHint

    public:
    IterSizeHint SizeHint() const;
    

    Gets hint about minimum and optional maximum items count this iterator can yield.

    Used internally by lazy-iterators, but in case user would like to implement their own iterators, or iterator consumers, or just wants to preallocate some memory for later consumed iterator items, this method is really useful for these usecases.

    See IterSizeHint.

    Example

    template <typename I>
    void IterCollectIntoArray(I&& Iterator, TArray<typename I::Item>& Result)
    {
    	const auto SizeHint = Iterator.SizeHint();
    	const auto Capacity = SizeHint.Maximum.IsSet() ? SizeHint.Maximum.GetValue() : SizeHint.Minimum;
    	Result.Reserve(Result.Num() + Capacity);
    	while (auto QueryItem = Iterator.Next())
    	{
    		Result.Add(QueryItem.GetValue());
    	}
    }
    
  • Skip

    public:
    TIterSkip<Self> Skip(
        uint32 Count
    );
    

    Skips iteration by number of elements.

    It's worth noting that this is one-shot skip, not repeated one, which means if we yield iterator of 10 elements and we skip 2 iterations, then it will skip just 2 values and yield rest 8.

    Example

    // [3, 4, 5, 6, 7]
    const TArray<int> Result = IterRange(0, 10).Skip(3).Take(5).CollectArray();
    

    Arguments

  • Sum

    public:
    Item Sum(
        Item InitialValue
    );
    

    Returns sum of values that iterator can yield.

    Note

    Make sure that type of iterator values actually implement operator+! Also since iterator can work on non-numerical types, user has to provide initial value, tha makes it work like TQuery::Fold.

    Example

    // 45
    const int Return = IterRange(0, 10).Sum(0);
    

    This is equivalent of:

    // 45
    const int Result = IterRange(0, 10).Fold(0, [](const auto& Accum, const auto& Value) { return Accum + Value; });
    

    Arguments

  • TQuery

    public:
    TQuery(
        USystemsWorld* Systems
    );
    

    Constructs query from systems.

    An equivalent of calling USystemsWorld::Query


    Arguments

    • Systems

      USystemsWorld* Systems
      

      Pointer to systems world of which actor components user wants to iterate on.

  • Take

    public:
    TIterTake<Self> Take(
        uint32 Count
    );
    

    Limits iterator to at most number of iterations.

    If we create an iterator that yields 10 values and we tell it to take 5, then it will stop iterating after next 5 values (or less, depends if there is enough values left in iterator).

    Example

    // [3, 4, 5, 6, 7]
    const TArray<int> Result = IterRange(0, 10).Skip(3).Take(5).CollectArray();
    

    Arguments

  • Views

    public:
    template <uint32 COUNT>
    TIterViews<Self, COUNT> Views();
    

    Yields sequences of consecutive views over set of values.

    If we create an iterator that yields 5 integer values and we tell it to view 2 at the same time, then it will yield TArrayView with values: [0, 1], [1, 2], [2, 3], [3, 4].

    Example

    // [(0, 1), (1, 2), (2, 3), (3, 4)]
    const TArray<TTuple<int, int>> Result =
    	IterRange(0, 5)
    		.Views<2>()
    		.Map<TTuple<int, int>>([](const auto& View) { return MakeTuple(View[0], View[1]); })
    		.CollectArray();
    
  • Zip

    public:
    template <typename ITER>
    TIterZip<Self, ITER> Zip(
        ITER&& Iter
    );
    

    Combines two iterators to make one that yields pair of both iterator values at the same time.

    Example

    // [(0, -5), (1, -4), (2, -3), (3, -2), (4, -1)]
    const TArray<TTuple<int, int>> Result = IterRange(0, 5).Zip(IterRange(-5, 0)).CollectArray();
    

    Arguments

  • begin

    public:
    TStlIterator<Self> begin();
    

    Allows this iterator to be used in for-in loop.

    Example

    // 0
    // 1
    // 2
    // 3
    // 4
    for (auto Value : IterRange(0, 5))
    {
    	UE_LOG(LogTemp, Info, TEXT("%i"), Value);
    }
    
  • end

    public:
    TStlIterator<Self> end();
    

    Allows this iterator to be used in for-in loop.

    Example

    // 0
    // 1
    // 2
    // 3
    // 4
    for (auto Value : IterRange(0, 5))
    {
    	UE_LOG(LogTemp, Info, TEXT("%i"), Value);
    }
    

Documentation built with Unreal-Doc v1.0.8 tool by PsichiX

Struct: TReceiverChannel

template <typename T>
struct TReceiverChannel;

SPMC (single-prodicer, multiple-consumer) receiver channel.

See TSenderChannel.

Example

	auto Sender = TSenderChannel<int>();
	auto Receiver = Sender.Receiver(1);

	Sender.Send(42);
	while (const auto Value = Receiver.Receive())
	{
		UE_LOG(LogTemp, Warning, TEXT("Received value: %i"), Value);
	}

Methods

  • Clear

    public:
    void Clear();
    

    Clears internal queue.

    User would rather want to read and conume stored messages but sometimes user might have valid reasons to discard all messages without reading them.

  • IsEmpty

    public:
    bool IsEmpty() const;
    

    Checks if there are incoming messages in queue.

  • Receive

    public:
    TOptional<T> Receive();
    

    Consumes and returns one message from internal queue.

    Example

    	auto Sender = TSenderChannel<int>();
    	auto Receiver = Sender.Receiver(1);
    
    	Sender.Send(42);
    	while (const auto Value = Receiver.Receive())
    	{
    		UE_LOG(LogTemp, Warning, TEXT("Received value: %i"), Value);
    	}
    
  • Unbind

    public:
    void Unbind();
    

    Unbind from sender.

    This forcefully unbinds reciever from sender and clears internal queue, though user do not have to call this method when its lifetime ends - receivers unbind themselves as soon as they get destroyed. This method exists only in case user would want to stop receiving further messages on demand, useful in more advanced scenarios.


Documentation built with Unreal-Doc v1.0.8 tool by PsichiX

Struct: TResult

template <typename T, typename E>
struct TResult;

Result type that can wrap either T value or E error data.

Useful for return types in functions that can return either valid value or some error. Given implementation tries to match TOptional API as much as possible.

Example

UENUM()
enum class EGuessError : uint8
{
	TooHigh,
	TooLow,
};

USTRUCT()
struct FShiaTheSecretKeeper
{
	GENERATED_BODY()

public:
	FShiaTheSecretKeeper() : Secret(), Password()
	{
	}

	FShiaTheSecretKeeper(FString InSecret, int InPassword) : Secret(InSecret), Password(InPassword)
	{
	}

	TResult<FString, EGuessError> TryGuess(int GuessPassword)
	{
		if (GuessPassword == this->Password)
		{
			return TResult<FString, EGuessError>(this->Secret);
		}
		else if (GuessPassword < this->Password)
		{
			return TResult<FString, EGuessError>(EGuessError::TooLow);
		}
		else
		{
			return TResult<FString, EGuessError>(EGuessError::TooHigh);
		}
	}

private:
	FString Secret = {};
	int Password = {};
};

int Main()
{
	const auto Shia = FShiaTheSecretKeeper("Just do it!", 42);
	const auto Result = Shia.TryGuess(42);
	if (Result.IsOk())
	{
		UE_LOG(LogTemp, Info, TEXT("Guessed! Secret: %s"), *Result.GetOk());
	}
	else
	{
		switch (Result.GetError())
		{
			case EGuessError::TooHigh:
				UE_LOG(LogTemp, Error, TEXT("Too high!"));
				break;

			case EGuessError::TooLow:
				UE_LOG(LogTemp, Error, TEXT("Too low!"));
				break;
		}
	}
}

Methods

  • AsError

    public:
    TOptional<E> AsError() const;
    

    Returns option with cloned error data.

    In case of result wrapping value data, it returns none.

  • AsOk

    public:
    TOptional<T> AsOk() const;
    

    Returns option with cloned value data.

    In case of result wrapping error data, it returns none.

  • GetError

    public:
    E& GetError();
    

    Returns reference to internal error.

    Note

    It panics if result wraps value data.

  • GetError

    public:
    const E& GetError() const;
    

    Returns reference to internal error.

    Note

    It panics if result wraps value data.

  • GetOk

    public:
    T& GetOk();
    

    Returns reference to internal value.

    Note

    It panics if result wraps error data.

  • GetOk

    public:
    const T& GetOk() const;
    

    Returns reference to internal value.

    Note

    It panics if result wraps error data.

  • IsError

    public:
    bool IsError() const;
    

    Tells if result wraps error data.

  • IsOk

    public:
    bool IsOk() const;
    

    Tells if result wraps value data.

  • SetError

    public:
    void SetError(
        const E& Error
    );
    

    Replaces internal data with cloned error data.


    Arguments

    • Error

      const E& Error
      

      Error to copy from.

  • SetError

    public:
    void SetError(
        E&& Error
    );
    

    Replaces internal data with moved error data.


    Arguments

    • Error

      E&& Error
      

      Error to consume.

  • SetOk

    public:
    void SetOk(
        const T& Value
    );
    

    Replaces internal data with cloned value data.


    Arguments

    • Value

      const T& Value
      

      Value to copy from.

  • SetOk

    public:
    void SetOk(
        T&& Value
    );
    

    Replaces internal data with moved value data.


    Arguments

    • Value

      T&& Value
      

      Value to consume.

  • TResult

    public:
    TResult(
        const T& Value
    );
    

    Copy constructor.


    Arguments

    • Value

      const T& Value
      

      Value data to copy from.

  • TResult

    public:
    TResult(
        T&& Value
    );
    

    Move constructor.


    Arguments

    • Value

      T&& Value
      

      Value to consume.

  • TResult

    public:
    TResult(
        const E& Error
    );
    

    Copy constructor.


    Arguments

    • Error

      const E& Error
      

      Error to copy from.

  • TResult

    public:
    TResult(
        E&& Error
    );
    

    Move constructor.


    Arguments

    • Error

      E&& Error
      

      Error to consume.

  • TResult

    public:
    TResult(
        const TResult& Other
    );
    

    Copy constructor.


    Arguments

    • Other

      const TResult& Other
      

      Result to copy from.

  • TResult

    public:
    TResult(
        TResult&& Other
    );
    

    Move constructor.


    Arguments

    • Other

      TResult&& Other
      

      Result to consume.

  • operator bool

    public:
    explicit operator bool() const;
    

    Cast to boolean.

    Handy shortcut for TResult::IsOk. Useful when used in if statement condition.

  • operator!=

    public:
    bool operator!=(
        const TResult& Lhs,
        const TResult& Rhs
    );
    

    Tells if two results aren't equal.

    Two results must both wrap either value or error, and if they do then their data is compared.


    Arguments

    • Lhs

      const TResult& Lhs
      
    • Rhs

      const TResult& Rhs
      
  • operator<<

    public:
    FArchive& operator<<(
        FArchive& Ar,
        TResult& Result
    );
    

    Serializes given result.


    Arguments

    • Ar

      FArchive& Ar
      
    • Result

      TResult& Result
      
  • operator=

    public:
    TResult& operator=(
        const TResult& Other
    );
    

    Copies other result.


    Arguments

    • Other

      const TResult& Other
      

      Result to copy from.

  • operator=

    public:
    TResult& operator=(
        TResult&& Other
    );
    

    Consumes other result.


    Arguments

    • Other

      TResult&& Other
      

      Result to consume.

  • operator=

    public:
    TResult& operator=(
        const T& Value
    );
    

    Replaces internal data with cloned value data.


    Arguments

    • Value

      const T& Value
      

      Value to copy from.

  • operator=

    public:
    TResult& operator=(
        T&& Value
    );
    

    Replaces internal data with moved value data.


    Arguments

    • Value

      T&& Value
      

      Value to consume.

  • operator=

    public:
    TResult& operator=(
        const E& Error
    );
    

    Replaces internal data with cloned error data.


    Arguments

    • Error

      const E& Error
      

      Error to copy from.

  • operator=

    public:
    TResult& operator=(
        E&& Error
    );
    

    Replaces internal data with cloned error data.


    Arguments

    • Error

      E&& Error
      

      Error to consume.

  • operator==

    public:
    bool operator==(
        const TResult& Lhs,
        const TResult& Rhs
    );
    

    Tells if two results are equal.

    Two results must both wrap either value or error, and if they do then their data is compared.


    Arguments

    • Lhs

      const TResult& Lhs
      
    • Rhs

      const TResult& Rhs
      

Documentation built with Unreal-Doc v1.0.8 tool by PsichiX

Struct: TSenderChannel

template <typename T>
struct TSenderChannel;

SPMC (single-prodicer, multiple-consumer) sender channel.

More in-depth explanation of channels can be found in this architecture book page.

Channels are similar to Unreal events, the difference is while channels sends data that gets stored immediatelly on receiver queue, user have to consume received messages by polling (intentionally asking for next message in queue).

The benefit of polling over pushing (Unreal events) is that user has full controll over when and where incoming messages are processed and executed, and that makes user avoid random/undefined data flow which is common pitfall when using events - with channels we bring determinism into the communication between parts of the game.

Note

User can freely send messages across multiple separate threads, for example if user spawn system on another thread to process some big chunk of data and that system has to send it processing results back to game thread.

User might notice there is method for creating bound receiver, but there is none fo destroying it - the reason is receivers are weakly connected so they will automaticall unbound from sender channel as soon as they get destroyed in their scope, and sender will tr to send any message to that already gone receiver.

Example

	auto Sender = TSenderChannel<int>();
	auto Receiver = Sender.Receiver(1);

	Sender.Send(42);
	while (const auto Value = Receiver.Receive())
	{
		UE_LOG(LogTemp, Warning, TEXT("Received value: %i"), Value);
	}

Methods

  • Receiver

    public:
    TReceiverChannel<T> Receiver(
        uint32 Capacity = 0
    );
    

    Creates receiver channel bound to this sender channel.


    Arguments

    • Capacity

      uint32 Capacity = 0
      

      Initial capacity of messages queue for created receiver channel.

  • Receivers

    public:
    uint32 Receivers() const;
    

    Returns number of actively bound receivers.

  • Send

    public:
    void Send(
        const T& Data
    );
    

    Sends message to all registered receivers.


    Arguments

    • Data

      const T& Data
      

      Data representation of given message to send.


Documentation built with Unreal-Doc v1.0.8 tool by PsichiX

Struct: TTaggedQuery

template <class... T>
struct TTaggedQuery;

Systems world tagged query iterator.

This is basically an extension to TQuery that allows to ensure additional components without the need for reading them - useful for tag components (ones that just mark actors and do not store any data).

Note

User should rather construct queries with USystemsWorld::TaggedQuery instead o constructing queries by hand.

Example

systems_world_tagged_query

Methods

  • Iter

    public:
    TQuery<T...> Iter() const;
    

    Constructs TQuery from requested actor components and tag components.

  • TTaggedQuery

    public:
    TTaggedQuery(
        USystemsWorld* InSystems
    );
    

    Constructs tagged query from systems.

    An equivalent of calling USystemsWorld::TaggedQuery


    Arguments

    • InSystems

      USystemsWorld* InSystems
      

      Pointer to systems world of which actor components user wants to iterate on.

  • With

    public:
    template <class W>
    TTaggedQuery& With();
    

    Handy wrapper for TTaggedQuery::WithRaw.

    Note

    Make sure W inherits from UActorComponent.

  • WithRaw

    public:
    TTaggedQuery& WithRaw(
        const UClass* Type
    );
    

    Request given component type to exists in queried actor without accesing its contents.

    Note

    Make sure Type inherits from UActorComponent.


    Arguments

    • Type

      const UClass* Type
      
  • Without

    public:
    template <class W>
    TTaggedQuery& Without();
    

    Handy wrapper for TTaggedQuery::WithoutRaw.

    Note

    Make sure W inherits from UActorComponent.

  • WithoutRaw

    public:
    TTaggedQuery& WithoutRaw(
        const UClass* Type
    );
    

    Request given component type to not exists in queried actors.

    Note

    Make sure Type inherits from UActorComponent.


    Arguments

    • Type

      const UClass* Type
      

Documentation built with Unreal-Doc v1.0.8 tool by PsichiX

Classes


Documentation built with Unreal-Doc v1.0.8 tool by PsichiX

Class: ASystemsActor

class SYSTEMS_API ASystemsActor
    : public AActor;

Reflection-enabled

Specifiers:

  • BlueprintType
  • Blueprintable
  • Abstract

Base class for systems actors.

Automatically registers this actor components that are not inheriting from USystemsActorComponent or USystemsSceneComponent and have Systems component tag applied to them. This simply allows Systems World to recognize and query built-in Unreal Engine components without registering them manually - one less boilerplate to make on user side.


Documentation built with Unreal-Doc v1.0.8 tool by PsichiX

Class: ASystemsGameMode

class SYSTEMS_API ASystemsGameMode
    : public AGameModeBase;

Reflection-enabled


Base class for game mode that has to install USystemsPipeline.

Note

Given pipeline will be installed only on server.


Documentation built with Unreal-Doc v1.0.8 tool by PsichiX

Class: ASystemsGameState

class SYSTEMS_API ASystemsGameState
    : public AGameStateBase;

Reflection-enabled


Base class for game state that has to install USystemsPipeline.

Note

Given pipeline will be installed only on client.


Documentation built with Unreal-Doc v1.0.8 tool by PsichiX

Class: ASystemsPawn

class SYSTEMS_API ASystemsPawn
    : public APawn;

Reflection-enabled

Specifiers:

  • BlueprintType
  • Blueprintable
  • Abstract

Base class for systems actors.

Does exactly the same as ASystemsActor but for Pawns.


Documentation built with Unreal-Doc v1.0.8 tool by PsichiX

Class: FSystemsReflection

class SYSTEMS_API FSystemsReflection;

Systems reflection singleton.

Stores application-wide database of named system functions to be used by ULambdaSystem.


Methods

  • FindByName

    public:
    TOptional<TFunction<SystemsWorld::LambdaSystemType>> FindByName(
        FName Name
    ) const;
    

    Find system function by name.


    Arguments

    • Name

      FName Name
      

      Unique name for given system function.

  • GetNames

    public:
    void GetNames(
        TArray<FString>& Result
    ) const;
    

    Gets list of system function names.


    Arguments

    • Result

      TArray<FString>& Result
      

      Output array to receive names.

  • Register

    public:
    void Register(
        FName Name,
        TFunction<SystemsWorld::LambdaSystemType> Callback
    );
    

    Register or overwrite system function under given name.


    Arguments

    • Name

      FName Name
      

      Unique name for given system function.

    • Callback

      TFunction<SystemsWorld::LambdaSystemType> Callback
      

      Reference to system function.

  • Unregister

    public:
    void Unregister(
        FName Name
    );
    

    Unregister system function under given name.


    Arguments

    • Name

      FName Name
      

      Unique name for given system function.


Documentation built with Unreal-Doc v1.0.8 tool by PsichiX

Class: UDynamicIterator

class SYSTEMS_API UDynamicIterator
    : public UObject;

Reflection-enabled

Specifiers:

  • BlueprintType


Methods

  • EstimateSizeLeft

    public:
    virtual int EstimateSizeLeft() const;
    

    Reflection-enabled

    Specifiers:

    • BlueprintCallable
    • Category = Systems

    Meta Specifiers:

    • DevelopmentOnly

    Calculates (usually minimal) number of items this iterator stage can yield.

  • Next

    public:
    virtual UObject* Next();
    

    Reflection-enabled

    Specifiers:

    • BlueprintCallable
    • Category = Systems

    Meta Specifiers:

    • DevelopmentOnly

    Performs iteration and returns yielded value.

    Null means no object left to iterate.

  • NextBranched

    public:
    UObject* NextBranched(
        EDynamicIteratorNextBranch& Branches
    );
    

    Reflection-enabled

    Specifiers:

    • BlueprintCallable
    • Category = Systems

    Meta Specifiers:

    • ExpandEnumAsExecs = Branches
    • DevelopmentOnly

    Handy wrapper for UDynamicIterator::Next for use in blueprints, where iteration can branch to yielded and completed execution nodes.


    Arguments

    • Branches

      EDynamicIteratorNextBranch& Branches
      

Documentation built with Unreal-Doc v1.0.8 tool by PsichiX

Class: UDynamicQuery

class SYSTEMS_API UDynamicQuery
    : public UDynamicIterator;

Reflection-enabled

Specifiers:

  • BlueprintType

Dynamic query useful for performing system world queries in blueprints.

One of the goals of Systems Architecture is to provide game designers with means to prototype systems without need for programmers. Dynamic query is a way of performing systems world queries in blueprints. Although blueprint API doesn't have same ergonomics as C++ API because of the lack of lazy iterators on blueprints side, technical game designers still might benefit from quickly testing simple systems without involving programmers into the process and being able to quickly test the idea - it makes failing quicker than it would without technical game designers getting their hands dirty once in a while, which is beneficial to the whole production process in long term.

See USystemsWorld::SpawnQuery.

Example

	UCLASS(BlueprintType)
	class EXAMPLE_API UShiaQueryBundle
	{
		GENERATED_BODY()

	public:
		UPROPERTY(BlueprintReadOnly)
		AActor* Actor = nullptr;

		UPROPERTY(BlueprintReadOnly)
		UShiaComponent* Shia = nullptr;
	};

	auto Query = Systems.DynamicQuery<UShiaQueryBundle>();
	auto* Bundle = NewObject<UShiaQueryBundle>(this, UShiaQueryBundle::StaticClass());
	while (Query->Next(Bundle))
	{
		Bundle->Shia->JustDoIt(Bundle->Actor);
	}

Methods

  • EstimateSizeLeft

    public:
    virtual int EstimateSizeLeft() const override;
    

    Calculates maximum number of items this query can yield.

  • Next

    public:
    virtual UObject* Next() override;
    

    Performs iteration and stores yielded values in returned object.

    Note

    This method uses reflection to figure out properties that gonna store yielded actor and components in returned object.

  • Setup

    public:
    void Setup(
        USystemsWorld* Systems,
        const UClass* Type
    );
    

    Resets internal iterator to state for interating current state of systems world.


    Arguments

    • Systems

      USystemsWorld* Systems
      

      Pointer to systems world to iterate on.

    • Type

      const UClass* Type
      

      Class of object that contains fields where yielded actor and components are gonna be stored.


Documentation built with Unreal-Doc v1.0.8 tool by PsichiX

Class: ULambdaSystem

class SYSTEMS_API ULambdaSystem
    : public USystem;

Reflection-enabled

Specifiers:

  • BlueprintType
  • Blueprintable

Handy wrapper for state-less systems (functions or lambdas).

See USystem

Example

UFUNCTION()
void BoidsFaceDirectionSystem(USystemsWorld& Systems);

void BoidsFaceDirectionSystem(USystemsWorld& Systems)
{
	for (auto& QueryItem : Systems.Query<UVelocityComponent, UBoidComponent>())
	{
		auto* Actor = QueryItem.Get<0>();
		const auto* Velocity = QueryItem.Get<1>();

		if (Velocity->Value.IsNearlyZero() == false)
		{
			Actor->SetActorRotation(Velocity->Value.Rotation());
		}
	}
}
Systems.InstallLambdaSystem(BoidsFaceDirectionSystem, FInstallSystemOptions("BoidsFaceDirection"));

Methods

  • Bind

    public:
    void Bind(
        TFunction<FunctorType>&& Func
    );
    

    Binds given function/lambda that runs some system logic.

    Method called by USystemsWorld::InstallLambdaSystem


    Arguments

    • Func

      TFunction<FunctorType>&& Func
      
  • Unbind

    public:
    void Unbind();
    

    Unbinds stored function/lambda that runs some system logic.


Documentation built with Unreal-Doc v1.0.8 tool by PsichiX

Class: UScriptableSystem

class SYSTEMS_API UScriptableSystem
    : public USystem;

Reflection-enabled

Specifiers:

  • BlueprintType
  • Blueprintable

Variant of system that works with blueprints.

User can override OnInit method for system initialization, as well as OnRun for performing system logic.

See USystem


Methods


Documentation built with Unreal-Doc v1.0.8 tool by PsichiX

Class: USystem

class SYSTEMS_API USystem
    : public UObject;

Reflection-enabled

Specifiers:

  • Abstract
  • EditInlineNew

Base class for unit of work over some data.

Architecture book page explains more in depth systems.

System's job is to process data. Usually it involves performing queries over game world and/or resources.

This is only one of few ways of system representation, used mostly in case your system has to cache some internal state or has to perform some logic on system initialization phase, which is not possible in case of for example lambda systems.

Example

UCLASS()
class EXAMPLE_API ULogBirdsNumberChangeSystem : public USystem
{
	GENERATED_BODY()

public:
	virtual void Run(USystemsWorld& Systems) override;

	UPROPERTY()
	uint32 LastCount = 0;
};

void ULogBirdsNumberChangeSystem::Run(USystemsWorld& Systems)
{
	Super::Run(Systems);

	if (Systems.ComponentsDidChanged<UBirdComponent>() == false)
	{
		return;
	}

	const auto Number = static_cast<int>(Systems.Query<UBirdComponent>().Count());
	const Difference = Number - this->LastCount;
	this->LastCount = Number;

	if (Difference > 0)
	{
		UE_LOG(LogTemp, Warning, TEXT("Added %i birds"), Difference);
	}
	else if (Difference < 0)
	{
		UE_LOG(LogTemp, Warning, TEXT("Removed %i birds"), -Difference);
	}
}

Methods

  • AdvancedRun

    public:
    virtual void AdvancedRun(
        USystemsWorld& Systems,
        const FName& Mode,
        const TObjectPtr<UObject>& Payload
    );
    

    Override to run system work logic in special mode triggered .

    This method is called by USystemsWorld::Process. See also USystem::Run.

    Here user can perform system logic in special mode, like for example systems dispatcher gets request to run systems in network rollback/rollforth mode so systems that support rollback/rollforth can go back in time and re-simulate their part of the game state.


    Arguments

    • Systems

      USystemsWorld& Systems
      

      Reference to systems world that triggers this system run.

    • Mode

      const FName& Mode
      

      Name of the mode system wants to run in (default mode is None).

    • Payload

      const TObjectPtr<UObject>& Payload
      

      Additional payload provided from systems dispatcher that contains extra data for given run mode.

  • Cleanup

    public:
    virtual void Cleanup(
        USystemsWorld& Systems
    );
    

    Override to cleanup system (its internal state).

    This method is called by USystemsWorld::Cleanup, right before systems world is scheduled to removal.

    Here user can perform cleanup of this system internal state, or free acquired resources.


    Arguments

    • Systems

      USystemsWorld& Systems
      

      Reference to systems world that triggers this system cleanup.

  • Init

    public:
    virtual void Init(
        USystemsWorld& Systems
    );
    

    Override to initialize system (setup its internal state).

    This method is called by USystemsWorld::SealAndInitialize, right after systems world is setup and sealed by the user.

    Here user can perform initialization of this system internal state, or do something useful with systems world (for example initialize some resources). At this stage systems world does not yet have registered any of world components so any query will yield no results.

    Example

    UCLASS()
    class BOIDS_API UBoidsMovementSystem : public USystem
    {
    	GENERATED_BODY()
    
    public:
    	virtual void Init(USystemsWorld& Systems) override;
    
    private:
    	TReceiverChannel<FMovementStep> MovementStep = {};
    };
    
    void UBoidsMovementSystem::Init(USystemsWorld& Systems)
    {
    	Super::Init(Systems);
    
    	auto* GameEvents = Systems.Resource<UGameEvents>();
    	if (IsValid(GameEvents))
    	{
    		this->MovementStep = GameEvents->MovementStep.Receiver(1);
    	}
    }
    

    Arguments

    • Systems

      USystemsWorld& Systems
      

      Reference to systems world that triggers this system initialization.

  • Run

    public:
    virtual void Run(
        USystemsWorld& Systems
    );
    

    Override to run system work logic (perform queries on world components and resources).

    This method is called by USystems::AdvancedRun when systems dispatcher gets request for default mode run.

    Here user can perform system logic, usually performs single task such as apply velocity accumulated by other systems on actor location, or perform actors AI tasks, or kill actors that has 0 or less health - whatever work you want to do make sure it's not monolithic, rather make it small, like don't do multiple things at once - always divide work to small data processing work units. Split bigger tasks between multiple systems and use components/resources/channels to share data between systems.

    Example

    UCLASS()
    class BOIDS_API UBoidsMovementSystem : public USystem
    {
    	GENERATED_BODY()
    
    public:
    	virtual void Run(USystemsWorld& Systems) override;
    };
    
    void UBoidsMovementSystem::Run(USystemsWorld& Systems)
    {
    	Super::Run(Systems);
    
    	const auto* BoidsSettings = Systems.Resource<UBoidsSettings>();
    	if (IsValid(BoidsSettings) == false)
    	{
    		return;
    	}
    	const auto TimeScale = BoidsSettings->TimeScale;
    	const auto DeltaTime = Systems.GetWorld()->GetDeltaSeconds() * TimeScale;
    
    	for (auto& QueryItem : Systems.Query<UVelocityComponent, UBoidComponent>())
    	{
    		auto* Actor = QueryItem.Get<0>();
    		const auto* Velocity = QueryItem.Get<1>();
    		const auto Position = Actor->GetActorLocation();
    
    		Actor->SetActorLocation(Position + Velocity->Value * DeltaTime);
    	}
    }
    

    Arguments

    • Systems

      USystemsWorld& Systems
      

      Reference to systems world that triggers this system run.


Documentation built with Unreal-Doc v1.0.8 tool by PsichiX

Class: USystemsActorComponent

class SYSTEMS_API USystemsActorComponent
    : public UActorComponent;

Reflection-enabled

Specifiers:

  • BlueprintType
  • Blueprintable
  • Abstract

Meta Specifiers:

  • BlueprintSpawnableComponent

Base class for systems actor components.

Architecture book page explains more in depth actor components relation with systems world but in a brief: they are data stored in game world actors. You should inherit from this class any new component class that should get automatically registered to and unregistered from globally accessible systems worlds in USystemsSubsystem and then can be queried using USystemsWorld::Query. You can of course not inherit components from this class and manually register or unregister this component at will to any systems world if you want to make it available from custom or just non-globally accessible systems world, this class is useful only for automation of registering/unregistering components.


Properties

  • SystemsWorlds

    public:
    TSet<FName> SystemsWorlds;
    

    Reflection-enabled

    Specifiers:

    • EditAnywhere
    • Category = Systems|Component|Actor

    List of subsystem's systems worlds where this component should be registered.

    If list is empty, it means this component will be added to every registered systems world.


Methods

  • CanBeRegisteredToSystemsWorld

    public:
    virtual bool CanBeRegisteredToSystemsWorld() const;
    

    Tells if given component can be automatically registered into systems worlds.

    Useful to override when component has to meet certain conditions to get considered for automatic registration, conditions such as for example being owned by server game instance, or actor being a pawn.

    By default this method always returns true.

  • IsRegistered

    public:
    bool IsRegistered() const;
    

    Tells if this component is registered to subsystem's systems world.

  • SetRegistered

    public:
    void SetRegistered(
        bool bMode,
        bool bForced = false
    );
    

    Registers this component to subsystem's systems worlds listed in USystemsActorComponent::SystemsWorlds.


    Arguments

    • bMode

      bool bMode
      

      If true, component gets registered, otherwise it gets unregistered.

    • bForced

      bool bForced = false
      

      Forces registration change.

      Note

      Be careful, when bMode is true, this will not unregister this component from systems worlds where this component is already registered - this is useful mainly when get called in UActorComponent::BeginPlay to enforce initial registration when internal bRegister gets set to true in editor.


Documentation built with Unreal-Doc v1.0.8 tool by PsichiX

Class: USystemsGroupComponent

class SYSTEMS_API USystemsGroupComponent
    : public USystemsActorComponent;

Reflection-enabled


Component that groups actor components of certain type.

The point of this component is to allow Systems World to have a single point of iteration on more than one component of given type, which is not possible to do by default in Systems Architecture, because queries are single-typed.

This is useful mostly for cases where actor might have multiple components of same type which we would like to iterate on.

Note

Since queries require concrete type for each component, it's best to create classes per each components group type and make them inherit this class, so you register that specialized group type into Systems World and use same specialized group type in component queries.


Methods

  • Iter

    public:
    template <typename T>
    auto Iter();
    

    Gives iterator that yields grouped components that casts to T.

  • TagIter

    public:
    template <typename T>
    auto TagIter(
        FName Tag
    );
    

    Gives iterator that yields grouped components that has Tag tag and casts to T.


    Arguments

    • Tag

      FName Tag
      

Documentation built with Unreal-Doc v1.0.8 tool by PsichiX

Class: USystemsPipeline

class SYSTEMS_API USystemsPipeline
    : public UDataAsset;

Reflection-enabled

Specifiers:

  • BlueprintType

Data asset used to describe Systems pipeline.


Properties

  • AssetResourcesToInstall

    public:
    TMap<TObjectPtr<UDataAsset>, FSystemsPipelineAssetResource> AssetResourcesToInstall;
    

    Reflection-enabled

    Specifiers:

    • EditAnywhere
    • Category = Resources|Assets

    Asset resources to be registered into systems world.

    Usually these are used as settings/config data sources.

  • CleanupSystemsToRun

    public:
    TArray<FSystemsPipelineSystem> CleanupSystemsToRun;
    

    Reflection-enabled

    Specifiers:

    • EditAnywhere
    • Category = Systems|Cleanup

    System templates to instantiate and run on systems world destruction.

    Order in the list represents order of execution.

  • ComponentsToRegister

    public:
    TMap<TSubclassOf<UActorComponent>, FSystemsPipelineComponent> ComponentsToRegister;
    

    Reflection-enabled

    Specifiers:

    • EditAnywhere
    • Category = Components|Types

    Typed components to be registered into systems world.

  • PersistentSystemsToInstall

    public:
    TArray<FSystemsPipelineSystem> PersistentSystemsToInstall;
    

    Reflection-enabled

    Specifiers:

    • EditAnywhere
    • Category = Systems|Persistent

    System templates to instantiate and register into systems world to run every tick.

    Order in the list represents order of execution.

  • PipelinesToImport

    public:
    TArray<TObjectPtr<USystemsPipeline>> PipelinesToImport;
    

    Reflection-enabled

    Specifiers:

    • EditAnywhere
    • Category = Pipelines

    List of additional pipelines to import setup from when installing into systems world.

  • StartupSystemsToRun

    public:
    TArray<FSystemsPipelineSystem> StartupSystemsToRun;
    

    Reflection-enabled

    Specifiers:

    • EditAnywhere
    • Category = Systems|Startup

    System templates to instantiate and run on systems world creation.

    Order in the list represents order of execution.

  • TypeResourcesToInstall

    public:
    TMap<TSubclassOf<UObject>, FSystemsPipelineTypeResource> TypeResourcesToInstall;
    

    Reflection-enabled

    Specifiers:

    • EditAnywhere
    • Category = Resources|Types

    Typed resources to be registered into systems world.

    Usually these are used as game managers.


Methods

  • Install

    public:
    void Install(
        UWorld* World
    ) const;
    

    Creates new global systems world under USystemsPipeline::WorldId name and installs this pipeline content into that systems world.


    Arguments

  • InstallInto

    public:
    void InstallInto(
        USystemsWorld& Systems,
        TSet<TObjectPtr<USystemsPipeline>>& PipelinesToIgnore
    ) const;
    

    Installs this pipeline content into given systems world.


    Arguments

    • Systems

      USystemsWorld& Systems
      

      Target USystemsWorld.

      NOTE: Make sure to install pipeline in its Setup phase, before it gets sealed.

    • PipelinesToIgnore

      TSet<TObjectPtr<USystemsPipeline>>& PipelinesToIgnore
      

      List of pipelines to ignore when installing.

      This is needed for internal importing mechanism to avoid cycles of cross-referencing pipelines. You most likely might want to leave it empty when called yourself.

  • Uninstall

    public:
    void Uninstall(
        UWorld* World
    ) const;
    

    Destroys systems world registered under USystemsPipeline::WorldId name.


    Arguments


Documentation built with Unreal-Doc v1.0.8 tool by PsichiX

Class: USystemsReflection

class SYSTEMS_API USystemsReflection
    : public UBlueprintFunctionLibrary;

Reflection-enabled


Systems reflection blueprint library.


Documentation built with Unreal-Doc v1.0.8 tool by PsichiX

Class: USystemsSceneComponent

class SYSTEMS_API USystemsSceneComponent
    : public USceneComponent;

Reflection-enabled

Specifiers:

  • BlueprintType
  • Blueprintable
  • Abstract

Meta Specifiers:

  • BlueprintSpawnableComponent

Base class for systems scene components.

This is scene component equivalent to USystemsActorComponent - scene components are useful where given component should have transformation relative to actor.


Properties

  • SystemsWorlds

    public:
    TSet<FName> SystemsWorlds;
    

    Reflection-enabled

    Specifiers:

    • EditAnywhere
    • Category = Systems|Component|Scene

    List of subsystem's systems worlds where this component should be registered.

    If list is empty, it means this component will be added to every registered systems world.


Methods

  • CanBeRegisteredToSystemsWorld

    public:
    virtual bool CanBeRegisteredToSystemsWorld() const;
    

    Tells if given component can be automatically registered into systems worlds.

    Useful to override when component has to meet certain conditions to get considered for automatic registration, conditions such as for example being owned by server game instance, or actor being a pawn.

    By default this method always returns true.

  • IsRegistered

    public:
    bool IsRegistered() const;
    

    Tells if this component is registered to subsystem's systems world.

  • SetRegistered

    public:
    void SetRegistered(
        bool bMode,
        bool bForced = false
    );
    

    Registers this component to subsystem's systems worlds listed in USystemsSceneComponent::SystemsWorlds.


    Arguments

    • bMode

      bool bMode
      

      If true, component gets registered, otherwise it gets unregistered.

    • bForced

      bool bForced = false
      

      Forces registration change.

      Note

      Be careful, when bMode is true, this will not unregister this component from systems worlds where this component is already registered - this is useful mainly when get called in UActorComponent::BeginPlay to enforce initial registration when internal bRegister gets set to true in editor.


Documentation built with Unreal-Doc v1.0.8 tool by PsichiX

Class: USystemsStatics

class SYSTEMS_API USystemsStatics
    : public UBlueprintFunctionLibrary;

Reflection-enabled


Library of useful boilerplate-wrapping functions related to systems components.


Methods

  • AddComponent

    public:
    static void AddComponent(
        UActorComponent* Component,
        const TSet<FName>& SystemsWorlds,
        UObject* WorldContext
    );
    

    Reflection-enabled

    Specifiers:

    • BlueprintCallable
    • Category = Systems|Components

    Meta Specifiers:

    • WorldContext = WorldContext
    • UnsafeDuringActorConstruction = true

    Tries to add component to specified global systems worlds.

    Useful when creating proxy components that inherits from engine components to make them auto added to global systems worlds on BeginPlay.


    Arguments

    • Component

      UActorComponent* Component
      

      Actor component to add to global systems world.

    • SystemsWorlds

      const TSet<FName>& SystemsWorlds
      

      List of global systems worlds given component should be registered into.

    • WorldContext

      UObject* WorldContext
      

      World context object.

  • AddComponentEverywhere

    public:
    static void AddComponentEverywhere(
        UActorComponent* Component,
        UObject* WorldContext
    );
    

    Reflection-enabled

    Specifiers:

    • BlueprintCallable
    • Category = Systems|Components

    Meta Specifiers:

    • WorldContext = WorldContext
    • UnsafeDuringActorConstruction = true

    Tries to add component to every global systems worlds.

    Useful when creating proxy components that inherits from engine components to make them auto added to global systems worlds on BeginPlay.


    Arguments

    • Component

      UActorComponent* Component
      

      Actor component to add to global systems world.

    • WorldContext

      UObject* WorldContext
      

      World context object.

  • GetComponentRaw

    public:
    static UActorComponent* GetComponentRaw(
        FName Id,
        AActor* Actor,
        const TSubclassOf<UActorComponent>& Type,
        UObject* WorldContext
    );
    

    Reflection-enabled

    Specifiers:

    • BlueprintPure
    • Category = Systems|Components

    Meta Specifiers:

    • WorldContext = WorldContext
    • UnsafeDuringActorConstruction = true
    • DisplayName = Get Component
    • DeterminesOutputType = Type

    Gets actor component from globally registered systems world by its label.

    This is a handy shortcut for USystemsStatics::GetSystemsWorld and then USystemsWorld::ComponentRaw on it.


    Arguments

    • Id

      FName Id
      

      Systems world ID.

    • Actor

      AActor* Actor
      

      Actor to search in.

    • Type

      const TSubclassOf<UActorComponent>& Type
      

      Type of component to search for.

    • WorldContext

      UObject* WorldContext
      

      World context object.

  • GetResource

    public:
    template <class T>
    static T* GetResource(
        FName Id,
        UObject* WorldContext
    );
    

    Gets resource from globally registered systems world by its label.

    This is a handy template shortcut for USystemsStatics::ResourceRaw.


    Arguments

    • Id

      FName Id
      

      Systems world ID.

    • WorldContext

      UObject* WorldContext
      

      World context object.

  • GetResourceRaw

    public:
    static UObject* GetResourceRaw(
        FName Id,
        const UClass* Type,
        UObject* WorldContext
    );
    

    Reflection-enabled

    Specifiers:

    • BlueprintPure
    • Category = Systems|Resources

    Meta Specifiers:

    • WorldContext = WorldContext
    • UnsafeDuringActorConstruction = true
    • DisplayName = Get Resource
    • DeterminesOutputType = Type

    Gets resource from globally registered systems world by its label.

    This is a handy shortcut for USystemsStatics::GetSystemsWorld and then USystemsWorld::ResourceRaw on it.


    Arguments

    • Id

      FName Id
      

      Systems world ID.

    • Type

      const UClass* Type
      

      Resource type user wants to get from global systems world.

    • WorldContext

      UObject* WorldContext
      

      World context object.

  • GetSystemsWorld

    public:
    static USystemsWorld* GetSystemsWorld(
        FName Id,
        UObject* WorldContext
    );
    

    Reflection-enabled

    Specifiers:

    • BlueprintPure
    • Category = Systems|World

    Meta Specifiers:

    • WorldContext = WorldContext
    • UnsafeDuringActorConstruction = true

    Gets globally registered systems world by its label.

    Useful in places where we need to perform systems qorld query, but there is no systems world provided into the scope.


    Arguments

    • Id

      FName Id
      

      Systems world ID.

    • WorldContext

      UObject* WorldContext
      

      World context object.

  • RemoveComponent

    public:
    static void RemoveComponent(
        UActorComponent* Component,
        UObject* WorldContext
    );
    

    Reflection-enabled

    Specifiers:

    • BlueprintCallable
    • Category = Systems|Components

    Meta Specifiers:

    • WorldContext = WorldContext
    • UnsafeDuringActorConstruction = true

    Tries to add component to global systems worlds.

    Useful when creating proxy components that inherits from engine components to make them auto removed from global systems worlds on EndPlay.


    Arguments

    • Component

      UActorComponent* Component
      

      Actor component to remove from global systems world.

    • WorldContext

      UObject* WorldContext
      

      World context object.


Documentation built with Unreal-Doc v1.0.8 tool by PsichiX

Class: USystemsSubsystem

class SYSTEMS_API USystemsSubsystem
    : public UGameInstanceSubsystem;

Reflection-enabled


Global accessible registry of systems worlds.

Architecture book page explains more in depth how to interact with systems subsystem, but in a brief: This is a registry of globally accessible systems worlds game can have at any time.

Example

UCLASS()
class EXAMPLE_API UExampleGameInstance : public UGameInstance
{
	GENERATED_BODY()

private:
	virtual void Init() override;
};

void UExampleGameInstance::Init()
{
	Super::Init();

	auto* Subsystem = USystemsSubsystem::Get(GetWorld());
	if (IsValid(Subsystem))
	{
		Subsystem->AcquireSystemsWorld(FName(),
			[&](auto& Systems)
			{
				Systems.RegisterComponent<UShiaComponent>();

				Systems.InstallResource<UShiaSettings>();

				Systems.InstallLambdaSystem(JustDoItSystem, FInstallSystemOptions("JustDoIt"));
			});
	}
}

Methods

  • AcquireSystemsWorld

    public:
    void AcquireSystemsWorld(
        FName Id,
        TFunction<SystemSetupFunctor> SetupFunctor
    );
    

    Create, setup and register new systems world.

    Example

    UCLASS()
    class EXAMPLE_API AExampleGameMode : public AGameModeBase
    {
    	GENERATED_BODY()
    
    private:
    	virtual void InitGame(const FString& MapName, const FString& Options, FString& ErrorMessage) override;
    
    	virtual void EndPlay(const EEndPlayReason::Type EndPlayReason) override;
    };
    
    void AExampleGameMode::InitGame(const FString& MapName, const FString& Options, FString& ErrorMessage)
    {
    	Super::InitGame(MapName, Options, ErrorMessage);
    
    	auto* Subsystem = USystemsSubsystem::Get(GetWorld());
    	if (IsValid(Subsystem))
    	{
    		Subsystem->AcquireSystemsWorld(FName(),
    			[&](auto& Systems)
    			{
    				Systems.RegisterComponent<UShiaComponent>();
    
    				Systems.InstallResource<UShiaSettings>();
    
    				Systems.InstallLambdaSystem(JustDoItSystem, FInstallSystemOptions("JustDoIt"));
    			});
    	}
    }
    
    void AExampleGameMode::EndPlay(const EEndPlayReason::Type EndPlayReason)
    {
    	Super::EndPlay(EndPlayReason);
    
    	auto* Subsystem = USystemsSubsystem::Get(GetWorld());
    	if (IsValid(Subsystem))
    	{
    		Subsystem->ReleaseSystemsWorld(ThisClass::SYSTEMS_WORLD);
    	}
    }
    

    Arguments

    • Id

      FName Id
      

      Systems world ID.

    • SetupFunctor

      TFunction<SystemSetupFunctor> SetupFunctor
      

      Function/lambda that performs setup of created systems world.

      Note

      SystemSetupFunctor has given signature: void(USystemsWorld& Systems).

  • BlueprintAcquireSystemsWorld

    public:
    void BlueprintAcquireSystemsWorld(
        FName Id,
        FOnSetupSystemsWorld SetupDelegate
    );
    

    Reflection-enabled

    Specifiers:

    • BlueprintCallable
    • Category = Systems|Subsystem

    Meta Specifiers:

    • DisplayName = Acquire Systems World

    Create, setup and register new systems world.

    Blueprint-side version of USystemsSubsystem::AcquireSystemsWorld.


    Arguments

    • Id

      FName Id
      

      Systems world ID.

    • SetupDelegate

      FOnSetupSystemsWorld SetupDelegate
      

      Delegate that performs setup of created systems world.

      Note

      This delegate has given signature: void(USystemsWorld* Systems).

  • Get

    public:
    static USystemsSubsystem* Get(
        UWorld* World
    );
    

    Gets instance of this subsystem.


    Arguments

    • World

      UWorld* World
      

      World context object.

  • GetSystemsWorld

    public:
    USystemsWorld* GetSystemsWorld(
        FName Id
    );
    

    Reflection-enabled

    Specifiers:

    • BlueprintCallable
    • Category = Systems|Subsystem

    Meta Specifiers:

    • Displayname = Get Systems World

    Gets systems world by its label.


    Arguments

    • Id

      FName Id
      

      Systems world ID.

  • GetSystemsWorldsIds

    public:
    void GetSystemsWorldsIds(
        TSet<FName>& Output
    ) const;
    

    Gets list of all registered systems worlds IDs.

    Useful only for more advanced usecases like editor tools that might want to list currently existing systems world.


    Arguments

  • ReleaseSystemsWorld

    public:
    void ReleaseSystemsWorld(
        FName Id
    );
    

    Reflection-enabled

    Specifiers:

    • BlueprintCallable
    • Category = Systems|Subsystem

    Meta Specifiers:

    • Displayname = Release Systems World

    Unregister given systems world.

    Example

    UCLASS()
    class EXAMPLE_API AExampleGameMode : public AGameModeBase
    {
    	GENERATED_BODY()
    
    private:
    	virtual void InitGame(const FString& MapName, const FString& Options, FString& ErrorMessage) override;
    
    	virtual void EndPlay(const EEndPlayReason::Type EndPlayReason) override;
    };
    
    void AExampleGameMode::InitGame(const FString& MapName, const FString& Options, FString& ErrorMessage)
    {
    	Super::InitGame(MapName, Options, ErrorMessage);
    
    	auto* Subsystem = USystemsSubsystem::Get(GetWorld());
    	if (IsValid(Subsystem))
    	{
    		Subsystem->AcquireSystemsWorld(FName(),
    			[&](auto& Systems)
    			{
    				Systems.RegisterComponent<UShiaComponent>();
    
    				Systems.InstallResource<UShiaSettings>();
    
    				Systems.InstallLambdaSystem(JustDoItSystem, FInstallSystemOptions("JustDoIt"));
    			});
    	}
    }
    
    void AExampleGameMode::EndPlay(const EEndPlayReason::Type EndPlayReason)
    {
    	Super::EndPlay(EndPlayReason);
    
    	auto* Subsystem = USystemsSubsystem::Get(GetWorld());
    	if (IsValid(Subsystem))
    	{
    		Subsystem->ReleaseSystemsWorld(ThisClass::SYSTEMS_WORLD);
    	}
    }
    

    Arguments

    • Id

      FName Id
      

Documentation built with Unreal-Doc v1.0.8 tool by PsichiX

Class: USystemsWorld

class SYSTEMS_API USystemsWorld
    : public UObject;

Reflection-enabled

Specifiers:

  • BlueprintType

Container that holds systems, resources and registry of components that belongs to actors.

Architecture book page explains more in depth what systems world is, but in a brief: systems world is a central point user interacts with using queries to resources and actor components. Think of it as database - actor components are records and resources are unique singleton-like data (usually config/settings or things you would call managers).


Methods

  • Actors

    public:
    FActorsIter Actors();
    

    Acquires lazy-iterator over all registered actors.

    This works similar to USystemsWorld::Query but it yields only actors without their components, and it yields all actors. This method exists only for a last resort use cases - user should have a valid reason to query all actors and should always try to solve problem with USystemsWorld::Query.

    The only use case i can think of is when user needs to for example count all registered actors, but there are other use cases which can definitely be solved with regular component queries.

    Common use case that would be wrong to query actors would be:

    for (const auto* Actor : Systems.Actors())
    {
    	auto* ShiaActor = Cast<AShiaActor>(Actor);
    	if (IsValid(ShiaActor))
    	{
    		ShiaActor->JustDoit();
    	}
    }
    

    User should instead for example add UShiaComponent actor component and query actors using USystemsWorld::Query to iterate only on these actors that are marked as "Shia" actor using actor component tag:

    for (const auto* Actor : Systems.Query<UShiaComponent>())
    {
    	auto* ShiaActor = Cast<AShiaActor>(Actor);
    	if (IsValid(ShiaActor))
    	{
    		ShiaActor->JustDoit();
    	}
    }
    

    Another thing is that user should avoid putting any logic into the actors itself and rather create system that performs work of AShiaActor::JustDoIt() method. Although sometimes, mostly in case of interacting with third-party code, user is forced to call logic of actor so in this case just marking actor with component tag would be a sufficient compromise.

  • ActorsCount

    public:
    uint32 ActorsCount() const;
    

    Reflection-enabled


    Counts all registered actors.

    Useful for debugging purposes to show number of registered actors, but any other use case would and most likely should be solved with regular queries.

  • AddComponent

    public:
    bool AddComponent(
        UActorComponent* Component
    );
    

    Reflection-enabled

    Specifiers:

    • BlueprintCallable
    • Category = Systems

    Add actor component to registry.

    Called in USystemsActorComponent::BeginPlay and USystemsSceneComponent::BeginPlay methods so user does not have to, but in case of user dynamically removing actor component to achieve support for behavior toggling, adding components back to registry can be achieved with this method.

    Note

    Actor components are not registered immediately to avoid undefined behavior or even game crashes when performing this while iterating over systems world queries - rather they ar queued and registered after all systems complete their run on current game tick.

    Return

    True if both actor and component are valid.

    Example

    void ASomeActor::ToggleTagComponent(USystemsWorld& Systems, UTagComponent* Tag)
    {
    	this->bTagEnabled = !this->bTagEnabled;
    	if (this->bTagEnabled)
    	{
    		Systems.AddComponent(this, Tag);
    	}
    	else
    	{
    		Systems.RemoveComponent(this, Tag);
    	}
    }
    

    Arguments

    • Component

      UActorComponent* Component
      

      Component to be registered.

  • Cleanup

    public:
    void Cleanup();
    

    Reflection-enabled

    Specifiers:

    • BlueprintCallable
    • Category = Systems

    Cleanup existing systems.

    Method called in next tick after: [class: USystemsSubsystem::ReleaseSystemsWorld].

  • Component

    public:
    template <class T>
    T* Component(
        AActor* Actor
    );
    

    Tries to get pointer to registered actor component.

    Handy shortcut for USystemsWorld::ComponentRaw

    Return

    Pointer to component or nullptr in case component does not exist in registry.

    Note

    T should inherit from UActorComponent

    Example

    Systems.Component<UShiaComponent>(Actor)->JustDoIt();
    

    Arguments

    • Actor

      AActor* Actor
      

      Actor owning given component.

  • ComponentIndex

    public:
    TOptional<uint32> ComponentIndex(
        const UActorComponent* Component
    ) const;
    

    Get component registry index.

    Useful when working directly with FArchetypeSignature, but user most likely won't have any high-level use case for that.

    For getting component index by its class use: USystemsWorld::ComponentTypeIndex


    Arguments

    • Component

      const UActorComponent* Component
      

      Component which index of we ask for.

  • ComponentRaw

    public:
    UActorComponent* ComponentRaw(
        AActor* Actor,
        TSubclassOf<UActorComponent> Type
    );
    

    Reflection-enabled

    Specifiers:

    • BlueprintPure
    • Category = Systems

    Meta Specifiers:

    • DisplayName = Get Component
    • DeterminesOutputType = Type

    Tries to get pointer to registered actor component.

    Note

    Because components are registered after systems run this will always return nullptr whe trying to get actor component just after calling USystemsWorld::AddComponent

    Also when trying to get actor component just after calling USystemsWorld::RemoveComponent will return given component instead o nullptr because components get unregistered after systems run.

    Return

    Pointer to component or nullptr in case component does not exist in registry.

    Example

    Systems.ComponentRaw(Actor, UShiaComponent::StaticClass())->JustDoIt();
    

    Arguments

    • Actor

      AActor* Actor
      

      Actor owning given component.

    • Type

      TSubclassOf<UActorComponent> Type
      

      Component class.

  • ComponentTypeIndex

    public:
    TOptional<uint32> ComponentTypeIndex(
        const UClass* Type
    ) const;
    

    Get component registry index.

    Useful when working directly with FArchetypeSignature, but user most likely won't have any high-level use case for that.

    For getting component index by component: USystemsWorld::ComponentIndex


    Arguments

    • Type

      const UClass* Type
      
  • Components

    public:
    template <class... T>
    TTuple<T*...> Components(
        AActor* Actor
    );
    

    Gets tuple of actor components.

    Handy wrapper for USystemsWorld::Component in case of asking for more than one actor component.

    Note

    Works similar way to USystemsWorld::Query but do not put actor in its first tuple element, rather gives exactly the pointers to actor components user requests. It's worth noting that in case of component not being registered, it returns nullptr in tuple elements corresponding to requested actor component type.


    Arguments

  • ComponentsDidChanged

    public:
    template <class... T>
    bool ComponentsDidChanged() const;
    

    Tells if components changed during last game tick.

    handy wrapper for USystemsWorld::ComponentsDidChangedRaw.

  • ComponentsDidChangedRaw

    public:
    bool ComponentsDidChangedRaw(
        const FArchetypeSignature& Signature
    ) const;
    

    Reflection-enabled


    Tells if components changed during last game tick.

    Note

    This checks if any, not all of components marked in signature changed.


    Arguments

    • Signature

      const FArchetypeSignature& Signature
      
  • ComponentsSignature

    public:
    FArchetypeSignature ComponentsSignature(
        const TArrayView<UActorComponent*>& View
    ) const;
    

    Get archetype signature of given set of components.

    Useful when working directly with FArchetypeSignature, but user most likely won't have any high-level use case for that.


    Arguments

    • View

      const TArrayView<UActorComponent*>& View
      
  • Count

    public:
    template <class... T>
    uint32 Count() const;
    

    Counts actors that contain given archetype signature.

    This is ergonomic shortcut for USystemsWorld::CountRaw that only counts types that should be included.

    Note

    T classes should inherit from UActorComponent!

    Example

    const auto Result = Systems.Count<UShiaComponent>();
    
  • CountRaw

    public:
    uint32 CountRaw(
        const FArchetypeSignature& IncludeSignature,
        const FArchetypeSignature& ExcludeSignature = {}
    ) const;
    

    Reflection-enabled


    Counts actors that contain given archetype include signature and do not contains exclude signature.

    This is more performant way of counting actors with given set of components (although non-ergonomic for sure):

    	// Instead of this:
    	const auto A = Systems.Query<UShiaComponent>().Count();
    
    	// You can do this:
    	auto Signature = FArchetypeSignature();
    	if (const auto Index = Systems.ComponentTypeIndex(UShiaComponent::StaticClass()))
    	{
    		Signature.EnableBit(Index.GetValue());
    	}
    
    	const auto B = Systems.CountRaw(Signature);
    

    See USystemsWorld::Count for more ergonomic use.

    Note

    For example if requested signature is: <A, B> and there are actors: 1: A, B, C and 2: A, C then only actor 1: A, B, C gets counted since only this one contains entire requested signature.


    Arguments

    • IncludeSignature

      const FArchetypeSignature& IncludeSignature
      

      Archetype signature with minimal set of components that counted actors should contain.

    • ExcludeSignature

      const FArchetypeSignature& ExcludeSignature = {}
      

      Archetype signature with minimal set of components that counted actors should not contain.

  • DynamicQuery

    public:
    template <class T>
    UDynamicQuery* DynamicQuery();
    

    Acquires lazy-iterator to dynamically queried actor components.

    Handy shortcut for USystemsWorld::SpawnQuery.

    See UDynamicQuery

  • InstallDefaultResource

    public:
    bool InstallDefaultResource(
        const UClass* Type
    );
    

    Reflection-enabled

    Specifiers:

    • BlueprintCallable
    • Category = Systems

    Register resource object.

    See USystemsWorld::InstallResourceRaw

    Return

    True if resource was successfully installed (registry is not sealed).

    Example

    Systems.InstallDefaultResource(UInventory::StaticClass());
    

    Arguments

    • Type

      const UClass* Type
      

      Resource class to get constructed and registered.

  • InstallDefaultSystem

    public:
    bool InstallDefaultSystem(
        const UClass* Type,
        FInstallSystemOptions Options
    );
    

    Reflection-enabled

    Specifiers:

    • BlueprintCallable
    • Category = Systems

    Install system.

    See USystemsWorld::InstallSystemRaw, USystem, FInstallSystemOptions

    Return

    True if system was successfully installed (registry is not sealed).

    Example

    Systems.InstallDefaultSystem(USomeSystem::StaticClass(), FInstallSystemOptions("Something"));
    

    Arguments

    • Type

      const UClass* Type
      

      Class of system being installed.

    • Options

      FInstallSystemOptions Options
      

      System install options.

  • InstallLambdaSystem

    public:
    bool InstallLambdaSystem(
        TFunction<SystemsWorld::LambdaSystemType>&& Functor,
        FInstallSystemOptions Options = FInstallSystemOptions()
    );
    

    Install state-less (function or lambda) system.

    Stateless systems are the most common ones because usually what system does it only processes the data, so creating function/lambda systems brings more ergonomics into codebase.

    See FInstallSystemOptions

    Return

    True if system was successfully installed (registry is not sealed).

    Example

    UFUNCTION()
    void BoidsFaceDirectionSystem(USystemsWorld& Systems);
    
    void BoidsFaceDirectionSystem(USystemsWorld& Systems)
    {
    	for (auto& QueryItem : Systems.Query<UVelocityComponent, UBoidComponent>())
    	{
    		auto* Actor = QueryItem.Get<0>();
    		const auto* Velocity = QueryItem.Get<1>();
    
    		if (Velocity->Value.IsNearlyZero() == false)
    		{
    			Actor->SetActorRotation(Velocity->Value.Rotation());
    		}
    	}
    }
    
    Systems.InstallLambdaSystem(BoidsFaceDirectionSystem, FInstallSystemOptions("BoidsFaceDirection"));
    

    Arguments

    • Functor

      TFunction<SystemsWorld::LambdaSystemType>&& Functor
      

      Function or lambda being installed as system

      Note

      LambdaSystemType should comply to given signature: void(USystemsWorld&)

    • Options

      FInstallSystemOptions Options = FInstallSystemOptions()
      

      System install options.

  • InstallProxyResource

    public:
    template <class T>
    bool InstallProxyResource(
        UObject* Resource,
        TFunction<SystemsWorld::LambdaFactoryType> Accessor
    );
    

    Register proxy resource object.

    Handy shortcut for USystemsWorld::InstallProxyResourceRaw

    Return

    True if resource was successfully installed (registry is not sealed).

    Example

    UCLASS()
    class EXAMPLE_API UInventoryWrapper : public UDataAsset
    {
    	GENERATED_BODY()
    
    public:
    	UPROPERTY()
    	UInventory* GeneratedInventory = nullptr;
    };
    
    auto* Wrapper = NewObject<UInventoryWrapper>(Systems, UInventoryWrapper::StaticClass());
    Systems.InstallProxyResource<UInventory>(Wrapper, [](auto* Wrapper) { return Wrapper->GeneratedInventory; });
    

    Arguments

    • Resource

      UObject* Resource
      

      Wrapper resource object.

    • Accessor

      TFunction<SystemsWorld::LambdaFactoryType> Accessor
      

      Accessor function that unpacks wrapper object to get its inner resource.

  • InstallProxyResourceRaw

    public:
    bool InstallProxyResourceRaw(
        const UClass* Type,
        UObject* Resource,
        TFunction<SystemsWorld::LambdaFactoryType> Accessor
    );
    

    Register proxy resource object.

    Proxy resources are typically some wrapper objects inner resource we want to access instead of the wrapper one. Basically it does the same what USystemsWorld::InstallResourceRaw does, except it allows user to provide unpacking of its inner content.

    Return

    True if resource was successfully installed (registry is not sealed).

    Example

    UCLASS()
    class EXAMPLE_API UInventoryWrapper : public UDataAsset
    {
    	GENERATED_BODY()
    
    public:
    	UPROPERTY()
    	UInventory* GeneratedInventory = nullptr;
    };
    
    Systems.InstallProxyResourceRaw(UInventory::StaticClass(),
    	NewObject<UInventoryWrapper>(Systems, UInventoryWrapper::StaticClass()),
    	[](auto* Wrapper) { return Wrapper->GeneratedInventory; });
    

    Arguments

    • Type

      const UClass* Type
      

      Resource object to get registered and managed by this systems world.

    • Resource

      UObject* Resource
      

      Wrapper resource object.

    • Accessor

      TFunction<SystemsWorld::LambdaFactoryType> Accessor
      

      Accessor function that unpacks wrapper object to get its inner resource.

  • InstallResource

    public:
    template <class T>
    bool InstallResource();
    

    Register resource object.

    Handy shortcut for USystemsWorld::InstallDefaultResource

    Return

    True if resource was successfully installed (registry is not sealed).

    Example

    Systems.InstallResource<UInventory>();
    
  • InstallResourceRaw

    public:
    bool InstallResourceRaw(
        UObject* Resource
    );
    

    Reflection-enabled

    Specifiers:

    • BlueprintCallable
    • Category = Systems

    Meta Specifiers:

    • DisplayName = Install Resource

    Register resource object.

    It accepts any object that inherits from UObject. Also systems world takes ownership over provided resource so its best to not pass any object that has its lifetime managed by other object.

    Return

    True if resource was successfully installed (registry is not sealed).

    Example

    Systems.InstallResourceRaw(NewObject<UInventory>(Systems, UInventory::StaticClass()));
    

    Arguments

    • Resource

      UObject* Resource
      

      Resource object to get registered and managed by this systems world.

  • InstallSystem

    public:
    template <class T>
    bool InstallSystem(
        FInstallSystemOptions Options
    );
    

    Install system.

    Handy shortcut for USystemsWorld::InstallDefaultSystem

    See USystem, FInstallSystemOptions

    Note

    Make sure T is a class that inherits from USystem!

    Return

    True if system was successfully installed (registry is not sealed).

    Example

    Systems.InstallSystem<USomeSystem>(FInstallSystemOptions("Something"));
    

    Arguments

    • Options

      FInstallSystemOptions Options
      

      System install options.

  • InstallSystemRaw

    public:
    bool InstallSystemRaw(
        USystem* System,
        FInstallSystemOptions Options
    );
    

    Reflection-enabled

    Specifiers:

    • BlueprintCallable
    • Category = Systems

    Meta Specifiers:

    • DisplayName = Install System

    Install system.

    Usually user would want to install systems using either USystemsWorld::InstallSystem or USystemsWorld::InstallLambdaSystem but in case of valid reasons user can install system by its instance.

    Note

    In case of FInstallSystemOptions::Label being empty, it will generate random label from new GUID.

    See USystem, FInstallSystemOptions

    Return

    True if system was successfully installed (registry is not sealed).

    Example

    Systems.InstallSystemRaw(
    	NewObject<USomeSystem>(Systems, USomeSystem::StaticClass()), FInstallSystemOptions("Something"));
    

    Arguments

    • System

      USystem* System
      

      Pointer to system being installed.

    • Options

      FInstallSystemOptions Options
      

      System install options.

  • IsSealed

    public:
    bool IsSealed() const;
    

    Reflection-enabled

    Specifiers:

    • BlueprintCallable
    • Category = Systems

    Tells if systems world is sealed.

    Sealed systems world means that it has completed its setup phase and is ready to run or already running.

  • LastChangedComponents

    public:
    const FArchetypeSignature& LastChangedComponents() const;
    

    Reflection-enabled


    Returns signature of component types that changed during last game tick.

    Useful for more use cases where user needs to cache and perform more advanced change detection between game ticks.

  • LastChangedResources

    public:
    const TSet<uint32>& LastChangedResources() const;
    

    Returns a set of unique type IDs of all resources that changed in last game tick.

    Useful for more advanced use cases where user needs to ask for all changes anyway and compare them with some cached set of previously stored changes.

  • MarkComponentChanged

    public:
    void MarkComponentChanged(
        UActorComponent* Component
    );
    

    Reflection-enabled

    Specifiers:

    • BlueprintCallable
    • Category = Systems

    Marks component as changed.

    Note

    This will mark component type, not component instance, as changed. The need for component instance here is purely to ensure we do not mark components we do not have access to.


    Arguments

  • MarkResourceChanged

    public:
    void MarkResourceChanged(
        UObject* Resource
    );
    

    Reflection-enabled

    Specifiers:

    • BlueprintCallable
    • Category = Systems

    Marks given resource as changed.

    Useful if user wants to create reactive systems and/or UI that should only trigger when given resource changes. The reason why user has to manually mark resources as changed is for optimizations purposes, to mark deliberate changes in resources instead of marking them automatically, to avoid a lot of boilerplate of that automation, when most of the times systems and UI do not require to ask for changes.

    Use USystemsWorld::ResourceDidChanged to ask if some resource has changed.


    Arguments

  • Process

    public:
    void Process();
    

    Reflection-enabled

    Specifiers:

    • BlueprintCallable
    • Category = Systems

    Process systems world.

    It performs:

    • unregistering of removed components and/or actors.
    • registering of added components and/or actors.
    • run systems logic.

    In case of registry not being sealed at the time of calling this method, none of steps above are gonna be performed.

    This method is called automatically for global systems world managed by USystemsSubsystem, but in case of user managing systems world on their own, user can do:

    	auto* Systems = NewObject<USystemsWorld>(this, USystemsWorld::StaticClass());
    
    	// [Systems world setup...]
    
    	Systems->Process();
    
  • ProxyResource

    public:
    template <typename T>
    T* ProxyResource();
    

    Tries to get pointer to proxy resource by its type.

    See:

    Return

    Pointer to proxy resource or nullptr in case resource does not exist in registry.

    Example

    Systems.ProxyResource<UInventory>()->AddItem(FItem{FItemType::Sword});
    
  • ProxyResourceRaw

    public:
    UObject* ProxyResourceRaw(
        const UClass* Type
    );
    

    Tries to get pointer to proxy resource by its type.

    See:

    Return

    Pointer to proxy resource or nullptr in case resource does not exist in registry.

    Example

    auto* Inventory = Cast<UInventory>(Systems.ProxyResourceRaw(UInventory::StaticClass()));
    Inventory->AddItem(FItem{FItemType::Sword});
    

    Arguments

    • Type

      const UClass* Type
      

      Resource type.

  • Query

    public:
    template <class... T>
    TQuery<T...> Query();
    

    Acquires lazy-iterator to query actor components.

    More about iterators in this architecture book page.

    Queries allow to yield tuples of actor and their components, and only those that comply to given query signature, so there is no iteration over any actor that do not have given component types - actors and components are registered to buckets called archetypes, and archetypes are unique as long as their signature is unique. Signature is constructed from types provided to query, as well as from types registered to systems world that belong to the same actor. Systems architecture focuses on performing queries as fast as possible and not iterating over actors that do not need to be queried was a priority.

    See TQuery

    Note

    Returned query iterator has always actor put as first item of item tuple and then follow requested components. So Systems->Query<A, B, C>() iterator will yield given tuple TTuple<AActor*, A*, B*, C*>

    Example

    	const auto Count = static_cast<int>(Systems.Query<UBoidComponent>().Count());
    	const auto Difference = Count - EXPECTED_POPULATION_NUMBER;
    
    	if (Difference > 0)
    	{
    		for (auto& QueryItem : Systems.Query<UBoidComponent>().Take(Difference))
    		{
    			auto* Actor = QueryItem.Get<0>();
    			Actor->Destroy();
    		}
    	}
    
  • RegisterComponent

    public:
    template <class T>
    bool RegisterComponent();
    

    Register component type.

    Templated shortcut for USystemsWorld::RegisterComponentRaw

    See UActorComponent

    Return

    True if component was successfully installed (registry is not sealed and registry haven't reached its capacity).

    Note

    T has to be a component that inherits from UActorComponent

    Example

    Systems.RegisterComponent<USomeComponent>();
    
  • RegisterComponentRaw

    public:
    bool RegisterComponentRaw(
        const UClass* Type
    );
    

    Reflection-enabled

    Specifiers:

    • BlueprintCallable
    • Category = Systems

    Meta Specifiers:

    • DisplayName = Register Component

    Register component type.

    Prior to FArchetypeSignature being usable for queries and other architecture parts, it has to be able to identify components and for that they has to be registered by their class.

    See UActorComponent

    Return

    True if component was successfully installed (registry is not sealed and registry haven't reached its capacity).

    Example

    Systems.RegisterComponentRaw(USomeComponent::StaticClass());
    

    Arguments

    • Type

      const UClass* Type
      

      Class of component that has to inherit from UActorComponent

  • RemoveComponent

    public:
    bool RemoveComponent(
        UActorComponent* Component
    );
    

    Reflection-enabled

    Specifiers:

    • BlueprintCallable
    • Category = Systems

    Remove actor component from registry.

    Called in USystemsActorComponent::EndPlay and USystemsSceneComponent::EndPlay methods so user does not have to, but in case of user dynamically adding actor component to achieve support for behavior toggling, removing components from registry can be achieved with this method.

    Note

    Actor components are not unregistered immediately to avoid undefined behavior or eve game crashes when performing this while iterating over systems world queries - rather the are queued and unregistered after all systems complete their run on current game tick.

    Return

    True if both actor and component are valid.

    Example

    void ASomeActor::ToggleTagComponent(USystemsWorld& Systems, UTagComponent* Tag)
    {
    	this->bTagEnabled = !this->bTagEnabled;
    	if (this->bTagEnabled)
    	{
    		Systems.AddComponent(this, Tag);
    	}
    	else
    	{
    		Systems.RemoveComponent(this, Tag);
    	}
    }
    

    Arguments

    • Component

      UActorComponent* Component
      

      Component to be unregistered.

  • Resource

    public:
    template <typename T>
    T* Resource();
    

    Tries to get pointer to resource by its type.

    See:

    Return

    Pointer to resource or nullptr in case resource does not exist in registry.

    Example

    Systems.Resource<UInventory>()->AddItem(FItem{FItemType::Sword});
    
  • ResourceDidChanged

    public:
    template <class T>
    bool ResourceDidChanged() const;
    

    Tells if given resource type did changed in last game tick.

    Handy wrapper for USystemsWorld::ResourceDidChangedRaw.

  • ResourceDidChangedRaw

    public:
    bool ResourceDidChangedRaw(
        const UClass* Type
    ) const;
    

    Reflection-enabled

    Specifiers:

    • BlueprintCallable
    • Category = Systems

    Tells if given resource type did changed in last game tick.

    See USystemsWorld::MarkResourceChanged.


    Arguments

    • Type

      const UClass* Type
      
  • ResourceRaw

    public:
    UObject* ResourceRaw(
        const UClass* Type
    );
    

    Reflection-enabled

    Specifiers:

    • BlueprintPure
    • Category = Systems

    Meta Specifiers:

    • DisplayName = Get Resource
    • DeterminesOutputType = Type

    Tries to get pointer to resource by its class.

    See:

    Return

    Pointer to resource or nullptr in case resource does not exist in registry.

    Example

    auto* Inventory = Cast<UInventory>(Systems.ResourceRaw(UInventory::StaticClass()));
    Inventory->AddItem(FItem{FItemType::Sword});
    

    Arguments

    • Type

      const UClass* Type
      

      Resource class.

  • SealAndInitialize

    public:
    void SealAndInitialize();
    

    Reflection-enabled

    Specifiers:

    • BlueprintCallable
    • Category = Systems

    Seal registry and initialize installed systems.

    Method called by: USystemsSubsystem::AcquireSystemsWorld.

    When user does not use USystemsSubsystem as global systems world registry, or wants to handle systems world on their own, user should call this method after systems world setup (registering components, installation of systems and resources) and then call this method.

    Note

    After calling this method, no further successful system or resource installation i possible, so make sure to install systems world components before sealing systems world!

    Example

    	auto* Systems = NewObject<USystemsWorld>(this, USystemsWorld::StaticClass());
    	if (IsValid(Systems) == false)
    	{
    		Systems->InstallResource<USomeResource>();
    
    		Systems->InstallLambdaSystem(SomeSystem, FInstallSystemOptions("Something"));
    
    		Systems->SealAndInitialize();
    	}
    
  • SpawnQuery

    public:
    UDynamicQuery* SpawnQuery(
        const UClass* BundleType
    );
    

    Reflection-enabled

    Specifiers:

    • BlueprintCallable
    • Category = Systems

    Meta Specifiers:

    • DisplayName = Query
    • DevelopmentOnly

    Acquires lazy-iterator to dynamically queried actor components.

    Because user cannot use templated types in blueprints, dynamic queries are a way to query systems world in blueprints. Also dynamic queries do not implement lazy-iterators so they are definitely not an ergonomic way to iterate over actor components and should be avoided in favor of USystems::Query.

    See UDynamicQuery


    Arguments

  • TaggedQuery

    public:
    template <class... T>
    TTaggedQuery<T...> TaggedQuery();
    

    Acquires lazy-iterator to query actor components with additional tag components.

    The difference between TQuery is that tagged queries allows to request existence of additional components on actor, ones that are not required for query to access - useful when user do not require any data of given components.

    See TTaggedQuery

    Example

    	for (auto& QueryItem : Systems.TaggedQuery<UVelocityComponent>().With<UBoidComponent>().Iter())
    	{
    		auto* Actor = QueryItem.Get<0>();
    		const auto* Velocity = QueryItem.Get<1>();
    		const auto Position = Actor->GetActorLocation();
    
    		Actor->SetActorLocation(Position + Velocity->Value * DletaTime);
    	}
    

Documentation built with Unreal-Doc v1.0.8 tool by PsichiX

Functions


Documentation built with Unreal-Doc v1.0.8 tool by PsichiX

Function: IterArray

public:
template <
	//// [ignore]
	const int N,
	//// [/ignore]
	typename T>
TIterArray<N, T> IterArray(
    std::initializer_list<T> Args
);

Iterator that yields values from fixed size array stored internally.

Example

// [1, 2, 3, 4, 5]
const TArray<int> Result = IterArray<5>({1, 2, 3, 4, 5}).CollectArray();

Arguments

  • Args

    std::initializer_list<T> Args
    

Documentation built with Unreal-Doc v1.0.8 tool by PsichiX

Function: IterGenerate

public:
template <typename T, typename F>
TIterGenerate<T, F> IterGenerate(
    F Functor
);

Iterator that generates values infinitely.

Useful for example for yielding random values, or anything that user would want to generate procedurally. It is useful to combine it with Take iterator to limit number of iterations in loop or for collecting values it yields.

Example

// [?, ?, ?, ?, ?]
const TArray<int> Result = IterGenerate<int>([]() { return FMath::Rand(); }).Take(5).CollectArray();

Arguments

  • Functor

    F Functor
    

    Functor that should comply to signature: T().


Documentation built with Unreal-Doc v1.0.8 tool by PsichiX

Function: IterOnce

public:
template <typename T>
TIterOnce<T> IterOnce(
    T Value
);

Iterator that yields one value only once.

Useful to use for yielding "leading/trailing/separator" values in chains of multiple iterators.

Example

// [0, 1, 2, 3, 4, -1, 5, 6, 7, 8, 9]
const TArray<int> Result = IterRange(0, 5).Chain(IterOnce(-1)).Chain(IterRange(5, 10)).CollectArray();

Arguments

  • Value

    T Value
    

    Value that iterator should yield once.


Documentation built with Unreal-Doc v1.0.8 tool by PsichiX

Function: IterRange

public:
template <typename T>
TIterRange<T> IterRange(
    T From,
    T To
);

Iterator that yields range of values.

Range:

  • inclusive lower bound.
  • exclusive upper bound. For range from 0 to 5 it means yielding values: 0, 1, 2, 3, 4.

Note

This iterator is value type agnostic, all it requires from type is for it to implemen operator++ and operator-;

Example

// [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
const TArray<int> Result = IterRange(0, 10).CollectArray();

Arguments

  • From

    T From
    

    Lower inclusive bound.

  • To

    T To
    

    Upper exclusive bound.


Documentation built with Unreal-Doc v1.0.8 tool by PsichiX

Function: IterRepeat

public:
template <typename T>
TIterRepeat<T> IterRepeat(
    T Value
);

Iterator that yields one value infinitely.

Useful to combine it with Take iterator to avoid infinite loops or collecting iterator values that never ends.

Example

// [1, 1, 1, 1, 1]
const TArray<int> Result = IterRepeat(1).Take(5).CollectArray();

Arguments

  • Value

    T Value
    

    Value that iterator should yield infinitely.


Documentation built with Unreal-Doc v1.0.8 tool by PsichiX

Function: IterStd

public:
template <typename T>
TIterStd<T> IterStd(
    T& Container
);

Iterator that yields mutable values from standard Unreal Engine collections (such as arrays, sets and maps).

Example

// [0, 1, 2, 3, 4]
TArray<int> Result = IterRange(0, 5).CollectArray();
// [0, 2, 4, 9, 16]
for (auto& Value : IterStd(Result))
{
	Value = Value * Value;
}

Arguments

  • Container

    T& Container
    

    Reference to Unreal Engine collection.


Documentation built with Unreal-Doc v1.0.8 tool by PsichiX

Function: IterStdConst

public:
template <typename T>
TIterStdConst<T> IterStdConst(
    const T& Container
);

Iterator that yields immutable values from standard Unreal Engine collections (such as arrays, sets and maps).

Example

// [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
const TArray<int> ResultA = IterRange(0, 10).CollectArray();
// [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
const TArray<int> ResultB = IterStd(ResultA).CollectArray();

Arguments

  • Container

    const T& Container
    

    Reference to Unreal Engine collection.


Documentation built with Unreal-Doc v1.0.8 tool by PsichiX