How do I refresh the UI smartest in a 2D WPF game?
我正在为WPF(以及后来的UWP)创建一个游戏引擎。我试图从DispatcherTimer移至StopWatch,以使游戏的速度保持恒定(因为如果游戏中有太多对象,DispatcherTimer会跳过转弯)。
我可以使用StopWatch编写用于正确更新游戏的代码(也就是说,当我检查自上次更新以来传递的滴答声或ms时,我的游戏对象的速度和位置可以正确计算),但是当我将DispatcherTimer的滴答事件替换为简单的while循环和对屏幕绘制方法的调用,GUI将永远不会重绘。
我知道有几种方法可以强制重新绘制窗口,但是在WPF中进行2D游戏的最佳选择是什么?我只是在寻找一种简单而有效的方法,而又不会带来很多麻烦。它并不一定要超级快,但是就恒定速度而言,它应该相当准确(也就是说,无论动态对象的数量如何,游戏都应以恒定速度运动)。
这是我当前用于在屏幕上放置对象的代码,每秒从我的游戏循环中调用多次:
1 2 3 4 5 6 7 8 9 | for (int i = 0; i < Bodies.NumberOfBodies(); { Body body = Bodies.Bodylist[i]; var shape = body.GetShape(); game.Children.Add(shape); //each movable body's shape is added again. Canvas.SetLeft(shape, body.PosX - offsetX); Canvas.SetTop(shape, body.PosY - offsetY); } |
到目前为止,我已经阅读了该主题,但是有很多解决方案,我不知道哪种方法最合适。
关于stackoverflow,我查看了许多答案,包括
以恒定速度移动物理物体
和
WPF强制重新绘制画布
和
关于在WPF中刷新UI的困惑
和
如何通过准确使用调度程序计时器来控制WPF中的帧速率?
和
如何获得更好的比赛时间安排?
这里有一个类似的问题:如何在WPF中进行渲染循环?回复相同,但情况略有不同。例如,我本身并不需要恒定的帧速率,我只希望定时合理。
这些事情似乎有很多混乱,我感到每当我开始探索其中一种想法时,我最终都会尝试处理非常复杂的问题。再一次,我要做的就是获取redraw方法,以每秒20次的速度实际重绘GUI。
谢谢!
皮特
编辑:
请求了我的DispatcherTimer代码,因此它是:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 | public void StartTimer() { //A note on the dispatcherTimer: http://www.wpf-tutorial.com/misc/dispatchertimer/ //The code below fires as often as possible (depending on the computer). //When moves are calulated, the real elapsed time is taken in consideration. /*Test: 300 added movable objects; a setting of 100ms * --> 99 calls to CalculateMoves in 30 seconds; should have been 300. * In other words, the game moves at a third of the actual speed. * With only 30 added objects, the same settings gives 270 calls per 30 seconds. * With only 3 added objects, the same settings gives 273 calls per 30 seconds. * */ var timer = new DispatcherTimer(); timer.Interval = new TimeSpan(0, 0, 0, 0, 10); // Each every n milliseconds (set low to avoid flicker) timer.Tick += EachTick; dtStart = DateTime.Now; //start time is set for performing various calculations //Add a bunch of objects for speed testing purposes for (int i = 0; i < 30; i++) { AddBody(); } stopWatch.Start(); timer.Start(); // Gameloop(); } private void EachTick(object sender, object e) { //while (true) //{ // System.Threading.Thread.Sleep(50); //gives the computer a chance to draw the gameboard (50 = 50 millieconds. //Increase to give less strain on the computer, but a too high value (above 10) will give a"strobo" effect. //This variable is used to get to the controls (labels etc) of the MainWindow (WPF) // MainWindow mainWin = System.Windows.Application.Current.Windows.Cast<System.Windows.Window>().FirstOrDefault(window => window is MainWindow) as MainWindow; if (runGame == false) return; //only go on if not in pause mode // mainWin.txtInfo.Text =""; time.ToString("mm\\\\:ss");//Shows the timer in a textbox, only showing minutes and seconds. //if (counter % 100 == 0) //{ // //Add a flower // AddBody(); //} //float rndBalloon = rand.Next(75, 250); //so that the balloons come at irregular intervals //if (counter % rndBalloon == 0) //{ // //Add a balloon // AddBalloon(); //} //change direction of cloud int cloudIndex = (Utilities.FindBodyIndex("cloud")); Body cloud = Bodies.Bodylist[cloudIndex]; if (cloud.PosX > 600) cloud.SpeedX = -0.3f; if (cloud.PosX < 100) cloud.SpeedX = 0.3f; TrackKeyboard(); //Used to see what keys are pressed by the user(s) EnemyMoves(); //Can be used to move enemies closer to the player CalculateMoves(); //move the bodies CollisionDetection(); //check for colliding bodies Bodies.DeleteBodiesMarkedForDeletion(); //remove bodies marked for deletion BoardSetup.DrawGame(game, Bodies.Bodylist); MainWindow mainWin = System.Windows.Application.Current.Windows.Cast<System.Windows.Window>().FirstOrDefault(window => window is MainWindow) as MainWindow; ////The score is updated onscreen mainWin.txtScore.Text = msg; // timesPassed.ToString(); //counter.ToString(); //If the infotext is displayed, this code will update it (but not toggle in on/off) if (updateDisplayInfo == true) BoardSetup.PrintInfo(InfoMessage().ToString()); //Each fireball becomes a little bit paler (they fade out and eventually disappear) FireballPaler(); counter++; //} } |
您尝试过为DispatcherTimer设置优先级时,可以使用"更高优先级"来获得准确的结果https://msdn.microsoft.com/zh-cn/library/system.windows.threading.dispatcherpriority(v=vs.110).aspx
如果您需要如此精确的帧频,此问题可能会有所帮助:
WPF实时渲染
但是您仍然可能会遇到一些问题,这个问题可以帮助您
WPF游戏的恒定帧率
不要将WPF用于游戏引擎。 根本没有可靠的方法来控制帧速率,并且渲染效率极低。 最好的选择是CompositionTarget.Rendering https://msdn.microsoft.com/zh-cn/library/system.windows.media.compositiontarget.rendering(v=vs.110).aspx,但这有时有时并不可靠。