Skip to content

Commit 72fbc79

Browse files
committed
feat: added unreal runner docs
1 parent ba6595d commit 72fbc79

File tree

3 files changed

+267
-1
lines changed

3 files changed

+267
-1
lines changed
Lines changed: 259 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,262 @@
11
<article>
22
<h1>Ecsact Runner</h1>
3+
<p>
4+
The Ecsact Runner is a UObject that is automatically created when your game
5+
starts that executes your Ecsact systems. The Ecsact Unreal plugin comes
6+
with 2 runner classes.
7+
<a [routerLink]="[]" fragment="sync-runner"
8+
><code>UEcsactSyncRunner</code></a
9+
>
10+
and
11+
<a [routerLink]="[]" fragment="async-runner"
12+
><code>UEcsactAsyncRunner</code></a
13+
>. As the names imply one is synchronous and the other is asynchronous.
14+
Alternatively you may create a
15+
<a [routerLink]="[]" fragment="custom-runner">custom runner</a> or
16+
<a [routerLink]="[]" fragment="no-runner">no runner</a>
17+
at all. Keep reading to understand the differences between the different
18+
runners.
19+
</p>
20+
<p>
21+
By the default the Ecsact Unreal plugin is configured to automatically
22+
choose between the two depending on which Runtime API functions are
23+
available.
24+
</p>
25+
26+
<section>
27+
<h2 id="execution">Execution</h2>
28+
<p>
29+
Each runner implementation handles execution differently, but the API is
30+
the same.
31+
</p>
32+
<h3 id="execution-push-action">Executing Ecsact Actions</h3>
33+
<p>
34+
Executing an <a routerLink="/docs/lang" fragment="actions">action</a> is
35+
as simple as calling <code>PushAction</code> on your runner instance. This
36+
can be done anywhere on the game thread and the runner implementation will
37+
queue up the action as soon as possible. Make sure you include your
38+
<a routerLink="/start/unreal/codegen">code generated</a> Ecsact C++
39+
header.
40+
</p>
41+
42+
<code-block-variation class="m-0 rounded-md mt-3">
43+
<pre
44+
codeBlockVariationOption
45+
optionTitle="C++"><code prism language="cpp">
46+
#include "CoolCharacter.h"
47+
#include "EcsactUnreal/EcsactExecution.h" // for accessing EcsactUnrealExecution::Runner
48+
#include "Example.ecsact.hh" // generated from Ecsact.ecsact file
49+
50+
void ACoolCharacter::DoCoolAction(const FInputActionValue{{'&'}} Value) {{'{'}}
51+
UEcsactRunner* Runner = EcsactUnrealExecution::Runner(GetWorld()).Get();
52+
Runner-&gt;PushAction(example::CoolAction{{'{'}}.power = 10.f{{'}'}});
53+
{{'}'}}
54+
</code></pre>
55+
</code-block-variation>
56+
57+
<h3 id="execution-entity">Create & Destroy Entities</h3>
58+
<p>
59+
While its not often that you should need to create and destroy entities
60+
there are times it is necessary. The runner execution has two functions
61+
for easily creating and destroying entities. <code>CreateEntity</code> and
62+
<code>DestroyEntity</code>.
63+
</p>
64+
<code-block-variation class="m-0 rounded-md mt-3">
65+
<pre
66+
codeBlockVariationOption
67+
optionTitle="C++"><code prism language="cpp">
68+
#include "CoolCharacter.h"
69+
#include "EcsactUnreal/EcsactExecution.h" // for accessing EcsactUnrealExecution::Runner
70+
#include "ecsact/runtime/common.h" // for ECSACT_INVALID_ID
71+
#include "Example.ecsact.hh" // generated from Ecsact.ecsact file
72+
73+
ACoolCharacter::ACoolCharacter() {{'{'}}
74+
// Set initial value to an invalid ID
75+
// Will be created later in CreatePlayerEntity()
76+
PlayerEntity = ECSACT_INVALID_ID(entity);
77+
{{'}'}}
78+
79+
void ACoolCharacter::CreatePlayerEntity() {{'{'}}
80+
UEcsactRunner* Runner = EcsactUnrealExecution::Runner(GetWorld()).Get();
81+
82+
// Assign the entity ID for later
83+
PlayerEntity = Runner-&gt;CreateEntity()
84+
.AddComponent(example::Position{{'{}'}})
85+
.AddComponent(example::Player{{'{}'}});
86+
{{'}'}}
87+
88+
void ACoolCharacter::DestroyPlayerEntity() {{'{'}}
89+
UEcsactRunner* Runner = EcsactUnrealExecution::Runner(GetWorld()).Get();
90+
Runner-&gt;DestroyEntity(PlayerEntity);
91+
PlayerEntity = ECSACT_INVALID_ID(entity);
92+
{{'}'}}
93+
</code></pre>
94+
</code-block-variation>
95+
96+
<h3 id="execution-add-component">Manipulating Components</h3>
97+
<p>
98+
Sometimes (but rarely) you'll even need to manipulate some components.
99+
Even though we recommend you use systems to read and write to components
100+
we have methods for conveniently doing so without.
101+
</p>
102+
103+
<code-block-variation class="m-0 rounded-md mt-3">
104+
<pre
105+
codeBlockVariationOption
106+
optionTitle="C++"><code prism language="cpp">
107+
#include "CoolCharacter.h"
108+
#include "EcsactUnreal/EcsactExecution.h" // for accessing EcsactUnrealExecution::Runner
109+
#include "Example.ecsact.hh" // generated from Ecsact.ecsact file
110+
111+
void ACoolCharacter::ColorizeMe() {{'{'}}
112+
UEcsactRunner* Runner = EcsactUnrealExecution::Runner(GetWorld()).Get();
113+
// Add the 'Color' component to me!
114+
// NOTE: this is considered an error if the entity already has this component
115+
// NOTE: prefer adding components in a system!
116+
Runner-&gt;AddCompnent(PlayerEntity, example::Color{{'{'}}.r = 1.0f, .g = 0.0f, .b = 0.0f{{'}'}});
117+
{{'}'}}
118+
119+
void ACoolCharacter::DecolorizeMe() {{'{'}}
120+
UEcsactRunner* Runner = EcsactUnrealExecution::Runner(GetWorld()).Get();
121+
// Remove the 'Color' component!
122+
// NOTE: this is considered an error if the entity does not have this component
123+
// NOTE: prefer removing components in a system!
124+
Runner-&gt;RemoveComponent&lt;example::Color&gt;(PlayerEntity);
125+
{{'}'}}
126+
127+
void ACoolCharacter::MakeMeBlue() {{'{'}}
128+
UEcsactRunner* Runner = EcsactUnrealExecution::Runner(GetWorld()).Get();
129+
// NOTE: this is considered an error if the entity does not have this component
130+
// NOTE: prefer updating components in a system!
131+
Runner-&gt;UpdateComponent(PlayerEntity, example::Color{{'{'}}.r = 0.0f, .g = 0.0f, .b = 1.0f{{'}'}});
132+
{{'}'}}
133+
</code></pre>
134+
</code-block-variation>
135+
</section>
136+
137+
<section>
138+
<h2 id="sync-runner">Synchronous Runner</h2>
139+
<p>
140+
The <code>USyncRunner</code> runs
141+
<code>ecsact_execute_systems</code> every tick. Meaning that every tick
142+
your Ecsact systems will execute. If you derive from this class make sure
143+
you're calling <code>Super::Tick</code> or none of your
144+
<a routerLink="/start/unreal/subsystems">runner subsystems</a> will fire
145+
any Ecsact events. Your
146+
<a routerLink="/start/unreal/runtime" fragment="building-the-runtime"
147+
>Ecsact Runtime</a
148+
>
149+
must built to support the
150+
<a routerLink="/docs/runtime" fragment="core">core module</a> at least
151+
partially for this runner to work. No other module is required for this
152+
runner to work.
153+
</p>
154+
<p>
155+
All of the
156+
<a [routerLink]="[]" fragment="execution">runner execution methods</a> are
157+
added to an internal execution options list and will be passed to
158+
<code>ecsact_execute_systems</code> on the next tick.
159+
</p>
160+
</section>
161+
162+
<section>
163+
<h2 id="async-runner">Asynchronous Runner</h2>
164+
<p>
165+
The <code>USyncRunner</code> runs
166+
<code>ecsact_async_flush_events</code> every tick. Meaning that all
167+
component events that have happened in the background will trigger every
168+
tick. If you derive from this class make sure you're calling
169+
<code>Super::Tick</code> or none of your
170+
<a routerLink="/start/unreal/subsystems">runner subsystems</a> will fire
171+
any Ecsact events. Your
172+
<a routerLink="/start/unreal/runtime" fragment="building-the-runtime"
173+
>Ecsact Runtime</a
174+
>
175+
must built to support the
176+
<a routerLink="/docs/runtime" fragment="async">async module</a> at least
177+
partially for this runner to work. No other module is required for this
178+
runner to work.
179+
</p>
180+
<p>
181+
All of the
182+
<a [routerLink]="[]" fragment="execution">runner execution methods</a> use
183+
the <code>ecsact_async_enqueue_execution_options</code> method under the
184+
hood making all execution options queued up asynchronous instantly.
185+
</p>
186+
</section>
187+
188+
<section>
189+
<h2 id="custom-runner">Make your own Runner</h2>
190+
<p>
191+
Making your own runner can be the perfect way to control
192+
<em>ecsactly</em> how your Ecsact execution works. However, it's a bit of
193+
an under taking. Sometimes when you find yourself wanting to create a
194+
custom runner you really just want to
195+
<a [routerLink]="[]" fragment="control-runner-start"
196+
>control when your runner starts</a
197+
>
198+
instead.
199+
</p>
200+
201+
<p>
202+
If you're still interested in creating your own runner then we recommend
203+
carefully studying the <code>UEcsactSyncRunner</code> or
204+
<code>UEcsactAsyncRunner</code>
205+
<a href="https://github.com/ecsact-dev/ecsact_unreal">source code</a>.
206+
Sometimes simplying deriving from one of the two is good enough.
207+
</p>
208+
<p>
209+
After you've created your Ecsact runner you can assign the
210+
<code>Custom Runner Class</code> in the Ecsact Unreal plugin settings. If
211+
you'd prefer to start it manually then set it to <code>None</code> and
212+
follow the
213+
<a [routerLink]="[]" fragment="control-runner-start">instructions below</a
214+
>.
215+
</p>
216+
217+
<h3 id="control-runner-start">Control when your runner starts</h3>
218+
<div class="flex flex-col items-center">
219+
<p>
220+
If you don't like that your Ecsact runner starts right away and want to
221+
choose when it starts then you came to the right place. Go to
222+
<code>Edit Project Settings Plugins Ecsact</code> and set your Runner as
223+
<code>Custom</code> and the Custom Runner Class to <code>None</code>.
224+
</p>
225+
<div>
226+
<a href="/assets/unreal/unreal-custom-runner-01.png">
227+
<img
228+
class="rounded-lg shadow-lg"
229+
src="/assets/unreal/unreal-custom-runner-01.png" />
230+
</a>
231+
</div>
232+
</div>
233+
<p>
234+
Now when your game starts nothing will happen. Fortunately this can be
235+
remedied with the by calling <code>StartCustomRunner</code> in the Ecsact
236+
game instance subsystem.
237+
</p>
238+
<code-block-variation class="m-0 rounded-md mt-3">
239+
<pre
240+
codeBlockVariationOption
241+
optionTitle="C++"><code prism language="cpp">
242+
auto* EcsactGameInstanceSubsystem = GetWorld()-&gt;GetGameInstance()-&gt;GetSubsystem&lt;UEcsactGameInstanceSubsystem&gt;();
243+
// NOTE: you can pass int a built-in runner _OR_ your own custom runner
244+
EcsactGameInstanceSubsystem-&gt;StartCustomRunner&lt;UEcsactSyncRunner&gt;();
245+
</code></pre>
246+
</code-block-variation>
247+
<p>Afterwards your runner should be running!</p>
248+
</section>
249+
250+
<section>
251+
<h2 id="no-runner">Using no runner</h2>
252+
<p>
253+
Choosing to use no runner disables a lot of the Ecsact Unreal plugin
254+
functionality. Consider using a
255+
<a [routerLink]="[]" fragment="custom-runner">custom runner</a> and
256+
<a [routerLink]="[]" fragment="control-runner-start"
257+
>manually controlling when it starts</a
258+
>
259+
instead.
260+
</p>
261+
</section>
3262
</article>

src/app/start/unreal/runner/unreal-runner.component.ts

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,19 @@
11
import {Component, OnInit, ChangeDetectionStrategy} from '@angular/core';
22
import {RouterLink} from '@angular/router';
33
import {PrismComponent} from '../../../../components/prism/prism.component';
4+
import {CodeBlockVariationComponent} from '../../../../components/code-block-variation/code-block-variation.component';
5+
import {CodeBlockVariationOptionDirective} from '../../../../components/code-block-variation/code-block-variation-option.directive';
46

57
@Component({
68
templateUrl: './unreal-runner.component.html',
79
changeDetection: ChangeDetectionStrategy.OnPush,
810
standalone: true,
9-
imports: [RouterLink, PrismComponent],
11+
imports: [
12+
RouterLink,
13+
PrismComponent,
14+
CodeBlockVariationComponent,
15+
CodeBlockVariationOptionDirective,
16+
],
1017
})
1118
export class UnrealRunnerComponent implements OnInit {
1219
constructor() {}
6.5 KB
Loading

0 commit comments

Comments
 (0)