我正试图弄清楚如何在Windows游戏中手动管理整个游戏循环,而不使用常规游戏Microsoft.Xna.Framework.Game类。
这样做的原因是使用常规的Game类会导致我的游戏中出现一些口吃。不多,但由于游戏的特殊性,它仍然是非常明显的。
在尝试了一堆不同的设置(vsync,fixedtimestep,各种帧率等)后,我决定尝试编写自己的Game类来完全控制时间。我不确定会解决它,但至少这种方式我可以完全控制。
基本上我需要:
任何人都知道如何做到这一点?事实上这听起来很容易,但找不到任何关于如何做的文档。
不确定我做错了什么,但我有以下代码(仅用于测试,时间将以不同方式处理),循环将运行一段时间然后停止。一旦我将鼠标指针移到窗口上,循环就会再次运行一段时间。
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();
}
但是在这种情况下,窗口仍然是空白的,因为看起来窗口实际上没有使用新内容重绘。我尝试了一个form.Refresh(),但仍然没有...任何想法?
答案 0 :(得分:7)
(添加了xbox信息)
对于windows你基本上需要创建一个Form并显示它,然后存储它的句柄和表单本身。 使用此句柄可以创建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,您可以将自己的自定义运行功能与游戏循环放在一个限制的while循环中。在内部运行之外,您可能需要使用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;
}
-Sorry用于分割代码,但它不是我最近写的东西,所以我很难记住部分。
编辑4
我的自定义游戏有一个奇怪的情况,我记得当我为它新建表格时 不得不绑定
private void IgnoreAlt(object pSender, KeyEventArgs pEventArgs)
{
if (pEventArgs.Alt && pEventArgs.KeyCode != Keys.F4)
pEventArgs.Handled = true;
}
到
form.KeyUp += IgnoreAlt;
form.KeyDown += IgnoreAlt;
否则我有一些可怕的摊位。
答案 1 :(得分:6)
我最近创建了一系列博客文章,介绍如何彻底消除对Microsoft.Xna.Framework.Game的依赖。我不喜欢关于Game类的几件事,最值得注意的是它严重违反了Single Responsibility Principle。我的文本冒险游戏和编辑器甚至没有引用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
最后,GitHub上的Text Adventure项目: