2

I am creating a game engine for WPF (and later also for UWP). I am trying to move away from DispatcherTimer to StopWatch, to get a more constant speed of the game (as DispatcherTimer skips turns if there are too many objects in the game).

I can write the code for updating the game correctly using a StopWatch (that is, my game objects' speeds and positions are calculated correctly when I check for ticks or ms passed since last update), but when I replace the DispatcherTimer's tick events with a simple while-loop and a call to the screen-drawing method, the GUI will never redraw.

I know there are several ways to force a redraw of the window, but what would be my best option for a 2D game in WPF? I'm just looking for something simple that works rather efficiently without a lot of hassle. It doesn't have to be super fast, but it should be rather accurate in terms of constant speed (i.e. the game should move at a constant speed regardless of the number of dynamic objects).

This is my current code for placing objects onscreen, called many times per second from my gameloop:

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);
}

So far, I have read up on the subject, but there are many solutions and I don't know what would be the most suitable.

On stackoverflow, I have looked at many answers, including

moving physics body at a constant speed

and

WPF forcing redraw of canvas

and

Confusion about Refreshing the UI in WPF

and

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

and

How can I get better timed events for my games?

There is a similiar question here: How to make a render loop in WPF? with the same reply, but the circumstances are a bit different. For example, I don't need a constant frame rate per se, I just want timing to be reasonably accurate.

There seems to be a lot of confusion about these things, and I feel that whenever I start exploring one of the ideas, I end up trying to work with very complex matters. Once again, all I want to do is to get the redraw method to actually redraw the GUI at, say, 20 times per second.

Thanks!

Petter

Edit:

My DispatcherTimer code was requested, so here it is:

    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++;
        //}
    }
petter
  • 63
  • 10
  • You are not showing the problematic code of the timers and your usage of them. Please do. DispatcherTimer is not just a timer, it is connected to the one UI thread you have in a WPF application.Therefore you need to have your own timer in a background thread doing the updates, and only calling the DispatcherTimer when you need to rerender. Try calling InvalidateVisual on the canvas. – Mishka Oct 18 '17 at 11:03
  • Hi, thanks! I added the code as requested. Thanks for trying to help me out! – petter Oct 18 '17 at 12:02
  • Possible duplicate of [How to make a render loop in WPF?](https://stackoverflow.com/questions/16991949/how-to-make-a-render-loop-in-wpf) – Asik Oct 18 '17 at 12:03
  • Hi, no I don't think it is. However, the link is to CompositionTarget.Rendering, which is relevant. I'm looking into that now. – petter Oct 18 '17 at 14:26

2 Answers2

3

Have you tried setting Priority for DispatcherTimer, you can use Higher Priority to get accurate results https://msdn.microsoft.com/en-us/library/system.windows.threading.dispatcherpriority(v=vs.110).aspx

If you need such exact frame rate this question might be a help : WPF real-time rendering

But you still might face some issues which this question can help you

Constant framerate in WPF for game

Ehsan Zargar Ershadi
  • 24,115
  • 17
  • 65
  • 95
  • Hi, thanks, but would that really accomplish what I try to do? From all info I've seen on stackoverflow, the DispatcherTimer won't do. – petter Oct 18 '17 at 12:03
  • Hi, thanks for the added links. Interestingly, one of them points to Andy Bealieu's PhysicsHelper project, which I used for several years (until it was abandoned). What I'm trying to do now is a replacement for that - a very simple game engine for first year programming students. In other words, exactness or performance is not so important. Also, it doesn't really have to be WPF, but since XNA and Silverlight are gone, then what else is there that isn't very different (apart from UWP)? I think from reading up on your links that CompostitionTarget_rendering would be the best bet (checking now). – petter Oct 18 '17 at 14:44
  • Yes, CompostitionTarget_renderin is a good option, but you can use DirectX as well. – Ehsan Zargar Ershadi Oct 18 '17 at 14:51
  • Hi again, thanks - really appreciated. I'm not sure how to use it though and there are very few examples of Composition... . I have my MainWindow and a canvas in there where the action is. th MainWindow.xaml.cs calls GameController.cs which takes care of the game logic (like the game loop). The BoardSetup class's DrawGame() method is then called from the GameLoop() method and redraws the objects on the canvas. So where should I add the event handler? I try to call it in MainWindow.XAML.cs and in the game loop but the UI hangs on me. – petter Oct 18 '17 at 15:45
  • Actually, calling my gameloop from the CompositionTarget.Rendering event does the trick. I have now successfully tried with 30 dynamic objects as well as 300 dynamic objects. The objects are not moving smoothly if I have 300 moving objects, but the time is accurate: I have run the game for four minutes (exactly, and the time is correct to the second (at least - I'm confirming with my cellphone's timer :) ). I will write an article about my game engine within a few weeks hopefully. If anyone needs my code now, then just PM me or so. – petter Oct 19 '17 at 16:28
1

Don't use WPF for a game engine. There is simply no reliable way to control the framerate, and the rendering is incredibly inefficient. Your best bet is CompositionTarget.Rendering https://msdn.microsoft.com/en-us/library/system.windows.media.compositiontarget.rendering(v=vs.110).aspx but even that is sometimes unreliable.

Asik
  • 21,506
  • 6
  • 72
  • 131
  • Hi, thanks for your reply. Well, I don't really care about the framerate, I care about timing (per second) being accurate, and this is why I'm thinking of using a StopWatch.For example, this would ensure that a soccer game running for 90 seconds would in fact run for 90 seconds. Also, this means that an object's "SpeedX" property would mean "n pixels per second". – petter Oct 18 '17 at 14:20
  • Once you get framerate issues I think you will start caring about it... Better use a technology that makes sense for the kind of program you're writing, if you have a choice. WPF gives you no control over when or how often it renders. That's going to be a problem for basically any video game. – Asik Oct 18 '17 at 23:55
  • Hi Asik, Thanks for your comment. However, the way proposed by Ehsan works really well. For me, it's not an option moving to other technologies (like Unity or MonoGame) - I need to do this in WPF/UWP. Luckliy, his answer was helpful. – petter Oct 19 '17 at 16:30