Замена класса GameTime для XNA-Control в среде Windows Forms - PullRequest
1 голос
/ 31 января 2012

Здравствуйте. В настоящее время я пытаюсь встроить модуль XNA в мой проект, который является приложением Windows Forms, с использованием этого руководства: http://create.msdn.com/en-US/education/catalog/sample/winforms_series_1. Я думаю, что это лучший учебник для легкой практики, поэтому я решил не отставать от него.

Проблема возникает, когда мне понадобился GameTime, в их реализации элемента управления XNA GameTime не существует. Я попытался найти быстрое решение в Google и попытался найти объяснение того, как GameTime реализован в обычной игре XNA, но чем больше информации я нашел, тем больше я запутался ... Вот вопросы:

  • В обычной игре XNA, GameTime.ElapsedGameTime, в описании указано «Количество прошедшего игрового времени с момента последнего обновления». - Что это значит? Дает ли это миллисекунды, которые прошли? Но это не имеет никакого смысла, потому что между чертежами и обновлениями существует постоянный промежуток времени, и это происходит каждые 16 мс более или менее ... Это кажется мне бессмысленным, и я хотел бы получить небольшое объяснение здесь. Я знаю, что ElapsedGameTime играет большую роль в сглаживании движений с помощью линейной интерполяции, но это не имеет никакого смысла, если его максимальное значение составляет около 16 мс

  • Есть ли реализация точного GameTime в элементе управления XNA? Если нет, то как лучше всего имитировать GameTime в Windows Forms?

Извините, если мои вопросы задавались ранее, это действительно очень важно для меня прямо сейчас, и я уже пытался получить ответ, основанный на поиске в Google, но не смог получить четкие ответы

Ответы [ 5 ]

1 голос
/ 31 января 2012

GameTime предоставляет фиксированное или переменное время, прошедшее с момента последнего обновления, общее количество времени с начала игры и флаг IsRunningSlowly, относящийся к целевой производительности.

Вот хорошая статья об игровых таймерах в WinForms: Когда WinForms встретились с Game Loop

0 голосов
/ 01 февраля 2012

За кулисами Xna предоставляет функциональность, позволяющую фиксировать временной шаг примерно до 60 кадров в секунду. Если вы не напишите эту функциональность в приложение winforms, у вас ее не будет. Но работа с переменным шагом по времени вместо этого фиксированного временного шага является хорошим вариантом.

Я решил эту проблему с помощью секундомера в классе, производном от GraphicsDeviveControl.

Затем в методе Draw () установите переменную равной истекшему времени, затем сбросьте ее. Вот пример:

public class XnaControl : GraphicsDeviceControl
{
    Stopwatch timer;

позже, в методе розыгрыша

    protected override void Draw()
    {
        float elapsed = (float)timer.Elapsed.TotalSeconds;
        timer.Restart();
        systemBase.UpdateSimulation(elapsed);
        systemBase.DrawSimulation(elapsed);
    }

Теперь, отправив 'elapsed' вместе с Update & draw, вы можете вычислять вещи интерполяционным образом, как в любой игре / приложении с переменным временным шагом.

0 голосов
/ 01 февраля 2012

Этот код работает хорошо, и единственным отличием является конструктор внутри игрового класса, остальное похоже на обычную игру XNA для Windows.

Это код, который я использовал для этого редактора для 2D-спрайтов и скелетной анимации

Program.cs

namespace SpriteEditor
{
#if WINDOWS
    static class Program
    {
        [STAThread]
        static void Main(string[] args)
        {               
            Application.EnableVisualStyles( );
            Application.SetCompatibleTextRenderingDefault( false );
            XnaControlGame.CreateAndShow<SkeletonManagerDialog, SkeletonXnaGame>( );           
        }
    }
#endif

Форма

public partial class SkeletonManagerDialog : Form, IXnaFormContainer
{        
    public Control XnaControl { get { return spriteBoneEditor1.XnaPicture; } }
    public XnaControlGame Game { get; set; }
    ....
}

XnaGame

public partial class SkeletonXnaGame : XnaControlGame
{
    public SkeletonXnaGame( IntPtr ptr, Form form, Control control ) 
        : base( ptr, form, control ) { }

    //--------------------------------------------------------------------------
    protected override void Update( GameTime gameTime )        
    {
        float Seconds = ( float ) gameTime.ElapsedGameTime.TotalSeconds;

        HandleMouse( );

        if ( TryHandleCamera( ) ) return;
        if ( MouseIsInsideViewport) HandleLeftClick( Seconds );
        if ( SkeletonManager.SelectedBone != null ) UpdateSelectedBone( Seconds );
        if ( SkeletonManager.SelectedShape != null ) UpdateSelectedShape( Seconds );
        if ( SkeletonManager.CurrentSequence != null ) SkeletonManager.CurrentSequence.Update( Seconds );
        base.Update( gameTime );
    }

    ....
}

XnaControlGame.cs

using System;
using System.Windows.Forms;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;

namespace SpriteEditor
{
    public interface IXnaFormContainer
    {
        Control XnaControl { get; }
        XnaControlGame Game { get; set; }
    }

    public abstract class XnaControlGame : Microsoft.Xna.Framework.Game
    {
        public Control Parent { get; private set; }

        public static void CreateAndShow<T, Q>( )
            where T : Form, IXnaFormContainer, new( )
            where Q : XnaControlGame
        {
            using ( T form = new T( ) )
            {
                form.Show( );

                using ( Q game = ( Q ) Activator.CreateInstance( typeof( Q ), new object[] { form.XnaControl.Handle, form, form.XnaControl } ) )
                {
                    form.Game = game;
                    game.Parent = form.XnaControl;
                    game.Run( );
                }
            }
        }


        #region Private Vars to Build Embedded Xna Control

        IntPtr _XnaDrawingSurface;
        GraphicsDeviceManager graphics;

        System.Windows.Forms.Form parentForm;
        System.Windows.Forms.Control controlXna;

        System.Windows.Forms.Control gameForm;

        #endregion

        #region Constructor

        public XnaControlGame( IntPtr handle,
            System.Windows.Forms.Form parentForm,
            System.Windows.Forms.Control surfaceControl )
        {
            graphics = new GraphicsDeviceManager( this );
            graphics.GraphicsProfile = GraphicsProfile.Reach;
            Content.RootDirectory = "Content";

            this.parentForm = parentForm;
            this.controlXna = surfaceControl;

            gameForm = System.Windows.Forms.Control.FromHandle( this.Window.Handle );
            gameForm.VisibleChanged += new EventHandler( gameForm_VisibleChanged );
            controlXna.SizeChanged += new EventHandler( pictureBox_SizeChanged );

            // preparing device settings handler. 
            _XnaDrawingSurface = handle;
            Mouse.WindowHandle = handle;

            graphics.PreparingDeviceSettings += OnPreparingDeviceSettings;
            graphics.PreferredBackBufferWidth = (controlXna.Width > 0) ? controlXna.Width : 50;
            graphics.PreferredBackBufferHeight = (controlXna.Height > 0) ? controlXna.Height : 50;

            parentForm.FormClosed += delegate( object sender, System.Windows.Forms.FormClosedEventArgs e )
            {
                this.Exit( );
                Application.Exit( );
            };
        }

        #endregion

        #region Events

        private void OnPreparingDeviceSettings( object sender, PreparingDeviceSettingsEventArgs e )
        {
            e.GraphicsDeviceInformation.PresentationParameters.DeviceWindowHandle = _XnaDrawingSurface;
        }

        private void gameForm_VisibleChanged( object sender, EventArgs e )
        {
            if ( gameForm.Visible == true )
                gameForm.Visible = false;
        }

        void pictureBox_SizeChanged( object sender, EventArgs e )
        {
            if ( parentForm.WindowState !=
                System.Windows.Forms.FormWindowState.Minimized )
            {
                graphics.PreferredBackBufferWidth = controlXna.Width;
                graphics.PreferredBackBufferHeight = controlXna.Height;
                graphics.ApplyChanges( );
                OnSizeChanged( );
            }
        }

        protected virtual void OnSizeChanged( ) { }

        #endregion         
    }      
}
0 голосов
/ 31 января 2012

GameTime . ElapsedGameTime дает вам объект TimeSpan , где вы можете затем извлечь любую единицу времени, которую вы хотите, включая (но не ограничиваясь) миллисекунды до свойства TotalMiliseconds .

Причина, по которой TimeSpan всегда одинакова, заключается в том, что XNA по умолчанию использует фиксированный временной шаг. Вы можете изменить это, установив свойство Game.IsFixedTimeStep , как указывает Дмитрий в комментариях. В этом вопросе отлично обсуждаются временные шаги Фиксированный временной шаг по сравнению с переменным временным шагом , а также некоторый код для их реализации.

0 голосов
/ 31 января 2012

Структура GameTime более актуальна, если вы решите использовать переменный шаг по времени. По умолчанию XNA выполняет фиксированный шаг по времени (поэтому все обновления происходят через регулярные промежутки времени при нормальных обстоятельствах).

Полагаю, вам нужен GameTime, потому что вы хотите обновить элемент управления не только на основе пользовательского ввода (то есть, как в игре, где все происходит, даже если пользователь ничего не трогает).

В этом случае одним из простых подходов будет иметь элемент управления таймером в вашей форме, который просто вызывает ваши функции обновления / рендеринга. Вы передадите интервал таймера своим функциям. У вас могут быть функции, которые в XNA обычно принимают GameTime, просто принимает double или float и т. Д., Или вы можете просто создать GameTime самостоятельно на основе интервала.

Другим вариантом будет создание другого потока, который будет пытаться обновлять как можно быстрее (возможно, до определенного момента). Затем это вызовет обратные вызовы в потоке пользовательского интерфейса, которые будут выполнять обновление / рендеринг. Работа с GameTime была бы аналогична описанной выше, поскольку вы можете записывать время последнего запуска и передавать разницу между тогда и сейчас как дельта времени.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...