В рамках этой лабораторной работы мы создадим игру – клон популярной игры Arkanoid. Наша игра будет обладать следующим набором характеристик.
Здесь мы создадим каркас игры и конструктор уровней для создания пользовательских уровней.
Создадим новый проект – P11_1. На рис. 15.1. вы можете видеть окно Project Explorer этого проекта.
(рис 15.1) Окно Project Explorer для проекта P11_1Игровая функциональность и функциональность конструктора уровней реализована в классе Game1. Класс gBaseClass используется в качестве базового класса для игровых объектов. В предыдущих работах мы уже пользовались подобным классом, здесь мы расширили его функциональность для того, чтобы он позволял размещать на экране объекты, которым соответствуют спрайты различных размеров.
Классы Ball, Bat, Brick1, Brick2 – это игровые компоненты, соответственно, для мяча (Ball), биты (Bat), и двух видов блоков – обычного, который уничтожается при попадании в него мяча (Brick1), и неуничтожимого (Brick2).
Класс BrickBuilder используется для создания указателя, который применяется в конструкторе уровней.
На рис. 15.2. вы можете видеть изображения графических ресурсов, примененных в игре.
(рис 15.2) Игровые ресурсыИзображения BrickBuild используются в конструкторе уровней. Указатель с изображением BrickBuild0.png очищает ячейку игрового поля, расположенную под ней. Указатель с BrickBuild1.png устанавливает в текущую позицию игрового поля объект Brick1, указатель с изображением BrickBuild2.png устанавливает в текущую ячейку объект Brick2.
На рис. 15.3. вы можете видеть игровой экран.
(рис 15.3) Игровой экран проекта P11_1На рис. 15.4 представлен экран в режиме конструктора уровней.
(рис 15.4) Конструктор уровней Рассмотрим коды классов, которые используются в игре.
В листинге 15.1. приведен код класса Game1.
using System;
using System.Collections.Generic;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Audio;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.GamerServices;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using Microsoft.Xna.Framework.Net;
using Microsoft.Xna.Framework.Storage;
namespace P11_1
{
public class Game1 : Microsoft.Xna.Framework.Game
{
GraphicsDeviceManager graphics;
SpriteBatch spriteBatch;
//Текстуры
Texture2D txtBack, txtBrick1, txtBrick2, txtBat, txtBall;
Texture2D txtBrBuild1, txtBrBuild2, txtBrBuild0;
//Массивы для хранения данных об уровнях
public byte[,] Layer1, Layer2, Layer3, UserLayer;
//Прямоугольник для фона
Rectangle recBack = new Rectangle(0, 0, 512, 640);
//Прямоугольник для "блока"
Rectangle recBrick = new Rectangle(0, 0, 64, 16);
//Прямоугольник для "мяча"
Rectangle recBall = new Rectangle(0, 0, 10, 10);
//Прямоугольник для биты
Rectangle recBat = new Rectangle(0, 0, 96, 16);
//Номер текущего уровня
//1 - первый
//2 - второй
//3 - третий
//-1 - пользовательский
int currentLayer;
//Установлена в false, если программа находится в
//режиме игры, при установке в True включается
//режим конструктора
bool IsBuild;
//Позиция указателя в режиме конструктора
Vector2 PositionBuild;
//Тип блока, который будет добавлен на игровое поле
//при нажатии соответствующей клавиши в режиме
//конструктора
byte typeOfBuildBlock;
public Game1()
{
graphics = new GraphicsDeviceManager(this);
Content.RootDirectory = "Content";
}
protected override void Initialize()
{
//Программа находится в игровом режиме
IsBuild = false;
//Позиция для указателя в режиме конструктора (0,0)
PositionBuild = new Vector2(0, 0);
//Тип блока по умолчанию в режиме конструктора - 1
typeOfBuildBlock = 1;
//пользовательский уровень по умолчанию пуст - ячейки
//заполнены нулями
UserLayer = new byte[40, 8];
//Заполняем массивы для разных уровней
//0 - пустая ячейка
//1 - блок первого типа (уничтожимый)
//2 - блок второго типа (неуничтожимый)
Layer1 = new byte[40, 8]{
{1,1,1,1,1,1,1,1},
{0,1,0,1,0,1,0,1},
{1,0,1,0,1,0,1,0},
{0,1,0,1,0,1,0,1},
{1,0,1,0,1,0,1,0},
{0,1,0,1,0,1,0,1},
{0,0,0,0,0,0,0,0},
{2,0,0,2,0,0,2,0},
{0,1,0,0,0,0,0,1},
{0,0,1,0,0,0,1,0},
{0,0,0,1,0,1,0,0},
{0,0,0,0,1,0,0,0},
{1,1,1,1,1,1,1,1},
{0,0,0,0,0,0,0,0},
{0,0,0,0,0,0,0,0},
{0,0,0,0,0,0,0,0},
{0,0,0,0,0,0,0,0},
{0,0,0,0,0,0,0,0},
{0,0,0,0,0,0,0,0},
{0,0,0,0,0,0,0,0},
{0,0,0,0,0,0,0,0},
{0,0,0,0,0,0,0,0},
{0,0,0,0,0,0,0,0},
{0,0,0,0,0,0,0,0},
{0,0,0,0,0,0,0,0},
{0,0,0,0,0,0,0,0},
{0,0,0,0,0,0,0,0},
{0,0,0,0,0,0,0,0},
{0,0,0,0,0,0,0,0},
{0,0,0,0,0,0,0,0},
{0,0,0,0,0,0,0,0},
{0,0,0,0,0,0,0,0},
{0,0,0,0,0,0,0,0},
{0,0,0,0,0,0,0,0},
{0,0,0,0,0,0,0,0},
{0,0,0,0,0,0,0,0},
{0,0,0,0,0,0,0,0},
{0,0,0,0,0,0,0,0},
{0,0,0,0,0,0,0,0},
{0,0,0,0,0,0,0,0}
};
Layer2 = new byte[40, 8]{
{0,0,0,0,0,0,0,0},
{0,0,0,0,0,0,0,0},
{0,0,0,0,0,0,0,0},
{0,0,0,0,0,0,0,0},
{0,0,0,0,0,0,0,0},
{0,0,0,0,0,0,0,0},
{0,0,0,0,0,0,0,0},
{0,0,0,0,0,0,0,0},
{0,0,0,0,0,0,0,0},
{0,0,0,0,0,0,0,0},
{0,0,0,0,0,0,0,0},
{0,0,0,0,0,0,0,0},
{0,0,0,0,0,0,0,0},
{0,0,0,0,0,0,0,0},
{0,0,0,1,2,1,0,0},
{0,0,0,0,0,0,0,0},
{0,0,0,0,0,0,0,0},
{0,0,0,0,0,0,0,0},
{0,0,0,0,0,0,0,0},
{0,0,0,0,0,0,0,0},
{0,0,0,0,0,0,0,0},
{0,0,0,0,0,0,0,0},
{0,0,0,0,0,0,0,0},
{0,0,0,0,0,0,0,0},
{0,0,0,0,0,0,0,0},
{0,0,0,0,0,0,0,0},
{0,0,0,0,0,0,0,0},
{0,0,0,0,0,0,0,0},
{0,0,0,0,0,0,0,0},
{0,0,0,0,0,0,0,0},
{0,0,0,0,0,0,0,0},
{0,0,0,0,0,0,0,0},
{0,0,0,0,0,0,0,0},
{0,0,0,0,0,0,0,0},
{0,0,0,0,0,0,0,0},
{0,0,0,0,0,0,0,0},
{0,0,0,0,0,0,0,0},
{0,0,0,0,0,0,0,0},
{0,0,0,0,0,0,0,0},
{0,0,0,0,0,0,0,0}
};
Layer3 = new byte[40, 8]{
{0,0,0,0,0,0,0,0},
{0,0,0,0,0,0,0,0},
{0,0,0,0,0,0,0,0},
{0,0,0,0,0,0,0,0},
{0,0,0,0,0,0,0,0},
{0,0,0,0,0,0,0,0},
{0,0,0,0,0,0,0,0},
{0,0,0,0,0,0,0,0},
{0,1,0,0,0,0,0,0},
{0,0,0,0,0,0,0,0},
{0,0,0,1,0,1,0,0},
{0,0,0,0,0,0,0,0},
{0,0,0,0,0,0,0,0},
{0,0,0,0,0,0,0,0},
{0,2,0,1,0,2,0,0},
{0,0,0,0,0,0,0,0},
{0,0,0,0,0,0,0,0},
{0,0,0,0,0,0,0,0},
{0,0,0,0,0,0,0,0},
{0,0,0,0,0,0,0,0},
{0,0,0,0,0,0,0,0},
{0,0,0,0,0,0,0,0},
{0,0,0,0,0,0,0,0},
{0,0,1,0,0,0,1,0},
{0,0,0,0,0,0,0,0},
{0,0,0,0,0,0,0,0},
{0,0,0,0,0,0,0,0},
{0,0,0,0,0,0,0,0},
{0,0,0,0,0,0,0,0},
{0,0,0,0,0,0,0,0},
{0,0,0,0,0,0,0,0},
{0,0,0,0,0,0,0,0},
{0,0,0,0,0,0,0,0},
{0,0,0,0,0,0,0,0},
{0,0,0,0,0,0,0,0},
{0,0,0,0,0,0,0,0},
{0,0,0,0,0,0,0,0},
{0,0,0,0,0,0,0,0},
{0,0,0,0,0,0,0,0},
{0,0,0,0,0,0,0,0}
};
//Текущий уровень - 1
currentLayer = 1;
//Устанавливаем разрешение игрового окна
//640х512
graphics.PreferredBackBufferWidth = 512;
graphics.PreferredBackBufferHeight = 640;
graphics.ApplyChanges();
base.Initialize();
}
/// <summary>
/// LoadContent will be called once per game and is the place to load
/// all of your content.
/// </summary>
protected override void LoadContent()
{
// Загружаем ресурсы
spriteBatch = new SpriteBatch(GraphicsDevice);
Services.AddService(typeof(SpriteBatch), spriteBatch);
txtBack = Content.Load<Texture2D>("Back");
txtBrick1 = Content.Load<Texture2D>("Brick1");
txtBrick2 = Content.Load<Texture2D>("Brick2");
txtBall = Content.Load<Texture2D>("Ball");
txtBat = Content.Load<Texture2D>("Bat");
txtBrBuild0 = Content.Load<Texture2D>("BrickBuild0");
txtBrBuild1 = Content.Load<Texture2D>("BrickBuild1");
txtBrBuild2 = Content.Load<Texture2D>("BrickBuild2");
//Создаем уровень
AddSprites(Layer1);
//Добавляем биту и мяч
addBallAndBAts();
}
//Процедура создания уровня
//расставляет элементы в соответствии с
//массивом
void AddSprites(byte [,] RenderLayer)
{
//Просматриваем массив
for (int i = 0; i < 40; i++)
{
for (int j = 0; j < 8; j++)
{
if (RenderLayer[i, j] == 1)
Components.Add(new Brick1(this, ref txtBrick1, new Vector2(j, i), recBrick, 64, 16));
if (RenderLayer[i, j] == 2)
Components.Add(new Brick2(this, ref txtBrick2, new Vector2(j, i), recBrick, 64, 16));
}
}
}
//Процедура для установки биты и мяча
void addBallAndBAts()
{
Components.Add(new Ball(this, ref txtBall, new Vector2(300, 550), recBall, 1, 1));
Components.Add(new Bat(this, ref txtBat, new Vector2(300, (640 - 16)), recBat, 1, 1));
}
protected override void UnloadContent()
{
// TODO: Unload any non ContentManager content here
}
protected override void Update(GameTime gameTime)
{
//получим состояние клавиатуры
KeyboardState kb = Keyboard.GetState();
//если IsBuild = false и нажата клавиша A
//переходим в режим конструктора уровней
if (kb.IsKeyDown (Keys .A)!IsBuild)
{
IsBuild = true;
builder();
}
//Если находимся в режиме конструктора уровней
if (IsBuild)
{
//При нажатой клавише "вверх" уменьшить текущую позицию Y
//указателя на 1 если позиция больше 0
if (kb.IsKeyDown(Keys.Up))
{
if (PositionBuild.Y > 0) PositionBuild.Y--;
}
//При нажатой клавише "вниз" увеличить позицию
//Y если она меньше 39
if (kb.IsKeyDown(Keys.Down))
{
if (PositionBuild.Y < 39) PositionBuild.Y++;
}
//При нажатой клавише "влево"
//уменьшить позицию X если текущая позиция
//больше 0
if (kb.IsKeyDown(Keys.Left))
{
if (PositionBuild.X >0) PositionBuild.X--;
}
//При нажатой клавише "вправо"
//увеличить позицию X если текущая позиция
//меньше 7
if (kb.IsKeyDown(Keys.Right))
{
if (PositionBuild.X < 7) PositionBuild.X++;
}
//Нажатие клавиши Z устанавливает тип блока в 0
//этот блок предназначен для "стирания"
//других блоков, то есть - для установки элемента
//массива, соответствующего текущему состоянию
//конструируемого уровня, в 0
if (kb.IsKeyDown(Keys.Z))
{
typeOfBuildBlock = 0;
}
//При нажатии клавиши X установим 1-й тип блока
if (kb.IsKeyDown(Keys.X))
{
typeOfBuildBlock = 1;
}
//При нажатии клавиши С установим 2-й тип блока
if (kb.IsKeyDown(Keys.C))
{
typeOfBuildBlock = 2;
}
//При нажатии клавиши W запишем в
//ячейку массива, соответствующую позиции указателя
//текущий тип блока
if (kb.IsKeyDown (Keys .W ))
{
UserLayer[(int)PositionBuild.Y, (int)PositionBuild.X] = typeOfBuildBlock;
}
//очищаем массив компонентов
Components.Clear();
//выводим пользовательский уровень
AddSprites(UserLayer);
//уничтожаем указатель и изменяем его в соответствии с типом выбранного блока
BrickBuilder br = null;
if (typeOfBuildBlock == 1)
br = new BrickBuilder(this, ref txtBrBuild1, PositionBuild, recBrick, 64, 16);
if (typeOfBuildBlock == 2)
br = new BrickBuilder(this, ref txtBrBuild2, PositionBuild, recBrick, 64, 16);
if (typeOfBuildBlock == 0)
br = new BrickBuilder(this, ref txtBrBuild0, PositionBuild, recBrick, 64, 16);
Components.Add(br);
//При нажатии клавиши W
//отключаем режим конструктора
//выводим пользовательский уровень
//и начинаем игру
if (kb.IsKeyDown(Keys.E))
{
IsBuild = false;
Components.Clear();
AddSprites(UserLayer);
addBallAndBAts();
currentLayer = -1;
}
}
//Если находимся в режиме игры
//проверим - завершен ли текущий уровень
//признак завершения уровня
if (!IsBuild)
{
IsLayerCompleted();
}
base.Update(gameTime);
}
//Процедура старта конструктора уровней
//очищаем набор компонентов
//Создаем и выводим указатель
public void builder()
{
Components.Clear();
BrickBuilder br = new BrickBuilder (this, ref txtBrBuild1,PositionBuild, recBrick, 64, 16);
Components.Add(br);
}
//Процедура проверки на завершенность
//текущего уровня
//Если на поле отсутствует мяч - игра начинается сначала
//если на поле нет ни одного блока типа Brick1 -
//переход на следующий уровень или переход на первый уровень
//с выводом надписи "Вы выиграли"
public void IsLayerCompleted()
{
//Предположим, что на поле нет ни одного блока
//типа Brick1
bool f = false;
//Предположим, что на поле нет мяча
bool Ball = false;
foreach (gBaseClass spr in this.Components)
{
//Если найден хотя бы один блок типа 1
//установим F в True
if (spr.GetType() == (typeof(Brick1)))
{
f = true;
}
//Если найден мяч - установим Ball в True
if (spr.GetType() == (typeof(Ball)))
{
Ball = true;
}
}
//Если мяч не обнаружен
//игра начинается сначала
if (!Ball)
{
Components.Clear();
currentLayer = 1;
AddSprites(Layer1);
addBallAndBAts();
}
//иначе
else
{
//Если не было найдено ни одного блока
//типа Brick1
if (!f)
{
//Очистим набор компонентов
Components.Clear();
//Увеличим номер уровня
currentLayer++;
//в зависимости от номера
//запустим соответствующий уровень
if (currentLayer == 2)
{
AddSprites(Layer2);
addBallAndBAts();
}
if (currentLayer == 3)
{
AddSprites(Layer3);
addBallAndBAts();
}
//4 - если был пройден 3-й уровень
//0 - при прохождении пользовательского уровня
if (currentLayer == 4 | currentLayer == 0)
{
currentLayer = 1;
AddSprites(Layer1);
addBallAndBAts();
this.Window.Title = "Вы выиграли!";
}
}
}
}
protected override void Draw(GameTime gameTime)
{
spriteBatch.Begin();
//выведем фоновое изображение
spriteBatch.Draw(txtBack, recBack, Color.White);
//Выведем игровые объекты
base.Draw(gameTime);
spriteBatch.End();
}
}
}
В листинге 15.2. приведен код класса gBaseClass.
using System;
using System.Collections.Generic;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Audio;
using Microsoft.Xna.Framework.GamerServices;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using Microsoft.Xna.Framework.Storage;
using Microsoft.Xna.Framework.Content;
namespace P11_1
{
public class gBaseClass : Microsoft.Xna.Framework.DrawableGameComponent
{
Texture2D sprTexture;
public Vector2 sprPosition;
public Rectangle sprRectangle;
public gBaseClass(Game game, ref Texture2D _sprTexture,
Vector2 _sprPosition, Rectangle _sprRectangle, int X_Coord, int Y_Coord)
: base(game)
{
sprTexture = _sprTexture;
//Здесь производится перевод индекса элемента массива
//в координаты на игровом экране
//Так как спрайты имеют различные размеры, для их размещения на экране
//используются коэффициенты X_Coord и Y_Coord
sprPosition.X = _sprPosition.X * X_Coord;
sprPosition.Y = _sprPosition.Y * Y_Coord;
sprRectangle = _sprRectangle;
}
public override void Draw(GameTime gameTime)
{
SpriteBatch sprBatch =
(SpriteBatch)Game.Services.GetService(typeof(SpriteBatch));
sprBatch.Draw(sprTexture, sprPosition, Color.White);
base.Draw(gameTime);
}
}
}
В листинге 15.3. приведен код класса Bat. Он занимается обработкой перемещений биты по горизонтали и контролем за пересечением битой левой и правой границ экрана.
using System;
using System.Collections.Generic;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Audio;
using Microsoft.Xna.Framework.GamerServices;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using Microsoft.Xna.Framework.Storage;
using Microsoft.Xna.Framework.Content;
namespace P11_1
{
public class Bat : gBaseClass
{
Rectangle scrBounds;
//True для движения биты влево и False - вправо
public bool direction;
public Bat(Game game, ref Texture2D _sprTexture,
Vector2 _sprPosition, Rectangle _sprRectangle, int x_c, int y_c)
: base(game, ref _sprTexture, _sprPosition, _sprRectangle, x_c, y_c)
{
scrBounds = new Rectangle(0, 0,
game.Window.ClientBounds.Width,
game.Window.ClientBounds.Height);
direction = true;
}
public override void Update(GameTime gameTime)
{
//Нажатия клавиш-стрелок влево и вправо
//вызывают перемещение объекта в соответствующем
//направлении
KeyboardState kb = Keyboard.GetState();
if (kb.IsKeyDown(Keys.Left))
{
sprPosition.X -= 2;
direction = true;
}
if (kb.IsKeyDown(Keys.Right))
{
sprPosition.X += 2;
direction = false;
}
//проверка на столкновение с границами экрана
Check();
base.Update(gameTime);
}
//Процедура проверки столкновения с левой
//и правой границами экрана
void Check()
{
if (sprPosition.X < scrBounds.Left)
{
sprPosition.X = scrBounds.Left;
}
if (sprPosition.X > scrBounds.Width - sprRectangle.Width)
{
sprPosition.X = scrBounds.Width - sprRectangle.Width;
}
}
}
}
В листинге 15.4. вы можете видеть код класса Ball. Этот класс отвечает за перемещение мяча и за обработку столкновений мяча и других объектов.
using System;
using System.Collections.Generic;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Audio;
using Microsoft.Xna.Framework.GamerServices;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using Microsoft.Xna.Framework.Storage;
using Microsoft.Xna.Framework.Content;
namespace P11_1
{
public class Ball : gBaseClass
{
//для хранения границ экрана
Rectangle scrBounds;
//скорость мяча
public Vector2 speed;
Random rand = new Random();
public Ball(Game game, ref Texture2D _sprTexture,
Vector2 _sprPosition, Rectangle _sprRectangle, int x_c, int y_c)
: base(game, ref _sprTexture, _sprPosition, _sprRectangle, x_c, y_c)
{
speed = new Vector2(1, -1);
scrBounds = new Rectangle(0, 0,
game.Window.ClientBounds.Width,
game.Window.ClientBounds.Height);
}
//Проверка на столкновение с границами экрана
void Check()
{
if (sprPosition.X < scrBounds.Left)
{
sprPosition.X = scrBounds.Left;
RandomiseSpeed();
speed.X *= -1;
}
if (sprPosition.X > scrBounds.Width - sprRectangle.Width)
{
sprPosition.X = scrBounds.Width - sprRectangle.Width;
RandomiseSpeed();
speed.X *= -1;
}
if (sprPosition.Y < scrBounds.Top)
{
sprPosition.Y = scrBounds.Top;
RandomiseSpeed();
speed.Y *= -1;
}
if (sprPosition.Y > scrBounds.Height - sprRectangle.Height)
{
this.Dispose();
}
}
//Проверка на столкновение с объектом
bool IsCollideWithObject(gBaseClass spr)
{
return (this.sprPosition.X + this.sprRectangle.Width > spr.sprPosition.X
this.sprPosition.X < spr.sprPosition.X + spr.sprRectangle.Width
this.sprPosition.Y + this.sprRectangle.Height > spr.sprPosition.Y
this.sprPosition.Y < spr.sprPosition.Y + spr.sprRectangle.Height);
}
//Процедуры случайного изменения скорости при контакте с объектами
//Для всех объектов - скорость может либо возрасти, либо упасть
void RandomiseSpeed()
{
speed.Y += (float)((rand.NextDouble() - rand.NextDouble())*0.2 );
speed.X += (float)((rand.NextDouble() - rand.NextDouble())*0.2 );
}
//Для объектов типа Brick1 скорость лишь растет
void RandomiseSpeedB1()
{
speed.Y += (float)(0.3*Math.Sign(speed.Y) * Math.Abs(((rand.NextDouble() - rand.NextDouble()))));
speed.X += (float)(0.3*Math.Sign(speed.X) * Math.Abs(((rand.NextDouble() - rand.NextDouble()))));
}
//Проверка на столкновения
public void IsCollide()
{
gBaseClass FindObj=null;
Bat FindedBat;
foreach (gBaseClass spr in Game.Components)
{
if (IsCollideWithObject(spr))
{
//Если столкнулись с блоком Brick1
if (spr.GetType() == (typeof(Brick1)))
{
//сохраним найденный объект
FindObj = spr;
//Изменим скорость
RandomiseSpeedB1();
//Обратим скорость
speed *= -1;
}
//Если столкнулись с блоком 2
//обращаем скорость
if (spr.GetType() == (typeof(Brick2)))
{
speed *= -1;
}
//если столкнулись с битой
if (spr.GetType() == (typeof(Bat)))
{
FindedBat =(Bat) spr;
RandomiseSpeed();
//Если бита двигалась влево
//Мяч отразится от нее влево
if (FindedBat.direction)
{
speed.Y = Math.Abs(speed.Y);
speed.X = Math.Abs(speed.X);
speed *= -1;
}
//Если двигалась вправо
//отразится вправо
else
{
speed.Y = Math.Abs(speed.Y);
speed.X = Math.Abs(speed.X);
speed.Y *= -1;
}
}
}
}
//Если был найден блок Block1
if (FindObj != null)
{
//Уничтожить его
FindObj.Dispose();
FindObj = null;
}
}
public override void Update(GameTime gameTime)
{
// TODO: Add your update code here
//Переместить мяч в соответствии со скоростью
this.sprPosition += this.speed;
//Ограничить скорость мяча 1.5 пикселями
if (speed.Y > 2) speed.Y = (float)2;
if (speed.Y < -2) speed.Y = (float)-2;
if (speed.X > 2) speed.X = (float)2;
if (speed.X < -2) speed.X = (float)-2;
//Проверить столкновение с границами
Check();
//проверить столкновение с объектами
IsCollide();
base.Update(gameTime);
}
}
}
Листинг 15.5. содержит коды классов Brick1, Brick2 и BrickBuilder. Эти классы наследованы от gBaseClass и не вносят в его функциональность никаких дополнений.
using System;
using System.Collections.Generic;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Audio;
using Microsoft.Xna.Framework.GamerServices;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using Microsoft.Xna.Framework.Storage;
using Microsoft.Xna.Framework.Content;
namespace P11_1
{
public class Brick1 : gBaseClass
{
public Brick1(Game game, ref Texture2D _sprTexture,
Vector2 _sprPosition, Rectangle _sprRectangle, int x_c, int y_c)
: base(game, ref _sprTexture, _sprPosition, _sprRectangle, x_c, y_c)
{
// TODO: Construct any child components here
}
}
}
using System;
using System.Collections.Generic;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Audio;
using Microsoft.Xna.Framework.GamerServices;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using Microsoft.Xna.Framework.Storage;
using Microsoft.Xna.Framework.Content;
namespace P11_1
{
public class Brick2 : gBaseClass
{
public Brick2(Game game, ref Texture2D _sprTexture,
Vector2 _sprPosition, Rectangle _sprRectangle, int x_c, int y_c)
: base(game, ref _sprTexture, _sprPosition, _sprRectangle, x_c, y_c)
{
// TODO: Construct any child components here
}
}
}
using System;
using System.Collections.Generic;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Audio;
using Microsoft.Xna.Framework.GamerServices;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using Microsoft.Xna.Framework.Storage;
using Microsoft.Xna.Framework.Content;
namespace P11_1
{
public class BrickBuilder : gBaseClass
{
public BrickBuilder(Game game, ref Texture2D _sprTexture,
Vector2 _sprPosition, Rectangle _sprRectangle, int x_c, int y_c)
: base(game, ref _sprTexture, _sprPosition, _sprRectangle, x_c, y_c)
{
// TODO: Construct any child components here
}
}
}
В этой лабораторной работе мы создали игру, предусматривающую несколько игровых уровней и конструктор уровней для пользователя. Как правило, разработка реальных игр включает в себя создание конструктора уровней, с помощью которого разработчик создает игровые уровни. Этот конструктор можно включить в поставку игры, предоставив пользователю возможность самостоятельно создавать новые уровни к игре. Конструкторы уровней так же разнообразны, как сами игры.
В этой лабораторной работе мы создали макет игры, допускающей использование нескольких уровней и содержащей конструктор уровней. Доработайте этот макет. А именно, выполните следующие улучшения исходного проекта:
В рамках этой лабораторной работы мы создадим игру – клон популярной игры Arkanoid. Наша игра будет обладать следующим набором характеристик.
Здесь мы создадим каркас игры и конструктор уровней для создания пользовательских уровней.
Создадим новый проект – P11_1. На рис. 15.1. вы можете видеть окно Project Explorer этого проекта.
(рис 15.1) Окно Project Explorer для проекта P11_1Игровая функциональность и функциональность конструктора уровней реализована в классе Game1. Класс gBaseClass используется в качестве базового класса для игровых объектов. В предыдущих работах мы уже пользовались подобным классом, здесь мы расширили его функциональность для того, чтобы он позволял размещать на экране объекты, которым соответствуют спрайты различных размеров.
Классы Ball, Bat, Brick1, Brick2 – это игровые компоненты, соответственно, для мяча (Ball), биты (Bat), и двух видов блоков – обычного, который уничтожается при попадании в него мяча (Brick1), и неуничтожимого (Brick2).
Класс BrickBuilder используется для создания указателя, который применяется в конструкторе уровней.
На рис. 15.2. вы можете видеть изображения графических ресурсов, примененных в игре.
(рис 15.2) Игровые ресурсыИзображения BrickBuild используются в конструкторе уровней. Указатель с изображением BrickBuild0.png очищает ячейку игрового поля, расположенную под ней. Указатель с BrickBuild1.png устанавливает в текущую позицию игрового поля объект Brick1, указатель с изображением BrickBuild2.png устанавливает в текущую ячейку объект Brick2.
На рис. 15.3. вы можете видеть игровой экран.
(рис 15.3) Игровой экран проекта P11_1На рис. 15.4 представлен экран в режиме конструктора уровней.
(рис 15.4) Конструктор уровнейРассмотрим коды классов, которые используются в игре.
В листинге 15.1. приведен код класса Game1.
using System;
using System.Collections.Generic;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Audio;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.GamerServices;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using Microsoft.Xna.Framework.Net;
using Microsoft.Xna.Framework.Storage;
namespace P11_1
{
public class Game1 : Microsoft.Xna.Framework.Game
{
GraphicsDeviceManager graphics;
SpriteBatch spriteBatch;
//Текстуры
Texture2D txtBack, txtBrick1, txtBrick2, txtBat, txtBall;
Texture2D txtBrBuild1, txtBrBuild2, txtBrBuild0;
//Массивы для хранения данных об уровнях
public byte[,] Layer1, Layer2, Layer3, UserLayer;
//Прямоугольник для фона
Rectangle recBack = new Rectangle(0, 0, 512, 640);
//Прямоугольник для "блока"
Rectangle recBrick = new Rectangle(0, 0, 64, 16);
//Прямоугольник для "мяча"
Rectangle recBall = new Rectangle(0, 0, 10, 10);
//Прямоугольник для биты
Rectangle recBat = new Rectangle(0, 0, 96, 16);
//Номер текущего уровня
//1 - первый
//2 - второй
//3 - третий
//-1 - пользовательский
int currentLayer;
//Установлена в false, если программа находится в
//режиме игры, при установке в True включается
//режим конструктора
bool IsBuild;
//Позиция указателя в режиме конструктора
Vector2 PositionBuild;
//Тип блока, который будет добавлен на игровое поле
//при нажатии соответствующей клавиши в режиме
//конструктора
byte typeOfBuildBlock;
public Game1()
{
graphics = new GraphicsDeviceManager(this);
Content.RootDirectory = "Content";
}
protected override void Initialize()
{
//Программа находится в игровом режиме
IsBuild = false;
//Позиция для указателя в режиме конструктора (0,0)
PositionBuild = new Vector2(0, 0);
//Тип блока по умолчанию в режиме конструктора - 1
typeOfBuildBlock = 1;
//пользовательский уровень по умолчанию пуст - ячейки
//заполнены нулями
UserLayer = new byte[40, 8];
//Заполняем массивы для разных уровней
//0 - пустая ячейка
//1 - блок первого типа (уничтожимый)
//2 - блок второго типа (неуничтожимый)
Layer1 = new byte[40, 8]{
{1,1,1,1,1,1,1,1},
{0,1,0,1,0,1,0,1},
{1,0,1,0,1,0,1,0},
{0,1,0,1,0,1,0,1},
{1,0,1,0,1,0,1,0},
{0,1,0,1,0,1,0,1},
{0,0,0,0,0,0,0,0},
{2,0,0,2,0,0,2,0},
{0,1,0,0,0,0,0,1},
{0,0,1,0,0,0,1,0},
{0,0,0,1,0,1,0,0},
{0,0,0,0,1,0,0,0},
{1,1,1,1,1,1,1,1},
{0,0,0,0,0,0,0,0},
{0,0,0,0,0,0,0,0},
{0,0,0,0,0,0,0,0},
{0,0,0,0,0,0,0,0},
{0,0,0,0,0,0,0,0},
{0,0,0,0,0,0,0,0},
{0,0,0,0,0,0,0,0},
{0,0,0,0,0,0,0,0},
{0,0,0,0,0,0,0,0},
{0,0,0,0,0,0,0,0},
{0,0,0,0,0,0,0,0},
{0,0,0,0,0,0,0,0},
{0,0,0,0,0,0,0,0},
{0,0,0,0,0,0,0,0},
{0,0,0,0,0,0,0,0},
{0,0,0,0,0,0,0,0},
{0,0,0,0,0,0,0,0},
{0,0,0,0,0,0,0,0},
{0,0,0,0,0,0,0,0},
{0,0,0,0,0,0,0,0},
{0,0,0,0,0,0,0,0},
{0,0,0,0,0,0,0,0},
{0,0,0,0,0,0,0,0},
{0,0,0,0,0,0,0,0},
{0,0,0,0,0,0,0,0},
{0,0,0,0,0,0,0,0},
{0,0,0,0,0,0,0,0}
};
Layer2 = new byte[40, 8]{
{0,0,0,0,0,0,0,0},
{0,0,0,0,0,0,0,0},
{0,0,0,0,0,0,0,0},
{0,0,0,0,0,0,0,0},
{0,0,0,0,0,0,0,0},
{0,0,0,0,0,0,0,0},
{0,0,0,0,0,0,0,0},
{0,0,0,0,0,0,0,0},
{0,0,0,0,0,0,0,0},
{0,0,0,0,0,0,0,0},
{0,0,0,0,0,0,0,0},
{0,0,0,0,0,0,0,0},
{0,0,0,0,0,0,0,0},
{0,0,0,0,0,0,0,0},
{0,0,0,1,2,1,0,0},
{0,0,0,0,0,0,0,0},
{0,0,0,0,0,0,0,0},
{0,0,0,0,0,0,0,0},
{0,0,0,0,0,0,0,0},
{0,0,0,0,0,0,0,0},
{0,0,0,0,0,0,0,0},
{0,0,0,0,0,0,0,0},
{0,0,0,0,0,0,0,0},
{0,0,0,0,0,0,0,0},
{0,0,0,0,0,0,0,0},
{0,0,0,0,0,0,0,0},
{0,0,0,0,0,0,0,0},
{0,0,0,0,0,0,0,0},
{0,0,0,0,0,0,0,0},
{0,0,0,0,0,0,0,0},
{0,0,0,0,0,0,0,0},
{0,0,0,0,0,0,0,0},
{0,0,0,0,0,0,0,0},
{0,0,0,0,0,0,0,0},
{0,0,0,0,0,0,0,0},
{0,0,0,0,0,0,0,0},
{0,0,0,0,0,0,0,0},
{0,0,0,0,0,0,0,0},
{0,0,0,0,0,0,0,0},
{0,0,0,0,0,0,0,0}
};
Layer3 = new byte[40, 8]{
{0,0,0,0,0,0,0,0},
{0,0,0,0,0,0,0,0},
{0,0,0,0,0,0,0,0},
{0,0,0,0,0,0,0,0},
{0,0,0,0,0,0,0,0},
{0,0,0,0,0,0,0,0},
{0,0,0,0,0,0,0,0},
{0,0,0,0,0,0,0,0},
{0,1,0,0,0,0,0,0},
{0,0,0,0,0,0,0,0},
{0,0,0,1,0,1,0,0},
{0,0,0,0,0,0,0,0},
{0,0,0,0,0,0,0,0},
{0,0,0,0,0,0,0,0},
{0,2,0,1,0,2,0,0},
{0,0,0,0,0,0,0,0},
{0,0,0,0,0,0,0,0},
{0,0,0,0,0,0,0,0},
{0,0,0,0,0,0,0,0},
{0,0,0,0,0,0,0,0},
{0,0,0,0,0,0,0,0},
{0,0,0,0,0,0,0,0},
{0,0,0,0,0,0,0,0},
{0,0,1,0,0,0,1,0},
{0,0,0,0,0,0,0,0},
{0,0,0,0,0,0,0,0},
{0,0,0,0,0,0,0,0},
{0,0,0,0,0,0,0,0},
{0,0,0,0,0,0,0,0},
{0,0,0,0,0,0,0,0},
{0,0,0,0,0,0,0,0},
{0,0,0,0,0,0,0,0},
{0,0,0,0,0,0,0,0},
{0,0,0,0,0,0,0,0},
{0,0,0,0,0,0,0,0},
{0,0,0,0,0,0,0,0},
{0,0,0,0,0,0,0,0},
{0,0,0,0,0,0,0,0},
{0,0,0,0,0,0,0,0},
{0,0,0,0,0,0,0,0}
};
//Текущий уровень - 1
currentLayer = 1;
//Устанавливаем разрешение игрового окна
//640х512
graphics.PreferredBackBufferWidth = 512;
graphics.PreferredBackBufferHeight = 640;
graphics.ApplyChanges();
base.Initialize();
}
/// <summary>
/// LoadContent will be called once per game and is the place to load
/// all of your content.
/// </summary>
protected override void LoadContent()
{
// Загружаем ресурсы
spriteBatch = new SpriteBatch(GraphicsDevice);
Services.AddService(typeof(SpriteBatch), spriteBatch);
txtBack = Content.Load<Texture2D>("Back");
txtBrick1 = Content.Load<Texture2D>("Brick1");
txtBrick2 = Content.Load<Texture2D>("Brick2");
txtBall = Content.Load<Texture2D>("Ball");
txtBat = Content.Load<Texture2D>("Bat");
txtBrBuild0 = Content.Load<Texture2D>("BrickBuild0");
txtBrBuild1 = Content.Load<Texture2D>("BrickBuild1");
txtBrBuild2 = Content.Load<Texture2D>("BrickBuild2");
//Создаем уровень
AddSprites(Layer1);
//Добавляем биту и мяч
addBallAndBAts();
}
//Процедура создания уровня
//расставляет элементы в соответствии с
//массивом
void AddSprites(byte [,] RenderLayer)
{
//Просматриваем массив
for (int i = 0; i < 40; i++)
{
for (int j = 0; j < 8; j++)
{
if (RenderLayer[i, j] == 1)
Components.Add(new Brick1(this, ref txtBrick1, new Vector2(j, i), recBrick, 64, 16));
if (RenderLayer[i, j] == 2)
Components.Add(new Brick2(this, ref txtBrick2, new Vector2(j, i), recBrick, 64, 16));
}
}
}
//Процедура для установки биты и мяча
void addBallAndBAts()
{
Components.Add(new Ball(this, ref txtBall, new Vector2(300, 550), recBall, 1, 1));
Components.Add(new Bat(this, ref txtBat, new Vector2(300, (640 - 16)), recBat, 1, 1));
}
protected override void UnloadContent()
{
// TODO: Unload any non ContentManager content here
}
protected override void Update(GameTime gameTime)
{
//получим состояние клавиатуры
KeyboardState kb = Keyboard.GetState();
//если IsBuild = false и нажата клавиша A
//переходим в режим конструктора уровней
if (kb.IsKeyDown (Keys .A)!IsBuild)
{
IsBuild = true;
builder();
}
//Если находимся в режиме конструктора уровней
if (IsBuild)
{
//При нажатой клавише "вверх" уменьшить текущую позицию Y
//указателя на 1 если позиция больше 0
if (kb.IsKeyDown(Keys.Up))
{
if (PositionBuild.Y > 0) PositionBuild.Y--;
}
//При нажатой клавише "вниз" увеличить позицию
//Y если она меньше 39
if (kb.IsKeyDown(Keys.Down))
{
if (PositionBuild.Y < 39) PositionBuild.Y++;
}
//При нажатой клавише "влево"
//уменьшить позицию X если текущая позиция
//больше 0
if (kb.IsKeyDown(Keys.Left))
{
if (PositionBuild.X >0) PositionBuild.X--;
}
//При нажатой клавише "вправо"
//увеличить позицию X если текущая позиция
//меньше 7
if (kb.IsKeyDown(Keys.Right))
{
if (PositionBuild.X < 7) PositionBuild.X++;
}
//Нажатие клавиши Z устанавливает тип блока в 0
//этот блок предназначен для "стирания"
//других блоков, то есть - для установки элемента
//массива, соответствующего текущему состоянию
//конструируемого уровня, в 0
if (kb.IsKeyDown(Keys.Z))
{
typeOfBuildBlock = 0;
}
//При нажатии клавиши X установим 1-й тип блока
if (kb.IsKeyDown(Keys.X))
{
typeOfBuildBlock = 1;
}
//При нажатии клавиши С установим 2-й тип блока
if (kb.IsKeyDown(Keys.C))
{
typeOfBuildBlock = 2;
}
//При нажатии клавиши W запишем в
//ячейку массива, соответствующую позиции указателя
//текущий тип блока
if (kb.IsKeyDown (Keys .W ))
{
UserLayer[(int)PositionBuild.Y, (int)PositionBuild.X] = typeOfBuildBlock;
}
//очищаем массив компонентов
Components.Clear();
//выводим пользовательский уровень
AddSprites(UserLayer);
//уничтожаем указатель и изменяем его в соответствии с типом выбранного блока
BrickBuilder br = null;
if (typeOfBuildBlock == 1)
br = new BrickBuilder(this, ref txtBrBuild1, PositionBuild, recBrick, 64, 16);
if (typeOfBuildBlock == 2)
br = new BrickBuilder(this, ref txtBrBuild2, PositionBuild, recBrick, 64, 16);
if (typeOfBuildBlock == 0)
br = new BrickBuilder(this, ref txtBrBuild0, PositionBuild, recBrick, 64, 16);
Components.Add(br);
//При нажатии клавиши W
//отключаем режим конструктора
//выводим пользовательский уровень
//и начинаем игру
if (kb.IsKeyDown(Keys.E))
{
IsBuild = false;
Components.Clear();
AddSprites(UserLayer);
addBallAndBAts();
currentLayer = -1;
}
}
//Если находимся в режиме игры
//проверим - завершен ли текущий уровень
//признак завершения уровня
if (!IsBuild)
{
IsLayerCompleted();
}
base.Update(gameTime);
}
//Процедура старта конструктора уровней
//очищаем набор компонентов
//Создаем и выводим указатель
public void builder()
{
Components.Clear();
BrickBuilder br = new BrickBuilder (this, ref txtBrBuild1,PositionBuild, recBrick, 64, 16);
Components.Add(br);
}
//Процедура проверки на завершенность
//текущего уровня
//Если на поле отсутствует мяч - игра начинается сначала
//если на поле нет ни одного блока типа Brick1 -
//переход на следующий уровень или переход на первый уровень
//с выводом надписи "Вы выиграли"
public void IsLayerCompleted()
{
//Предположим, что на поле нет ни одного блока
//типа Brick1
bool f = false;
//Предположим, что на поле нет мяча
bool Ball = false;
foreach (gBaseClass spr in this.Components)
{
//Если найден хотя бы один блок типа 1
//установим F в True
if (spr.GetType() == (typeof(Brick1)))
{
f = true;
}
//Если найден мяч - установим Ball в True
if (spr.GetType() == (typeof(Ball)))
{
Ball = true;
}
}
//Если мяч не обнаружен
//игра начинается сначала
if (!Ball)
{
Components.Clear();
currentLayer = 1;
AddSprites(Layer1);
addBallAndBAts();
}
//иначе
else
{
//Если не было найдено ни одного блока
//типа Brick1
if (!f)
{
//Очистим набор компонентов
Components.Clear();
//Увеличим номер уровня
currentLayer++;
//в зависимости от номера
//запустим соответствующий уровень
if (currentLayer == 2)
{
AddSprites(Layer2);
addBallAndBAts();
}
if (currentLayer == 3)
{
AddSprites(Layer3);
addBallAndBAts();
}
//4 - если был пройден 3-й уровень
//0 - при прохождении пользовательского уровня
if (currentLayer == 4 | currentLayer == 0)
{
currentLayer = 1;
AddSprites(Layer1);
addBallAndBAts();
this.Window.Title = "Вы выиграли!";
}
}
}
}
protected override void Draw(GameTime gameTime)
{
spriteBatch.Begin();
//выведем фоновое изображение
spriteBatch.Draw(txtBack, recBack, Color.White);
//Выведем игровые объекты
base.Draw(gameTime);
spriteBatch.End();
}
}
}
В листинге 15.2. приведен код класса gBaseClass.
using System;
using System.Collections.Generic;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Audio;
using Microsoft.Xna.Framework.GamerServices;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using Microsoft.Xna.Framework.Storage;
using Microsoft.Xna.Framework.Content;
namespace P11_1
{
public class gBaseClass : Microsoft.Xna.Framework.DrawableGameComponent
{
Texture2D sprTexture;
public Vector2 sprPosition;
public Rectangle sprRectangle;
public gBaseClass(Game game, ref Texture2D _sprTexture,
Vector2 _sprPosition, Rectangle _sprRectangle, int X_Coord, int Y_Coord)
: base(game)
{
sprTexture = _sprTexture;
//Здесь производится перевод индекса элемента массива
//в координаты на игровом экране
//Так как спрайты имеют различные размеры, для их размещения на экране
//используются коэффициенты X_Coord и Y_Coord
sprPosition.X = _sprPosition.X * X_Coord;
sprPosition.Y = _sprPosition.Y * Y_Coord;
sprRectangle = _sprRectangle;
}
public override void Draw(GameTime gameTime)
{
SpriteBatch sprBatch =
(SpriteBatch)Game.Services.GetService(typeof(SpriteBatch));
sprBatch.Draw(sprTexture, sprPosition, Color.White);
base.Draw(gameTime);
}
}
}
В листинге 15.3. приведен код класса Bat. Он занимается обработкой перемещений биты по горизонтали и контролем за пересечением битой левой и правой границ экрана.
using System;
using System.Collections.Generic;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Audio;
using Microsoft.Xna.Framework.GamerServices;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using Microsoft.Xna.Framework.Storage;
using Microsoft.Xna.Framework.Content;
namespace P11_1
{
public class Bat : gBaseClass
{
Rectangle scrBounds;
//True для движения биты влево и False - вправо
public bool direction;
public Bat(Game game, ref Texture2D _sprTexture,
Vector2 _sprPosition, Rectangle _sprRectangle, int x_c, int y_c)
: base(game, ref _sprTexture, _sprPosition, _sprRectangle, x_c, y_c)
{
scrBounds = new Rectangle(0, 0,
game.Window.ClientBounds.Width,
game.Window.ClientBounds.Height);
direction = true;
}
public override void Update(GameTime gameTime)
{
//Нажатия клавиш-стрелок влево и вправо
//вызывают перемещение объекта в соответствующем
//направлении
KeyboardState kb = Keyboard.GetState();
if (kb.IsKeyDown(Keys.Left))
{
sprPosition.X -= 2;
direction = true;
}
if (kb.IsKeyDown(Keys.Right))
{
sprPosition.X += 2;
direction = false;
}
//проверка на столкновение с границами экрана
Check();
base.Update(gameTime);
}
//Процедура проверки столкновения с левой
//и правой границами экрана
void Check()
{
if (sprPosition.X < scrBounds.Left)
{
sprPosition.X = scrBounds.Left;
}
if (sprPosition.X > scrBounds.Width - sprRectangle.Width)
{
sprPosition.X = scrBounds.Width - sprRectangle.Width;
}
}
}
}
В листинге 15.4. вы можете видеть код класса Ball. Этот класс отвечает за перемещение мяча и за обработку столкновений мяча и других объектов.
using System;
using System.Collections.Generic;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Audio;
using Microsoft.Xna.Framework.GamerServices;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using Microsoft.Xna.Framework.Storage;
using Microsoft.Xna.Framework.Content;
namespace P11_1
{
public class Ball : gBaseClass
{
//для хранения границ экрана
Rectangle scrBounds;
//скорость мяча
public Vector2 speed;
Random rand = new Random();
public Ball(Game game, ref Texture2D _sprTexture,
Vector2 _sprPosition, Rectangle _sprRectangle, int x_c, int y_c)
: base(game, ref _sprTexture, _sprPosition, _sprRectangle, x_c, y_c)
{
speed = new Vector2(1, -1);
scrBounds = new Rectangle(0, 0,
game.Window.ClientBounds.Width,
game.Window.ClientBounds.Height);
}
//Проверка на столкновение с границами экрана
void Check()
{
if (sprPosition.X < scrBounds.Left)
{
sprPosition.X = scrBounds.Left;
RandomiseSpeed();
speed.X *= -1;
}
if (sprPosition.X > scrBounds.Width - sprRectangle.Width)
{
sprPosition.X = scrBounds.Width - sprRectangle.Width;
RandomiseSpeed();
speed.X *= -1;
}
if (sprPosition.Y < scrBounds.Top)
{
sprPosition.Y = scrBounds.Top;
RandomiseSpeed();
speed.Y *= -1;
}
if (sprPosition.Y > scrBounds.Height - sprRectangle.Height)
{
this.Dispose();
}
}
//Проверка на столкновение с объектом
bool IsCollideWithObject(gBaseClass spr)
{
return (this.sprPosition.X + this.sprRectangle.Width > spr.sprPosition.X
this.sprPosition.X < spr.sprPosition.X + spr.sprRectangle.Width
this.sprPosition.Y + this.sprRectangle.Height > spr.sprPosition.Y
this.sprPosition.Y < spr.sprPosition.Y + spr.sprRectangle.Height);
}
//Процедуры случайного изменения скорости при контакте с объектами
//Для всех объектов - скорость может либо возрасти, либо упасть
void RandomiseSpeed()
{
speed.Y += (float)((rand.NextDouble() - rand.NextDouble())*0.2 );
speed.X += (float)((rand.NextDouble() - rand.NextDouble())*0.2 );
}
//Для объектов типа Brick1 скорость лишь растет
void RandomiseSpeedB1()
{
speed.Y += (float)(0.3*Math.Sign(speed.Y) * Math.Abs(((rand.NextDouble() - rand.NextDouble()))));
speed.X += (float)(0.3*Math.Sign(speed.X) * Math.Abs(((rand.NextDouble() - rand.NextDouble()))));
}
//Проверка на столкновения
public void IsCollide()
{
gBaseClass FindObj=null;
Bat FindedBat;
foreach (gBaseClass spr in Game.Components)
{
if (IsCollideWithObject(spr))
{
//Если столкнулись с блоком Brick1
if (spr.GetType() == (typeof(Brick1)))
{
//сохраним найденный объект
FindObj = spr;
//Изменим скорость
RandomiseSpeedB1();
//Обратим скорость
speed *= -1;
}
//Если столкнулись с блоком 2
//обращаем скорость
if (spr.GetType() == (typeof(Brick2)))
{
speed *= -1;
}
//если столкнулись с битой
if (spr.GetType() == (typeof(Bat)))
{
FindedBat =(Bat) spr;
RandomiseSpeed();
//Если бита двигалась влево
//Мяч отразится от нее влево
if (FindedBat.direction)
{
speed.Y = Math.Abs(speed.Y);
speed.X = Math.Abs(speed.X);
speed *= -1;
}
//Если двигалась вправо
//отразится вправо
else
{
speed.Y = Math.Abs(speed.Y);
speed.X = Math.Abs(speed.X);
speed.Y *= -1;
}
}
}
}
//Если был найден блок Block1
if (FindObj != null)
{
//Уничтожить его
FindObj.Dispose();
FindObj = null;
}
}
public override void Update(GameTime gameTime)
{
// TODO: Add your update code here
//Переместить мяч в соответствии со скоростью
this.sprPosition += this.speed;
//Ограничить скорость мяча 1.5 пикселями
if (speed.Y > 2) speed.Y = (float)2;
if (speed.Y < -2) speed.Y = (float)-2;
if (speed.X > 2) speed.X = (float)2;
if (speed.X < -2) speed.X = (float)-2;
//Проверить столкновение с границами
Check();
//проверить столкновение с объектами
IsCollide();
base.Update(gameTime);
}
}
}
Листинг 15.5. содержит коды классов Brick1, Brick2 и BrickBuilder. Эти классы наследованы от gBaseClass и не вносят в его функциональность никаких дополнений.
using System;
using System.Collections.Generic;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Audio;
using Microsoft.Xna.Framework.GamerServices;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using Microsoft.Xna.Framework.Storage;
using Microsoft.Xna.Framework.Content;
namespace P11_1
{
public class Brick1 : gBaseClass
{
public Brick1(Game game, ref Texture2D _sprTexture,
Vector2 _sprPosition, Rectangle _sprRectangle, int x_c, int y_c)
: base(game, ref _sprTexture, _sprPosition, _sprRectangle, x_c, y_c)
{
// TODO: Construct any child components here
}
}
}
using System;
using System.Collections.Generic;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Audio;
using Microsoft.Xna.Framework.GamerServices;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using Microsoft.Xna.Framework.Storage;
using Microsoft.Xna.Framework.Content;
namespace P11_1
{
public class Brick2 : gBaseClass
{
public Brick2(Game game, ref Texture2D _sprTexture,
Vector2 _sprPosition, Rectangle _sprRectangle, int x_c, int y_c)
: base(game, ref _sprTexture, _sprPosition, _sprRectangle, x_c, y_c)
{
// TODO: Construct any child components here
}
}
}
using System;
using System.Collections.Generic;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Audio;
using Microsoft.Xna.Framework.GamerServices;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using Microsoft.Xna.Framework.Storage;
using Microsoft.Xna.Framework.Content;
namespace P11_1
{
public class BrickBuilder : gBaseClass
{
public BrickBuilder(Game game, ref Texture2D _sprTexture,
Vector2 _sprPosition, Rectangle _sprRectangle, int x_c, int y_c)
: base(game, ref _sprTexture, _sprPosition, _sprRectangle, x_c, y_c)
{
// TODO: Construct any child components here
}
}
}
В этой лабораторной работе мы создали игру, предусматривающую несколько игровых уровней и конструктор уровней для пользователя. Как правило, разработка реальных игр включает в себя создание конструктора уровней, с помощью которого разработчик создает игровые уровни. Этот конструктор можно включить в поставку игры, предоставив пользователю возможность самостоятельно создавать новые уровни к игре. Конструкторы уровней так же разнообразны, как сами игры.
В этой лабораторной работе мы создали макет игры, допускающей использование нескольких уровней и содержащей конструктор уровней. Доработайте этот макет. А именно, выполните следующие улучшения исходного проекта:
Для получения официальных документов о завершении программы дополнительного профессионального образования (удостоверения о повышении квалификации, дипломов о профессиональной переподготовке и MBA) необходимо предоставить:
Внимание! Вы можете не заказывать доставку бумажной версии официального документы, а скачать его в электронном виде и распечатать самостоятельно. Информация о выданном документе в течение 1 месяца загружается в Федеральную информационную систему «Федеральный реестр сведений о документах об образовании и (или) о квалификации, документах об обучении» - ФИС ФРДО.
Доступ на новый сайт осуществляется с использованием адреса электронной почты, который был указан вами при регистрации на "старом". Мы постарались перенести все ваши данные с прежнего ресурса, однако не исключена вероятность потери части информации.
При возникновении проблемы со входом, воспользуйтесь функцией сброса пароля
Если вы обнаружите несоответствия, пожалуйста, сообщите нам.