Windows中的自定义XNA游戏循环

sin*_*sro 10 xna rendering game-loop

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

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

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

基本上我需要:

  1. 设置游戏窗口
  2. 在循环中:像往常一样进行所有渲染,然后将结果刷新到屏幕,管理后台缓冲区等.

谁知道怎么做?事实上这听起来很容易,但找不到任何关于如何做的文档.


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

    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();
        }
    }
Run Code Online (Sandbox Code Playgroud)

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

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

    while (alive)
    {
      Thread.Sleep(100);
      gametime.update();
      Update(gametime);
      Draw(gametime);
      GraphicsDevice.Present();
    }
Run Code Online (Sandbox Code Playgroud)

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

exn*_*031 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);
}
Run Code Online (Sandbox Code Playgroud)

编辑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();
Run Code Online (Sandbox Code Playgroud)

编辑3

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

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

        return null;
    }
Run Code Online (Sandbox Code Playgroud)

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

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

        ContentManager content = new ContentManager(this);

        content.RootDirectory = sPath;

        return content;

    }
Run Code Online (Sandbox Code Playgroud)

另外,我创建了一个实现IGraphicsDeviceService的类,并引用了我的GraphicsDevice.然后我就像这样创建一个属性和字段

    //in implementer of IGraphicsDeviceService 
    private GraphicsDevice graphicsDevice;
    public GraphicsDevice GraphicsDevice
    {
        get
        {
            return graphicsDevice;
        }
    }
Run Code Online (Sandbox Code Playgroud)

所以这个电话最终会变得有些像

MyServiceProvider m = new MyServiceProvider(graphicsDevice);
ContentManager content = m.CreateContentManager("Content");
Run Code Online (Sandbox Code Playgroud)

哪里

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

MyGraphicsDeviceService(GraphicsDevice gfxDevice)
{
     graphicsDevice = gfxDevice;
}
Run Code Online (Sandbox Code Playgroud)

- 为了分割代码而烦恼,但它不是我最近写的东西所以我很难记住部分.

编辑4

我有一个奇怪的情况与我的自定义游戏我记得当我新的表格为它我必须绑定

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

    }
Run Code Online (Sandbox Code Playgroud)

    form.KeyUp += IgnoreAlt;
    form.KeyDown += IgnoreAlt;
Run Code Online (Sandbox Code Playgroud)

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

  • 我认为对于xbox你需要使用IntPtr.Zero调用graphicsdevice构造函数或者其他东西来创建一个设备,因为你没有访问System.Windows.Forms ...虽然不确定 (2认同)

Nat*_*nSr 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