Windows窗体环境中XNA-Control的GameTime类替换

时间:2012-01-31 19:21:20

标签: c# winforms time xna

您好我正在尝试使用本教程在我的项目中嵌入一个XNA模块,这是一个Windows窗体应用程序: http://create.msdn.com/en-US/education/catalog/sample/winforms_series_1。 我想这是最简单的练习教程,所以我决定跟上它。

当我需要GameTime时出现问题,在他们的XNA控件的实现中,GameTime不存在。我试图在谷歌寻找一个快速的解决方案,并试图找到一个解释如何在常规XNA游戏中实现GameTime,但我发现的信息越来越多,我变得更加困惑...... 以下是问题:

  • 在常规XNA游戏GameTime.ElapsedGameTime中,描述说明“自上次更新以来经过的游戏时间。” - 那是什么意思?它是否给出了经过的毫秒数?但这没有任何意义,因为图纸和更新之间有一个恒定的时间跨度,它每16毫秒或多或少发生..对我来说似乎毫无意义,我想在这里做一点解释。我知道ElapsedGameTime在使用线性插值平滑运动方面发挥了重要作用,但如果它的最大值大约是16ms则没有任何意义

  • 在XNA控件中是否有完全GameTime的实现?如果没有,那么在Windows窗体中模拟GameTime的最佳做法是什么?

很抱歉,如果我之前已经提出过我的问题,那对我来说真的很重要,并且已经尝试根据Google搜索获得答案,但未能获得任何明确的答案

5 个答案:

答案 0 :(得分:1)

GameTime提供自上次更新以来经过的固定或可变时间,自游戏开始以来的总时间以及与目标效果相关的IsRunningSlowly标记。

这是一篇关于WinForms中游戏计时器的好文章:When WinForms met Game Loop

答案 1 :(得分:0)

如果您选择使用可变时间步,则GameTime结构更相关。默认情况下,XNA运行固定的时间步长(因此在正常情况下所有更新都会定期发生)。

我猜你需要一个GameTime,因为你想要不仅根据用户输入刷新控件(比如游戏,即使用户没有碰到任何东西,也会发生事情)。

在这种情况下,一种简单的方法是在表单上设置一个定时器控件,简单地调用更新/渲染函数。您将计时器的间隔传递给您的功能。您可以使用XNA中的函数通常接受GameTime只接受double或float等。或者您可以根据间隔自己创建GameTime

另一种选择是创建另一个尝试尽可能快地更新的线程(可能达到一个点)。然后,这将在UI线程上引发回调,这将进行更新/呈现。处理GameTime将与上述相似,因为您可以记录上次运行的时间,并将当时和现在之间的差异作为时间增量传递。

答案 2 :(得分:0)

GameTimeElapsedGameTime为您提供了一个TimeSpan对象,您可以在其中检索您想要的任何时间单位,包括(但不限于){{3}的毫秒数属性。

TimeSpan始终相同的原因是因为默认情况下XNA使用固定的时间步长。您可以通过设置TotalMiliseconds属性来更改此项,因为Dmitry在评论中指出了这一点。关于此问题Game.IsFixedTimeStep中的时间步长以及实现它们的一些代码,我们进行了很好的讨论。

答案 3 :(得分:0)

这段代码效果很好,唯一的区别是游戏类中的构造函数,其余的就像是一个普通的xna游戏。

这是我用于此editor for 2D sprites and skeletal animations

的代码

<强> 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         
    }      
}

答案 4 :(得分:0)

在幕后,Xna提供了将时间步长固定为大约60 FPS的功能。除非您将该功能写入winforms应用程序,否则您将无法使用它。但是使用可变时间步而不是固定时间步是一个很好的选择。

我通过使用从GraphicsDeviveControl派生的类中的秒表来解决这个问题。

然后,在Draw()方法中,将变量设置为它的经过时间,然后重置它。这是一个例子:

public class XnaControl : GraphicsDeviceControl
{
    Stopwatch timer;

之后,在draw方法中

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

现在,通过发送'elapsed'以及Update&amp;绘制时,您可以像任何可变时间步骤游戏/应用程序一样以插值方式计算事物。