Hi,
A complicated question: I am developing a 2D game engine. Up until now, I have used a Dispatcher Timer/EachTick to call my gameloop code. This has worked fine, but the problem with a dispatcher timer is that if the gameloop skips a step (because of too many objects in the game to fit in one time event), the code will wait until the next dispatched time. So for example: if it's set to 100 ms and the gameloop takes 110 ms to execute, the game will pause until 200 ms have passed. This is problematic, as it causes the games to be quick or slow depending on the number of objects in the game. For example, the more enemies have been shot off, the faster the game runs.
In addition, I'd really like to be able to make a game run at a constant speed in case a user of the game engine wants to create a time-based game (say a soccer game), where the number of seconds matter and must be constant.
Therefore I have tried substituting this for a simple while-loop and then recalculating the moves of objects: SpeedX = SpeedX*timePassed , meaning that the speedX value is the X speed per second, and timePassed is the amount of ms passed since last trip in the game loop.
To my surprise, this solution didn't work. First, the game hanged as wasn't drawn at all. I guess is that it never took the time to draw the game, due to threading problems. Therefore, I inserted a Thread.Sleep(50) to slow things down and allow for the game to be drawn. Looking at breakpoints, the code runs as it should, and the objects are where they are supposed to be, but the screen (XAML canvas in WPF) is not redrawn. I have tried switching back and forth, and when the gameloop method is instead set to EachTick, it works as before but not with my changes.
With all this background, how can I fix my gameloop, or rather, how can I force the screen to update? (Code below, and the screen is usually redrawn calling BoardSetup.DrawGame(game, Bodies.Bodylist);.)
In addition I can't really understand why this happens, so an explanation would be awesome. (I suspect that using a Dispatcher Timer, a threading solution takes care of drawing the window separately, or something along those lines.)
Small update: I found that more people have the same issue, but I am not sure of the best way to solve it. I just want something that works well and is fast.
Thanks a lot!
Petter
private void Gameloop()
{
while (true)
{
System.Threading.Thread.Sleep(50);
if (runGame == false) return;
TrackKeyboard();
EnemyMoves();
CalculateMoves();
CollisionDetection();
Bodies.DeleteBodiesMarkedForDeletion();
BoardSetup.DrawGame(game, Bodies.Bodylist);
MainWindow mainWin = System.Windows.Application.Current.Windows.Cast<System.Windows.Window>().FirstOrDefault(window => window is MainWindow) as MainWindow;
mainWin.txtScore.Text = msg;
if (updateDisplayInfo == true) BoardSetup.PrintInfo(InfoMessage().ToString());
counter++;
}
}
...and here's my original DispatcherTimer code (which works, and is called from MainWindow.xaml.cs):
var timer = new DispatcherTimer();
timer.Interval = new TimeSpan(0, 0, 0, 0, 10);
timer.Tick += EachTick;
...and then a call to EachTick.
Finally, this is where I draw the game. The code is a bit long and clumsy, since it allows for both "normal" games and scrolling games. Also, I see that the logic is not so good here (it's a work in progress), but it doesn't impact my question at hand.
static public void DrawGame(Canvas game, IReadOnlyList<Body> bodylist)
{
float offsetX = 0;
float offsetY = 0;
if (Variables.scrollingGame)
{
float centerX = 300;
float centerY = 250;
int index = Utilities.FindBodyIndex("Player");
if (index < 0) return;
Body player = bodylist[index];
offsetX = player.PosX - centerX;
offsetY = player.PosY - centerY;
}
foreach (Rectangle rect in game.Children.OfType<Rectangle>().ToArray())
{
if (!rect.Name.StartsWith("static_"))
{
game.Children.Remove(rect);
}
}
foreach (Ellipse circle in game.Children.OfType<Ellipse>().ToArray())
{
if (!circle.Name.StartsWith("static_"))
{
game.Children.Remove(circle);
}
}
for (int i = 0; i < Bodies.NumberOfBodies(); i++)
{
Body body = Bodies.Bodylist[i];
var shape = body.GetShape();
game.Children.Add(shape);
if (body.Movable)
{
body.PosX = body.PosX + body.SpeedX;
body.PosY = body.PosY + body.SpeedY;
}
Canvas.SetLeft(shape, body.PosX - offsetX);
Canvas.SetTop(shape, body.PosY - offsetY);
}
}
What I have tried:
I have tried different variations of my code, but don't know really what to look for.