【问题标题】:GameTime class replacement for XNA-Control in Windows Forms environmentWindows 窗体环境中 XNA-Control 的 GameTime 类替换
【发布时间】:2012-02-01 22:59:42
【问题描述】:

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

当我需要 GameTime 时出现问题,在他们的 XNA Control 实现中,GameTime 不存在。我尝试在 Google 中寻找快速解决方案,并试图找到有关 GameTime 如何在常规 XNA 游戏中实现的解释,但我找到的信息越多,我就越困惑... 以下是问题:

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

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

对不起,如果我的问题以前被问过,这对我来说真的很重要,并且已经尝试根据谷歌搜索得到答案,但没有得到任何明确的答案

【问题讨论】:

  • 仅供参考:您可以通过将 Game.IsFixedTimeStep 设置为 false 来获得可变时间跨度

标签: c# winforms time xna


【解决方案1】:

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

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

【讨论】:

  • +1。那篇文章和他的大多数文章一样,确实是这里最好的答案。
  • 比使用具有所有功能的默认 Xna Game 类更好吗? :P
  • 我已经假设(虽然可能不正确),OP 由于某种原因不能使用 Game 类,这就是他需要自己实现游戏循环的原因。
【解决方案2】:

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

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

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

现在,通过与更新和绘制一起发送“经过”,您可以像任何可变时间步长游戏/应用程序一样以插值方式计算事物。

【讨论】:

  • 您能否展示一个为 XnaControl 获取鼠标移动的示例?
【解决方案3】:

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

我猜您需要GameTime,因为您不仅希望根据用户输入来刷新控件(即,就像游戏一样,即使用户没有触摸任何东西,事情也会发生)。

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

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

【讨论】:

    【解决方案4】:

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

    TimeSpan 总是相同的原因是 XNA 默认使用固定的时间步长。正如 Dmitry 在 cmets 中指出的那样,您可以通过设置 Game.IsFixedTimeStep 属性来更改此设置。在这个问题Fixed time step vs Variable time step 中有关于时间步长的精彩讨论,以及一些实现它们的代码。

    【讨论】:

      【解决方案5】:

      这段代码运行良好,唯一的区别是游戏类中的构造函数,其余的就像一个普通的 windows 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         
          }      
      }
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多