Windows中的自定义XNA游戏循环

时间:2011-06-15 17:23:04

标签: xna rendering game-loop

我正试图弄清楚如何在Windows游戏中手动管理整个游戏循环,而不使用常规游戏Microsoft.Xna.Framework.Game类。

这样做的原因是使用常规的Game类会导致我的游戏中出现一些口吃。不多,但由于游戏的特殊性,它仍然是非常明显的。

在尝试了一堆不同的设置(vsync,fixedtimestep,各种帧率等)后,我决定尝试编写自己的Game类来完全控制时间。我不确定会解决它,但至少这种方式我可以完全控制。

基本上我需要:

  1. 设置游戏窗口
  2. 循环:像往常一样进行所有渲染,然后将结果刷新到屏幕,管理后台缓冲区等。
  3. 任何人都知道如何做到这一点?事实上这听起来很容易,但找不到任何关于如何做的文档。


    不确定我做错了什么,但我有以下代码(仅用于测试,时间将以不同方式处理),循环将运行一段时间然后停止。一旦我将鼠标指针移到窗口上,循环就会再次运行一段时间。

        private void Application_Idle(object pSender, EventArgs pEventArgs)
        {
            Thread.Sleep(500);
            //Message message;
            //while (!PeekMessage(out message, IntPtr.Zero, 0, 0, 0))
            {
                gametime.update();
                Update(gametime);
                Draw(gametime);
                GraphicsDevice.Present();
            }
        }
    

    如果启用“while PeekMessage”,循环将连续运行,但忽略睡眠并在鼠标移动窗口时停止。不知道这里发生了什么......

    我认为最好我只想在主渲染循环中做这样简单的事情:

        while (alive)
        {
          Thread.Sleep(100);
          gametime.update();
          Update(gametime);
          Draw(gametime);
          GraphicsDevice.Present();
        }
    

    但是在这种情况下,窗口仍然是空白的,因为看起来窗口实际上没有使用新内容重绘。我尝试了一个form.Refresh(),但仍然没有...任何想法?

2 个答案:

答案 0 :(得分:7)

(添加了xbox信息)

对于windows你基本上需要创建一个Form并显示它,然后存储它的句柄和表单本身。 使用此句柄可以创建GraphicsDevice。 然后将Application.Idle挂接到调用更新和呈现的自己的函数。 例如

public class MyGame
{
public Form form;
public GraphicsDevice GraphicsDevice;

public MyGame()
{
    form = new Form();
    form.ClientSize = new Size(1280, 1024);
    form.MainMenuStrip = null;

    form.Show();
}

public void Run()
{      
    PresentationParameters pp = new PresentationParameters();
    pp.DeviceWindowHandle = form.Handle;

    pp.BackBufferFormat = SurfaceFormat.Color;
    pp.BackBufferWidth = 1280;
    pp.BackBufferHeight = 1024;
    pp.RenderTargetUsage = RenderTargetUsage.DiscardContents; 
    pp.IsFullScreen = false; 

    pp.MultiSampleCount = 16;

    pp.DepthStencilFormat = DepthFormat.Depth24Stencil8;

    GraphicsDevice = new GraphicsDevice(GraphicsAdapter.DefaultAdapter,
                                              GraphicsProfile.HiDef,
                                              pp);
    Application.Idle += new EventHandler(Application_Idle);
    Application.Run(form);
}

 private void Application_Idle(object pSender, EventArgs pEventArgs)
 {
    Message message;
    while (!PeekMessage(out message, IntPtr.Zero, 0, 0, 0))
    {
        /* Your logic goes here
         Custom timing and so on
        Update();
        Render();
        */
    }

 }

 void Render()
 {
      GraphicsDevice.Clear(ClearOptions.DepthBuffer | ClearOptions.Target, Color.Black, 1, 0);
      //Your logic here.
     GraphicsDevice.Present();
 }
    [StructLayout(LayoutKind.Sequential)]
    private struct Message
    {
        public IntPtr hWnd;
        public int msg;
        public IntPtr wParam;
        public IntPtr lParam;
        public uint time;
        public Point p;
    }

    [return: MarshalAs(UnmanagedType.Bool)]
    [SuppressUnmanagedCodeSecurity, DllImport("user32.dll", CharSet = CharSet.Auto)]
    private static extern bool PeekMessage(out Message msg, IntPtr hWnd, uint
        messageFilterMin, uint messageFilterMax, uint flags);
}

编辑1

对于xbox,您可以将自己的自定义运行功能与游戏循环放在一个限制的while循环中。在内部运行之外,您可能需要使用IntPtr.Zero作为手柄进行图形设备初始化和验证

编辑2 我使用这样的东西(来自http://www.koonsolo.com/news/dewitters-gameloop/

        private long nextGameTick;

        private Stopwatch stopwatch;

        const int ticksPerSecond = 60;
        const int skipTicks = 1000 / ticksPerSecond;
        private const int maxSkip = 10;
        `constructor 
         stopwatch = Stopwatch.StartNew();

        nextGameTick = stopwatch.ElapsedMilliseconds; 

        `loop 
        int loops = 0;
        long currentTick = stopwatch.ElapsedMilliseconds;
        while ( (ulong)(currentTick - nextGameTick) > skipTicks && loops < maxSkip)
        {
            Update(16.667f);
            nextGameTick += skipTicks;
            loops++;

        }

        PreRender();
        Render();
        PostRender();

编辑3

创建内容管理器需要更多工作,但仍然可以管理。您需要创建一个实现IServiceProvider的类。此类在其构造函数中采用GraphicsDevice,以便创建实现IGraphicsDeviceProvider的下一个类。另外我像这样实现GetService

    //in implementer of IServiceProvider
    public object GetService ( Type serviceType )
    {
        if ( serviceType == typeof ( IGraphicsDeviceService ) )
        {
            return myGraphicsService;
        }

        return null;
    }

为方便起见,我还在类中添加了一个创建和返回管理器的方法

    //in implementer of IServiceProvider
    public ContentManager CreateContentManager( string sPath )
    {

        ContentManager content = new ContentManager(this);

        content.RootDirectory = sPath;

        return content;

    }

此外,我创建了一个实现IGraphicsDeviceService的类,并引用了我的GraphicsDevice。然后我在其中创建一个属性和字段

    //in implementer of IGraphicsDeviceService 
    private GraphicsDevice graphicsDevice;
    public GraphicsDevice GraphicsDevice
    {
        get
        {
            return graphicsDevice;
        }
    }

所以这个电话最终会像

一样
MyServiceProvider m = new MyServiceProvider(graphicsDevice);
ContentManager content = m.CreateContentManager("Content");

,其中

MyServiceProvider(GraphicsDevice graphicsDevice)
{
      myGraphicsService = new MyGraphicsDeviceService(graphicsDevice);
}

MyGraphicsDeviceService(GraphicsDevice gfxDevice)
{
     graphicsDevice = gfxDevice;
}

-Sorry用于分割代码,但它不是我最近写的东西,所以我很难记住部分。

编辑4

我的自定义游戏有一个奇怪的情况,我记得当我为它新建表格时 不得不绑定

    private void IgnoreAlt(object pSender, KeyEventArgs pEventArgs)
    {
        if (pEventArgs.Alt && pEventArgs.KeyCode != Keys.F4)
            pEventArgs.Handled = true;

    }

    form.KeyUp += IgnoreAlt;
    form.KeyDown += IgnoreAlt;

否则我有一些可怕的摊位。

答案 1 :(得分:6)

我最近创建了一系列博客文章,介绍如何彻底消除对Microsoft.Xna.Framework.Game的依赖。我不喜欢关于Game类的几件事,最值得注意的是它严重违反了Single Responsibility Principle。我的文本冒险游戏和编辑器甚至没有引用Microsoft.Xna.Framework.Game程序集。

无论如何,这里是五个帖子的链接,按顺序:

http://blog.thecognizantcoder.com/2012/01/replacing-xnas-game-class-xnacontrol.html http://blog.thecognizantcoder.com/2012/01/replacing-xnas-game-class-xnagame.html http://blog.thecognizantcoder.com/2012/01/replacing-xnas-game-class-putting-it.html http://blog.thecognizantcoder.com/2012/01/replacing-xnas-game-class-renderers.html http://blog.thecognizantcoder.com/2012/01/replacing-xnas-game-class-updaters.html

最后,GitHub上的Text Adventure项目:

https://github.com/NathanAlden/TextAdventure