Windows中的自定义XNA游戏循环

Windows中的自定义XNA游戏循环,xna,rendering,game-loop,Xna,Rendering,Game Loop,我试图弄清楚如何在Windows游戏中手动管理整个游戏循环,而不使用常规游戏Microsoft.Xna.Framework.game类 原因是使用常规的游戏类会导致我的游戏出现一些口吃。不多,但由于游戏的特殊性质,它仍然是相当明显的 在尝试了一系列不同的设置(vsync、fixedtimestep、各种帧速率等)之后,我决定尝试编写自己的游戏类,以完全控制计时。我不确定这是否能解决问题,但至少这样我就能完全控制局面 基本上,我需要: 设置游戏窗口 在循环中:像往常一样进行所有渲染,然后将结果刷新

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

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

在尝试了一系列不同的设置(vsync、fixedtimestep、各种帧速率等)之后,我决定尝试编写自己的游戏类,以完全控制计时。我不确定这是否能解决问题,但至少这样我就能完全控制局面

基本上,我需要:

  • 设置游戏窗口
  • 在循环中:像往常一样进行所有渲染,然后将结果刷新到屏幕,管理backbuffers等
  • 有人知道怎么做吗?事实上,这听起来很容易,但找不到任何关于如何操作的文档


    我不确定我做错了什么,但我有以下代码(只是为了测试,计时将以不同的方式处理),循环将运行一段时间,然后停止。一旦我将鼠标滑过窗口,循环将再次运行一段时间

        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 peek message”,循环将持续运行,但忽略睡眠,并在鼠标移动到窗口上时停止。不知道这里发生了什么

    我认为最佳情况下,我只想在主渲染循环中执行以下简单操作:

        while (alive)
        {
          Thread.Sleep(100);
          gametime.update();
          Update(gametime);
          Draw(gametime);
          GraphicsDevice.Present();
        }
    
    但是在这种情况下,窗口仍然是空白的,因为看起来窗口实际上没有被新内容重新绘制。我尝试了一个form.Refresh(),但仍然没有成功。。。有什么想法吗?

    (添加了xbox信息)

    对于windows,基本上需要创建一个窗体并显示它,然后存储它的句柄和窗体本身。 使用此句柄可以创建图形设备。 然后将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 true循环中。在while true的顶部之外运行的内部,您可能必须使用IntPtr.Zero作为句柄来执行图形设备初始化和验证

    编辑2 我用的是这样的东西(来自)

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

        //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");
    
    在哪里

    -很抱歉把代码分成碎片,但这不是我最近写的东西,所以我很难记住部分

    编辑4

    我有一个奇怪的情况,我的自定义游戏,我只是记得当我新的形式为它我 不得不绑

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


    否则我会遇到一些可怕的停顿。

    我认为对于xbox,您需要使用IntPtr.Zero或其他方法调用graphicsdevice构造函数来创建设备,因为您无法访问System.Windows.Forms。。。不确定谢谢,你提供的代码非常有用。我现在已经启动并运行了游戏循环,但是,我想知道如何准确地计算每次更新的时间?我需要对其进行设置,使渲染调用恰好每x毫秒执行一次(或在渲染前一帧后尽快执行)。上面的代码似乎只是尽可能快地渲染。我试图添加一个线程。睡眠,但效果不好…添加了一个关于循环时间的新编辑。我不能把它放在评论里,谢谢,不过那部分很清楚。原来是PeekMessage引起了麻烦,因为MouseeEvents在应该没有更新的时候触发了更新。我刚刚移除了那个内部循环,然后它看起来像是在按预期工作。:)顺便问一下,您是如何制作自己的ContentManager来加载图形和其他数据的?通常情况下,游戏类通过内容来处理这个问题…@sinsro我对使用这些网站还不熟悉,它会在我更新答案时通知你吗,还是我需要发表评论?XNA游戏确实不是你滞后的原因。不可能。我强烈怀疑您的问题与
    游戏
    类本身无关,替换它不是正确的解决方案。但是,我无法解释这种口吃行为。我检查过了,没有垃圾收集在进行,但是,在随机间隔中,每次调用draw之间的测量时间是不同的,即使所绘制的内容的复杂性保持不变。我尝试了很多不同的方法,这是我最后的努力…@VincentKoeman:我同意。我的游戏引擎以300fps的速度运行4km x 4km场景,没有任何口吃,这是一个很小的变化,它是Microsoft.XNA.game。
    MyServiceProvider m = new MyServiceProvider(graphicsDevice);
    ContentManager content = m.CreateContentManager("Content");
    
    MyServiceProvider(GraphicsDevice graphicsDevice)
    {
          myGraphicsService = new MyGraphicsDeviceService(graphicsDevice);
    }
    
    MyGraphicsDeviceService(GraphicsDevice gfxDevice)
    {
         graphicsDevice = gfxDevice;
    }
    
        private void IgnoreAlt(object pSender, KeyEventArgs pEventArgs)
        {
            if (pEventArgs.Alt && pEventArgs.KeyCode != Keys.F4)
                pEventArgs.Handled = true;
    
        }
    
        form.KeyUp += IgnoreAlt;
        form.KeyDown += IgnoreAlt;