Пользовательский игровой цикл XNA в Windows - PullRequest
10 голосов
/ 15 июня 2011

Я пытаюсь выяснить, как управлять всем игровым циклом вручную в игре Windows, без использования обычного класса Game 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();
        }
    }

Если включить «while PeekMessage», цикл будет работать непрерывно, но игнорируя режим сна, а также останавливаться, когдамышь движется над окном.Не уверен, что здесь происходит ...

Я думаю, что оптимально, я просто хотел бы сделать что-то простое в основном цикле рендеринга:

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

Но в этом случае окноостается пустым, так как кажется, что окно фактически не перерисовывается с новым содержимым.Я попробовал форму. Refresh (), но все равно не идет ... Есть идеи?

Ответы [ 2 ]

7 голосов
/ 15 июня 2011

(добавлена ​​информация о xbox)

для окон В основном вам нужно создать форму и показать ее, затем сохранить ее дескриптор и саму форму. Используя этот дескриптор, вы можете создать 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 вы можете просто разместить собственную пользовательскую функцию запуска с игровым циклом в ограниченном цикле true. Внутри, которая выходит за пределы вершины true, вам, вероятно, придется выполнить инициализацию и проверку графического устройства с 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;
}

-Извините, что фрагментировал код, но это не то, что я написал слишком недавно, поэтому у меня возникают трудности с запоминанием частей.

РЕДАКТИРОВАТЬ 4

У меня был странный случай с моей пользовательской игрой, которую я только что вспомнил, когда я создал новую форму для нее. пришлось связать

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

    }

до

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

В противном случае у меня есть несколько ужасных киосков.

6 голосов
/ 24 января 2012

Недавно я создал серию постов в блоге о том, как полностью устранить зависимость от Microsoft.Xna.Framework.Game.Мне не нравятся некоторые вещи в классе Game, особенно в том, что он грубо нарушает принцип единой ответственности.Игра и редактор My Text Adventure даже не ссылаются на сборку 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

И, наконец, проект Text Adventure на GitHub:

https://github.com/NathanAlden/TextAdventure

...