0

I am creating a rhythm game (a not falling game like Guitar Hero - but with arrows) for a Computer Science project using WPF and have had some performance issues. I have used a lot of DispatcherTimers which slowed down the application and caused it to freeze up. To fix this, I found an answer, which advised to use System.Threading.Timers.

I understand that by using this I create new threads for these timers which allows them all to run at the same time without slowing down the app to terrible speeds.

My timer creating method is this:

    private void SetTimers()
    {
        _arrowKeytimer = new Timer(new TimerCallback(Timer_ArrowTick), null, 0, 775);
        //_timerHealth = new Timer(new TimerCallback(Timer_HealthTick), null, 0, 775);
        _MediaTimer = new Timer(new TimerCallback(Timer_MediaTick), null, 0, 775);
        _leftFall = new Timer(new TimerCallback(LeftArrowFall), null, 0, 775);
        _rightFall = new Timer(new TimerCallback(RightArrowFall), null, 0, 775);
        _upFall = new Timer(new TimerCallback(UpArrowFall), null, 0, 775);
        _downFall = new Timer(new TimerCallback(DownArrowFall), null, 0, 775);
        _spawner = new Timer(new TimerCallback(Timer_SpawnTick), null, 0, 775);
    }

Once I have changed all my methods from the old ticks, I ran the program. Instantly I got the 'Can't access object from another thread.' exception.

Looking at this answer, I used the Dispatcher.Invoke method on the tick that has caused the exception:

    private void DownArrowFall(object state)
    {
        this.Dispatcher.Invoke((Action)(() => //Added the Invoke thing that the answer said to do
        {
        double y;
        for (int z = 0; z < TheDirector.DownArrowList.Count; z++) // Before I would get the exception on this line
        {
            if ((string)TheDirector.DownArrowList[z].Tag == "Spawned")
            {
                y = Canvas.GetTop(TheDirector.DownArrowList[z]);
                y += FallSpan;
                Canvas.SetTop(TheDirector.DownArrowList[z], y);
            }
        }
        double position = Canvas.GetTop(TheDirector.DownArrowList[0]);
        if (position >= 700)
        {
            TheDirector.RemoveDownArrow();
        }
        }));
    }

I ran the program again and it ended up throwing the same exception in my draw method in my LeftArrow class (which draws the falling left arrow on the screen). However, adding that invoke didnt work:

    public void Draw()
    {          
        this.Dispatcher.Invoke((Action)(() => //Apparently 'Dispatcher' doesn't exist
        {

             for (int x = 0; x < _arrows.Count; x++)
             {
                 if ((string)_arrows[x].Tag == "NotSpawned")
                 {
                     _arrows[x].Tag = "Spawned";
                     _canvas.Children.Add(_arrows[x]);
                     Canvas.SetLeft(_arrows[x], Coordinates.LeftArrowX);
                     Canvas.SetTop(_arrows[x], Coordinates.Y);
                     break;
                 }
             }
        }));
    }

I have no idea what I'm doing - I have checked all the other answers pertaining to this issue. I know that my _arrows list has been created by the main thread, which is why I'm getting these exceptions, but i don't know what to do about them.

Thanks.

More information about my code:

Each of the ArrowFall timers search through a list of all arrows on the screen (depends on what list it is, there are four, 1 for left arrows, 2nd for right arrows etc.)and adds a certain amount to their Y coordinate before setting the new position to the canvas.

So basically, I'm just taking two objects - the canvas I'm adding to and one of the lists. The other threads only need access to their own list and the canvas.

Note: the Lists are stored in a class called Director (named instance is TheDirector in this case) with the rectangle lists as properties. These properties return lists which are populated by classes for each arrow - Class 'RightArrow', 'LeftArrow' etc.

Community
  • 1
  • 1
  • You are still very far from encountering the *real* problems you'll have to solve when you've got 8 completely unsynchronized threads running wild on your game logic. Scratch this approach. Google "wpf game loop" for the next one. – Hans Passant May 13 '16 at 07:59
  • This is to do with each timer having its own thread, I gather? – K. Amanowski May 13 '16 at 08:44
  • I checked out the game loop - and it doesn't work the way I want it to. The game is even slower than before. Would having timers with different durations help any? If not, then the above needs to work. I can always cut off a few timers to handle less threads. – K. Amanowski May 14 '16 at 22:17

1 Answers1

0

In the end, I took off a few timers and made my code more efficient by taking a lot of the 'ArrowFall' methods and put them all into one. By then, the dispatcher worked with the fewer timers and I got no errors. Since I don't know what exactly caused the problem in the first place, I guess I ended up fixing it somehow.