Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles / game

Tropical Tunes. Scratch Versus C#.

5.00/5 (7 votes)
8 Feb 2021CPOL8 min read 7.8K   194  
If you are a programmer that tried to introduce a kid to programming with Scratch, this article is for you. Comparing a simple game between Scratch and C#.
This article compares a game written in Scratch to the same game written in C#. The objective is to show the difference between a visual block language to a common professional computer language.

Tropical Tunes

Introduction

Anyone with school kids in their family have heard about the Scratch - the visual programming language for children. The language was developed by the MIT Media Lab to encourage young people to develop computer skills. The Scratch website has over 64,000,000 registered users. Many of them when they grow up will join CodeProject. Having my own grandchildren, I downloaded the Scratch application from the MIT website. I purchased the “Coding Games in Scratch” book by Jon Woodcock. Together with my grandson, we tried some of the games. One of the games is “Tropical Tunes”. I modified it to my liking. Then I decided to rewrite it in C#. My objective was to show the difference between a visual block language to a common professional computer language. This article highlights the extra development effort involved in writing a C# type language.

The Tropical Tunes Game

The Tropical Tunes is a memory game. There are four drums. Each has a different tune. The program plays a random sequence of drums (or tunes). The player must repeat the sequence. The game starts with a sequence of one tune and ends with seven tunes. If the player is successful, the next sequence is played. If the player makes an error, the game ends. Each correct response adds one to the score. With seven games from one to seven tunes, the perfect score is 28.

The Scratch Program

My version of the Scratch program TropicalTunes.sb3 is attached to this article. The program code is shown later in the article. To see the Scratch version of the game in action, go to the Scratch MIT website, select Create and run it in your web browser. Or go to Scratch download website and install the scratch app to your machine. In both cases, click on File and Load from your computer and navigate to the TropicalTunes.sb3 file.

If you find it hard to memorize seven notes, as I do, press up arrow and the list of notes will be displayed. Pressing down arrow will hide the list. Just don’t tell anyone.

A note to hackers. The .sb3 file is a .zip file. If you change the extension to .zip, you can see the incorporated files. The names of the files are random sequence of hex digits. The file list is made of resources files and one project.json file. The resources file extensions are what you would expect. If you double click on any of them, you will see or hear the resource content. For .svg files, you will need vector graphics editor such as Inkscape. If you open the project.json file with Visual Studio and go to Edit, Advanced and Format Document, you can read it.

Developing Tropical Tunes in C#

The main difference between C# development and Scratch development is the availability of image and audio resources. The Scratch environment provides many choices of background images (called stages), sprites images, and audio clips. To develop a similar game with C#, you need to create or acquire these images and audio clips yourself. Images will typically be .jpg, .bmp, or .png files and audio clips will be .wav files. Sometimes, it is convenient to create an image in vector format .svg. However, at the end, the image must be exported as a .png file.

Background Image or Stage in Scratch Lingo

For background image, I wanted an image with aspect ratio of 16:9 and resolution of 2560 by 1440. If you go to Google Images and type “tropical Paradise 2560 by 1440”, you will find many image files to choose from. My choice was this picture from Pinterest website.

Tropical Paradise

Drums

For drums, the program needs eight images. Four normal size drums each with a different color scheme and four matching larger drums. When a drum is played, the program replaces the normal size with a larger drum. I found it convenient to look for one vector graphics drum that can be easily modified by a vector graphics editor to produce all the eight drums. Vector graphics images have .svg extensions. My vector graphics editor is Inkscape. For more information, go to inkscape website. I found suitable drum at FREE*SVG website. For each drum, I saved the modified vector graphics image and exported a .png file as the resource file. The vector graphics files are attached even though they are not needed to run the program.

Original drum Original Drum
Normal drum Normal Drum
Active drum Trommel

Audio Clips Resources

Unlike images, audio samples are not easily available on the Internet. I was not able to find suitable .wav files for this project. The Scratch application has Add Extension option. One of the choices is Music Play instruments and drums. I wrote a four lines program to play four drum notes. I downloaded Audacity, a free audio editing program, from Audacity website. While Scratch was playing the four notes, Audacity was recording the sound. Next, I used Audacity to break the sound clip into four distinct .wav files. One for each note.

Audacity

Audacity four drum notes

Embedding Image and Audio files into the Executable File

Once you have your image files and audio files resources, you want to embed them into the executable file.

  • Load the resources files into the Resources directory of your project
  • In Solution Explorer, right click the project and select Properties.
  • Select Resources.
  • In the first tab, select Images.
  • In the second tab, Add Resources, select Add Existing File.
  • Select all image files.
  • Go back to the first tab and select Audio.
  • Repeat the previous two steps for audio files.
  • Open the Resources folder in Solution Explorer. All your resource files should be there.
  • Select all the files in the Resources folder.
  • Right click on the selection and click on Properties.
  • Change Build Action to Embedded Resource.
  • To access the resource from the C# program type: Properies.Resources.<resource-name>. For example, Bitmap BGImageFile = Properties.Resources.Background;
  • Image resources will be returned as Bitmap class. Audio resources will be returned as Stream class.

Resizing the Application Frame

When the Tropical Tunes application starts, it will occupy 90% of the computer screen. The user can resize the frame by dragging the sides or the corners. The user can click on the full screen icon, or on the maximize button. In all cases, the background image will be displayed with the correct aspect ratio.

Display Images to Fit the Screen

This method displays the background image with the correct aspect ratio to fit the client size of the frame. It is a two-step process. In step one, we create a memory image of the background and four normal sized drums. In step two, we draw the combined image to the screen. The reason for using two-steps is to avoid the DPI AWARENESS issue with running the program on notebooks with higher DPI than 96 dpi. We use the memory image to restore the image after a large drum was displayed. We draw the normal sized drum and the area around it over the large drum.

C#
// Draw background image
internal void DrawBackground()
{
// assume BG image width is equal to client size width
BGImageWidth = Parent.ClientSize.Width;

// calculate BG image height to preserve aspect ratio
BGImageHeight = BGImageFile.Height * BGImageWidth / BGImageFile.Width;

// height is too big. We need to recalculate and find image width
if(BGImageHeight > Parent.ClientSize.Height)
    {
    BGImageHeight = Parent.ClientSize.Height;
    BGImageWidth = BGImageFile.Width * BGImageHeight / BGImageFile.Height;
    }

// image position within the client area of the frame
BGPosX = (Parent.ClientSize.Width - BGImageWidth) / 2;
BGPosY = (Parent.ClientSize.Height - BGImageHeight) / 2;

// release previous background image memory
if(BGMemoryImage != null) BGMemoryImage.Dispose();

// create background memory image
BGMemoryImage = new Bitmap(BGImageWidth, BGImageHeight);

// create graphics object to draw the background image
Graphics ImageGraph = Graphics.FromImage(BGMemoryImage);

// paint a scaled version of the background image to fit the area
ImageGraph.DrawImage(BGImageFile, 0, 0, BGImageWidth, BGImageHeight);

// paint the four normal sized drums
for(int Index = 0; Index < 4; Index++) Parent.DrumArray[Index].DrawNormalDrum(ImageGraph);

// dispose the image graph object
ImageGraph.Dispose();

// draw the background including drums on the computer monitor
Graphics ScreenGraph = Parent.CreateGraphics();
ScreenGraph.FillRectangle(Brushes.LightGray, 0, 0, 
            Parent.ClientSize.Width, Parent.ClientSize.Height);
ScreenGraph.DrawImageUnscaled(BGMemoryImage, BGPosX, BGPosY);
ScreenGraph.Dispose();
return;
}
C#
// Draw normal drum image over the background memory image
internal void DrawNormalDrum(Graphics ImageGraph)
{
// shortcut for background width and height
BGPosX = Parent.BGImage.BGPosX;
BGPosY = Parent.BGImage.BGPosY;

// shortcut for background width and height
int BGImageWidth = Parent.BGImage.BGImageWidth;
int BGImageHeight = Parent.BGImage.BGImageHeight;

// displayed drum width and height
NormalDrumWidth = (int) Math.Floor(NormalDrumScale * BGImageWidth);
NormalDrumHeight = NormalDrumWidth * NormalImage.Height / NormalImage.Width;
LargeDrumWidth = (int) Math.Floor(LargeDrumScale * BGImageWidth);
LargeDrumHeight = LargeDrumWidth * LargeImage.Height / LargeImage.Width;

// drum center position on memory image
DrumPosX = (int) (RelPosX * BGImageWidth);
DrumPosY = (int) (RelPosY * BGImageHeight);

// normal drum rectangle relative to memory image
NormalRect = new Rectangle(DrumPosX - NormalDrumWidth / 2, 
      DrumPosY - NormalDrumHeight / 2, NormalDrumWidth, NormalDrumHeight);

// large drum rectangle
LargeRect = new Rectangle(DrumPosX - LargeDrumWidth / 2, 
     DrumPosY - LargeDrumHeight / 2, LargeDrumWidth, LargeDrumHeight);

// draw normal sized drum in memory image
ImageGraph.DrawImage(NormalImage, NormalRect);
return;
}

Scratch Program Game Logic

The Tropical Tunes program is divided into two sections overall control (the background) and drum control. Since there are four drums, in total, there are five independent execution paths. Looking at the code, it is easy to see the main building blocks of any software program: assignment statements, conditional statements, loops, and control statements.

Scratch Stage

Background control program

Scratch Drum

Drum control program

C# Program Game Logic

The heart of the C# program, the game control logic, is a loop controlled by game state logic. There are six states.

  • GameState.Off - Off state. The program is waiting for the user to press the Go (green) icon.
  • GameState.Init - Game initialization. Reset the score and set the game number to game 1.
  • GameState.StartSequence - Start a sequence of notes for one game.
  • GameState.PlayNote - Play a single random drum or note to the player. The game logic method exits the loop and waits for the note playing to end.
  • GameState.PlayNoteDone - Control returns to this state after a note was played. The program will continue to the next note in the sequence (back to GameState.PlayNote. If it is the last note in the sequence, the program will wait for the user to repeat the full sequence.
  • GameState.UserNote - Control will return to this state after each drum or note was played by the user. If the user did not click any drum for 10 seconds, the game stops. If the correct drum was played, the program waits for the next drum in the sequence. If it is the last drum of the sequence, we go back to GameState.StartSequence. If it is the last sequence, game number seven, it is perfect score. If the user did not hit the correct drum, game is over.

Each state ends with either continue or return. Block ending with continue means that transition to the next state is immediate. Block ending in return means that the program will return to the game logic after a timer event or mouse click event.

C#
/// Game loop
/// This is the main method that controls the game
private void GameLoop()
{
// switch based on game state
for(;;) switch(State)
    {
    // game is off. waiting for click on go icon
    case GameState.Off:
    default:
        ActiveDrum = 0;
        NoteTimer.Enabled = false;
        TimeoutTimer.Enabled = false;
        return;

    // start a full game
    case GameState.Init:
        Score = 0;
        ScoreArea.DrawScore(Score);
        GameNo = 1;
        State = GameState.StartSequence;
        continue;

    // start one sequence of notes
    case GameState.StartSequence:
        Array.Clear(DrumSequence, 0, MaxGames);
        NoteNo = 0;
        State = GameState.PlayNote;
        continue;

    // play one note
    case GameState.PlayNote:
        // random drum number 1 to 4
        SetActiveDrumNo();

        // save drum number in drum sequence array
        DrumSequence[NoteNo] = ActiveDrum;

        // display in list area
        if(ShowList) ListArea.DrawList();

        // play drum sound
        DrumArray[ActiveDrum - 1].PlayDrum();

        // next state after play drum is done
        State = GameState.PlayNoteDone;
        NoteTimer.Enabled = true;
        return;

    // play of single note is done
    case GameState.PlayNoteDone:
        // update note number and test for end of sequence
        if(++NoteNo < GameNo)
            {
            // not end of the sequence. play another note in the sequence
            State = GameState.PlayNote;
            continue;
            }
        // wait for user to play back the sequence of notes
        ActiveDrum = 0;
        NoteNo = 0;
        State = GameState.UserNote;
        TimeoutTimer.Enabled = true;
        return;

    // user played one note
    case GameState.UserNote:
        // reset wait timeout
        TimeoutTimer.Enabled = false;

        // timeout. user did not play a note within timeout time
        if(ActiveDrum == 0)
            {
            MessageArea.DisplayMessage("Timeout");
            State = GameState.Off;
            continue;
            }
        // user played the correct note
        if(ActiveDrum == DrumSequence[NoteNo])
            {
            // update score
            ScoreArea.DrawScore(++Score);

            // reset active drum
            ActiveDrum = 0;

            // update note number and test for end of notes sequence
            if(++NoteNo < GameNo)
                {
                // there are more notes in the sequence
                TimeoutTimer.Enabled = true;
                return;
                }
            // update game number and test for end of games
            if(++GameNo <= MaxGames)
                {
                // after delay continue with next game
                State = GameState.StartSequence;
                NoteTimer.Enabled = true;
                return;
                }
            // end of game with perfect score
            PerfectSound.Play();
            MessageArea.DisplayMessage("Perfect Score");
            GameNo = 0;
            State = GameState.Off;
            return;
            }
        // stop the game, user made an error
        GameOverSound.Play();
        MessageArea.DisplayMessage("Wrong note");
        ActiveDrum = 0;
        State = GameState.Off;
        return;
    }
}

Conclusion

There is no question in my mind that Scratch is an excellent tool at introducing anyone young or old to computer programming. It is visually simple to see the basic programming building block. It is a very good educational tool. The Scratch language is very effective in putting together a simple game. Developing the same game in C# code is an order of magnitude more complicated. But it was real fun for me. It is rather obvious that the Scratch language has its limitation going further than a simple game.

History

  • 27th January, 2021: Version 1.0 Original version

License

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