如何刷新2D WPF游戏中的最智能UI?

时间:2017-10-18 10:39:20

标签: c# wpf 2d game-engine game-physics

我正在为WPF创建一个游戏引擎(后来也为UWP创建)。我正试图从DispatcherTimer转移到StopWatch,以获得更恒定的游戏速度(如果游戏中有太多对象,DispatcherTimer会跳过转动)。

我可以使用StopWatch编写正确更新游戏的代码(也就是说,当我检查自上次更新以来的滴答或ms时,我的游戏对象的速度和位置是否正确计算),但是当我替换DispatcherTimer的时候使用简单的while循环和调用屏幕绘制方法的事件,GUI将永远不会重绘。

我知道有几种方法可以强制重绘窗口,但是在WPF中2D游戏的最佳选择是什么?我只是在寻找一些简单而有效的方法而不会有太多麻烦。它不一定非常快,但就恒定速度而言应该是相当准确的(即无论动态对象的数量如何,游戏都应以恒定速度移动)。

这是我目前在屏幕上放置对象的代码,每秒从我的游戏循环中调用多次:

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上,我看了很多答案,包括

moving physics body at a constant speed

WPF forcing redraw of canvas

Confusion about Refreshing the UI in WPF

How to control frame rate in WPF by using dispatcher timer accurately?

How can I get better timed events for my games?

这里有一个类似的问题:How to make a render loop in WPF?有相同的回复,但情况有点不同。例如,我本身不需要恒定的帧速率,我只是希望时间合理准确。

对这些事情似乎有很多困惑,我觉得每当我开始探索其中一个想法时,我最终都会尝试处理非常复杂的问题。再一次,我想做的就是让重绘方法实际重绘GUI,比如每秒20次。

谢谢!

皮特

编辑:

我的DispatcherTimer代码已被请求,所以这里是:

    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++;
        //}
    }

2 个答案:

答案 0 :(得分:1)

您是否尝试过为DispatcherTimer设置优先级,您可以使用更高优先级来获得准确的结果https://msdn.microsoft.com/en-us/library/system.windows.threading.dispatcherpriority(v=vs.110).aspx

如果你需要这么精确的帧率,这个问题可能会有所帮助:  WPF real-time rendering

但你仍然可能面临一些问题,这个问题可以帮到你

Constant framerate in WPF for game

答案 1 :(得分:0)

不要将WPF用于游戏引擎。没有可靠的方法来控制帧速率,渲染效率非常低。你最好的选择是CompositionTarget.Rendering https://msdn.microsoft.com/en-us/library/system.windows.media.compositiontarget.rendering(v=vs.110).aspx,但即便如此也是不可靠的。