Next to that add the following services to your dependency injection container in the Program.cs file:
Now we are ready to play some sounds. To do this we can simply inject the IHowl interface to the component where we want to play sounds and then use it. You can do this in your own classes or in any Blazor component. For example:
The parameter you pass in to the play method can be a relative path to the sound file or an absolute path. You also have the possibility to pass in a second parameter containing HowlOptions. This gives you the possibility to set the volume, let a sound loop, etc.
I have made a small demo project named Example4 that can be found here: https://github.com/henthoven/BlazorFunExamples
Building a real snake game
Now we have seen how we can create a gameloop, draw sprites to the screen, handle input and add sound we can start building a real game. I have combined all of those things and made a simple snake game. I will explain how I approached this and how I implemented the game logic.
Basic game components
Since I am not using any game engine I started defining some basic things that I need to build the game. In my examples from my previous blogs (see part 1 and part 2) I programmed all the logic inside the GameLoop method that was defined in the code block of the razor component. When building a game like snake there are a lot of different parts that have their own behavior and look. With that said I decided to split the gameloop into 2 parts. One part to update the state of all the parts in the game and one part to render them to the screen. So for that I decided to make a class called GameBase. This class will implement the GameLoop method and define 2 abstract methods named Update and Render. Both methods will be called from the GameLoop method. When I create the game I have to make a class deriving from this class and implement the Update and Render methods.
Now the GameLoop is in place and split up into Update and Render we need to define what we want to render and update. For this I decided to make another base class called GameObject. This base class also defines an Update and a Render method. Inside those methods each GameObject can decide how it wants to render itself (for example by drawing an image) and how it updates itself. The GameObject base class also contains an X and an Y position on the screen so the Render method can decide where to draw itself. Inside the update method we can change the X and Y positions so the GameObject can move over the screen.
With the GameBase class and the GameObject class in place we have the basic elements for our game. What I then added is a list of GameObjects that are part of the game. Inside the GameBase Update and Render method I just loop trough all the GameObjects and call the update and Render method on them. Every GameObject can then also have a reference to other GameObjects. In that way I create some sort of graph of GameObjects that will be updated and rendered every time the GameLoop runs.
One last thing I needed to add was that not all GameObjects are always active on the screen. A game can for example have a main menu, a game screen and a high scores screen. All those screens contain different GameObjects. To support this I added GameStates. Every screen has its own game state and for every game state you can define what GameObject are part of it. In that way the GameBase class can call the update and render methods of alle GameObjects in the current GameState.
Implementing the snake game logic
When you think of a snake game you see a field with some walls, apples 🍎 and a snake moving through the field. When the snake 🐍 eats an apple the snake 🐍 will grow and when a certain amount of apples is eaten the snake will continue in the next level. And last but not least when the snake 🐍 moves into a wall or itself it of course dies :).
To define the levels I decided to make JSON files with some properties on it like name of the level, number of apples 🍎 that must be eaten and the level layout. The layout of a level will be a grid. For example as follows:
To define this layout in a JSON file I decided to make a multidimensional array of integers. Every integer will then define if that part of the grid is a wall or not (or maybe other type of parts that you want in the game). The JSON file then looks like the following:
With the definition of the level in place we then need a snake 🐍. I implemented the snake as a GameObject. The snake then has a list of SnakeParts that are all GameObjects itself. Every snake part will have a position in the level grid. Now to make the snake move I remove the last part of the snakepart list and put it in front of the snake. When doing this over and over again it will look to the game player as if the snake is moving. When I see that I have to place the snakepart in front of the snake onto a position where a wall or an apple 🍎 is I can let the snake die (in case of a wall) or grow (in case of an apple 🍎).
Although the performance is fairly good I had to do some optimizations. The most important one was to add a hidden canvas for caching things that don’t change so often. In case of my snake game I was updating and drawing all the elements of the level every gameloop. When a level becomes bigger I saw the performance dropping. Then I realized that it was not needed to update and render all the walls in the levels every time because they always stay the same. So what I did was drawing the level to a separate hidden canvas and then every time the gameloop is called I just draw the entire hidden canvas on the real canvas. This is much faster because we don’t have to draw all the separate parts of the level each time but just one big image that I rendered in the first loop to the hidden canvas.
In the paragraphs above I tried to explain how I programmed the game and how I implemented the game logic. Of course there is nothing better telling what it does as the code itself. You can check out all the code for the game on my GitHub page:
In case you want to see the result you can check it out here:
This then also marks the end of my blog series “Blazor Game Development”. If you have any questions or comments regarding my blogs feel free to contact me. Hope you enjoyed it!