I'm adding sound to a simple matching game.
I have a sound for a flipping a card, a match, and no match.
The problem I am having is when 2 cards are match, the flip and match sounds play simultaneous.
So I tried calling Sleep at line 146, which gives me the correct behaviour for the sounds, however it delays turning the 2nd card over. I have also tried Sleep in other portions of the code.
Perhaps calling Sleep is not the best approach?
Not a huge deal, just trying to selfteach.

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using System.Media;

namespace MatchGame
{
    public partial class Form1 : Form
    {

        SoundPlayer flip = new SoundPlayer(MatchGame.Properties.Resources.flip);
        SoundPlayer match = new SoundPlayer(MatchGame.Properties.Resources.chimes);
        SoundPlayer noMatch = new SoundPlayer(MatchGame.Properties.Resources.noMatch);
        SoundPlayer win = new SoundPlayer(MatchGame.Properties.Resources.tada);

        // firstClicked points to the first Label control  
        // that the player clicks, but it will be null  
        // if the player hasn't clicked a label yet
        Label firstClicked = null;

        // secondClicked points to the second Label control  
        // that the player clicks
        Label secondClicked = null;

        // Use this Random object to choose random icons for the squares
        Random random = new Random();

        /// <summary> 
        /// Check every icon to see if it is matched, by  
        /// comparing its foreground color to its background color.  
        /// If all of the icons are matched, the player wins 
        /// </summary> 
        private void CheckForWinner()
        {
            // Go through all of the labels in the TableLayoutPanel,  
            // checking each one to see if its icon is matched 
            foreach (Control control in tableLayoutPanel1.Controls)
            {
                Label iconLabel = control as Label;

                if (iconLabel != null)
                {
                    if (iconLabel.ForeColor == iconLabel.BackColor)
                        return;
                }
            }
            // If the loop didn’t return, it didn't find 
            // any unmatched icons 
            // That means the user won. Show a message and close the form
            win.Play();
            MessageBox.Show("You matched all the icons!", "Congratulations");
            Close();
        }

        // Each of these letters is an interesting icon 
        // in the Webdings font, 
        // and each icon appears twice in this list
        List<string> icons = new List<string>() 
    { 
        "!", "!", "N", "N", ",", ",", "k", "k",
        "b", "b", "v", "v", "w", "w", "z", "z"
    };

        /// <summary> 
        /// Assign each icon from the list of icons to a random square 
        /// </summary> 
        private void AssignIconsToSquares()
        {
            // The TableLayoutPanel has 16 labels, 
            // and the icon list has 16 icons, 
            // so an icon is pulled at random from the list 
            // and added to each label 
            foreach (Control control in tableLayoutPanel1.Controls)
            {
                Label iconLabel = control as Label;
                if (iconLabel != null)
                {
                    int randomNumber = random.Next(icons.Count);
                    iconLabel.Text = icons[randomNumber];
                    iconLabel.ForeColor = iconLabel.BackColor;
                    icons.RemoveAt(randomNumber);
                }
            }

        }
        public Form1()
        {
            InitializeComponent();
            AssignIconsToSquares();
        }

        /// <summary> 
        /// Every label's Click event is handled by this event handler 
        /// </summary> 
        /// <param name="sender">The label that was clicked</param>
        /// <param name="e"></param>
        private void label_Click(object sender, EventArgs e)
        {
            // The timer is only on after two non-matching  
            // icons have been shown to the player,  
            // so ignore any clicks if the timer is running 
            if (timer1.Enabled == true)
                return;

            Label clickedLabel = sender as Label;

            if (clickedLabel != null)
            {
                // If the clicked label is black, the player clicked 
                // an icon that's already been revealed -- 
                // ignore the click 
                if (clickedLabel.ForeColor == Color.Black)
                    return;

                if (firstClicked == null)
                {
                    flip.Play();
                    firstClicked = clickedLabel;
                    firstClicked.ForeColor = Color.Black;

                    return;
                }
                // If the player gets this far, the timer isn't 
                // running and firstClicked isn't null, 
                // so this must be the second icon the player clicked 
                // Set its color to black

                secondClicked = clickedLabel;
                secondClicked.ForeColor = Color.Black;
                flip.Play();
                //System.Threading.Thread.Sleep(750);

                CheckForWinner();

                // If the player clicked two matching icons, keep them  
                // black and reset firstClicked and secondClicked  
                // so the player can click another icon 
                if (firstClicked.Text == secondClicked.Text)
                {
                    System.Threading.Thread.Sleep(750);
                    match.Play();
                    firstClicked = null;
                    secondClicked = null;
                    return;
                }

                // If the player gets this far, the player  
                // clicked two different icons, so start the  
                // timer (which will wait three quarters of  
                // a second, and then hide the icons)


                timer1.Start();
            }
        }
        /// <summary> 
        /// This timer is started when the player clicks  
        /// two icons that don't match, 
        /// so it counts three quarters of a second  
        /// and then turns itself off and hides both icons 
        /// </summary> 
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void timer1_Tick(object sender, EventArgs e)
        {
            // Stop the timer
            timer1.Stop();

            // Hide both icons
            noMatch.Play();
            firstClicked.ForeColor = firstClicked.BackColor;
            secondClicked.ForeColor = secondClicked.BackColor;

            // Reset firstClicked and secondClicked  
            // so the next time a label is 
            // clicked, the program knows it's the first click
            firstClicked = null;
            secondClicked = null;
        }
    }
}

Edited 3 Years Ago by Triryche

The Sleep function will pause the current thread it is called on. As this is the UI thread from what I can see it will simply 'pause' the entire program for that amount of time.

You could use a background worker or second thread to handle the sound being played and then when you sleep that thread it would not impact the turning over of the cards on the main UI thread.

Edited 3 Years Ago by Mike Askew

Thanks for the reply!!
As I'm sure you can tell, I am a beginner, I wouldn't even know where to start with someything like that yet.

If no one else comes up with the code I will have a toy at work tomorrow if I get time and see if I can get a rough example done

It's really no big deal, so don't put too much effort into it.
I'm going through 3 or 4 different toturials/books, so i usually take an example lesson and try to expand on it with what I already know.
Fo this one my mnxt step is to either randomly pull out of a larger data set for the card and/or prompt the user for 1 of 5 different sets.

Right so I've done some toying around this morning. Seems I misunderstood how the sound play works slightly. The .wav is played on its own thread automatically.

Edited 3 Years Ago by Mike Askew

If you call PlaySync instead of Play it will queue the audio so that they play one after the other, not over the top of each other.

If you want any more complicated scenarios then you will need to create your own audio manager.

Edited 3 Years Ago by Ketsuekiame

Comments
Epeen++;

Thanks for the replies!!
So I removed line 146 and replaced line 147 with match.PlaySync();
This causes a delay of line 135 secondClicked.ForeColor = Color.Black; "card flip" when there is a match. It does play the flip sound, then the match sound in order. But I don't understand the delay because line 135 should execute before line 139 CheckForWinner(); is called.

IIRC, calling something along the lines of this.Refresh(); after line 135 will force the form's redraw event before the thread continues and sound plays.

Edited 3 Years Ago by Mike Askew

Thanks guys!!!
The combonation of PlaySync() and this.Refresh works!!

This question has already been answered. Start a new discussion instead.