Previous slide Next slide Toggle fullscreen Open presenter view
Gameloop, Update method & Component
Software design what patterns?
Software design patterns are blueprints of possible solutions to common problems
They are hints to how you can solve a particular problem
They're only that, they're no law
Different contexts call for different solutions
Language agnostic
Gang of four
Game Programming Patterns
Game programming patterns - Game loop
Game loop
int APIENTRY wWinMain (_In_ HINSTANCE hInstance,
_In_opt_ HINSTANCE hPrevInstance,
_In_ LPWSTR lpCmdLine,
_In_ int nCmdShow)
{
UNREFERENCED_PARAMETER (hPrevInstance);
UNREFERENCED_PARAMETER (lpCmdLine);
LoadStringW (hInstance, IDS_APP_TITLE, szTitle, MAX_LOADSTRING);
LoadStringW (hInstance, IDC_TESTTEMP, szWindowClass, MAX_LOADSTRING);
MyRegisterClass (hInstance);
if (!InitInstance (hInstance, nCmdShow)) {
return FALSE;
}
HACCEL hAccelTable = LoadAccelerators (hInstance, MAKEINTRESOURCE (IDC_TESTTEMP));
MSG msg;
while (GetMessage (&msg, nullptr , 0 , 0 )) {
if (!TranslateAccelerator (msg.hwnd, hAccelTable, &msg)) {
TranslateMessage (&msg);
DispatchMessage (&msg);
}
}
return (int ) msg.wParam;
}
Game programming patterns - Game loop
Game loop
Probably the simplest or most obvious pattern there is, yet it's tricky to get it right.
while (true )
{
processInput ();
update ();
render ();
}
What are we missing here?
Game programming patterns - Game loop
Game loop
Indeed we need to exit somehow:
bool do_continue = true ;
while (do_continue)
{
do_continue = process_input ();
update ();
render ();
}
Game programming patterns - Game loop
In the browser?
This won't work in the browser
bool do_continue = true ;
while (do_continue)
{
do_continue = process_input ();
update ();
render ();
}
Via emscripten we can compile our c++ programs to webassembly. We can't maintain our infinite loop however, we need to provide a callback:
void RunOneFrame ()
{
do_continue = process_input ();
update ();
render ();
}
emscripten_set_main_loop (&RunOneFrame, 0 , true );
https://www.jamesfmackenzie.com/howto/getting-started-with-webassembly-part-3-emscripten-loops/
Game programming patterns - Game loop
Turbo speed!
Game programming patterns - Game loop
Don't go too fast
bool do_continue = true ;
while (do_continue)
{
const auto start = high_resolution_clock::now ();
do_continue = process_input ();
update ();
render ();
const auto sleep_time = start + milliseconds (ms_per_frame) - high_resolution_clock::now ();
this_thread::sleep_for (sleep_time);
}
What else goes wrong here?
Game programming patterns - Game loop
Adapt to the actual time
bool do_continue = true ;
auto last_time = high_resolution_clock::now ();
while (do_continue)
{
const auto current_time = high_resolution_clock::now ();
const float delta_time = duration <float >(current_time – last_time).count ();
last_time = current_time;
do_continue = process_input ();
update (delta_time);
render ();
const auto sleep_time = current_time + milliseconds (ms_per_frame) - high_resolution_clock::now ();
this_thread::sleep_for (sleep_time);
}
Game programming patterns - Game loop
Catch up in fixed steps
bool do_continue = true ;
auto last_time = high_resolution_clock::now ();
float lag = 0.0f ;
while (do_continue)
{
const auto current_time = high_resolution_clock::now ();
const float delta_time = duration <float >(current_time – last_time).count ();
last_time = current_time;
lag += delta_time;
do_continue = process_input ();
while (lag >= fixed_time_step)
{
fixed_update (fixed_time_step);
lag -= fixed_time_step;
}
update (delta_time);
render ();
const auto sleep_time = current_time + milliseconds (ms_per_frame) - high_resolution_clock::now ();
this_thread::sleep_for (sleep_time);
}
Game programming patterns - Game loop
Fixed time step
That fixed_time_step, how much should it be?
It should be a config setting, every game has different requirements
Important: the fixed update is only relevant for physics and networking
Game programming patterns - Update method
Update method
The game maintains a collection of objects (often called "game objects" or "actors")
Every frame the update method is called on those objects.
void scene::update ()
{
for (auto object : m_objects)
{
object->update ();
}
}
Game programming patterns - Update method
Update method
You need to split code and keep some state around
while (true ) {
int i = 0 ;
for (; i < 100 ; ++i) skeleton.set_x (i);
for (; i >= 0 ; --i) skeleton.set_x (i);
}
That simple code becomes now
int i = 0 ;
bool is_walking_left = false ;
void update ()
{
if (is_walking_left)
{
i--;
if (i == 0 ) is_walking_left = false
}
else
{
i++;
if (i == 100 ) is_walking_left = true ;
}
skeleton.set_x (i);
}
The state pattern comes into view here
Game programming patterns - Update method
Update method
That is one of the advantages of coroutines in Unity
private IEnumerator DoPatrol ()
{
float direction = 1.0f ;
while (true )
{
for (int i = 0 ; i < 100 ; ++i)
{
skeleton.transform.position += direction * Vector3.right;
yield return null ;
}
direction = -direction;
}
}
https://docs.unity3d.com/Manual/Coroutines.html
Game programming patterns - Update method
Update order?
Be aware of interdependencies
If object A comes before B in the update list.
When A updates it sees B’s old state
But when B updates it sees A’s new state
Example: If object C is the camera and object E is the player...
Solution?
Double Buffer pattern
LateUpdate method
Game programming patterns - Update method
Deleting objects
What happens when one object removes another in its Update?
Game programming patterns - Update method
Deleting objects
What happens when one object removes another in its Update?
Don’t remove them until all have been updated, but “mark” as dead.
Delete them after all updates have been finished.
Game programming patterns - Update method
Delta time?
Do we pass the deltaTime to the Update method? (as we have seen in the game loop pattern)
void update (float delta_time)
{
if (is_walking_left) {
i--;
if (i == 0 ) is_walking_left = false
}
else {
i++;
if (i == 100 ) is_walking_left = true ;
}
skeleton.set_x (i * delta_time);
}
This is the approach chosen by Unreal
void SomeActor::Tick (float DeltaTime)
{
Super::Tick (DeltaTime);
}
Game programming patterns - Update method
Delta time?
You could use a singleton, as for example happens in Unity:
private IEnumerator DoPatrol ()
{
float direction = 1.0f ;
while (true )
{
for (int i = 0 ; i < 100 ; ++i)
{
skeleton.transform.position += direction * Vector3.right * Time.deltaTime;
yield return null ;
}
direction = -direction;
}
}
Advantage? You can use it in other places than the Update method as well (as in the above example).
Note: I wrote singleton with a lower case s
Game programming patterns - Update method
Update method
On what objects do we call this Update method?
namespace dae
{
class Texture2D ;
class GameObject
{
Transform m_transform{};
std::shared_ptr<Texture2D> m_texture{};
public :
virtual void Update () ;
virtual void Render () const ;
void SetTexture (const std::string& filename) ;
void SetPosition (float x, float y) ;
GameObject () = default ;
virtual ~GameObject ();
GameObject (const GameObject& other) = delete ;
GameObject (GameObject&& other) = delete ;
GameObject& operator =(const GameObject& other) = delete ;
GameObject& operator =(GameObject&& other) = delete ;
};
}
Game programming patterns - Component
Prefer composition over inheritance
Prefer
composition
over
inheritance
Game programming patterns - Component
Prefer composition over inheritance
Prefer composition
over inheritance
Game programming patterns - Component
Prefer composition over inheritance
Composition
should be preferred
over inheritance
Game programming patterns - Component
Component
Prefer composition over inheritance. (Item 34 in "C++ coding standards" by Herb Sutter)
The game world exists of “GameObjects”.
Every game object has several components that govern an aspect of the Gameobject
RenderComponent does the rendering
AudioComponent plays the audio
PhysicsComponent takes care of the physics
...
Redirect the update method to the components.
Game programming patterns - Component
Inheritance
Wait, inheritance is bad?
NO
Obviously not, we’ll be using a lot of inheritance while implementing the component pattern.
Choose the right tool for the right job.
Don’t use a swiss knife to hammer in a nail.
Inherit, not to reuse, but to be reused. (Item 37 in "C++ coding standards" by Herb Sutter)
Game programming patterns - Component
Components in Unity
Unity is a state-of-the-art example
Several Components
MeshRenderer
RigidBody
AudioSource
Camera
NavMeshAgent
...
Create your own by inheriting from MonoBehaviour
Game programming patterns - Component
Component communication
Via common state in the GameObject
Hard to tell what that data should be, if the components are unknown.
Order in which the components are updated becomes relevant
Component.transform
Get the component you want to talk to from the GameObject
Simple and fast
Tightly coupled
GameObject.GetComponent<T>
Sending messages to the GameObject
All components receive it.
GameObject.SendMessage
Game programming patterns - Component
Components in Unreal
In Unreal we have this too:
AActor is the game object class
UActorComponent is the base component class
Several Components
UCameraComponent
UParticleSystemComponent
UStaticMeshComponent
...
To get the component you want to talk to from the actor:
auto component =
Cast <T>(GetOwner ()->GetComponentByClass (T::StaticClass ()));
Lots of common state
USceneComponent* RootComponent
default windows c++ desktop application looks like this
What’s so special: most programs don’t need this and wait for user input before doing something. But with games, something is happening all the time even without input. This makes this first pattern unique (almost) to games.
We need to exit somehow
The 0 is the fps, 0 to let the browser decide and true is for "simulate infinite loop"
In "the olden days" when computers were slow enough, this loop was enough. The hardware could not go very fast, so updating, displaying and doing it again meant that you were rendering at 15 fps.
Ironically - that's why there was a turbo button on old pc's: they were too fast for some games so then you could switch the turbo off and the game would run fine.
Today, computers run a lot faster and - more important - run multiple programs at the time. You need to leave some cpu breathing space so other programs on your pc get a chance + if you utilize the pc all the time running at 240 fps you're wasting power (aka batteries in a mobile context)
Games run just fine at 60 fps, 90 fps for VR
But often the game can’t keep that pace and you’ll run slower than 60 fps, making your game appearing slower.
Instead of waiting, measure the actual time that has passed each frame and adjust your simulation to that value.
Slower PC’s will take bigger steps each frame, but at least you’ll advance at the same speed.
Faster PC’s will render more often which is what you want.
If VSYNC is enabled the Render call will cap to the refresh rate of the screen.
But: different players will have different rounding errors – problem in multiplayer games
Rounding errors (and certainly small values) are VERY bad for the physics engine and networking.
We still adapt to the actual time, but we do it in fixed steps. That way the rounding errors will be less and multiplayer is sure to be in sync.
fixed_time_step -> too long, gameplay will be choppy. Too short, there won’t be enough time and you’ll never catch up.
Unity does something similar, they do the fixed time step thingy in the FixedUpdate, but the Update is called once per frame, just like the LateUpdate
With coroutines you can actually get the best of both worlds.
Most of the time it’s even better to use coroutines than the update method.
Don’t know Coroutines? Maybe later.
If Obj 2 deletes Obj 3 while iterating, elements will shift and Obj 4 will be skipped. Easy solution would be to iterate backwards, but no: if Obj 5 deletes Obj 4 elements shift and Obj 5 gets updated twice.
this is an application of so-called dependency injection
Ideal moment for a break here.
The GameObect class in Minigin has the Update method and gets called by the scene.
Elaborate on responsibilities here – should the gameobject hold the render data? What about animations? What about sound? What about AI? Do we subclass? Do we make a Guard/Skeleton/Hero/Animal/… class?