Skip to content

Commit 08a152c

Browse files
committed
Rework since removing services chapter
1 parent fdf95b8 commit 08a152c

File tree

5 files changed

+108
-54
lines changed

5 files changed

+108
-54
lines changed
Loading

articles/tutorials/building_2d_games/04_creating_a_class_library/index.md

Lines changed: 30 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -20,12 +20,12 @@ Think of a class library like a toolbox for your game development. Just as a mec
2020
The following diagrams show how this works:
2121

2222
| ![Figure 4-1: Without using a class library, common modules are duplicated across projects](./images/without-class-library.png) |
23-
| :---: |
24-
| **Figure 4-1: Without using a class library, common modules are duplicated across projects** |
23+
|:-------------------------------------------------------------------------------------------------------------------------------:|
24+
| **Figure 4-1: Without using a class library, common modules are duplicated across projects** |
2525

2626
| ![Figure 4-2: Using a class library, common modules are shared across projects](./images/with-class-library.png) |
27-
| :---: |
28-
| **Figure 4-2: Using a class library, common modules are shared across projects** |
27+
|:----------------------------------------------------------------------------------------------------------------:|
28+
| **Figure 4-2: Using a class library, common modules are shared across projects** |
2929

3030
> [!NOTE]
3131
> A class library is a project type that compiles into a [Dynamic Link Library](https://learn.microsoft.com/en-us/windows/win32/dlls/dynamic-link-libraries) (DLL) instead of an executable. It contains reusable code that can be referenced by other projects, making it perfect for sharing common functionality across multiple games.
@@ -130,46 +130,44 @@ When using the *MonoGame Game Library* project template, the generated project c
130130
131131
## Creating Our First Library Module
132132

133-
Let's validate our class library setup by creating a simple component that counts frames per second (FPS). This will demonstrate that we can successfully create classes in our library and use them in our game.
133+
Let's create a class for our library called `Core`. This class will extend the MonoGame [**Game**](xref:Microsoft.Xna.Framework.Game) class and provide a starting point for game development with some common functionality built in. Creating this will also let us validate that our class library reference setup was correct.
134134

135-
Create a new file called *FramesPerSecondCounter.cs* in the root of the *MonoGameLibrary* project and add the following code:
135+
Create a new file called *Core.cs* in the *MonoGameLibrary* project and add the following code:
136136

137-
[!code-csharp[](./snippets/framespersecondcounter.cs)]
137+
[!code-csharp[](./snippets/core.cs)]
138138

139-
This class:
139+
The `Core` class provides the following features
140140

141-
- Tracks how many frames are rendered each second.
142-
- Updates the FPS calculation based on elapsed time.
143-
- Provides the current FPS through a property.
141+
1. It extends the MonoGame [**Game**](xref:Microsoft.Xna.Framework.Game) class, so it inherits all of the base functionality.
142+
2. It implements a singleton pattern through the `Instance` property, ensure only one core exists.
143+
3. It provides static access to the graphics device manager and the sprite batch.
144+
4. It simplifies the game window setup with a constructor that handles common initializations.
144145

145-
## Adding the Module To Our Game
146+
This approach provides a consistent foundation for all our games, handling common setup tasks and providing convenient access to core functionality.
146147

147-
Now that we have the `FramesPerSecondCounter` class created, let's add it to our game. Doing this will also help ensure that the project references were setup correctly. Open the *Game1.cs* file and make the following changes:
148+
> [!NOTE]
149+
> As this tutorial progress, we'll be coming back to this `Core` class to add more to it.
148150
149-
[!code-csharp[](./snippets/game1.cs?highlight=4,13-14,22-23,41-42,51-55)]
151+
## Updating Our Game to Use the Core Class
150152

151-
The key changes made here are:
153+
Now that we have our `Core` class, let's modify our game project to use it. Doing this will also help ensure that the project references were setup correctly.
152154

153-
1. The `using MonoGameLibrary` using directive was added so we can use the new `FramesPerSecondCounter` class.
154-
2. The field `_fpsCounter` was added to track the `FramesPerSecondCounter` instance.
155-
3. In the constructor, a new instance of the `FramesPerSecondCounter` is created.
156-
4. In [**Update**](xref:Microsoft.Xna.Framework.Game.Update(Microsoft.Xna.Framework.GameTime)), the `Update` method for the frames per second counter is called.
157-
5. In [**Draw**](xref:Microsoft.Xna.Framework.Game.Draw(Microsoft.Xna.Framework.GameTime))
158-
1. The `UpdateCOunter` method for the frames per second counter is called.
159-
2. The game window title is updated to display the frames per second value.
155+
Open the *Game1.cs* file and make the following changes:
160156

161-
When you run the game now, you'll see the current FPS displayed in the window title bar. This confirms that:
157+
[!code-csharp[](./snippets/game1.cs?highlight=4,8,10,22-25)]
162158

163-
- Our class library project is correctly set up.
164-
- The game project can reference and use library classes.
165-
- The basic structure for creating reusable components works.
159+
The key changes made here are:
166160

167-
| ![Figure 4-3: The game window showing the frames per second in the title bar of the window](./images/game_showing_fps.png) |
168-
| :---: |
169-
| **Figure 4-3: The game window showing the frames per second in the title bar of the window** |
161+
1. Adding `using MonoGameLibrary;` directive to reference our library.
162+
1. Removed the [**GraphicsDeviceManager**](xref:Microsoft.Xna.Framework.GraphicsDeviceManager) and [**SpriteBatch**](xref:Microsoft.Xna.Framework.Graphics.SpriteBatch) fields, these are now supplied through the `Core` class.
163+
1. Changed `Game1` class to inherit from `Core` instead of `Game`.
164+
1. Updated the constructor to call the `Core` base constructor with our game configuration.
170165

171-
> [!TIP]
172-
> While this FPS counter is a simple example, it demonstrates the pattern we'll use throughout the tutorial: create reusable components in the library project, then reference and use them in games. This same approach will work for more complex components like sprite management, input handling, and collision detection.
166+
Running the game now will show the same window as before, only now it is at a 1280x720 resolution as per the configuration and it is using the `Core` class from our library. This may not seem like a big change visually, but it demonstrates how our library can simplify and standardize game initializations.
167+
168+
| ![Figure 4-3: The game window at 1280x720 with the title Dungeon Slime](./images/game-window.png) |
169+
|:-------------------------------------------------------------------------------------------------:|
170+
| **Figure 4-3: Figure 4-3: The game window at 1280x720 with the title Dungeon Slime** |
173171

174172
> [!IMPORTANT]
175173
> If you receive an error stating that the following:
@@ -191,7 +189,7 @@ Let's review what you accomplished in this chapter:
191189
- Added the library as a reference to your game project
192190
- Created your first reusable component and referenced and used it in the game project.
193191

194-
In the next chapter, we'll go over MonoGame's Game Component system and how we can use it with our class library.
192+
In the next chapter, we'll learn about the Content Pipeline and how to load game assets.
195193

196194
## Test Your Knowledge
197195

Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
using System;
2+
using Microsoft.Xna.Framework;
3+
using Microsoft.Xna.Framework.Graphics;
4+
5+
namespace MonoGameLibrary;
6+
7+
public class Core : Game
8+
{
9+
internal static Core _instance;
10+
11+
/// <summary>
12+
/// Gets a reference to the Core instance.
13+
/// </summary>
14+
public static Core Instance => _instance;
15+
16+
/// <summary>
17+
/// Gets the graphics device manager to control the presentation of graphics.
18+
/// </summary>
19+
public static GraphicsDeviceManager Graphics { get; private set; }
20+
21+
/// <summary>
22+
/// Gets the sprite batch used for all 2D rendering.
23+
/// </summary>
24+
public static SpriteBatch SpriteBatch {get; private set;}
25+
26+
/// <summary>
27+
/// Creates a new Core instance.
28+
/// </summary>
29+
/// <param name="title">The title to display in the title bar of the game window.</param>
30+
/// <param name="width">The initial width, in pixels, of the game window.</param>
31+
/// <param name="height">The initial height, in pixels, of the game window.</param>
32+
/// <param name="fullScreen">Indicates if the game should start in fullscreen mode.</param>
33+
public Core(string title, int width, int height, bool fullScreen)
34+
{
35+
// Ensure that multiple cores are not created.
36+
if(_instance != null)
37+
{
38+
throw new InvalidOperationException($"Only a single Core instance can be created");
39+
}
40+
41+
// Store reference to engine for global member access.
42+
_instance = this;
43+
44+
// Create a new graphics device manager.
45+
Graphics = new GraphicsDeviceManager(this);
46+
47+
// Set the graphics defaults
48+
Graphics.PreferredBackBufferWidth = width;
49+
Graphics.PreferredBackBufferHeight = height;
50+
Graphics.IsFullScreen = fullScreen;
51+
52+
// Apply the graphic presentation changes
53+
Graphics.ApplyChanges();
54+
55+
// Set the window title
56+
Window.Title = title;
57+
58+
// Set the root directory for content
59+
Content.RootDirectory = "Content";
60+
61+
// Mouse is visible by default
62+
IsMouseVisible = true;
63+
}
64+
65+
protected override void LoadContent()
66+
{
67+
// Create the sprite batch instance.
68+
SpriteBatch = new SpriteBatch(GraphicsDevice);
69+
}
70+
}

articles/tutorials/building_2d_games/04_creating_a_class_library/snippets/game1.cs

Lines changed: 8 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -5,41 +5,31 @@
55

66
namespace DungeonSlime;
77

8-
public class Game1 : Game
8+
public class Game1 : Core
99
{
10-
private GraphicsDeviceManager _graphics;
11-
private SpriteBatch _spriteBatch;
12-
13-
// Tracks the FramesPerSecondCounter instance.
14-
private FramesPerSecondCounter _fpsCounter;
15-
16-
public Game1()
10+
public Game1() : base("Dungeon Slime", 1280, 720, false)
1711
{
18-
_graphics = new GraphicsDeviceManager(this);
19-
Content.RootDirectory = "Content";
20-
IsMouseVisible = true;
2112

22-
// Create a new FramesPerSecondCounter.
23-
_fpsCounter = new FramesPerSecondCounter();
2413
}
2514

2615
protected override void Initialize()
2716
{
17+
// TODO: Add your initialization logic here
18+
2819
base.Initialize();
2920
}
3021

3122
protected override void LoadContent()
3223
{
33-
_spriteBatch = new SpriteBatch(GraphicsDevice);
24+
// TODO: use this.Content to load your game content here
3425
}
3526

3627
protected override void Update(GameTime gameTime)
3728
{
3829
if (GamePad.GetState(PlayerIndex.One).Buttons.Back == ButtonState.Pressed || Keyboard.GetState().IsKeyDown(Keys.Escape))
3930
Exit();
4031

41-
// Update the frames per second instance.
42-
_fpsCounter.Update(gameTime);
32+
// TODO: Add your update logic here
4333

4434
base.Update(gameTime);
4535
}
@@ -48,12 +38,8 @@ protected override void Draw(GameTime gameTime)
4838
{
4939
GraphicsDevice.Clear(Color.CornflowerBlue);
5040

51-
// Update the frame counter.
52-
_fpsCounter.UpdateCounter();
53-
54-
// Update the window title to show the frames per second.
55-
Window.Title = $" FPS: {_fpsCounter.FramesPerSecond}";
41+
// TODO: Add your drawing code here
5642

5743
base.Draw(gameTime);
5844
}
59-
}
45+
}

0 commit comments

Comments
 (0)