Вступительное слово:
Здравствуйте уважаемые пользователи портала GcUp.Ru! В этой статье я расскажу Вам, как создать свой игровой уровень в XNA GameStudio. Весь приведенный ниже код проверен, и отлично функционирует в версии 4.0. Но, так же он должен работать и в других версиях (3.0 и 3.1; на счет 2.0 не уверен). Перед знакомством с этим уроком рекомендую прочитать мои предыдущие статьи, так как этот урок является их продолжением, а это значит, что Вам понадобятся те знания, которые Вы получили из этих статей.
Так же перед прочтением данного урока Вы должны обладать базовыми знаниями языка программирования C#, и иметь программу Microsoft Visual C# Express необходимой Вам версии (для XNA 3.1 - Microsoft Visual C# Express 2008, а для XNA 4.0 - Microsoft Visual C# Express 2010) с установленной библиотекой XNA GameStudio.
Итак, если Вы имеете все вышеприведенное, тогда приступим!
Немного теории:
Структура уровня:
Для начала давайте разберемся с тем, что же такое игровой уровень, и из чего он состоит. Игровой уровень (или игровая карта) — это отдельная область виртуального мира игры. Чаще всего он представляет собой определённую локацию, например, здание или город. Уровень любого платформера (а в нашем случае мы создаем именно его) состоит из блоков. Весь мир такой игры поделен на отдельные участки. Например, на этом скриншоте изображен такой уровень:
Он, как уже говорилось, поделен на участки. В каждом участке нарисован или не нарисован определенный вид блока. Представим этот уровень в виде сетки:
Каждая ячейка сетки является таким участком. В каждой из них мы можем нарисовать или не нарисовать свой блок.
Как Вы, наверное, догадались, именно по такому принципу мы будем строить наш уровень. Мы разделим наше игровое окно на ячейки и добавим в некоторые из них текстуры наших блоков. Таким образом, мы построим наш игровой уровень.
Преимуществом такого принципы является то, что Вы сможете стоить любые уровни буквально за пару минут. В этом нам поможет наш генератор.
Генератор (редактор) уровней:
Для того чтобы строить свои карты, мы создадим небольшой генератор. Он будет представлять собой массив, который содержит информацию о карте, и цикл, которым мы будем рисовать объекты из массива. Рассмотрим это на примере. Предположим, нам нужно нарисовать свой уровень. Мы создаем двумерный массив, в который записываем такие значения:
Code int[,] map = new int[, ] {{2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2}, {2,0,0,0,0,0,0,0,0,0,2,2,2,2,2,2,2,0,0,2}, {2,0,0,2,2,2,2,2,0,0,0,0,0,0,0,0,0,0,0,2}, {2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2}, {2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2}, {2,0,0,0,0,0,0,0,0,0,2,2,2,2,0,0,0,0,0,2}, {2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2}, {2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2}, {2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,2}, {2,0,0,1,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,2}, {1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1}};
Затем мы пишем в цикле, что каждая ячейка массива равна 30-ти пикселям (в моем случае), и что если в массиве встречается число 1, то на экране мы должны изобразить блок земли. Если же встречается число 2, тогда мы изображаем простой блок камня. А если число 0, тогда мы вообще ничего не изображаем. В итоге у нас на экране получится такой результат (фон я рисовал отдельно; это описано в моей первой статье):
Как Вы видите, ничего сложного!
Итак, если Вы разобрались с теорией, тогда давайте переходить к практике!
Создание класса для рисования блока:
Для начала давайте создадим новый проект (как это сделать, подробно описывалось в моей первой статье) и назовем его Maps.
Теперь давайте создадим класс (как это сделать, описывалось во второй статье), который поможет нам в создании объектов (блоков) и их рисовании. Назовем его Box.
После создания наш класс имеет такой вид:
Code using System; using System.Collections.Generic; using System.Linq; using System.Text;
namespace Maps { class Box { } }
Теперь добавим необходимые переменные. Как Вы помните, для рисования спрайта нам нужны текстура этого спрайта и его позиция. Создадим две переменные:
Code Texture2D Texture; Rectangle Position;
Как Вы видите, нам не хватает пространств имен (это описывалось в моей статье). Нажмем сочетание клавиш «Alt+Shift+F10» и выберем самое первое. Оно добавиться. Проделаем это для обоих переменных.
Теперь создадим конструктор класса и присвоим в нем значения нашим переменным:
Code public Box(Texture2D texture, Rectangle rect) { this.Texture = texture; this.Position = rect; }
Итак, теперь мы можем создать объект нашего класса. Осталось только сделать метод для рисования. После конструктора добавьте код:
Code public void Draw(SpriteBatch spriteBatch) { spriteBatch.Draw(Texture, Position, Color.White); }
Тем самым мы создали метод, в котором описали рисование всех блоков на экране. Это нам поможет в дальнейшем.
Полный код класса Box выглядит так:
Code using System; using System.Collections.Generic; using System.Linq; using System.Text; using Microsoft.Xna.Framework; using Microsoft.Xna.Framework.Graphics;
namespace Maps { class Box { Texture2D Texture; Rectangle Position;
public Box(Texture2D texture, Rectangle rect) { this.Texture = texture; this.Position = rect; }
public void Draw(SpriteBatch spriteBatch) { spriteBatch.Draw(Texture, Position, Color.White); } } }
Итак, теперь перейдем в класс Game1 и создадим наш генератор.
Создание генератора (редактора) уровней:
Для начала в классе Game1 добавим переменные для текстур наших блоков. Перед конструктором запишем:
Code Texture2D boxTexture1; Texture2D boxTexture2;
Теперь загрузим эти текстуры в папку Content и назовем их box1 и box2. А в метод LoadContent() добавим такие строки:
Code boxTexture1 = Content.Load<Texture2D>("box1"); boxTexture2 = Content.Load<Texture2D>("box2");
Итак, теперь мы можем приступать к самой главной части статьи, а именно к созданию генератора!
После метода LoadContent() создайте метод CreateMaps():
Code void CreateMaps() {
}
В нем создадим наш двумерный целочисленный массив под названием map и заполним его необходимыми значениями:
Code map = new int[,] {{2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2}, {2,0,0,0,0,0,0,0,0,0,2,2,2,2,2,2,2,0,0,2}, {2,0,0,2,2,2,2,2,0,0,0,0,0,0,0,0,0,0,0,2}, {2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2}, {2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2}, {2,0,0,0,0,0,0,0,0,0,2,2,2,2,0,0,0,0,0,2}, {2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2}, {2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2}, {2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,2}, {2,0,0,1,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,2}, {1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1}};
Теперь создадим переменные, которые будут переводить ячейки массива в координаты на экране. Назовем их x и y:
Так как мы начинаем строить уровень с самого начала, то сейчас они равны нулю.
Теперь давайте создадим цикл, с помощью которого мы будем листать наш массив:
Code for (int i = 0; i < map.GetLength(0); i++) { for (int j = 0; j < map.GetLength(1); j++) { } }
Так как эту статью могут читать новички, которые еще не очень хорошо ориентируются в массивах, я более подробно остановлюсь на это участке кода. Как Вы поняли, наш массив представляет собой таблицу, которая имеет строки и столбцы.
Первым циклом for мы пролистываем строки, а вторым – столбцы. На рисунке строки изображены по вертикали (сверху вниз), а столбцы – по горизонтали (слева направо). Например, красная линия – это строка, а синяя – столбец.
Т.е. с самого начала наша переменная i (после операции «++») принимает значение 1, т.е. начинается первая строка.
В этой строке проверяется каждый столбец (т.е. управление переходит внутреннему циклу), с первого квадратика (ячейки) по двадцатый. Потом управление опять переходит к первому циклу, и проверяется вторая строка.
Таким образом, мы проверяем весь массив.
Теперь давайте переведем ячейки массива в координаты.
Так как каждая клетка в моей игре равна 30 пикселей (это зависит от размеров ваших блоков), то при проверке столбцов мы будем прибавлять к переменной x по 30 пикселей. Т.е. после проверки каждого столбца наша переменная будет увеличиваться на 30. Это поможет нам, когда мы будем указывать нашим блокам в качестве координаты оси оX переменную x.
Например, когда проверка массива дойдет в первой строке до пятого столбца (к примеру) и обнаружит там цифру 2, то переменная x к тому моменту будет равна 150 пикселей (каждый раз, в течение пяти проверок, прибавлять по 30). Соответственно, мы нарисуем наш блок по координатам X = 150. (Y будет равен нулю, так как это только первая строка).
Соответственно, после того, как строка закончится, и начнется проверка следующей строки, то наша X координата снова становится равна нулю (чтобы опять начать увеличиваться до двадцати), а переменная Y увеличивается на 30 (так как новая строка, это новый ряд блоков, высота которых так же равна 30 пикселей).
Следуя всему вышесказанному, изменим наш цикл таким образом:
Code for (int i = 0; i < map.GetLength(0); i++) { for (int j = 0; j < map.GetLength(1); j++) { x += 30; }
x = 0; y += 30; }
Теперь в цикле создадим прямоугольник, координатами которого будут являться переменные x и y, а размер его будет равен размеру одной клетки (или одного блока). У меня это 30x30.
Соответственно, в цикле запишем:
Code Rectangle rect = new Rectangle(x, y, 30, 30);
Теперь добавим в цикл переменную, которая будет хранить значения массива:
Все. Основной код генератора написан. Теперь просто сделаем проверку значений массива. Для этого мы будем использовать простой условный оператор if. После объявления переменной напишем:
Code if (a == 1) {
Box box = new Box(boxTexture1, rect); }
else if (a == 2) {
Box box = new Box(boxTexture2, rect); }
Этим самым мы говорим:
Если в массиве встречается цифра 1, то рисуем блок первого вида, а если цифра 2 – второго.
Основной код написан. Но, есть небольшая загвоздка. Таким образом, координаты блоков будут постоянно изменяться, и рисоваться блоки будут каждый раз в новом месте. Т.е. у нас будет один блок первого вида и один блок второго вида, и они будут изменять свои координаты на экране в процессе пролистывания массива. Но, мы этого не увидим, так как это произойдет мгновенно, и наши блоки просто примут координаты последних значений в массиве. А нам нужно в каждой необходимой позиции нарисовать по блоку.
Для этого мы будем запоминать (точнее записывать) все позиции, и потом нарисуем в каждой из них по блоку.
Вернемся в начало класса Game1 и перед конструктором создадим список из блоков:
Теперь в начале метода CreateMaps() инициализируем его:
Code boxs = new List<Box>();
И при проверке, в цикле, будем добавлять в этот список все нарисованные блоки. Изменим код:
Code if (a == 1) {
Box box = new Box(boxTexture1, rect); }
else if (a == 2) {
Box box = new Box(boxTexture2, rect); }
На:
Code if (a == 1) {
Box box = new Box(boxTexture1, rect); boxs.Add(box); }
else if (a == 2) {
Box box = new Box(boxTexture2, rect); boxs.Add(box); }
Все, мы на «финишной прямой». Теперь перейдем в метод Draw() и добавим в него такой код:
Code spriteBatch.Begin(); foreach (Box box in boxs) { box.Draw(spriteBatch); }
spriteBatch.End();
Здесь мы циклом foreach пролистываем наш список, и ищем блоки. После чего рисуем их.
Осталось только запустить наш метод в методе LoadContent(), добавив туда строку:
Если Ваш уровень будет изменяться в процессе игры, тогда добавьте эту строчку и в метод Update().
Все, наш генератор готов! Теперь давай изменим размер экрана.
Размер экрана:
Давайте посчитаем, чему будет равен размер нашего экрана. Наш массив имеет 20 ячеек по горизонтали (20 столбцов) и 11 ячеек по вертикали (11 строк). Так как каждая ячейка имеет размер 30x30 пикселей, то размер нашего экрана будет равен:
600 (20 * 30) x 330 (11 * 30). Т.е. 600x330. Перейдем в конструктор класса Game1 и добавим туда такой код:
Code graphics.PreferredBackBufferHeight = 330; graphics.PreferredBackBufferWidth = 600;
Все. Теперь Вы можете запустить игру (F5) (предварительно откомпилируйте проект) и посмотреть на свой уровень.
Теперь Вы можете изменять содержимое массива, за место ноликов ставить единицы или двойки, добавлять новые виды блоков и т.п. Так же Вы можете добавлять новые строки и столбцы, но не забывайте после этого изменять размер игрового окна. Попробуйте изменить Ваш уровень, изменив массив. Короче говоря, экспериментируйте! У меня получилось так:
Теперь давайте добавим в нашу игру больше уровней!
Новые уровни:
Для того чтобы добавить в игру новые уровни, создадим переменную (перед конструктором), и назовем ее NumberMap:
Эта переменная будет показывать номер нашего уровня. Вначале она равна одному, так как наш начальный уровень – первый.
Теперь создадим два обработчика данных с клавиатуры. Об этом подробно написано в предыдущей моей статье. Они нам необходимы, чтобы сделать нажатие клавиши однократным (т.е. один раз нажал на клавишу, действие выполнилось один раз). Это мы делаем для того, чтобы нажав на любую клавишу (например «F») мы смогли перейти на следующий уровень. Реализуем мы это таким образом.
При каждом нажатии клавиши мы будем прибавлять к переменной NumberMap по одному. И создадим логическое условие:
Если NumberMap = 1, то включен первый уровень, если NumberMap = 2 – то второй.
Но, если сделать обычную обработку данных с клавиатуры, как мы делали в предыдущих уроках, то при нажатии клавиши значение переменной будет увеличиваться очень быстро, в течение того времени, пока мы держим клавишу нажатой. Это не подходит для нашего случая, поэтому мы введем два обработчика событий. Один из которых будет состоянием клавиатуры в настоящее время, а второй – состояние клавиатуры в прошлом.
И если в предыдущем состоянии клавиша была отпущена, а в настоящем – нажата, тогда нужно увеличивать значение переменной NumberMap на один. Для этого добавим перед конструктором строки:
Code KeyboardState keys; KeyboardState Oldkeys;
Теперь добавим в метод Update такой код:
Code keys = Keyboard.GetState();
if (keys.IsKeyDown(Keys.F) && Oldkeys.IsKeyUp(Keys.F)) { NumberMap++;
}
Oldkeys = keys;
Этим самым мы сделали нажатие клавиши «F» одиночным, т.е. нажав на эту клавишу, сколько бы мы ее не держали, значение переменной увеличится лишь раз (за одно нажатие). Теперь переходим в метод CreateMaps(). Давайте в нем сделаем условие выбора уровня, как говорилось выше. Для этого заменим код этого метода на такой:
Code boxs = new List<Box>();
int[,] map;
if (NumberMap == 1) { map = new int[,] {{2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2}, {2,0,0,0,0,0,0,0,0,0,2,2,2,2,2,2,2,0,0,2}, {2,0,0,2,2,2,2,2,0,0,0,0,0,0,0,0,0,0,0,2}, {2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2}, {2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2}, {2,0,0,0,0,0,0,0,0,0,2,2,2,2,0,0,0,0,0,2}, {2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2}, {2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2}, {2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,2}, {2,0,0,1,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,2}, {1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1}};
int x = 0; int y = 0;
for (int i = 0; i < map.GetLength(0); i++) { for (int j = 0; j < map.GetLength(1); j++) { Rectangle rect = new Rectangle(x, y, 30, 30); int a = map[i, j];
if (a == 1) {
Box box = new Box(boxTexture1, rect); boxs.Add(box); }
else if (a == 2) {
Box box = new Box(boxTexture2, rect); boxs.Add(box); } x += 30; }
x = 0; y += 30; } }
else if (NumberMap == 2) {
map = new int[,] {{1,1,1,1,1,1,1,1,1,1,1,1,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,2,0,0,0,0,0,0,0}, {0,0,0,0,0,0,1,0,0,0,0,2,2,2,0,0,0,0,0,0}, {0,0,0,0,0,1,1,0,0,0,2,2,2,2,2,0,0,0,0,0}, {0,0,0,0,1,1,1,0,0,0,0,2,2,2,0,0,0,0,0,0}, {0,0,0,1,1,1,1,0,0,0,0,0,2,0,0,0,0,0,0,0}, {0,0,1,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0}, {0,1,1,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0}, {1,1,1,1,1,1,1,1,0,1,0,1,0,1,0,1,0,1,0,1}, {1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1}}; int x = 0; int y = 0;
for (int i = 0; i < map.GetLength(0); i++) { for (int j = 0; j < map.GetLength(1); j++) { Rectangle rect = new Rectangle(x, y, 30, 30); int a = map[i, j];
if (a == 1) {
Box box = new Box(boxTexture1, rect); boxs.Add(box); }
else if (a == 2) {
Box box = new Box(boxTexture2, rect); boxs.Add(box); } x += 30; }
x = 0; y += 30; } }
else if (NumberMap == 3) {
map = new int[,] {{1,1,1,1,1,1,1,1,1,1,1,1,1,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,1}, {1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1}, {1,0,0,0,0,0,0,0,0,0,0,0,1,1,0,0,0,0,0,1}, {1,0,0,0,0,0,0,0,1,1,0,0,1,0,0,0,0,0,0,1}, {1,2,2,0,0,2,2,0,1,0,1,0,1,0,0,0,2,0,0,1}, {1,0,2,0,0,2,0,0,1,0,0,1,1,0,0,2,0,2,0,1}, {1,0,0,2,2,0,0,0,1,0,0,0,1,0,0,2,2,2,0,1}, {1,0,2,0,0,2,0,0,1,0,0,0,1,0,2,0,0,0,2,1}, {1,2,2,0,0,2,2,0,0,0,0,0,0,0,2,0,0,0,2,1}, {1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1}};
int x = 0; int y = 0;
for (int i = 0; i < map.GetLength(0); i++) { for (int j = 0; j < map.GetLength(1); j++) { Rectangle rect = new Rectangle(x, y, 30, 30); int a = map[i, j];
if (a == 1) {
Box box = new Box(boxTexture1, rect); boxs.Add(box); }
else if (a == 2) {
Box box = new Box(boxTexture2, rect); boxs.Add(box); } x += 30; }
x = 0; y += 30; } }
Таким образом, можно строить абсолютно любые уровни до бесконечности, просто добавляя к условию if новую ветку else if и изменяя массив. Осталось только объявить наш метод в методе Update (так как теперь этот метод будет изменяться в течении игры). Для этого добавьте с конец метода Update() такую строку:
Полный код класса Gmae1 выглядит так:
Code using System; using System.Collections.Generic; using System.Linq; 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.Media;
namespace Maps { /// <summary> /// This is the main type for your game /// </summary> public class Game1 : Microsoft.Xna.Framework.Game { GraphicsDeviceManager graphics; SpriteBatch spriteBatch;
Texture2D boxTexture1; Texture2D boxTexture2;
int NumberMap = 1;
List<Box> boxs;
KeyboardState keys; KeyboardState Oldkeys;
public Game1() { graphics = new GraphicsDeviceManager(this); Content.RootDirectory = "Content";
graphics.PreferredBackBufferHeight = 330; graphics.PreferredBackBufferWidth = 600; }
/// <summary> /// Allows the game to perform any initialization it needs to before starting to run. /// This is where it can query for any required services and load any non-graphic /// related content. Calling base.Initialize will enumerate through any components /// and initialize them as well. /// </summary> protected override void Initialize() { // TODO: Add your initialization logic here
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() { // Create a new SpriteBatch, which can be used to draw textures. spriteBatch = new SpriteBatch(GraphicsDevice);
boxTexture1 = Content.Load<Texture2D>("box1"); boxTexture2 = Content.Load<Texture2D>("box2");
// TODO: use this.Content to load your game content here CreateMaps(); }
void CreateMaps() {
boxs = new List<Box>();
int[,] map;
if (NumberMap == 1) { map = new int[,] {{2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2}, {2,0,0,0,0,0,0,0,0,0,2,2,2,2,2,2,2,0,0,2}, {2,0,0,2,2,2,2,2,0,0,0,0,0,0,0,0,0,0,0,2}, {2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2}, {2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2}, {2,0,0,0,0,0,0,0,0,0,2,2,2,2,0,0,0,0,0,2}, {2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2}, {2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2}, {2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,2}, {2,0,0,1,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,2}, {1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1}};
int x = 0; int y = 0;
for (int i = 0; i < map.GetLength(0); i++) { for (int j = 0; j < map.GetLength(1); j++) { Rectangle rect = new Rectangle(x, y, 30, 30); int a = map[i, j];
if (a == 1) {
Box box = new Box(boxTexture1, rect); boxs.Add(box); }
else if (a == 2) {
Box box = new Box(boxTexture2, rect); boxs.Add(box); } x += 30; }
x = 0; y += 30; } }
else if (NumberMap == 2) {
map = new int[,] {{1,1,1,1,1,1,1,1,1,1,1,1,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,2,0,0,0,0,0,0,0}, {0,0,0,0,0,0,1,0,0,0,0,2,2,2,0,0,0,0,0,0}, {0,0,0,0,0,1,1,0,0,0,2,2,2,2,2,0,0,0,0,0}, {0,0,0,0,1,1,1,0,0,0,0,2,2,2,0,0,0,0,0,0}, {0,0,0,1,1,1,1,0,0,0,0,0,2,0,0,0,0,0,0,0}, {0,0,1,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0}, {0,1,1,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0}, {1,1,1,1,1,1,1,1,0,1,0,1,0,1,0,1,0,1,0,1}, {1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1}}; int x = 0; int y = 0;
for (int i = 0; i < map.GetLength(0); i++) { for (int j = 0; j < map.GetLength(1); j++) { Rectangle rect = new Rectangle(x, y, 30, 30); int a = map[i, j];
if (a == 1) {
Box box = new Box(boxTexture1, rect); boxs.Add(box); }
else if (a == 2) {
Box box = new Box(boxTexture2, rect); boxs.Add(box); } x += 30; }
x = 0; y += 30; } }
else if (NumberMap == 3) {
map = new int[,] {{1,1,1,1,1,1,1,1,1,1,1,1,1,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,1}, {1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1}, {1,0,0,0,0,0,0,0,0,0,0,0,1,1,0,0,0,0,0,1}, {1,0,0,0,0,0,0,0,1,1,0,0,1,0,0,0,0,0,0,1}, {1,2,2,0,0,2,2,0,1,0,1,0,1,0,0,0,2,0,0,1}, {1,0,2,0,0,2,0,0,1,0,0,1,1,0,0,2,0,2,0,1}, {1,0,0,2,2,0,0,0,1,0,0,0,1,0,0,2,2,2,0,1}, {1,0,2,0,0,2,0,0,1,0,0,0,1,0,2,0,0,0,2,1}, {1,2,2,0,0,2,2,0,0,0,0,0,0,0,2,0,0,0,2,1}, {1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1}};
int x = 0; int y = 0;
for (int i = 0; i < map.GetLength(0); i++) { for (int j = 0; j < map.GetLength(1); j++) { Rectangle rect = new Rectangle(x, y, 30, 30); int a = map[i, j];
if (a == 1) {
Box box = new Box(boxTexture1, rect); boxs.Add(box); }
else if (a == 2) {
Box box = new Box(boxTexture2, rect); boxs.Add(box); } x += 30; }
x = 0; y += 30; } }
}
/// <summary> /// UnloadContent will be called once per game and is the place to unload /// all content. /// </summary> protected override void UnloadContent() { // TODO: Unload any non ContentManager content here }
/// <summary> /// Allows the game to run logic such as updating the world, /// checking for collisions, gathering input, and playing audio. /// </summary> /// <param name="gameTime">Provides a snapshot of timing values.</param> protected override void Update(GameTime gameTime) { // Allows the game to exit if (GamePad.GetState(PlayerIndex.One).Buttons.Back == ButtonState.Pressed) this.Exit();
keys = Keyboard.GetState();
if (keys.IsKeyDown(Keys.F) && Oldkeys.IsKeyUp(Keys.F)) { NumberMap++;
}
Oldkeys = keys; // TODO: Add your update logic here
CreateMaps();
base.Update(gameTime); }
/// <summary> /// This is called when the game should draw itself. /// </summary> /// <param name="gameTime">Provides a snapshot of timing values.</param> protected override void Draw(GameTime gameTime) { GraphicsDevice.Clear(Color.CornflowerBlue);
spriteBatch.Begin(); foreach (Box box in boxs) { box.Draw(spriteBatch); }
spriteBatch.End();
base.Draw(gameTime); } } }
В итоге у меня получились такие уровни:
Послесловие:
Если Вы читаете эту заключительную часть статьи, то хочу поблагодарить Вас за то, что Вы не пожалели своего времени и все же прочитали этот урок. Если у Вас будут какие-то проблемы с созданием уровней в XNA – обращайтесь. Помогу по мере сил. Надеюсь, что этот урок помог Вам в освоении этой не совсем простой, но очень интересной среды разработки! |