Callbacks

In OpenTK you don't have access to the windows message loop. Instead OpenTK provides a number of very useful callbacks that you can use to do things during the lifecycle of the window. Set all of the callbacks before calling the Run method of the window.

Initialize

The initialize function gets called right after the window is created, before the windows first update / render cycle.

The function takes a sender object and a EventArgs event, it returns void. This is how you can hook up the Initialize callback:

using System;
using OpenTK;
using System.Drawing;
using OpenTK.Graphics.OpenGL;

namespace GameApplication {
    class MainGameWindow : OpenTK.GameWindow {
        //reference to OpenTK window
        public static OpenTK.GameWindow Window = null; 

        public static void Initialize(object sender, EventArgs e) {
            // Do initialization code here
        }

        [STAThread]
        public static void Main() {
            //create static(global) window instance
            Window = new MainGameWindow();

            //hook up the initialize callback
            Window.Load += new EventHandler<EventArgs>(Initialize);

            //set window title and size
            Window.Title = "Game Name";
            Window.ClientSize = new Size(800, 600);

            // turn on vsynch, prevents screen tearing when swapping buffers
            // This is on by default, but take no changes!
            Window.VSync = VSyncMode.On;

            //run game at 60fps. will not return until window is closed
            Window.Run(60.0f);
            Window.Dispose();
        }
    }
}

Update

The Update function gets called 1 time every frame. The function takes a sender object and a FrameEventArgs event, it returns void. You need to hook up to the UpdateFrame method of the OpenTK window.

You can get the delta time of the update loop trough the FrameEventArgs argument of the function.

// ...

namespace GameApplication {
    class MainGameWindow : OpenTK.GameWindow {
        // ...

        public static void Update(object sender, FrameEventArgs e) {
            float deltaTime = (float)e.Time;
        }

        [STAThread]
        public static void Main() {
            // ...

            Window.UpdateFrame += new EventHandler<FrameEventArgs>(Update);

            // ...

            Window.Run(60.0f);
            Window.Dispose();
        }
    }
}

Render

The Render function is similar to the Update function. The function gets hooked up to the windows RenderFrame callback. Just like Update the Render function takes a sender object and a FrameEventArgs event, it returns void.

Because the second argument is a FrameEventArgs event you can access delta time the same way you did for Update if you need it, tough generally in a Render function you wont.

Inside this function we need to do the following

  • Tell OpenGL what color to clear the screen to
  • Clear the screen
  • Render our game
  • Swap the back and front display buffers
    • These are managed by OpenTK.
    • OpenTK is by default double buffered.

This code contains the first two lines of OpenGL we are going to write. Don't worry about what they mean yet, for now just copy them in.

// ...

namespace GameApplication {
    class MainGameWindow : OpenTK.GameWindow {
        public static OpenTK.GameWindow Window = null; 

        // ...

        public static void Render(object sender, FrameEventArgs e) {
            // Tell OpenGL what color to clear the screen to
            GL.ClearColor(Color.CadetBlue);
            // Tell OpenGL to clear the screen.
            GL.Clear(ClearBufferMask.ColorBufferBit | ClearBufferMask.DepthBufferBit);

            // TODO: Render your game here

            Window.SwapBuffers();
        }

        // ..

        [STAThread]
        public static void Main() {
            // ...

            Window.RenderFrame += new EventHandler<FrameEventArgs>(Render);

            // ...

            Window.Run(60.0f);
            Window.Dispose();
        }
    }
}

Resize

The resize callback is a bit special. Not in the good way. This callback is the whole reason that our MainGameWindow class has to extend the OpenTK.GameWindow class.

Resize does not have a callback event, instead the OpenTK.GameWindow class implements it as a virtual function that we must override. This function will take a EventArgs event for an argument and return void.

Getting the width and height of the window on resize is also a bit dumb. That information is not passed in with the event argument (practicaly nothing is!). Instead you must check a variable called ClientRectangle that is inherited from OpenTK.Window. ClientRectangle is a System.Drawing.Rectangle object.

// ...

namespace GameApplication {
    class MainGameWindow : OpenTK.GameWindow {

        // ...

        protected override void OnResize(EventArgs e) {
            // You must call this!
            base.OnResize(e);

            // ClientRectangle is inherited from OpenTK.GameWindow
            Rectangle drawingArea = ClientRectangle;

            // Do resize window stuff here
        }

        // ...

        [STAThread]
        public static void Main() {

            // ...

        }
    }
}

Shutdown

The shutdown callback is similar to the initialize callback. The function takes a sender object and a EventArgs event, it returns void. You hook shutdown up the the windows Unload callback

// ...

namespace GameApplication {
    class MainGameWindow : OpenTK.GameWindow {

        // ...

        public static void Shutdown(object sender, EventArgs e) {

        }

        [STAThread]
        public static void Main() {

            // ...

            Window.Unload += new EventHandler<EventArgs>(Shutdown);

            // ...

            Window.Run(60.0f);
            Window.Dispose();
        }
    }
}

All together

The code below puts all of the above callbacks in place. IT also sets the window Title and Size, trough public getters and setters exposed to the GameWindow class. This should be everything we need for the main window.

If you're curious, a complete list of the callbacks and properties of the GameWindow class can be found on the OpenTK Doxygen documentation page.

using System;
using OpenTK;
using System.Drawing;
using OpenTK.Graphics.OpenGL;

namespace GameApplication {
    class MainGameWindow : OpenTK.GameWindow {
        //reference to OpenTK window
        public static OpenTK.GameWindow Window = null; 

        public static void Initialize(object sender, EventArgs e) {

        }

        public static void Update(object sender, FrameEventArgs e) {
            float deltaTime = (float)e.Time;
        }

        public static void Render(object sender, FrameEventArgs e) {
            GL.ClearColor(Color.CadetBlue);
            GL.Clear(ClearBufferMask.ColorBufferBit | ClearBufferMask.DepthBufferBit);

            // TODO: Render your game here

            Window.SwapBuffers();
        }

        protected override void OnResize(EventArgs e) {
            // You must call this!
            base.OnResize(e);

            // ClientRectangle is inherited from OpenTK.GameWindow
            Rectangle drawingArea = ClientRectangle;
        }

        public static void Shutdown(object sender, EventArgs e) {

        }

        [STAThread]
        public static void Main() {
            //create static(global) window instance
            Window = new MainGameWindow();

            //hook up the initialize callback
            Window.Load += new EventHandler<EventArgs>(Initialize);
            //hook up the update callback
            Window.UpdateFrame += new EventHandler<FrameEventArgs>(Update);
            //hook up render callback
            Window.RenderFrame += new EventHandler<FrameEventArgs>(Render);
            //hook up shutdown callback
            Window.Unload += new EventHandler<EventArgs>(Shutdown);

            //set window title and size
            Window.Title = "Game Name";
            Window.ClientSize = new Size(800, 600);

            // turn on vsynch, prevents screen tearing when swapping buffers
            // This is on by default, but take no changes!
            Window.VSync = VSyncMode.On;

            //run game at 60fps. will not return until window is closed
            Window.Run(60.0f);

            Window.Dispose();
        }
    }
}

results matching ""

    No results matching ""