miércoles, julio 12, 2006

El bucle de juego definitivo

El bucle de juego (game loop) es una de las características más importantes de los juegos. Al revés de las aplicaciones de gestión, que estan guiadas por eventos, un juego generalmente tiene cosas que hacer (p.ej. animaciones) aunque el usuario no haga nada.
Un bucle de juego eficiente hará que el juego ocupe menos CPU y por lo tanto indirectamente, pueda generar más fps.

En este post, Tom Miller propone un bucle de juego muy interesante, tanto por su sencillez como por su eficiencia. Se basa en el uso del API Win32 PeekMessage().

Basándome en este bucle de juego he creado una clase que simplemente lo encapsula. El código de la clase (a la que presuntuosamente he llamado GameEngine) es el siguiente:

namespace foo

{

    class GameEngine

    {

        Form1 frm = new Form1();

        public void MainLoop()

        {

            System.Windows.Forms.Application.Idle += new EventHandler(OnApplicationIdle);

            frm.InitializeGraphics();

            System.Windows.Forms.Application.Run(frm);

        }

 

        private void OnApplicationIdle(object sender, EventArgs e)

        {

            while (AppIddle)

            {

                // Update Graphics

                // Render Graphics

            }

        }

 

        private bool AppIddle

        {

            get

            {

                NativeMethods.Message msg;

                return !NativeMethods.PeekMessage(out msg, IntPtr.Zero, 0, 0, 0);

            }

        }

    }

}

static class NativeMethods

{

        [StructLayout(LayoutKind.Sequential)]

        public struct Message

        {

            public IntPtr hWnd;

            public IntPtr msg;

            public IntPtr wParam;

            public IntPtr lParam;

            public uint time;

            public System.Drawing.Point p;

        }

 

        [System.Security.SuppressUnmanagedCodeSecurity]

        [DllImport("User32.dll", CharSet = CharSet.Auto)]

        public static extern bool PeekMessage(out Message msg, IntPtr hWnd, uint messageFilterMin, uint messageFilterMax, uint flags);

}



Nota: Form1 es el nombre de la clase formulario principal.

Usando esa clase la función Main() quedaría de la siguiente manera:

        [STAThread]

        static void Main()

        {

            // Inicializaciones previas

            GameEngine ge = new GameEngine();

            ge.MainLoop();       

            // Destrucciones finales

        }

    }



Simple, sencillo... y eficaz!

No hay comentarios: