Sign in or Sign up

Create a game with C# and Monogame
Started by deadeye


Rate this topic
  • 0 Vote(s) - 0 Average
  • 1
  • 2
  • 3
  • 4
  • 5


10 posts in this topic
deadeye Offline
Programmer
***


Registered
Posts: 247
Threads: 19
Joined: Sat Apr 2017
Reputation: 2

CZPoints: 28 CZP
HQMember
04-15-2017, 06:55 AM -
#1
Welcome to my little Monogame tutorial. It's my first tutorial, so any feedback is appreciated. I hope you will enjoy it and learn some things.
I'm programming games with MG since a few months. one of it is a Diablo-like for Windows and another one is a 2D-sidescroller for Android.

Part 1 - Setup

But at first some little background information to Monogame.
Monogame is a wrapper for the XNA framework, which was created by Microsoft for programming fast 2D and 3D games.
Monogame adds a content pipeline (this allows you to import your assets such as textures or sound effects to your project) and some little utilities to the existing framework.
It is open source and you can export the projects to many platforms. In this tutorial I will show you a Windows project - if you want to see a tutorial for mobile platforms, just post here or send me a PM.

In my opinion it's the perfect way to learn game programming and the programming language C#.

But now let's start with setting up the project.
Before you can do this, you have to download the MG Extension for Visual Studio (I'm using VS 2015 Community). You can download it on http://www.monogame.net

After you have installed it you can proceed with creating a new project in Visual Studio: just select File > New > Project.
There you can choose which project type you'd like to use. Select Monogame > Monogame Windows Project and enter the name of your game.
I have called my game simply "Pong", because this is what we are going to create, if you follow this thread.

After the project has loaded, you can select the C#-code file with the name "Game1.cs" in the solution explorer.
You will see following code (I have removed the auto-generated comments to save some space):
Code:
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;

namespace Pong
{
    public class Game1 : Game
    {
        GraphicsDeviceManager graphics;
        SpriteBatch spriteBatch;

        public Game1()
        {
            graphics = new GraphicsDeviceManager(this);
            Content.RootDirectory = "Content";
        }

        protected override void Initialize()
        {
            base.Initialize();
        }

        protected override void LoadContent()
        {
            spriteBatch = new SpriteBatch(GraphicsDevice);
        }

        protected override void UnloadContent()
        {
        }

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

            base.Update(gameTime);
        }

        protected override void Draw(GameTime gameTime)
        {
            GraphicsDevice.Clear(Color.CornflowerBlue);
            base.Draw(gameTime);
        }
    }
}

Monogame's four core methods are:
1. LoadContent() - it's for loading your assets from the pipeline to your project
2. UnloadContent() - to unload the assets
3. Update() - this method is called 60 times per second (for 60 FPS) and is for e.g. catching input. you can also update a texture's position or an animation here
4. Draw() - for drawing the content on the game screen. It's also called 60 times per second

I can explain the method's parameters later, as soon as we need them.

As you can see, there is a little bit code in Update() and Draw() and LoadContent(). The code in Update() let's you exit the running application when you press the ESC key. Also the base class (called Game) is updated since Game1 inherits from Game.
In Draw() there is code for drawing a blue background and calling Game's Draw method. If you run the application, you can see and test these auto generated features.
In LoadContent() a Sprite Batch is created. This is needed for drawing textures on the screen.
The code in the constructor and in Initialize() initializes the game window, creates an instance of a GraphicsDeviceManager and sets the path of the content folder to our content.


I will add the following part of the tutorial to this thread very soon. Stay tuned :)
This post was last modified: 04-17-2017, 08:13 AM by deadeye.
[Image: cw2gsbi.png]

Discord: deadeye#7164

Disclaimer: I have no idea what I'm talking about.
ScorpionOfWar Offline
Super Moderator
******


Super Moderators
Posts: 239
Threads: 11
Joined: Fri Mar 2017
Reputation: 14

CZPoints: 38 CZP
Contributor
04-15-2017, 07:41 AM -
#2
Thank you for the tutorial! I am sure this is going to help some people :)
[Image: PWXNX5p.gif]

Discord - @ScorpionOfWar#0001
deadeye Offline
Programmer
***


Registered
Posts: 247
Threads: 19
Joined: Sat Apr 2017
Reputation: 2

CZPoints: 28 CZP
HQMember
04-17-2017, 08:12 AM -
#3
Part 2: Player and Ball

In this part we are going to create the objects for the player and ball.

Start with creating two new classes. You can do it by right-clicking the project in the Solution Explorer and choosing Add > Class or pressing Shift+Alt+C.

Name the classes "Player" and "Ball".
The code of these classes is very simple. It gives

Put this code in the player class:
Code:
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Content;

public class Player
   {
       public Texture2D Texture;
       public Rectangle Rect;
       public Vector2 Position;
       public int Score = 0;

       public void loadcontent(ContentManager content)
       {
        //with this code the texture is loaded to the game
        //before you can load it, you have to create a png file with some graphic program and add it to the project via the MG Pipeline
        //the following "playerTex" is the name of the file
           Texture = content.Load<Texture2D>("playerTex");
        //the rectangle for the collisions is created; it's size and position depends on the textures size and position
           Rect = new Rectangle((int)Position.X, (int)Position.Y, Texture.Width, Texture.Height);
       }
   }

For the ball object it's almost the same:
Code:
public class Ball
   {
       public Texture2D Texture;
       public Rectangle Rect;
       public Vector2 Position;
       public Vector2 NeutralPosition;
       public Vector2 Velocity;
       
       public void loadcontent(ContentManager content)
       {
           Texture = content.Load<Texture2D>("ball");
           Rect = new Rectangle((int)Position.X, (int)Position.Y, Texture.Width, Texture.Height);
       }
   }

I made a 25x25 circle for the ball. Add it to the project like the player textures.


Now we can initialize the players and the ball. Change the Initialize() method in the class Game1 to this:
Code:
protected override void Initialize()
       {
           base.Initialize();
           player1.Position = new Vector2(0, GraphicsDevice.Viewport.Height / 2 - player1.Texture.Height / 2);
           player2.Position = new Vector2(GraphicsDevice.Viewport.Width - 10, GraphicsDevice.Viewport.Height / 2 - player2.Texture.Height / 2);
           ball.Position = new Vector2(GraphicsDevice.Viewport.Width / 2 - ball.Texture.Width / 2, GraphicsDevice.Viewport.Height / 2 - ball.Texture.Height / 2);
           ball.NeutralPosition = new Vector2(GraphicsDevice.Viewport.Width / 2 - ball.Texture.Width / 2, GraphicsDevice.Viewport.Height / 2 - ball.Texture.Height / 2);
           ball.Velocity = new Vector2(600, 600);
           int ran = random.Next(1, 3);
           if (ran == 1)
               ball.Velocity.X *= 1;
           if (ran == 2)
               ball.Velocity.X *= -1;
           ball.Velocity.Y *= random.Next(200, 400);
       }

This code sets the player's positions to the borders of the window and the ball to the middle of the window. Also the movement speed of ball and the players is set. At the beginning of the game, the ball moves to a random direction. This is set by the random object and the if statements.

Also, add this code below "GraphicsDeviceManager graphics" and "SpriteBatch spriteBatch":
Code:
       Player player1 = new Player();
       Player player2 = new Player();
       Ball ball = new Ball();
       Random random = new Random();

It initializes the Player, Ball and Random objects that we can use in Initialize() and later in the methods Update and Draw.

In order to draw the textures on the screen, you have to load the content.
Do this by calling the LoadContent methods of the player and ball classes inside Game1's LoadContent method.

It will look like this:
Code:
protected override void LoadContent()
       {
           player1.loadcontent(Content);
           player2.loadcontent(Content);
           ball.loadcontent(Content);
       }

Now we can draw the players and the ball by adding following code to the Draw method:
Code:
    spriteBatch.Begin();        
    spriteBatch.Draw(player1.Texture, player1.Position, Color.DarkGray);
    spriteBatch.Draw(player2.Texture, player2.Position, Color.DarkGray);
    spriteBatch.Draw(ball.Texture, ball.Position, Color.Red);
    spriteBatch.End();

The player's and ball's texture is drawn at the previously set position. Also some color is added. If you want custom colors change the "Color.DarkGray" and "Color.Red" to "Color.White" if you want to have the image file's colors or to some other value. Monogame gives us a large palette of colors.


Feel free to pm me if you have any questions.
In the next part of this tutorial we will add input and movement to the game. Stay tuned :)
[Image: cw2gsbi.png]

Discord: deadeye#7164

Disclaimer: I have no idea what I'm talking about.
rustyarts Offline
Junior Member
**


Registered
Posts: 24
Threads: 5
Joined: Thu Apr 2017
Reputation: 0

CZPoints: 0 CZP
04-17-2017, 08:30 AM -
#4
:)))) Thanks for this tutorial im sure it will help a lot of people good job <3
deadeye Offline
Programmer
***


Registered
Posts: 247
Threads: 19
Joined: Sat Apr 2017
Reputation: 2

CZPoints: 28 CZP
HQMember
04-17-2017, 09:04 AM -
#5
Thanks ScorpionOfWar and rustyarts for your feedback :)
[Image: cw2gsbi.png]

Discord: deadeye#7164

Disclaimer: I have no idea what I'm talking about.
ScorpionOfWar Offline
Super Moderator
******


Super Moderators
Posts: 239
Threads: 11
Joined: Fri Mar 2017
Reputation: 14

CZPoints: 38 CZP
Contributor
04-17-2017, 10:22 AM -
#6
Another awesome part of the tutorial! I love it :)
[Image: PWXNX5p.gif]

Discord - @ScorpionOfWar#0001
deadeye Offline
Programmer
***


Registered
Posts: 247
Threads: 19
Joined: Sat Apr 2017
Reputation: 2

CZPoints: 28 CZP
HQMember
04-17-2017, 11:36 AM -
#7
(04-17-2017, 10:22 AM)ScorpionOfWar Wrote: Another awesome part of the tutorial! I love it :)

Thanks again for your very positive feedback. I appreciate it :D
[Image: cw2gsbi.png]

Discord: deadeye#7164

Disclaimer: I have no idea what I'm talking about.
deadeye Offline
Programmer
***


Registered
Posts: 247
Threads: 19
Joined: Sat Apr 2017
Reputation: 2

CZPoints: 28 CZP
HQMember
04-18-2017, 04:44 AM -
#8
Part 3: Input and movement


Today we are going to add keyboard inputs for the player movement. Also the movement of the ball will be covered in this part of the tutorial.

To move the player, we have to catch the input and alter the player's y value of the position according to it. To achieve this, simply put the following code into the update method. (You can change the player's speed by setting the current velocity (8) to another value)
Code:
if (Keyboard.GetState().IsKeyDown(Keys.W))
    player1.Position.Y -= 8;

if (Keyboard.GetState().IsKeyDown(Keys.S))
    player1.Position.Y += 8;

if (Keyboard.GetState().IsKeyDown(Keys.Up))
       player2.Position.Y -= 8;

if (Keyboard.GetState().IsKeyDown(Keys.Down))
       player2.Position.Y += 8;

To prevent that the player leaves the game window, we reset his position if he reaches the lower or upper border of the window:
Code:
if (player1.Position.Y < 0)
    player1.Position.Y = 0;

if (player2.Position.Y < 0)
    player2.Position.Y = 0;

if (player1.Position.Y > GraphicsDevice.Viewport.Height - player1.Texture.Height)
    player1.Position.Y = GraphicsDevice.Viewport.Height - player1.Texture.Height;

if (player2.Position.Y > GraphicsDevice.Viewport.Height - player2.Texture.Height)
    player2.Position.Y = GraphicsDevice.Viewport.Height - player2.Texture.Height;

We already programmed the ball's velocity in the last tutorial and now we are going to use it.
Now, to move the ball, add this code to the update method.
Code:
ball.Position += ball.Velocity * (float)gameTime.ElapsedGameTime.TotalSeconds;

When you start the application now, you will see that the ball leaves the screen.
We will add it's collision with the bounds of the window and the players in the next part of this tutorial series.
[Image: cw2gsbi.png]

Discord: deadeye#7164

Disclaimer: I have no idea what I'm talking about.
ScorpionOfWar Offline
Super Moderator
******


Super Moderators
Posts: 239
Threads: 11
Joined: Fri Mar 2017
Reputation: 14

CZPoints: 38 CZP
Contributor
04-18-2017, 04:50 AM -
#9
Another awesome part dude! Thanks for the hq posts and threads
[Image: PWXNX5p.gif]

Discord - @ScorpionOfWar#0001
deadeye Offline
Programmer
***


Registered
Posts: 247
Threads: 19
Joined: Sat Apr 2017
Reputation: 2

CZPoints: 28 CZP
HQMember
04-19-2017, 10:45 AM -
#10
Part 4: Collisions and Score


Hey guys, in this part we will add the collisions and the score count. This will be the last part of the tutorial for the game's core. If you are interested in how to make mouse input and how to create the game menu, hit me up per pm or post here. :)

Let's start with collisions: the Rectangle class that is included in the XNA Framework contains a method that is called Intersects. This method checks if one rectangle lies within another one.
Let's assume we have two rects: A and B. The usage of the Intersects method would look like this:
Code:
if (A.Intersects(B))
{
    //do stuff
}

It also could be like this:
Code:
if (B.Intersects(A))
{
    //do stuff
}

It doesn't matter which rect intersects the other one.

Now, when we add following code to our update method, the ball will change its direction, as soon as a collision with a player is detected.
Code:
if (ball.Rect.Intersects(player1.Rect))
{
    ball.Velocity.X *= -1;
    ball.Velocity.Y = random.Next(-400, 400);
}

if (ball.Rect.Intersects(player2.Rect))
{
    ball.Velocity.X *= -1;
    ball.Velocity.Y = random.Next(-400, 400);
}

We also have to update the rectangles of the players and the ball as well. This is done by following code, which has to be put also in the update method:
Code:
player1.Rect = new Rectangle((int)player1.Position.X, (int)player1.Position.Y, player1.Texture.Width, player1.Texture.Height);
player2.Rect = new Rectangle((int)player2.Position.X, (int)player2.Position.Y, player2.Texture.Width, player2.Texture.Height);
ball.Rect = new Rectangle((int)ball.Position.X, (int)ball.Position.Y, 25, 25);


When the ball leaves the screen at one of the both sides where the players are located at, a point is added to the player, which had the last contact with the ball.
Code:
if (ball.Position.X < 0)
{
    ball.Position = ball.NeutralPosition;
    player2.Score++;
    ball.Velocity.Y *= 0;
}

if (ball.Position.X > GraphicsDevice.Viewport.Width)
{
    ball.Position = ball.NeutralPosition;
    player1.Score++;
    ball.Velocity.Y *= 0;
}
Also, the ball's Y velocity is set to 0, that it doesn't move "uncontrolled".

We will also give the player, that lost the point, the opportunity to score the next point. That's why we change the ball's direction by this code:
Code:
if (ball.Position.Y < 0)
    ball.Velocity.Y *= -1;

if (ball.Position.Y > GraphicsDevice.Viewport.Height - ball.Texture.Height)
    ball.Velocity.Y *= -1;

Since we want to know how many points each player has, we have to draw some text on the screen.
At first, you have to open (if it's not open already) the Monogame Pipeline. Right click on "Content", then Add > New item > SpriteFont Description. By double clicking on the newly created item, you can open it with your favourite text editor, where you can edit the size etc. of the font.

Load it to your project by adding following code to the LoadContent method:
Code:
Font = Content.Load<SpriteFont>("DefaultFont");

And put
Code:
SpriteFont Font;
to the other object declarations.

Let's put the text in the centre of the upper screen half (nearly ;) )
Put this code in the draw method.
Code:
string ScoreText = string.Format("{0} : {1}", player1.Score, player2.Score);
var FontWidth = Font.MeasureString(ScoreText);
spriteBatch.DrawString(Font, ScoreText, new Vector2(GraphicsDevice.Viewport.Width / 2 - FontWidth.X / 2, 50), Color.Green);


Now you should have a working game, if that's not the case, hit me up via pm and we'll fix your issues.

Here's the full code, in case you missed something.

Code:
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using Microsoft.Xna.Framework.Content;
using System;

namespace Pong
{
    public class Player
    {
        public Texture2D Texture;
        public Rectangle Rect;
        public Vector2 Position;
        public int Score = 0;

        public void loadcontent(ContentManager content)
        {
            Texture = content.Load<Texture2D>("player"); //your texture name here
            Rect = new Rectangle((int)Position.X, (int)Position.Y, Texture.Width, Texture.Height);
        }
    }

    public class Ball
    {
        public Texture2D Texture;
        public Rectangle Rect;
        public Vector2 Position;
        public Vector2 NeutralPosition;
        public Vector2 Velocity;
        
        public void loadcontent(ContentManager content)
        {
            Texture = content.Load<Texture2D>("ball"); //your texture name here
            Rect = new Rectangle((int)Position.X, (int)Position.Y, Texture.Width, Texture.Height);
        }
    }

    public class Game1 : Game
    {
        Player player1 = new Player();
        Player player2 = new Player();
        Ball ball = new Ball();
        GraphicsDeviceManager graphics;
        SpriteBatch spriteBatch;
        SpriteFont Font;
        Random random = new Random();

        public Game1()
        {
            graphics = new GraphicsDeviceManager(this);
            Content.RootDirectory = "Content";
            IsMouseVisible = true;
        }

        protected override void Initialize()
        {
            base.Initialize();
            player1.Position = new Vector2(0, GraphicsDevice.Viewport.Height / 2 - player1.Texture.Height / 2);
            player2.Position = new Vector2(GraphicsDevice.Viewport.Width - 10, GraphicsDevice.Viewport.Height / 2 - player2.Texture.Height / 2);
            ball.Position = new Vector2(GraphicsDevice.Viewport.Width / 2 - ball.Texture.Width / 2, GraphicsDevice.Viewport.Height / 2 - ball.Texture.Height / 2);
            ball.NeutralPosition = new Vector2(GraphicsDevice.Viewport.Width / 2 - ball.Texture.Width / 2, GraphicsDevice.Viewport.Height / 2 - ball.Texture.Height / 2);
            ball.Velocity = new Vector2(600, 600);
            int ran = random.Next(1, 3);
            if (ran == 1)
                ball.Velocity.X *= 1;
            if (ran == 2)
                ball.Velocity.X *= -1;
            ball.Velocity.Y *= random.Next(200, 400);
        }

        protected override void LoadContent()
        {
            player1.loadcontent(Content);
            player2.loadcontent(Content);
            ball.loadcontent(Content);
            Font = Content.Load<SpriteFont>("Font"); //your font name here
            spriteBatch = new SpriteBatch(GraphicsDevice);
        }

        protected override void UnloadContent() { }

        public void Update(GameTime gameTime)
        {
            if (Keyboard.GetState().IsKeyDown(Keys.W))
                player1.Position.Y -= 8;

            if (Keyboard.GetState().IsKeyDown(Keys.S))
                player1.Position.Y += 8;

            if (Keyboard.GetState().IsKeyDown(Keys.Up))
                player2.Position.Y -= 8;

            if (Keyboard.GetState().IsKeyDown(Keys.Down))
                player2.Position.Y += 8;

            if (player1.Position.Y < 0)
                player1.Position.Y = 0;

            if (player2.Position.Y < 0)
                player2.Position.Y = 0;

            if (player1.Position.Y > GraphicsDevice.Viewport.Height - player1.Texture.Height)
                player1.Position.Y = GraphicsDevice.Viewport.Height - player1.Texture.Height;

            if (player2.Position.Y > GraphicsDevice.Viewport.Height - player2.Texture.Height)
                player2.Position.Y = GraphicsDevice.Viewport.Height - player2.Texture.Height;

            if (ball.Position.X < 0)
            {
                ball.Position = ball.NeutralPosition;
                player2.Score++;
                ball.Velocity.Y *= 0;
            }

            if (ball.Position.X > GraphicsDevice.Viewport.Width)
            {
                ball.Position = ball.NeutralPosition;
                player1.Score++;
                ball.Velocity.Y *= 0;
            }
  
            if (ball.Rect.Intersects(player1.Rect))
            {
                ball.Velocity.X *= -1;
                ball.Velocity.Y = random.Next(-400, 400);
            }

            if (ball.Rect.Intersects(player2.Rect))
            {
                ball.Velocity.X *= -1;
                ball.Velocity.Y = random.Next(-400, 400);
            }

            if (ball.Position.Y < 0)
                ball.Velocity.Y *= -1;

            if (ball.Position.Y > GraphicsDevice.Viewport.Height - ball.Texture.Height)
                ball.Velocity.Y *= -1;

            player1.Rect = new Rectangle((int)player1.Position.X, (int)player1.Position.Y, player1.Texture.Width, player1.Texture.Height);
            player2.Rect = new Rectangle((int)player2.Position.X, (int)player2.Position.Y, player2.Texture.Width, player2.Texture.Height);
            ball.Position += ball.Velocity * (float)gameTime.ElapsedGameTime.TotalSeconds;
            ball.Rect = new Rectangle((int)ball.Position.X, (int)ball.Position.Y, 25, 25);
        }

        protected override void Draw(GameTime gameTime)
        {
            GraphicsDevice.Clear(Color.Black);
            spriteBatch.Begin();        
            spriteBatch.Draw(player1.Texture, player1.Position, Color.DarkGray);
            spriteBatch.Draw(player2.Texture, player2.Position, Color.DarkGray);
            spriteBatch.Draw(ball.Texture, ball.Position, Color.Red);
            string ScoreText = string.Format("{0} : {1}", player1.Score, player2.Score);
            var FontWidth = Font.MeasureString(ScoreText);
            spriteBatch.DrawString(Font, ScoreText, new Vector2(GraphicsDevice.Viewport.Width / 2 - FontWidth.X / 2, 50), Color.Green);
            spriteBatch.End();
            base.Draw(gameTime);
        }
    }
}
This post was last modified: 04-19-2017, 10:52 AM by deadeye.
[Image: cw2gsbi.png]

Discord: deadeye#7164

Disclaimer: I have no idea what I'm talking about.




Users browsing this thread: 1 Guest(s)