Archive for December, 2012

Developing 2D Games For Windows 8 Using MonoGame Part Three–Game Logic

We reached the most interesting part (at least for me 🙂 ) of this series. After drawing our game objects which was covered in part two, in this part we will add movement, input (keyboard and touch) and collision detection.

Movement

First, we will implement movement logic for the ball because the ball is independent from external input.

Open the GameObject.cs class and add the Move method:

public virtual void Move(Vector2 amount)
{
	Position += amount;
}

Movement in 2D games is quite simple. All that you have to do is, add some 2D vector to a position vector to have a new position. Repeating this, let’s say 30 times per second, will result in movement.

In the first line you will notice we use the ‘virtual’ keyword. We will override this method in the classes that inherit from GameObject (in our case Player and Ball).

Open the Ball.cs class and change it to the following:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Microsoft.Xna.Framework;

namespace PongClone
{
	public class Ball : GameObject
	{
		public Vector2 Velocity;
		public Random random;

		public Ball()
		{
			random = new Random();
		}
	}
}

 

The velocity of the ball will vary, therefore we need a vector to hold the velocity. The Random object will be used to launch the ball from the center of the screen to the left or right side (50% probability to the left, 50% probability to the right). The constructor in lines 15, 16, 17 and 18 is used to create the Random object that we will use in the Launch method.

Add the following Launch method to the Ball.cs class:

public void Launch(float speed)
{
	Position = new Vector2(Game1.ScreenWidth / 2 - Texture.Width / 2, Game1.ScreenHeight / 2 - Texture.Height / 2);
	// get a random + or - 60 degrees angle to the right
	float rotation = (float) ( Math.PI/2 + (random.NextDouble() * (Math.PI/1.5f) - Math.PI/3));

	Velocity.X = (float) Math.Sin(rotation);
	Velocity.Y = (float)Math.Cos(rotation);

	// 50% chance whether it launches left or right
	if (random.Next(2) == 1)
	{
		Velocity.X *= -1; //launch to the left
	}

	Velocity *= speed;
}

 

Don’t get immediately scared from the maths in this method. It is quite simple, I will explain everything in detail.

As you can see in the first line, the Launch method takes a parameter for speed. This speed will represent the initial speed of the ball when it launches.

In line 3, we set the initial position of the ball to the center of the screen.

After that, in line 5 we create a variable to hold a value for the rotation that later in lines 7 and 8 will be used to calculate a launch angle. If the value of rotation is 0, Sin(0) = 0 and Cos(0) = 1. Because the values calculated from the Sin function are stored in Velocity.X and the values calculated from the Cos function are stored in Velocity.Y, the ball will launch straight down. As I mentioned in the previous part, (0,0) on the screen is on the top left, that means the positive direction of Y is downwards.

First to the rotation value we give a value of Math.PI / 2 which will result in launching the ball to the right side, because PI / 2 = 90 degrees, Sin(90 degrees) = 1, Cos(90 degrees) = 0. Then, to that value we use the Random object to generate a value between +60 and –60 (if you think in degrees and not radians). Note that the calculations are done using radians, I am converting the values to degrees in this explanation to make it more clear.

How did we get to +60 and –60? random.NextDouble generates values between 0.0 and 1.0. That value is multiplied by Math.PI/1.5f (120 degrees). Now we have values from 0 do 120 degrees. Then, we subtract Math.PI / 3 (60 degrees) which results in the desired interval of random values (-60, +60).

Now our ball will launch at a random angle between –60 and +60 degrees to the right. In line 11 we generate an integer between 1 and 0. The chances to get a 1 or 0 are 50-50. If we get a 1, the X component of Velocity (Velocity.X) is multiplied by –1 which changes the launching direction from right to left.

Finally we multiply the Velocity vector by the scalar speed to get the initial velocity of the ball.

Note: If this maths does not make sense to you, after we type the code in Game1.cs, you can come back to this method and change the rotation values. Start from giving the value 0 to rotation, and increase/decrease from there to see how the values affect the launch angle. If you still do not understand how it works, please leave a comment below.

Open the Game1.cs class and on top, where we defined our first constant in the previous part, PADDLE_OFFSET = 70, add a new constant for the initial speed of the ball:

const float BALL_START_SPEED = 8f;

Remove the following line for setting the position of the ball in LoadContent because we already have the same line in our Launch method:

ball.Position = new Vector2(ScreenWidth / 2 - ball.Texture.Width / 2, ScreenHeight / 2 - ball.Texture.Height / 2);

 

On its place, put the following line:

ball.Launch(BALL_START_SPEED);

 

In the Update method, under the lines for screen width and screen height, add the line ball.Move(ball.Velocity):

ScreenWidth = GraphicsDevice.Viewport.Width;
ScreenHeight = GraphicsDevice.Viewport.Height;
ball.Move(ball.Velocity);
base.Update(gameTime);

This line will fire the Move method where the Velocity vector is added to the Position vector.

Press F5 or Debug –> Start Debugging to start the game. You will notice that the ball is moving and after a short while it leaves the screen. Before typing collision and reset logic, we will first implement player input.

Input

First we will create a new class which will be used to process player input. Right-click on the ‘Classes’ folder Add –> Class… change the name to ‘Input’ (without quotation marks) and click Add.

Change the Input.cs class to the following:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Input;
using Microsoft.Xna.Framework.Input.Touch;

namespace PongClone
{
    public static class Input
    {
        public static List<GestureSample> Gestures;

        static Input()
        {
            Gestures = new List<GestureSample>();
        }

    }
}

 

You will first notice the new libraries that are added so we can implement the input logic. Then you will notice, in line 12, we are creating a static class. That means that the class itself is the object and only one Input object exists in the program. In line 14 we declare a list, Gestures, that will store all the touch gestures. The static constructor, static Input(), initializes the list so we can later use it.

First we will type the keyboard input method. Add the following method to the class:

public static Vector2 GetKeyboardInputDirection(PlayerIndex playerIndex)
{
	Vector2 direction = Vector2.Zero;
	KeyboardState keyboardState = Keyboard.GetState(playerIndex);

	if (playerIndex == PlayerIndex.One)
	{
		if (keyboardState.IsKeyDown(Keys.W))
			direction.Y += -1;
		if (keyboardState.IsKeyDown(Keys.S))
			direction.Y += 1;
	}

	if (playerIndex == PlayerIndex.Two)
	{
		if (keyboardState.IsKeyDown(Keys.Up))
			direction.Y += -1;
		if (keyboardState.IsKeyDown(Keys.Down))
			direction.Y += 1;
	}
	return direction;
}

 

This method returns a Vector2 object and takes a PlayerIndex parameter which indicates player 1, player 2, player 3, etc. In line 3, we initialize a vector to (0,0) which will store the direction of movement. In line 4, we declare a KeyboardState variable that takes the state of the keyboard. Then in line 6 we check whether the input is for the first player. If true, we check whether the ‘W’ key is pressed. If yes, we decrease the Y component of the direction vector by 1 (Y decreases if we move upwards). The same thing is checked for the ‘S’ key for moving down. Then we check the same things with different keys (Up arrow and Down arrow) for the second player. In the end, we return the direction vector that has the direction of movement. The direction can be up, down or it can stay (0,0) which represents no movement.

Open the Game1.cs class and add another constant for the paddle speed when keyboard is used:

const float KEYBOARD_PADDLE_SPEED = 10f;

 

In the Update method, under the line ball.Move(ball.Velocity); add the following lines:

ball.Move(ball.Velocity);

Vector2 player1Velocity = Input.GetKeyboardInputDirection(PlayerIndex.One) * KEYBOARD_PADDLE_SPEED;
Vector2 player2Velocity = Input.GetKeyboardInputDirection(PlayerIndex.Two) * KEYBOARD_PADDLE_SPEED;

player1.Move(player1Velocity);
player2.Move(player2Velocity);

 

First we declare and initialize the velocities of each player. Because the method that we implemented in the Input class returns a direction, we multiply it with a scalar speed (the constant that we defined). Then we call the Move methods for moving the players.

Press F5 or Debug –> Start Debugging to test the player input. You will notice that the players can leave the screen. In the next part, collisions, we will limit the player movement to stay within the screen.

Before we implement collision detection, first we will implement touch input. Open the Input.cs class and add the following method:

public static void ProcessTouchInput(out Vector2 player1Velocity, out Vector2 player2Velocity)
{
	Gestures.Clear();
	while (TouchPanel.IsGestureAvailable)
	{
		Gestures.Add(TouchPanel.ReadGesture());
	}
	player1Velocity = Vector2.Zero;
	player2Velocity = Vector2.Zero;

	foreach (GestureSample gestureSample in Gestures)
	{
		if (gestureSample.GestureType == GestureType.FreeDrag)
		{
			if (gestureSample.Position.X >= 0 && gestureSample.Position.X <= Game1.ScreenWidth / 2)
                        player1Velocity.Y += gestureSample.Delta.Y;
			if (gestureSample.Position.X >= Game1.ScreenWidth/2 && gestureSample.Position.X <= Game1.ScreenWidth)
                        player2Velocity.Y += gestureSample.Delta.Y;
		}
	}
}

 

This method returns two values as you can see from the first line. In line 3 we clear the Gestures list. Then, in the while loop we check whether touch input has occurred. If yes, we add all the gestures to our list. Then we initialize the vectors to (0,0) and we check all the gestures in our list. If we have FreeDrag input, we check the position where such an input has happened. If the FreeDrag has happened on the left half, we add the Y component of the drag to the velocity vector for player 1. If the FreeDrag occurred on the right half, the same thing is done for player 2.

Open the Game1.cs class and on top add the following library:

using Microsoft.Xna.Framework.Input.Touch;

 

Next, change the constructor to the following:

public Game1()
{
	_graphics = new GraphicsDeviceManager(this);
	Content.RootDirectory = "Content";
	TouchPanel.EnabledGestures = GestureType.FreeDrag;
}

You will notice we added the 5th line. This line enables FreeDrag gestures on the screen.

In the Update method, after calling the Move methods for player1 and player2 add the following:

player1.Move(player1Velocity);
player2.Move(player2Velocity);

Vector2 player1TouchVelocity, player2TouchVelocity;
Input.ProcessTouchInput(out player1TouchVelocity, out player2TouchVelocity);
player1.Move(player1TouchVelocity);
player2.Move(player2TouchVelocity);

First, in line 4 we declare two vectors where we will store the velocities for touch input. After we call the method to process touch input, we fire the Move methods again with touch velocities.

As shown on the image below, change the debugging machine from Local Machine to Simulator so you can test the Touch input.

Press F5 or Debug –> Start Debugging to launch the simulator and test touch input.

Collision Detection

We have three things that we need to do in order to complete this part: Collision between upper and lower walls and the ball, limiting the player movement to stay within the screen, and collision between the players and the ball.

We will first start with the ball. Open the Ball.cs class and add the following method:

public void CheckWallCollision()
{
	if (Position.Y < 0)
	{
		Position.Y = 0;
		Velocity.Y *= -1;
	}
	if (Position.Y + Texture.Height > Game1.ScreenHeight)
	{
		Position.Y = Game1.ScreenHeight - Texture.Height;
		Velocity.Y *= -1;
	}
}

This method first checks whether the ball leaves the screen from the top of the screen. If true, we set the Y component to 0 and then change the direction of the velocity (we get a bouncing effect with this). In the second if-test we check the same thing but for the bottom of the screen. You notice that we also need to use the height of the ball itself because the position of the ball indicates the position of the top-left corner.

We will call this method in the overridden Move method in the Ball.cs class. Add the following method to the Ball.cs class:

public override void Move(Vector2 amount)
{            
	base.Move(amount);
	CheckWallCollision();
}

This method first calls the GameObject Move method (the class that it inherits from) and then calls the method to check the collision with the top and bottom ‘walls’ of the screen.

Before testing, we will also add a few lines to reset the position of the ball when it leaves the screen from the left or right side.

Open the Game1.cs class and add the following lines right after processing touch input:

player1.Move(player1TouchVelocity);
player2.Move(player2TouchVelocity);

if (ball.Position.X + ball.Texture.Width < 0)
{
	ball.Launch(BALL_START_SPEED);
}

if (ball.Position.X > ScreenWidth)
{
	ball.Launch(BALL_START_SPEED);
}

 

The first if-test checks whether the ball leaves the screen from the left side. If true, it launches the ball from the center of the screen. The second if-test checks whether the ball leaves the screen from the right side.

Press F5 or Debug –> Start Debugging to test the latest changes. You will notice that the ball now bounces from the top and bottom ‘walls’ and resets to the center of the screen when it leaves the screen from the left or right side.

Our next step is to limit the player movement to stay within the screen. Open the Player.cs class and add the following library:

using Microsoft.Xna.Framework;

Then add the following method:

public override void Move(Vector2 amount)
{
	base.Move(amount);
	if (Position.Y <= 0)
		Position.Y = 0;
	if (Position.Y + Texture.Height >= Game1.ScreenHeight)
		Position.Y = Game1.ScreenHeight - Texture.Height;
}

 

Like in the Ball.cs class, we first call the GameObject Move method (the class which we inherit from) and then check whether the player tries to leave the screen from the top. If yes, we set the Y component to 0 which limits the player from moving off-screen. The same thing happens in the second if-test where we check whether the player tries to leave the screen from the bottom.

Press F5 or Debug –> Start Debugging to test the latest changes. You will notice that now the players cannot move off-screen.

The last thing we need to do is to add collision between the players and the ball.

Open the GameObject.cs class and add the following property:

public Rectangle Bounds
{
	get { return new Rectangle((int)Position.X, (int)Position.Y, Texture.Width, Texture.Height); }
}

This property returns a rectangle with the bounds of our objects (players and ball in our case).

In the same class (GameObject.cs) add the following method:

public static bool CheckPaddleBallCollision(Player player, Ball ball)
{
	if (player.Bounds.Intersects(ball.Bounds))
		return true;
	return false;
}

This method returns a boolean value (true or false). As an input parameter it takes a Player and Ball object. By using the Bounds property we get the bounds of the players and ball, and then check whether those rectangles intersect. If they intersect, we return true. If not, we return false.

This method is static and can be called by typing GameObject.CheckPaddleBallCollision(…);

Open the Game1.cs class and add the following library:

using System;

Then in the Update method, right after the touch Move methods and before checking whether the ball leaves the screen, add the following lines:

player1.Move(player1TouchVelocity);
player2.Move(player2TouchVelocity);

if (GameObject.CheckPaddleBallCollision(player1, ball))
{
	ball.Velocity.X = Math.Abs(ball.Velocity.X);
}

if (GameObject.CheckPaddleBallCollision(player2, ball))
{
	ball.Velocity.X = -Math.Abs(ball.Velocity.X);
}

if (ball.Position.X + ball.Texture.Width < 0)
{
	ball.Launch(BALL_START_SPEED);
}

 

In line 4 you will notice we check collision between the first player and the ball. If a collision occurs, the X component of the ball’s velocity is reversed (gets a positive direction, positive is to the right). Then we check for collision between player 2 and the ball. If collision occurs, we set a negative direction to the X component of the ball’s velocity.

Press F5 or Debug –> Start Debugging to test.

With this step we are done with the third part of this series. If you are having trouble understanding something, please leave a comment below. To download my Solution, click here.

Note: You will need to set up the correct references to the MonoGame project in order to test my Solution.

In the final part of this series, we will polish the game by adding ball spin, sounds, point indicators etc.

Advertisements

Развивање 2Д Игри За Windows 8 Со MonoGame Трет Дел–Логика На Игра

Стигнавме до најинтересниот дел (барем за мене 🙂 ) од овој серијал. По цртањето на објектите кое што беше опфатено во вториот дел, во овој дел ќе додадеме движење, инпут (со тастатура и допир) и колизии.

Движење

Прво, ќе имплементираме движење на топчето бидејќи топчето е независно од надворешен инпут.

Отворете ја GameObject.cs класата и додадете го методот Move:

public virtual void Move(Vector2 amount)
{
	Position += amount;
}

 

Движењето во 2Д игри е прилично едноставно. Се што се прави е, на вектор за позиција се додава друг вектор за да се добие нова позиција. Со повторување на овој метод 30 пати во секунда, ќе се добие некакво движење.

На првата линија забележувате искористен е зборот virtual. Овој метод ќе го искористиме во наследените класи Player и Ball со тоа што на двете класи ќе им додадеме уште неколку линии кои што не се заеднички меѓу нив.

Отворете ја Ball.cs класата и преправете ја во следното:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Microsoft.Xna.Framework;

namespace PongClone
{
	public class Ball : GameObject
	{
		public Vector2 Velocity;
		public Random random;

		public Ball()
		{
			random = new Random();
		}
	}
}

 

Топчето ќе има променлива брзина, затоа ни треба еден вектор кој што ќе ја прати брзината (и насоката) на топчето. Random објектот ќе се користи за лансирање на топчето на левата или десната страна (50% веројатност на едната страна, 50% веројатност на другата страна). Конструкторот на линиите 15, 16, 17 и 18 се користи за креирање на Random објектот кој што ќе го користиме во Launch методот.

Додадете го следниот Launch метод во Ball.cs класата:

public void Launch(float speed)
{
	Position = new Vector2(Game1.ScreenWidth / 2 - Texture.Width / 2, Game1.ScreenHeight / 2 - Texture.Height / 2);
	// get a random + or - 60 degrees angle to the right
	float rotation = (float) ( Math.PI/2 + (random.NextDouble() * (Math.PI/1.5f) - Math.PI/3));

	Velocity.X = (float) Math.Sin(rotation);
	Velocity.Y = (float)Math.Cos(rotation);

	// 50% chance whether it launches left or right
	if (random.Next(2) == 1)
	{
		Velocity.X *= -1; //launch to the left
	}

	Velocity *= speed;
}

 

Не плашете се од математиката во овој метод, многу е едноставна, ќе ви објаснам што се случува на секоја линија.

Како што може да видите на првата линија, Launch методот добива параметар за брзина (има разлика помеѓу speed и Velocity; speed е брзина скалар, velocity е брзина со насока, вектор). Оваа брзина (speed) ќе ни ја претставува почетната брзина на топчето.

Во третата линија, се сетира почетната позиција на топчето (во нашиот случај, на центарот на екранот).

Потоа, во линија 5 креираме променлива за вредност на ротација кој што потоа во линиите 7 и 8 се користи за да се добие аголот на кој што ќе се лансира топчето. Доколку ротацијата е 0, Sin(0) = 0, и Cos(0) = 1. Бидејќи вредноста која што произлегува од Sin функцијата ја ставаме во Velocity.X, а вредностa што произлегува од Cos функцијата ја ставаме во Velocity.Y , топчето ќе се лансира надолу. Како што кажав на претходниот пост, (0,0) на екранот се наоѓа на горниот лев агол, тоа значи дека позитивната страна на Y оди надолу.

Најпрво на вредноста за ротација му даваме вредност Math.PI / 2 со која што ќе добиеме лансирање на десната страна бидејки Pi / 2 = 90 степени, Sin(90 степени) = 1, Cos(90 степени) = 0. Потоа на таа вредност додаваме нова вредност со користење на Random објектот за да генерираме вредност помеѓу +60 и –60 степени (во нашиот случај рачунаме со радијани, поради едноставност, бројките во ова објаснување ги конвертирам во степени).

Како дојдовме до +60 и –60? random.NextDouble() генерира вредности помеѓу 0.0 и 1.0. Таа вредност ја множиме со Math.PI / 1.5f (120 степени). Сега добиваме вредности од 0 до 120 степени. Затоа, на крај имаме – Math.PI / 3 (60 степени) со кој што го добиваме посакуваниот интервал (-60,+60)

Сега топчето иако ќе се лансира по случаен агол измеѓу –60 и +60 степени, постојано ќе се лансира на десната страна. Во линија 11 генерираме цел број помеѓу 1 и 0. Шансите да се добие 1 или 0 се 50-50. Доколку се добие 1, вредноста на Velocity.X ја множиме со –1 со што ја променуваме насоката на лансирање од десно на лево.

Конечно вектор брзината (Velocity) ја множиме со скаларната брзина (speed) за да ја добиеме конечната почетна брзина на топчето.

Напомена: Доколку оваа математика не ви е јасна, откако ќе го напишеме кодот во Game1.cs, може да се вратите на овој метод и да ги промените вредностите кои што се во rotation. Почнете со давање на вредност 0 и зголемувајте/намалувајте од тука за да видите што се случува со аголот на лансирање. Ако повторно не ви е јасно, слободно оставете коментар подолу.

Отворете ја Game1.cs класата и најгоре каде што ја дефиниравме првата константа PADDLE_OFFSET = 70, додадете нова константа за почетна брзина на топчето:

const float BALL_START_SPEED = 8f;

Избришете ја следната линијата за сетирање на позицијата на топчето во LoadContent бидејќи истата ја имаме во Launch методот:

ball.Position = new Vector2(ScreenWidth / 2 - ball.Texture.Width / 2, ScreenHeight / 2 - ball.Texture.Height / 2);

 

На тоа место ставете ја следната линија:

ball.Launch(BALL_START_SPEED);

 

Во Update методот, под линиите за ширина и висина на екранот, додадете ја линијата

ball.Move(ball.Velocity);

ScreenWidth = GraphicsDevice.Viewport.Width;
ScreenHeight = GraphicsDevice.Viewport.Height;
ball.Move(ball.Velocity);
base.Update(gameTime);

Оваа линија ќе го активира Move методот каде што се собираат векторите Position и Velocity, и резултатот се зачувува во Position.

Притиснете F5 или Debug –> Start Debugging за да ја пуштите апликацијата. Ќе забележите дека топчето се движи и после кратко време излегува од видното поле. Пред да преминеме на пишување логика за колизии, ќе го имплементираме инпутот за играчите.

Инпут

Најпрво ќе креираме нова класа која што ќе се користи за процесирање на инпутот од играчите. Притиснете десен клик на фолдерот ‘Classes’ Add –> Class… крстете ја новата класа ‘Input’ (без наводници).

Преправете ја Input.cs класата во следното:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Input;
using Microsoft.Xna.Framework.Input.Touch;

namespace PongClone
{
    public static class Input
    {
        public static List<GestureSample> Gestures;

        static Input()
        {
            Gestures = new List<GestureSample>();
        }

    }
}

 

Ќе забележите дека се додадени неколку библиотеки кои што ќе се потребни за имплементирање на инпутот. Потоа ќе забележите дека креираме статична класа (линија 12). Тоа значи дека самата класа ни претставува еден и единствен објект (не ни требаат повеќе објекти за да се процесира инпут). Потоа во линија 14 декларираме една листа Gestures која што ќе ги чува сите инпути на допир (Touch). Во конструкторот static Input(), ја иницијализираме листата за да можеме да ја користиме покасно.

Прво ќе го напишеме методот за инпут од тастатура. Додадете го следниот метод во класата:

public static Vector2 GetKeyboardInputDirection(PlayerIndex playerIndex)
{
	Vector2 direction = Vector2.Zero;
	KeyboardState keyboardState = Keyboard.GetState(playerIndex);

	if (playerIndex == PlayerIndex.One)
	{
		if (keyboardState.IsKeyDown(Keys.W))
			direction.Y += -1;
		if (keyboardState.IsKeyDown(Keys.S))
			direction.Y += 1;
	}

	if (playerIndex == PlayerIndex.Two)
	{
		if (keyboardState.IsKeyDown(Keys.Up))
			direction.Y += -1;
		if (keyboardState.IsKeyDown(Keys.Down))
			direction.Y += 1;
	}
	return direction;
}

 

Oвој метод враќа Vector2 објект и прима еден параметар PlayerIndex со кој што се дефинира за кој играч станува збор (првиот, вториот итн.). Во третата линија иницијализираме еден вектор на (0,0) кој што ќе ја содржи насоката на движење. Во линија 4, декларираме KeyboardState променлива и ја земаме состојбата на тастатурата. Потоа во шестата линија проверуваме дали станува збор за првиот играч. Доколку одговорот е да, проверуваме дали буквата W е притисната. Ако е притисната, ја намалуваме вредноста на Y оската за 1 (Y се намалува ако се движиме нагоре). Истото се проверува и со буквата S за движење надолу. Доколку не станува збор за првиот играч, се проверуваат истите работи со различни копчиња (стрелките за горе и долу) за вториот играч. На крајот се враќа вредноста на векторот direction кој што ја содржи насоката. Насоката може да биде нагоре, надолу, или нема промена на местото ако нема никаков инпут.

Отворете ја Game1.cs класата и додадете уште една константа за брзината на играчите кога инпутот е преку тастатура:

const float KEYBOARD_PADDLE_SPEED = 10f;

 

Во Update методот, под линијата ball.Move(ball.Velocity); додадете ги следните линии:

ball.Move(ball.Velocity);

Vector2 player1Velocity = Input.GetKeyboardInputDirection(PlayerIndex.One) * KEYBOARD_PADDLE_SPEED;
Vector2 player2Velocity = Input.GetKeyboardInputDirection(PlayerIndex.Two) * KEYBOARD_PADDLE_SPEED;

player1.Move(player1Velocity);
player2.Move(player2Velocity);

 

Прво декларираме и ги иницијализираме брзините на играчите во два Vector2 променливи. Бидејќи методот што претходно го напишавме во инпут класата враќа вектор со насока, го множиме резултатот со константата каде што е дефинирана брзината. Потоа ги повикуваме Move методите за движење на играчите.

Притиснете F5 или Debug –> Start Debugging и тестирајте го движењето на играчите. Ќе забележите дека играчите го напуштаат видното поле, во следниот дел за колизии ќе ги поставиме границите на движење.

Пред да преминеме на колизии, најпрво ќе го имплементираме инпутот преку допир (Touch). Отворете ја Input.cs класата и додадете го следниот метод:

public static void ProcessTouchInput(out Vector2 player1Velocity, out Vector2 player2Velocity)
{
	Gestures.Clear();
	while (TouchPanel.IsGestureAvailable)
	{
		Gestures.Add(TouchPanel.ReadGesture());
	}
	player1Velocity = Vector2.Zero;
	player2Velocity = Vector2.Zero;

	foreach (GestureSample gestureSample in Gestures)
	{
		if (gestureSample.GestureType == GestureType.FreeDrag)
		{
			if (gestureSample.Position.X >= 0 && gestureSample.Position.X <= Game1.ScreenWidth / 2)
                        player1Velocity.Y += gestureSample.Delta.Y;
			if (gestureSample.Position.X >= Game1.ScreenWidth/2 && gestureSample.Position.X <= Game1.ScreenWidth)
                        player2Velocity.Y += gestureSample.Delta.Y;
		}
	}
}

 

Овој метод враќа два вредности како што може да се види во првата линија. Во линија 3 ја бришиме цела листа и потоа во while циклус проверуваме дали има некаков инпут преку допир. Доколку има, сите инпути ги додаваме во листата Gestures. Потоа ги иницијализираме векторите на (0,0) и ги проверуваме сите инпути што ги имаме во листата Gestures. Доколку имаме инпут со влечење (FreeDrag), проверуваме на која позиција од екранот се случило влечењето. Доколку се случило во левата половина, брзината од Y оската на влечењето ја додаваме на брзината на играчот во Y оската. Доколку влечењето е во десната половина, истата математика ја правиме за брзината на вториот играч.

Отворете ја Game1.cs класата и најгоре, додадете ја следната библиотека:

using Microsoft.Xna.Framework.Input.Touch;

 

Потоа, сменете го конструкторот во следното:

public Game1()
{
	_graphics = new GraphicsDeviceManager(this);
	Content.RootDirectory = "Content";
	TouchPanel.EnabledGestures = GestureType.FreeDrag;
}

Забележувате ја додадовме 5тата линија. Оваа линија ни дозволува инпут преку влечење на екранот.

Во Update методот, под повикувањето на методите за движење на играчите со тастатура, додадете ги следните линии:

player1.Move(player1Velocity);
player2.Move(player2Velocity);

Vector2 player1TouchVelocity, player2TouchVelocity;
Input.ProcessTouchInput(out player1TouchVelocity, out player2TouchVelocity);
player1.Move(player1TouchVelocity);
player2.Move(player2TouchVelocity);

 

Прво, во линија 4 декларираме два вектори каде што ќе ги чуваме вредностите за брзината на влечење. Откако ќе се повика методот за процесирање на Touch инпутот, повторно ги повикуваме методите за движење.

Како што е покажано на сликата подолу, сменете ја машината за дебагирање од Local Machine во Simulator за да може да го тестирате Touch инпутот.

Притиснете F5 или Debug –> Start Debugging за да го уклучите симулаторот и да го тестирате Touch инпутот.

Колизии

Имаме три работи кои што треба да ги средиме за да го завршиме овој дел. Колизија помеѓу горната и долната страна на екранот и топчето, ограничување на движењето на играчите и колизија помеѓу играчите и топчето.

Прво ќе го средиме топчето. Отворете ја Ball.cs класата и додадете го следниот метод:

public void CheckWallCollision()
{
	if (Position.Y < 0)
	{
		Position.Y = 0;
		Velocity.Y *= -1;
	}
	if (Position.Y + Texture.Height > Game1.ScreenHeight)
	{
		Position.Y = Game1.ScreenHeight - Texture.Height;
		Velocity.Y *= -1;
	}
}

 

Овој метод прво проверува дали топчето излегува од горната страна. Доколку излегува од горната страна, ја сетираме Y координатата на 0 и ја променуваме насоката на брзината (со ова добиваме одбивање од горната страна). Во вториот if-тест се проверува истото за долната страна меѓутоа приметувате дека треба да се искористи и висината на самата текстура бидејќи позицијата се наоѓа на горниот лев агол на самото топче.

Овој метод сега ќе го повикаме во Move методот на топчето. Додадете го следниот метод во Ball.cs класата:

public override void Move(Vector2 amount)
{            
	base.Move(amount);
	CheckWallCollision();
}

 

Овој метод прво го повикува Move методот од GameObject (класата од кој што наследува) и потоа го повикува методот за проверка на колизии со горната и долната страна на екранот.

Пред да тестираме, ќе додадеме неколку линии за да се ресетира топчето доколку излезе од левата или десната страна на екранот.

Отворете ја Game1.cs класата и внесете го следниот код веднаш по процесирањето на Touch инпутот:

player1.Move(player1TouchVelocity);
player2.Move(player2TouchVelocity);

if (ball.Position.X + ball.Texture.Width < 0)
{
	ball.Launch(BALL_START_SPEED);
}

if (ball.Position.X > ScreenWidth)
{
	ball.Launch(BALL_START_SPEED);
}

 

Првиот if-тест проверува дали топчето излегло од левата страна и доколку да, повторно го лансира од центарот на екранот. Вториот if-тест проверува дали топчето излегло од десната страна.

Притиснете F5 или Debug –> Start Debugging за да ги тестирате последните промени. Ќе забележите дека сега топчето се одбива од горната и долната страна, а кога излегува од левата или десната страна се ресетира повторно на центарот на екранот.

Следниот чекор е да се ограничи движењето на играчите. Отворете ја Player.cs класата и додадете ја следната библиотека:

using Microsoft.Xna.Framework;

Потоа, додадете го следниот метод:

public override void Move(Vector2 amount)
{
	base.Move(amount);
	if (Position.Y <= 0)
		Position.Y = 0;
	if (Position.Y + Texture.Height >= Game1.ScreenHeight)
		Position.Y = Game1.ScreenHeight - Texture.Height;
}

 

Како и за топчето, најпрво се повикува Move методот од GameObject (класата од која што наследува) потоа се проверува дали играчот се наоѓа на горната граница  и се ограничува позицијата доколку играчот се обиде да излезе од екранот. Истото се случува и во вториот if-тест каде што се проверува дали играчот сака да излезе од долната страна.

Притиснете F5 или Debug –> Start Debugging и тестирајте ги последните промени. Ќе забележите дека сега играчот не смее да го напушти екранот.

Останува уште последниот дел од колизиите, колизија помеѓу топчето и играчите.

Отворете ја GameObject.cs класата и додадете го следното Property:

public Rectangle Bounds
{
	get { return new Rectangle((int)Position.X, (int)Position.Y, Texture.Width, Texture.Height); }
}

 

Ова Property враќа правоаголник со границите на објектите (играчите и топчето во нашиот случај).

Во истата класа (GameObject.cs) додадете го следниот метод:

public static bool CheckPaddleBallCollision(Player player, Ball ball)
{
	if (player.Bounds.Intersects(ball.Bounds))
		return true;
	return false;
}

 

Овој метод враќа булеан (true или false), а како параметри зема играч и топче. Со користење на Bounds Property-то за добивање на правоаголникот околу објектите се проверува дали правоаголникот на играчот се пресекува со правоаголникот на топчето. Доколку постои пресек, враќаме true. Во спротивост враќаме false.

Овој метод е статичен и може да се повика преку GameObject.CheckPaddleBallCollision(…);

Отворете ја Game1.cs класата и додадете ја следната библиотека:

using System;

 

Потоа во Update методот, веднаш после Touch движењето и пред проверката за повторно лансирање на топчето внесете ги линиите како што е покажано:

player1.Move(player1TouchVelocity);
player2.Move(player2TouchVelocity);

if (GameObject.CheckPaddleBallCollision(player1, ball))
{
	ball.Velocity.X = Math.Abs(ball.Velocity.X);
}

if (GameObject.CheckPaddleBallCollision(player2, ball))
{
	ball.Velocity.X = -Math.Abs(ball.Velocity.X);
}

if (ball.Position.X + ball.Texture.Width < 0)
{
	ball.Launch(BALL_START_SPEED);
}

 

Во четвртата линија забележувате се проверува колизија помеѓу првиот играч и топчето. Доколку постои колизија, брзината на топчето во X оската добива позитивна насока (позитивна насока во случајот значи надесно). Потоа се проверува колизија помеѓу вториот играч и топчето. Доколку постои колизија, се дава негативна вредност на брзината во X оската.

Притиснете F5 или Debug –> Start Debugging за да тестирате.

Со овој чекор го завршуваме третиот дел од овој серијал. Доколку нешто не ви е јасно, или заглавивте некаде, оставете коментар подолу. За да го превземете мојот Solution од овој дел, притиснете тука.

Напомена: Ќе треба да ги сетирате референците до MonoGame за да го тестирате мојот Solution.

Во наредниот, последен дел од овој серијал, ќе ја дотераме играта. Ќе додадеме ‘вртење’ на топчето кога играчот ќе го мава, ќе додадеме индикатори за поени, звуци и слично.

Developing 2D Games For Windows 8 Using MonoGame Part Two–Drawing Sprites

As I mentioned in the first part of this series, in this part I will start developing a Pong clone. First I will explain how to prepare your external assets (sprites, images, sounds etc.), then we will create the project where we will develop the game, we will copy the compiled .xnb assets, we will create few classes and finally we will draw few objects on the screen. Without further ado, let’s start with the first step.

Preparing the External Assets

In the first part I mentioned that the Content Pipeline for the Windows 8 version of MonoGame is still not ready. Therefore, we will need XNA to compile the external assets and then use the compiled files in our MonoGame project.

First of all, download the .rar compressed file ‘Pong Assets’ from here. After you finish downloading, extract the files to a folder of your choice (e.g. Desktop). Launch Visual Studio 2012 Express for Windows Phone and create a new XNA Windows Phone Game (4.0) project. Change the name to PongContent and press OK.

 

After the project is created, right-click on the Content project that you can find on the right side then Add –> Existing Item…

Navigate to the folder where you previously extracted the files, select all of the files and click Add to add them to the project.

Our next step is to compile the project. Click on Build –> Build Solution or press CTRL+Shift+B on your keyboard.

After the Build process is complete, close Visual Studio 2012 Express for Windows Phone and launch Visual Studio 2012 Express for Windows 8.

Create a New Project, pick MonoGame Game, change the name to PongClone and click OK.

If you followed my instructions on the first part to set up MonoGame by cloning the Git repository, now you will need to repeat the steps that I wrote under ‘Testing MonoGame’. They were: removing the existing references, adding the MonoGame Windows 8 project and adding a reference in your project to the MonoGame Windows 8 project. If you do not remember how this was done, read the instructions again here (look at the end ‘Testing MonoGame’)

If you installed MonoGame via the installer, you do not have to do anything else to set it up. With the creation of the project, all the required references will be added automatically.

Press F5 or Debug –> Start Debugging to test whether MonoGame is functional before we continue. If you see a blue screen, everything is fine.

Right-click on the PongClone project Add –> New Folder. Name the new folder ‘Content’ (without quotation marks).

Right-click on the folder Add –> Existing Item… and navigate to:

…\Documents\Visual Studio 2012\Projects\PongContent\PongContent\PongContent\bin\Windows Phone\Debug\Content

Select all the .xnb files and click Add.

 

 

 

 

Next, select all the added files as shown on the picture on the left, under Properties change the Build Action from None to Content. (Click on the picture for bigger view)

 

 

 

 

 

 

This step completes the preparation of the external assets. There are other methods that might be slightly faster (adding Windows Phone library and Content projects), but for that you need Visual Studio 2012 Professional or above and they are slightly more complex.

Game Loop

MonoGame has all the XNA namespace, class, method etc. names. Open the Game1.cs file where the ‘Main’ of the application is located. In this same file, besides the constructor, you will notice 5 more methods: Initialize, LoadContent, UnloadContent, Update and Draw.

The first two are used to set up the initial values and loading external assets. UnloadContent is used when you want to dispose the loaded assets (e.g. closing the game). Update and Draw are the methods where the magic happens! These two methods constantly run in a loop which is called the Game Loop. The speed of the loop is measured in frames per second (FPS) and can be manually set.

In Update we put the game logic (movement, input, collision detection, physics etc.). After all the calculations, we move to Draw which is used for drawing objects in a given position.

 

GameObject, Player and Ball

For our Pong clone, we will create few classes. In this part of the series, our goal is to draw our objects on the screen. Before we begin with typing the classes for our objects, we will first type a few lines where we will hold the screen dimensions at any given time.

Open Game1.cs if you did not open it before and under the existing lines for _graphics and _spriteBatch, add two static int variables where we will hold the width and height of the screen.

GraphicsDeviceManager _graphics;
SpriteBatch _spriteBatch;

public static int ScreenWidth;
public static int ScreenHeight;

Scroll down a bit, in the Initialize method, initialize the two variables.

ScreenWidth = GraphicsDevice.Viewport.Width;
ScreenHeight = GraphicsDevice.Viewport.Height;

base.Initialize();

Now, in the Update method, add the same two lines as above.

ScreenWidth = GraphicsDevice.Viewport.Width;
ScreenHeight = GraphicsDevice.Viewport.Height;

base.Update(gameTime);

With these lines, we will have the screen dimensions at any given time, whenever the dimensions change, they will be updated (e.g. Snap View on Windows 8).

Now we can create the classes for our in-game objects. Because the players and the ball share several attributes, we will create a GameObject superclass and then Player and Ball will inherit from that class.

Right-click on the PongClone project and add a new Folder ‘Classes’ (without quotation marks). Right click on the ‘Classes’ folder and Add –> Class…

Change the name to GameObject and click Add.

Repeat the same thing to add the Player and Ball classes.

Open the GameObject.cs class and change it to the following:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;

namespace PongClone
{
    public class GameObject
    {
        public Vector2 Position;
        public Texture2D Texture;
    }
}

You will notice in lines 6 and 7, we added two libraries that containt the Vector2 and Texture2D classes that are used in lines 13 and 14. Also, in line 9 we removed .Classes in order to have access to the GameObject class without writing Classes.GameObject.

In line 11, we added the public keyword in order to have access to the class from other classes. In line 13, we declared a two-dimensional vector to store a position that contains X and Y coordinates. In line 14, we declared a variable that will hold the texture of the object.

Note: In our game I will avoid using other keywords like private, protected etc. for simplicity.

Next, we need to add a method for drawing. Add the following method in the GameObject class:

public void Draw(SpriteBatch spriteBatch)
{
	spriteBatch.Draw(Texture, Position, Color.White);
}

 

This method uses a SpriteBatch object to draw 2D objects on screen. spriteBatch.Draw(…) has several overloads, I will use the fourth overload that takes Texture2D, Vector2 and Color parameters. This line draws a given texture called Texture on a given position called Position. The last parameter is used for tinting the color of the texture. Color.White is used if you do not want to apply any tinting and use the original color of the texture.

Open the Player.cs class and change it to the following:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace PongClone
{
    public class Player : GameObject
    {
    }
}

In line 7, we removed .Classes. In line 9, we added the public keyword and : GameObject to inherit from the GameObject class.

Open the Ball.cs class in change it to the following:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace PongClone
{
    public class Ball : GameObject
    {
    }
}

The same changes are also done to the Ball class.

Drawing the Players and the Ball

Open the Game1.cs class and create variables for the players and ball. Also, we will create an integer constant to set a player paddle offset used to offset the players from the left and right edges of the screen.

const int PADDLE_OFFSET = 70;

Player player1;
Player player2;
Ball ball;

The value 70 represents 70 pixels offset. In our case we will offset from the left and right edges of the screen.

Change the Initialize method to the following:

protected override void Initialize()
{
	// TODO: Add your initialization logic here

	ScreenWidth = GraphicsDevice.Viewport.Width;
	ScreenHeight = GraphicsDevice.Viewport.Height;

	player1 = new Player();
	player2 = new Player();
	ball = new Ball();
            
	base.Initialize();
}

In lines 8, 9 and 10 we created the objects for the players and the ball. Next, we will need to load the textures and set the initial positions of these three objects.

Change the LoadContent method to the following:

protected override void LoadContent()
{
	// Create a new SpriteBatch, which can be used to draw textures.
	_spriteBatch = new SpriteBatch(GraphicsDevice);

	// TODO: use this.Content to load your game content here
	player1.Texture = Content.Load<Texture2D>("Paddle");
	player2.Texture = Content.Load<Texture2D>("Paddle");

	player1.Position = new Vector2(PADDLE_OFFSET, ScreenHeight / 2 - player1.Texture.Height / 2);
	player2.Position = new Vector2(ScreenWidth - player2.Texture.Width - PADDLE_OFFSET, ScreenHeight / 2 - player2.Texture.Height / 2);

	ball.Texture = Content.Load<Texture2D>("Ball");
	ball.Position = new Vector2(ScreenWidth / 2 - ball.Texture.Width / 2, ScreenHeight / 2 - ball.Texture.Height / 2);
}

In lines 7 and 8 we load the same texture for each of the players. The texture i s located in the Content folder that we previously created. After that, we set the positions of the players. In line 10, for the X coordinate, we use the paddle offset of 70 pixels. For the Y coordinate, we use the screen height and the height of the player texture to correctly position it on the center of the Y axis.

In line 11, we take into account the complete screen width in order to position the second player on the right side.

In lines 13 and 14, we load the texture for the ball and set its initial position.

Note: (0,0) on the screen is the top-left corner. Look at the image here.

Finally we reached the last step, drawing the objects. Change the Draw method to the following:

protected override void Draw(GameTime gameTime)
{
	GraphicsDevice.Clear(Color.Black);

	// TODO: Add your drawing code here
	_spriteBatch.Begin();
	player1.Draw(_spriteBatch);
	player2.Draw(_spriteBatch);
	ball.Draw(_spriteBatch);
	_spriteBatch.End();
	base.Draw(gameTime);
}

 

In line 3, we changed the color of the background from CornflowerBlue to Black. Then to begin drawing, the method _spriteBatch.Begin() is fired. In lines 7, 8 and 9 we use the method that we previously typed in the GameObject class to draw the elements. After we finish drawing, we stop the SpriteBatch object by firing _spriteBatch.End().

Press F5 or Debug –> Start Debugging. If you do not have any syntax errors, you should see the following:

The complete solution until this point can be found here.

Note: You will need to set up the correct references to the MonoGame projects in order to test my Solution.

If you have any problems, leave a comment below.

In part three, we will implement input, movement and collision detection.

Развивање 2Д Игри За Windows 8 Со MonoGame Втор Дел–Цртање Спрајтови

Како што спомнав на крајот на првиот дел од овој серијал,  во овој дел ќе започнам со развивање на Pong клон. Прво ќе ви објаснам како да ги подготвите екстерните фајлови (слики, спрајтови, звуци итн.), па ќе го креираме проектот каде што ќе ја развиваме играта, ќе ги внесеме компајлираните .xnb фајлови од екстерните фајлови, ќе креираме неколку класи за на крај да ги нацртаме објектите на екран. Без да должам, одиме со првиот чекор.

Подготовка на Екстерни Фајлови

Како што спомнав во првиот дел, Content Pipeline-от за Windows 8 верзијата на MonoGame сеуште не е спремен и затоа ни треба XNA за да ги компајлираме сите надворешни фајлови. Потоа, овие компајлирани фајлови можеме да ги искористиме во нашиот MonoGame проект.

Најпрво превземете го .rar компресираниот фајл наречен Pong Assets од тука. По превземањето, екстрактирајте ги фајловите кои што се внатре во фолдер по ваш избор (пример Desktop). Пуштете го Visual Studio 2012 Express for Windows Phone и креирајте нов Windows Phone Game (4.0) проект. Крстете го PongContent и притиснете ОК.

 

Откако ќе се креира проектот, притиснете десен клик на Content проектот кој што се наоѓа десно Add –> Existing Items…

Навигирајте до фолдерот каде што претходно ги екстрактиравте фајловите, селектирајте ги сите фајлови и притиснете Add за да ги додадете во проектот.

Наредниот чекор е да се компајлира проектот. Притиснете Build –> Build Solution или CTRL+Shift+B на тастатура.

Откако компајлирањето ќе се комплетира, исклучете го Visual Studio 2012 Express for Windows Phone и пуштете Visual Studio 2012 Express for Windows 8.

Креирајте нов проект, изберете MonoGame Game, крстете го PongClone и притиснете OK.

Доколку ги пратевте инструкциите од првиот дел за сетирање на MonoGame преку клонирање на Git репозиторија, сега ќе треба да се повторат чекорите кои што ги напишав под ‘Тестирање на MonoGame’. Тие беа: бришење на постоечките референци, додавање на MonoGame проектот и додавање референца до MonoGame проектот. Доколку не се сеќавате како се правеше тоа, прочитајте ги повторно тука (инструкциите се наоѓаат на крајот – ‘Тестирање на MonoGame’).

Ако го инсталиравте MonoGame преку инсталерот, нема потреба од дополнително сетирање. Со самото креирање на проектот, автоматски се додаваат точните референци до потребните библиотеки.

За секој случај, притиснете F5 или Debug –> Start Debugging за да тестирате дали MonoGame функционира. Доколку ви се појави сина боја, се е во ред и може да продолжиме со наредниот чекор.

Притиснете десен клик на PongClone проектот Add –> New Folder. Крстете го новиот фолдер ‘Content’ (без наводници).

Притиснете десен клик на фолдерот Add –> Existing Item…и навигирајте во:

…\Documents\Visual Studio 2012\Projects\PongContent\PongContent\PongContent\bin\Windows Phone\Debug\Content

Селектирајте ги сите .xnb фајлови и притиснете Add.

 

 

 

 

Следно, селектирајте ги додадените фајлови како што е покажано на сликата лево, под Properties сменете го Build Action од None во Content. (Притиснете на сликата за зум)

 

 

 

 

 

 

Oвој чекор ја комплетира подготовката на екстерните фајлови. Постојат и други начини кои што се малку побрзи (додавање Windows Phone library и Content проект) но за нив потребен е Visual Studio 2012 Professional/Ultimate и се малку покомплексни.

Game Loop

MonoGame ги има истите XNA имиња на namespace-ови, класи, методи итн. Отворете го Game1.cs фајлот каде што е напишан ‘Main’-от на апликацијата. Во истиот фајл, покрај конструкторот, ќе забележите 5 методи: Initialize, LoadContent, UnloadContent, Update и Draw.

Првите два се користат за сетирање на почетните вредности и вчитување на екстерните фајлови. UnloadContent, се користи кога се исклучува апликацијата за да се ослободат вчитаните фајлови. Update и Draw се методите каде што се случуваат чуда! Овие два методи се постојано во еден циклус кој што се нарекува Game Loop и во зависност од платформата може да се сетира брзината во фрејмови во една секунда (FPS).

Во Update се става целата логика (движење, инпут, процесирање на колизии, физика итн.). После сите калкулации се преминува на Draw кој што се користи за цртање на објектите во дадена позиција.

 

GameObject, Player и Ball

За нашиот Pong клон, ќе креираме неколку класи. Во овој дел од серијалот, целта ни е да ги нацртаме објектите на екран. Пред да почнеме со пишување на класите за објектите, ќе напишеме неколку линии за да ги зачуваме димензиите на екранот во секој момент.

Отворете го Game1.cs фајлот доколку претходно не го отворивте и под веќе напишаните линии за _graphics и _spriteBatch додадете две статични int променливи каде што ќе се чува ширината и висината на екранот.

        
GraphicsDeviceManager _graphics;
SpriteBatch _spriteBatch;

public static int ScreenWidth;
public static int ScreenHeight;

 

Малку подолу, во методот Initialize, иницијализирајте ги двете променливи:

ScreenWidth = GraphicsDevice.Viewport.Width;
ScreenHeight = GraphicsDevice.Viewport.Height;

base.Initialize();

 

Сега, во Update методот додадете ги истите две линии.

ScreenWidth = GraphicsDevice.Viewport.Width;
ScreenHeight = GraphicsDevice.Viewport.Height;

base.Update(gameTime);

 

Со овие линии, ќе ги имаме димензиите на екранот во секој момент, кога и да се сменат (пример Snap View во Windows 8).

Сега може да ги креираме класите за објектите кои што ќе ги користиме во играта. Бидејќи играчите и топчето имаат неколку заеднички атрибути, ќе креираме една супер класа наречена GameObject и од таа класа ќе има наследување за Player и Ball.

Притиснете десен клик на PongClone проектот и додадете нов фолдер ‘Classes’ (без наводници). Притиснете десен клик на ‘Classes’ фолдерот, Add –> Class…

Крстете ја класата GameObject и притиснете Add.

Повторете го истото за да ги додадете класите за Player и Ball.

Отворете ја GameObject класата и преправете ја во следното:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;

namespace PongClone
{
    public class GameObject
    {
        public Vector2 Position;
        public Texture2D Texture;
    }
}

 

Ќе забележите во линиите 6 и 7 додадени се две библиотеки кои што ги содржат Vector2 и Texture2D класите искористени во линиите 13 и 14. Исто така во линија 9 избришан е .Classes за да можеме да пристапиме до GameObject класата без да пишуваме Classes.GameObject.

Во линија 11 додаден е зборот public за да може да пристапиме до класата од други класи. Во линија 13, деклариран е дво-димензионален вектор за позиција кој што содржи X и Y координата. Во линија 14, декларирана е променлива за да се зачува текстурата на објектот.

Напомена: Во нашата игра ќе избегнувам користење на private, protected итн. зборови поради едноставност.

Нареден чекор е да се додаде метод за цртање. Додадете го следниот метод:

public void Draw(SpriteBatch spriteBatch)
{
	spriteBatch.Draw(Texture, Position, Color.White);
}

 

Со овој метод се користи SpriteBatch објект за цртање на 2Д објекти на екран. spriteBatch.Draw(…) методот има неколку overload-и, јас ќе го искористам четвртиот каде што се користат Texture2D, Vector2 и Color параметри. Оваа линија ја црта дадената текстура Texture во дадена позиција Position. Последниот параметар се користи за менување на бојата на текстурата. Color.White се користи за зачувување на оригиналната боја на самата текстура.

Отворете ја Player.cs класата и преправете ја во следното:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace PongClone
{
    public class Player : GameObject
    {
    }
}

 

Во линија 7 избришан е .Classes. Во линија 9, додаден е зборот public и : GameObject за да се означи наследување од GameObject.

Отворете ја Ball.cs класата и преправете ја во следното:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace PongClone
{
    public class Ball : GameObject
    {
    }
}

 

Исто и тука, направени се истите промени.

Цртање на Играчите и Топчето

Отворете ја Game1.cs класата и креирајте променливи за играчите и топчето. Исто така ќе креираме и една int константа за офсет на играчите од десниот и левиот крај на екранот:

const int PADDLE_OFFSET = 70;

Player player1;
Player player2;
Ball ball;

 

Бројката 70 ќе ни претставува 70 пиксели офсет од левата и десната страна на екранот.

Преправете го Initialize методот во следното:

protected override void Initialize()
{
	// TODO: Add your initialization logic here

	ScreenWidth = GraphicsDevice.Viewport.Width;
	ScreenHeight = GraphicsDevice.Viewport.Height;

	player1 = new Player();
	player2 = new Player();
	ball = new Ball();
            
	base.Initialize();
}

Во линиите 8,9 и 10 се креираат објектите за играчите и топчето. Следно, ќе треба да се вчитаат текстурите и да се сетираат почетните позиции на трите објекти.

Преправете го LoadContent во следното:

protected override void LoadContent()
{
	// Create a new SpriteBatch, which can be used to draw textures.
	_spriteBatch = new SpriteBatch(GraphicsDevice);

	// TODO: use this.Content to load your game content here
	player1.Texture = Content.Load<Texture2D>("Paddle");
	player2.Texture = Content.Load<Texture2D>("Paddle");

	player1.Position = new Vector2(PADDLE_OFFSET, ScreenHeight / 2 - player1.Texture.Height / 2);
	player2.Position = new Vector2(ScreenWidth - player2.Texture.Width - PADDLE_OFFSET, ScreenHeight / 2 - player2.Texture.Height / 2);

	ball.Texture = Content.Load<Texture2D>("Ball");
	ball.Position = new Vector2(ScreenWidth / 2 - ball.Texture.Width / 2, ScreenHeight / 2 - ball.Texture.Height / 2);
}

 

Во линиите 7 и 8 ја вчитуваме истата текстура за двајцата играчи. Текстурата ни се наоѓа во Content фолдерот. Потоа ги сетираме позициите на играчите. Во 10тата линија, за X координатата го користиме офсет-от од 70 пиксели. За Y координатата ја земаме во предвид висината на екранот и висината на текстурата на играчот за точно да се постави на средина.

Во 11тата линија се зема во предвид целата ширина на екранот за да се постави вториот играч на десната страна.

Во линиите 13 и 14, се вчитува текстурата за топчето и се сетира позицијата на топчето.

Напомена: (0,0) на екранот се наоѓа на горниот лев агол. Слика тука.

Конечно стигнавме до последниот дел, цртање на објектите. Преправете го Draw методот во следното:

protected override void Draw(GameTime gameTime)
{
	GraphicsDevice.Clear(Color.Black);

	// TODO: Add your drawing code here
	_spriteBatch.Begin();
	player1.Draw(_spriteBatch);
	player2.Draw(_spriteBatch);
	ball.Draw(_spriteBatch);
	_spriteBatch.End();
	base.Draw(gameTime);
}

 

Во третата линија, смената е бојата на позадината од CornflowerBlue во Black. Потоа за да се започне со цртање, се користи методот _spriteBatch.Begin() . Во линиите 7, 8 и 9 го користиме методот кој што го напишавме во GameObject класата за да ги нацртаме елементите. Откако се завршува со цртање, се прекинува SpriteBatch објектот со _spriteBatch.End().

Притиснете F5 или Debug –> Start Debugging. Доколку немате некакви синтаксички грешки би требало да го гледате ова:

Целосниот Solution на овој дел, може да го спуштете од тука.

Напомена: Ќе треба да ги сетирате референците до MonoGame за да го тестирате мојот Solution.

Доколку имате некакви проблеми, оставете коментар подолу.

Во третиот дел, ќе ставиме инпут, движење и колизии.

Developing 2D Games For Windows 8 Using MonoGame Part One–Preparation

This series will consist of several parts where I will try to explain the steps you need to take in order to develop a simple 2D game for Windows 8 that can be published to the Windows Store.

Part One – Preparation

Part Two – Drawing Sprites

Part Three – Game Logic

Part Four – Polishing

XNA is a great framework for beginners and you can easily and quickly develop little games or prototypes. You can also develop more serious and complex games like Fez, Terraria, Bastion etc. Unfortunately, Microsoft decided not to continue their active support, so the games created with XNA cannot be published to the Windows Store. On the other hand, the ordinary desktop games work just fine. Because of this, I had to look for other alternatives.

MonoGame is an open-source implementation of the XNA Framework, mainly created for XNA users so they can port their games to other platforms such as iOS, Android, Mac etc. Thanks to Tom Spilman and James Ford from Sickhead Games, and few other talented developers, MonoGame now supports Windows 8. The games that are created with MonoGame can be published to the Windows Store. In this series, I will use MonoGame to show you how you can create a simple Pong clone which is considered the “Hello World!” of Game Development 🙂

There are other alternatives for developing Windows 8 games that can be published to the Windows Store such as HTML5/JavaScript or DirectX.

             

Preparing the System

Before moving to the set up of MonoGame, first we need the following:

  1. Windows 8 (Release Preview can be downloaded here)

You are probably wondering why you need Visual Studio 2012 for Windows Phone. XNA has a Content Pipeline that is used to compile all the assets for your game (sprites, models, sounds etc.). This Content Pipeline is not yet implemented in the Windows 8 version of MonoGame, so you will need to first compile the external assets in Visual Studio 2012 for Windows Phone (that supports XNA) and then copy the resulting .xnb files to your Visual Studio 2012 for Windows 8 project. I will talk about this in detail in one of the upcoming parts in this series.

If you already have Visual Studio 2012 Professional or above, you will need to install Windows Phone 8.0 SDK in order to create an XNA Windows Phone project for the assets.

If you get stuck somewhere, leave a comment below.

MonoGame Set up

You can choose between two approaches in order to install MonoGame. The first one is to download the MonoGame installer from here. The second one (which I will explain in detail in this post) is via a Git repository. Why the second approach? Well, compared to the installer, the Git repository is updated more often with the latest bug fixes and new features.

For this part, you will need a Git client. I am using TortoiseGit because it is pretty simple. If you prefer another client, feel free to use it.

  1. Download and install TortoiseGit
  2. Download and install Git

After completing the installations, you will need to clone MonoGame. To do that, first navigate to a directory of your choice where you will download all the necessary files. I will clone it on my Desktop.

Note: The complete clone has StarterKits and Sample projects, therefore it is about 1.6GB.

Right click on a blank spot on the desktop, and then click on Git Clone…

Paste the following address in the URL field: https://github.com/mono/MonoGame

Check the Recursive checkbox and click OK.

Now, you will have to wait a while until the cloning process is complete. As I said earlier, the clone weighs about 1.6GB

Creating and Installing a MonoGame Template in Visual Studio 2012

After the cloning process is done:

  1. Open the MonoGame folder and navigate to ProjectTemplates\VisualStudio2012\Game
  2. Select all files inside and create a ZIP file.


  3. Copy the ZIP file to the Visual Studio 2012 Visual C# Templates directory: C:\Users\yourUsername\Documents\Visual Studio 2012\Templates\ProjectTemplates\Visual C#
  4. Optional: If you also want the XAML template, do the same thing for the files in the XamlGame folder: MonoGame\ProjectTemplates\VisualStudio2012\XamlGame


Testing MonoGame

  1. Launch Visual Studio 2012 for Windows 8 (or Visual Studio 2012 if you have Pro or above) and create a New Project.
  2. Under Visual C#, you should now have MonoGame template(s). Pick the Game template and click OK.


  3. Because the template is the same one that is installed via the installer, the Path values of the references lead to wrong directories. Select the references as shown on the image below and delete them:


  4. Right click on the Solution –> Add –> Existing Project.


  5. Navigate to MonoGame\MonoGame.Framework and add MonoGame.Framework.Windows8.csproj to your solution.


  6. Now you need to reference the MonoGame project that you just added. Right click on References (in your project) and click Add Reference…


  7. On the left side, pick Solution –> Project and check the checkbox as shown on the image below:


  8. Press F5 to compile and run the project. If you see a Cornflower Blue screen, you have successfully set up MonoGame.

If you have any problems, leave a comment below.

In the next part, we will start with the development of Pong.