Click here to Skip to main content
15,879,326 members
Please Sign up or sign in to vote.
0.00/5 (No votes)
See more:
Hi there and welcome to this thread.
Let me introduce you a layout first:

1. Program background
2. The problem I have (music lagging)
3. My ideas how to solve it

Please notice, that the third point (My ideas how to solve it), were ideas that failed to solve the problem. However, it is here to show that I've been thinking about this problem alot, and to avoid getting these solutions as an answer, because it didn't work.

I would like to add one more thing, and that is that I am working in C# 4 years already, and with XNA like 4 months. Just to give you an idea who are you dealing with. But now, lets move to the topic itself...

1. Program background
I coded a Billard game (or Pool game if you want). To be honest, I used the ball to ball and ball to border collision snippets, topspin calucation, and the ghost ball AI stuff code from someone else's project (it didn't work properly, I dont know whether it was an anti-noob-copier-misstake, but I dont htink so...generally solving the border collision using rectangle.Intersects() is better that calculating with radius of ball, distances and math functions...) but the rest is my work. The game has audio class, wich takes control over music.
when a new game is started, a backgound music starts playing, and is supposed to be looping like background music of the game.

A void PlayNextMusic() is called, and it just calls PlayNextMusic_threaded() in thread.
That stops any music (meaning background music) that may be playing at the moment, and
Creates a new current_instance of the music, wich is later on played.
Please notice, that Audio.Music..... are both structures, containing all music held in SoundEffect variable.

C#
  public struct Audio
        {
            public static SoundEffect CueHit;
            public static SoundEffect WallHit;
            public struct InPocketSounds
            {
                public static List<SoundEffect> sounds = new List<SoundEffect>();
            }
            public struct HitSounds
            {
                public static List<SoundEffect> sounds = new List<SoundEffect>();
            }
            public struct Music
            {
                public static SoundEffectInstance current_instance;
                public static float volume = 0.3f; //0.0f - minimum   1.0f - max
                public static int Index_of_current_music_playing = 0;
                public static List<SoundEffect> music = new List<SoundEffect>();
            }
        }
public static void PlayNextMusic()
        {
            if (MUTE)
                return;

            Thread t = new Thread(PlayNextMusic_threaded);
            t.Priority = ThreadPriority.Highest;
            t.Start();
        }
        public static void PlayNextMusic_threaded()
        {            
            StopMusic(true);

            if (Audio.Music.Index_of_current_music_playing + 1 > Audio.Music.music.Count - 1)
                Audio.Music.Index_of_current_music_playing = 0;


            Audio.Music.current_instance = Audio.Music.music[Audio.Music.Index_of_current_music_playing].CreateInstance();
            Audio.Music.current_instance.Volume = Audio.Music.volume;
            Audio.Music.current_instance.Play();
        }


But have a look at the call hiearchy.

Game1 is the first code run (on the top). Its Update() void calls Menu.Update() wich checks for events in menu (clicking on play image, clicking on exit image and so on). When user clicks on Play image (starts new game), it calls a new void Game_Option_clicked(), wich sets up new game AND plays the music AudioProvider.PlayNextMusic()

To make it easier, here is schema:
C#
Game1.Update() -> Menu.Update() -> Game_Option_clicked() -> AudioProvider.PlayNextMusic() -> 
NEW THREAD -> PlayNextMusic_threaded() -> and music plays


Now to move to the AI

When user plays, an AI plays. Because calculating all (nor all literally, please, somthing like 40 possible moves...?) moves takes quite long time, I created a thread system handling it.

When user ends playing, an Play() method is called. That mainly just calls Play_threaded() IN THREAD. That generates the moves, and etc. Now. To add all the moves to the array, I used someething like thread system. Now, please just check the code, and I will continue explaining after the code...

C#
public static void Play()
        {
            if (PoolMechanics.Billard_core.Game_variables.WinStruct.IsGameOver)
                return;

            //only few  sequences of cue will be draws
            PlayerManager.Players[1].Cue_properties.IsCueLocked = true;
            PlayerManager.Players[1].Cue_properties.Cue_Point = new MyVector2D(0, 0);

            //sets the variable so user cant do anything XD
            //PlayerManager.SetSomeonesTurn(false);

            //start in thread so the bonuses, animation and so on will be drawn and rendeered
            Thread t = new Thread(Play_threaded);
            t.Start();

            //variable so player can play is changed IN THREAD because theres a loop waiting for all to be
            //done, wich cant be put in here, because animations of bonuses wouldn't be drawn.
        }
        private static void Play_threaded()
        {          
          //clear previously generated shots
            AI_shoots.Clear();

            Balls_AI_copy = Other.Functions.CopyOneListToAnother(PoolMechanics.Billard_core.Balls);

            PlayerManager.Is_AI_Testing_Shots = true;
            
            //ghost balls
            AllGhostBalls = new List<PoolMechanics.Ball>();
            foreach (PoolMechanics.Ball b in Balls_AI_copy)
            {
                if (b.IsInPocket)
                    continue;

                List<PoolMechanics.Ball> tmp_ghost_balls = GetGhostBalls(b, false);
                foreach (PoolMechanics.Ball ghost_ball in tmp_ghost_balls)
                {                    
                    AllGhostBalls.Add(ghost_ball);
                }
            }
                      
            Threads.active_threads = 0;
            foreach (PoolMechanics.Ball g_ball in AllGhostBalls)
            {
                for (int Strength = 3; Strength < 13; Strength++)
                {
                    Threads.Analyze_Move_in_thread(Strength, g_ball);
                    PlayerManager.Players[1].Cue_properties.Cue_Point = 
                        Other.Functions.FlipPointForCueDirection(g_ball.CentreX, g_ball.CentreY);                   
                }
            }
            Thread.Sleep(2);
            while(Threads.active_threads > 0)
            {
                Thread.Sleep(2000);
            }
           
            //select the best move
            int index = 0;
            int score = int.MinValue;

            int counter = 0;
            lock (AI_shoots)
                foreach (AiShootStruc struc in AI_shoots)
                {
                    if (struc.Score > score)
                    {
                        score = struc.Score;
                        index = counter;
                    }
                    counter++;
                }

            PlayerManager.Is_AI_Testing_Shots = false;

            PlayerManager.Players[1].Cue.Velocity_struc.Selected_velocity = AI_shoots[index].Strength;

            MyVector2D cue_vector = new MyVector2D(AI_shoots[index].CUE_X, AI_shoots[index].CUE_Y);
            PlayerManager.Players[1].Cue_properties.Cue_Point = cue_vector;

            PoolMechanics.Billard_core.StrikedCue(cue_vector, false);

           if(PoolMechanics.FallenBallsProcessing.CanPlayerPlayAgain())
           {
               PlayerManager.SetSomeonesTurn(false);
           }
           else
           {
            //set users turn
            PlayerManager.SetSomeonesTurn(true);
           }           
        }


So as I said, I created a thread system to add the moves to the array.
I cut the sequence of the code here to make it easier to understand for you, and then I explain it.
C#
foreach (PoolMechanics.Ball g_ball in AllGhostBalls)
           {
               for (int Strength = 3; Strength < 13; Strength++)
               {
                   Threads.Analyze_Move_in_thread(Strength, g_ball);
                   PlayerManager.Players[1].Cue_properties.Cue_Point =
                       Other.Functions.FlipPointForCueDirection(g_ball.CentreX, g_ball.CentreY);
               }
           }


So. You see the Threads.Analyze_Move_in_thread(Strength, g_ball);
The threads class is in the same class, just to organise the code.

The idea is:

Threads.Analyze_Move_in_thread calls a void, that calls a new void, Analyze_Move_threaded IN NEW THREAD. The Analyze_Move_threaded void just does some stuff, and puts the move to the array that is acesible for all (lock accesibility included ofcourse). When a new thread from Play_threaded() void is created (in the for loop above), a number variable is increased, counting the number of threads. Then, the code in Play_threaded() waits, until the variable is 0, so to tell in in other way, until all threads are finished.

You may say that it is bad solution, but if you think about it further, if i Wouldn't include the thread system like this, it would freeze, and like this, it is maybe waiting in loop until the threads are done, but the thread of Play_threaded() method is sleeped...? (thread.sleep(2000)), wich gives the application time to do other stuff and decrease the work and priority and flood in the thread (in my opinion).

Once again, I try to make overall scheme of the call hiearchy
C#
Game1.Update() //here is checked the user input, like click -> PoolMechanics.Billard_core.StrikedCue() -> NEW THREAD -> PoolMechanics.Billard_core.StrikedCueThreaded() //here is ball movement done and so on, the AI plays -> AI.Play() -> NEW THREAD -> AI.Play_threaded() //moves are generated -> 
AI.Threads.Analyze_Move_in_thread() *** -> NEW THREAD -> Analyze_Move_threaded() //moves are analyzed, and added to an array

Please, notice the "***" I wrote behind the AI.Threads.Analyze_Move_in_thread(). That is because at this moment, after this method is called, a cue location variable is changed.

C#
Threads.Analyze_Move_in_thread(Strength, g_ball);

                    PlayerManager.Players[1].Cue_properties.Cue_Point = 
                        Other.Functions.FlipPointForCueDirection(g_ball.CentreX, g_ball.CentreY);        


In draw method, the cue is drawn according to this location, so we see how is PC moving cue.

2. The problem I have (music lagging)
When the cue is moving (PC is thinking about moves, he is analyzing them), the music is lagging. When PC is done and ,,strikes" the cue ball, it is ok...

3 My ideas how to solve it
I tried to assign the AI thread the lowest priority, and the Music thread the highest priority. I tried to increase the waiting interval (when AI thread waits until all sub threads are done). I went through the code like 3 times to find whether I missed something, however, i dod not find anything. My personal opinion is, that maybe the problem is in the soundEffect object or the way the music is played...? Because the threads seem to be done precisely.

I will be really thankfull for any ideas, any ideas at all, because I spent months doing this project, and I managed to bypass all problem I had. Except this one.

Thanks for your time, if you red it just to this line.
Posted
Updated 23-Jun-13 13:38pm
v2
Comments
Member 9096381 24-Jun-13 3:29am    
I tried playing the music without running it in thread, however, it is still lagging. Your idea cleared my mind, and I realized that it is lagging maybe when the threads are created. However, the threads are there to minimize the time of waiting for AI to take shoot. Each thread analyzes one move, wich greatly increases the performance. However, if the sound if played in thread automatically, I dont understand, why is it being interupted (lagging) by some work somewhere else...That is why theads were developed...to make some work without being affected by some work somewhere else...

So in my opinion there are 2 ways to solve it. First, maybe play the music other way (i have no idea how), or deal with the AI threads. But how would you solve it? This is in my opinion the fastest solution, running each analysis in thread.

1 solution

Why are you using a new thread to play each sound effect?

SoundEffectInstance.Play() plays the music in the background, its not blocking. I think you are getting caught up in threads, spawning too many. Try it without spawning a thread and see if your performance improves.
 
Share this answer
 

This content, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)



CodeProject, 20 Bay Street, 11th Floor Toronto, Ontario, Canada M5J 2N8 +1 (416) 849-8900