Windows窗体环境中XNA控件的GameTime类替换

本文关键字:GameTime 替换 控件 XNA 窗体 环境 Windows | 更新日期: 2023-09-27 18:21:58

你好,我目前正在尝试使用本教程将XNA模块嵌入到我的项目中,该项目是一个Windows窗体应用程序:http://create.msdn.com/en-US/education/catalog/sample/winforms_series_1.我想这是最容易练习的教程,所以我决定继续学习

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

  • 在一款常规的XNA游戏GameTime.ElapedGameTime中,描述上写着"自上次更新以来的游戏时间。"-这是什么意思?它给出了已经过去的毫秒吗?但这没有任何意义,因为绘图和更新之间有一个恒定的时间跨度,而且或多或少每隔16毫秒就会发生一次。。这对我来说似乎毫无意义,我想在这里解释一下。我知道ElapsedGameTime在使用线性插值平滑运动方面发挥着重要作用,但如果它的最大值在16ms 左右,那就没有任何意义了

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

很抱歉,如果以前有人问过我的问题,现在对我来说真的很重要,我已经尝试过根据谷歌搜索得到答案,但没有得到任何明确的答案。

Windows窗体环境中XNA控件的GameTime类替换

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

这是一篇关于WinForms中游戏计时器的好文章:当WinForms遇到game Loop 时

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

我已经通过在从GraphicsDeviveControl派生的类中使用Stopwatch解决了这个问题。

然后,在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);
    }

现在,通过将"消逝"与Update&绘制,您可以像任何可变时间步长游戏/应用程序一样以插值方式计算事物。

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

我猜你需要一个GameTime,因为你想刷新控件,而不仅仅是基于用户输入(例如,就像游戏一样,即使用户没有触摸任何东西,事情也会发生)。

在这种情况下,一种简单的方法是在窗体上设置一个计时器控件,该控件只需调用更新/渲染函数。你会把计时器的间隔传给你的函数。你可以让你的函数在XNA中通常接受GameTime,只接受double或float等,或者你可以根据间隔自己创建GameTime

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

GameTime.ElapedGameTime为您提供了一个TimeSpan对象,然后您可以在其中检索您想要的任何时间单位,包括(但不限于)通过TotalMiliseconds属性的毫秒。

TimeSpan始终相同的原因是XNA默认使用固定的时间步长。你可以通过设置Game.IsFixedTimeStep属性来改变这一点,正如Dmitry在评论中指出的那样。在这个问题中有一个关于时间步长的极好的讨论固定时间步长与可变时间步长,以及一些实现它们的代码。

这段代码运行良好,唯一的区别是游戏类中的构造函数,其余的就像windows的普通xna游戏。

这是我为这个编辑器使用的代码,用于2D精灵和骨骼动画

程序.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         
    }      
}