Gameloop, Update method & Component

Software design patterns

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

Programming 4
Software design patterns

Gang of four

center

Programming 4
Software design patterns

Game Programming Patterns

Programming 4
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;
}
Programming 4
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?

Programming 4
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();
}
Programming 4
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/

Programming 4
Game programming patterns - Game loop

On the NES

Remember this from "Retro console & emulator programming"?

center

Cruise, T. (2024). Classic game programming on the NES: Make Your Own Retro Video Game.

Programming 4
Game programming patterns - Game loop

Turbo speed!

center

Programming 4
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?

Programming 4
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);
}
Programming 4
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);
}
Programming 4
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

center

center

  • Important: the fixed update is only relevant for physics and networking
Programming 4
Game programming patterns - Game loop

center

https://docs.unity3d.com/Manual/ExecutionOrder.html

Programming 4
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();
  }
}
Programming 4
Game programming patterns - Update method

Update method

You need to split code and keep some state around

while(true) {
  int i = 0;
  // move right
  for(; i < 100; ++i) skeleton.set_x(i);
  // move left
  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

Programming 4
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

Programming 4
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

center

Example: If object C is the camera and object E is the player...

Solution?

  • Double Buffer pattern
  • LateUpdate method
Programming 4
Game programming patterns - Update method

Deleting objects

What happens when one object removes another in its Update?

center

Programming 4
Game programming patterns - Update method

Deleting objects

What happens when one object removes another in its Update?

center

  • Don’t remove them until all have been updated, but “mark” as dead.
  • Delete them after all updates have been finished.
Programming 4
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);
  // code
}
Programming 4
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

Programming 4
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;
  };
}
Programming 4
Game programming patterns - Component

Prefer composition over inheritance

Prefer
composition
over
inheritance
Programming 4
Game programming patterns - Component

Prefer composition over inheritance

Prefer composition
over inheritance
Programming 4
Game programming patterns - Component

Prefer composition over inheritance

Composition
should be preferred
over inheritance
Programming 4
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.
Programming 4
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)

Programming 4
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

Programming 4
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
Programming 4
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
Programming 4

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?