Среда, 15 Августа 2018, 14:38

Приветствую Вас Гость

Меню сайта
Категории каталога
Создание игр [309]
Статьи об общих понятиях связанных с созданием игр.
Программирование [69]
Гайды по программированию на разных ЯП.
Движки и Гейммейкеры [123]
Статьи о программах для создания игр, уроки и описания.
Софт [27]
Различные программы, в том числе в помощь игроделам.
2D-графика [11]
Уроки по рисованию, растр, пиксель-арт, создание спрайтов и пр.
3D-графика [10]
Уроки по моделированию, ландшафт, модели, текстурирование и пр.
Моддинг игр [4]
Модификация компьютерных игр, создание дополнений, перевод, хакинг.
Игры [81]
Статьи об играх, в том числе и сделанных на гейммейкерах.
Разное [63]
Статьи, которые не вошли в определённые разделы.
Наш опрос
Какой жанр игр вам больше нравится играть?
Всего ответов: 2167
Главная » Статьи » Создание игр

XNA для начинающих: создание игрового уровня.



Вступительное слово:


Здравствуйте уважаемые пользователи портала 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);


Теперь добавим в цикл переменную, которая будет хранить значения массива:

Code
int a = map [i, j];


Все. Основной код генератора написан. Теперь просто сделаем проверку значений массива. Для этого мы будем использовать простой условный оператор if. После объявления переменной напишем:



Code

if (a == 1)
{

Box box = new Box(boxTexture1, rect);

}

else if (a == 2)
{

Box box = new Box(boxTexture2, rect);

}

Этим самым мы говорим:

Если в массиве встречается цифра 1, то рисуем блок первого вида, а если цифра 2 – второго.

Основной код написан. Но, есть небольшая загвоздка. Таким образом, координаты блоков будут постоянно изменяться, и рисоваться блоки будут каждый раз в новом месте. Т.е. у нас будет один блок первого вида и один блок второго вида, и они будут изменять свои координаты на экране в процессе пролистывания массива. Но, мы этого не увидим, так как это произойдет мгновенно, и наши блоки просто примут координаты последних значений в массиве. А нам нужно в каждой необходимой позиции нарисовать по блоку.

Для этого мы будем запоминать (точнее записывать) все позиции, и потом нарисуем в каждой из них по блоку.

Вернемся в начало класса Game1 и перед конструктором создадим список из блоков:

Code

List<Box> boxs;  


Теперь в начале метода 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(), добавив туда строку:

Code
CreateMaps();


Если Ваш уровень будет изменяться в процессе игры, тогда добавьте эту строчку и в метод Update().

Все, наш генератор готов! Теперь давай изменим размер экрана.

Размер экрана:

Давайте посчитаем, чему будет равен размер нашего экрана. Наш массив имеет 20 ячеек по горизонтали (20 столбцов) и 11 ячеек по вертикали (11 строк). Так как каждая ячейка имеет размер 30x30 пикселей, то размер нашего экрана будет равен:

600 (20 * 30) x 330 (11 * 30). Т.е. 600x330. Перейдем в конструктор класса Game1 и добавим туда такой код:

Code
graphics.PreferredBackBufferHeight = 330;
graphics.PreferredBackBufferWidth = 600;



Все. Теперь Вы можете запустить игру (F5) (предварительно откомпилируйте проект) и посмотреть на свой уровень.

Теперь Вы можете изменять содержимое массива, за место ноликов ставить единицы или двойки, добавлять новые виды блоков и т.п. Так же Вы можете добавлять новые строки и столбцы, но не забывайте после этого изменять размер игрового окна. Попробуйте изменить Ваш уровень, изменив массив. Короче говоря, экспериментируйте! У меня получилось так:



Теперь давайте добавим в нашу игру больше уровней!

Новые уровни:

Для того чтобы добавить в игру новые уровни, создадим переменную (перед конструктором), и назовем ее NumberMap:


Code
int NumberMap = 1;



Эта переменная будет показывать номер нашего уровня. Вначале она равна одному, так как наш начальный уровень – первый.

Теперь создадим два обработчика данных с клавиатуры. Об этом подробно написано в предыдущей моей статье. Они нам необходимы, чтобы сделать нажатие клавиши однократным (т.е. один раз нажал на клавишу, действие выполнилось один раз). Это мы делаем для того, чтобы нажав на любую клавишу (например «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() такую строку:

Code
CreateMaps();



Полный код класса 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 – обращайтесь. Помогу по мере сил. Надеюсь, что этот урок помог Вам в освоении этой не совсем простой, но очень интересной среды разработки!
Категория: Создание игр | Добавил: Stalker_Shooter (20 Октября 2011) | Автор: Максим
Просмотров: 17897 | Комментарии: 24 | Рейтинг: 4.2/16 |
Теги: Visual C# Express, 2d, C#, xna, Создание уровня, Программирование, массив, для начинающих, карта, редактор карт
Дополнительные опции:
Также если вы считаете, что данный материал мог быть интересен и полезен кому-то из ваших друзей, то вы бы могли посоветовать его, отправив сообщение на e-mail друга:

Игровые объявления и предложения:
Если вас заинтересовал материал «XNA для начинающих: создание игрового уровня.», и вы бы хотели прочесть что-то на эту же тему, то вы можете воспользоваться списком схожих материалов ниже. Данный список сформирован автоматически по тематическим меткам раздела. Предлагаются такие схожие материалы: Если вы ведёте свой блог, микроблог, либо участвуете в какой-то популярной социальной сети, то вы можете быстро поделиться данной заметкой со своими друзьями и посетителями.

Всего комментариев: 24
+0-
24 bicer   (27 Ноября 2013 03:37)
bicerСпасибо, статья очень нужная! А есть примеры рандомной генерации карт?

+0-
22 danielskachkov   (24 Июня 2012 20:19)
danielskachkovСтатья действительно замечательная! Очень мне помогла! Спасибо огромное! Надеюсь, будут еще уроки.

+0-
23 Stalker_Shooter   (24 Июня 2012 21:40)
Stalker_ShooterОбязательно будут. И, если получится, не только по 2D

+0-
20 Maxim1296   (09 Мая 2012 15:50)
Maxim1296а когда будут новые уроки?
например, взаимодействие главного героя с созданной нами картой smile

+0-
21 Stalker_Shooter   (09 Мая 2012 18:19)
Stalker_ShooterПрошу прощения, все нет времени. Планировал урок по физике, чтобы игрок сначала научился прыгать, а уже потом взаимодействовал с картой, но времени не хватает. Постараюсь в скором времени, но ничего не обещаю)

+0-
17 Maxim1296   (28 Марта 2012 11:49)
Maxim1296Спасибо! То что искал (редактор уровней) smile

И у меня вопрос, можно ли на XNA слепить стратегию? Если можно, то как?

+0-
18 Stalker_Shooter   (29 Марта 2012 17:27)
Stalker_ShooterКонечно можно. XNA можно даже другие движки писать, ни то, что стратегию! =) А вот как... Это зависит лишь от Ваших умений. Смотря какую стратегию Вы желаете написать: 2D/3D? 2D, естественно, проще, но и над ней поработать придется. С 3D же вообще не стоит начинать). Я посоветовал бы начать с платформера или TDS, чтобы набраться опыта, так сказать =).

+0-
19 Maxim1296   (29 Марта 2012 22:29)
Maxim1296ну это и так понятно smile

+0-
14 Rollcage   (15 Ноября 2011 18:54)
Вот хорошие статьи по XNA Game Studio только для Xbox 360

+0-
15 allxumuk   (16 Ноября 2011 14:19)
allxumukЧто для Хкоробки оно не страшно, там по сути только управление подправить.

+0-
16 Stalker_Shooter   (17 Ноября 2011 15:12)
Stalker_ShooterЕли мне не изменяет память, то это не статьи, а книга Горнакова. happy Поэтому, проще скачать полную книгу, чем искать инфу на этом сайте.

+0-
9 Йакуд   (31 Октября 2011 08:12)
ЙакудНа сколько мне известно, ключевое слово namespace не создает класс. Она создает область видимости тех переменных, классов, функций, которые находятся внутри тела этого ключевого слова.
Тут целесообразнее было использовать именно class, а не namespace. Ну а так, статья хорошая! Спасибо.

+0-
10 Stalker_Shooter   (02 Ноября 2011 16:19)
Stalker_ShooterСпасибо за полезный отзыв! happy Только я не понял, что Вас смущает? Где именно я использовал namespace для объявления класс?

+2-
11 TideS   (04 Ноября 2011 22:37)
TideSМмм, вроде бы нельзя написать namespace вместо class и наоборот wink

+0-
12 Stalker_Shooter   (04 Ноября 2011 22:50)
Stalker_ShooterПолностью согласен! Namespace - это пространство имен, так называемый сборник всех классов и методов проекта. Только я вроде нигде на писал namespase за место class и наоборот. Если Вы увидели такую ошибку, сообщите. Буду благодарен!

P.S. Как говорилось выше, код проверен и полностью рабочий. Я думаю, компилятор бы точно обругал меня за такую ошибку =)

+1-
13 Amri   (05 Ноября 2011 13:59)
AmriВ классе Game1? Так там все и должно быть.

+1-
7 allxumuk   (25 Октября 2011 10:16)
allxumukПиши ещё biggrin
Как будет свободное время, обязательно попробую выполнить этот урок.

+0-
8 Stalker_Shooter   (25 Октября 2011 21:10)
Stalker_ShooterБлагодарю! Как будет время, обязательно напишу еще happy

+3-
3 GameMix   (21 Октября 2011 15:53)
GameMixКлассная статья! Все понятно и подробно расписано respect

+2-
4 Stalker_Shooter   (21 Октября 2011 17:29)
Stalker_ShooterБлагодарю за хороший отзыв! Я старался сделать все как можно более понятным для новичков happy

+1-
5 TLT   (23 Октября 2011 23:30)
TLTИ правда, очень интересная статья.

+0-
6 Stalker_Shooter   (24 Октября 2011 15:22)
Stalker_ShooterБлагодарю! Рад, что Вам понравилось.

+2-
1 Amri   (20 Октября 2011 22:23)
AmriОтличная статья! То что необходимо!

+1-
2 Stalker_Shooter   (21 Октября 2011 08:14)
Stalker_ShooterСпасибо за хороший отзыв! happy

Добавлять комментарии могут только зарегистрированные пользователи.
[ Регистрация | Вход ]
Поиск по сайту
10 случ. движков
  • Intersect Engine
  • SpriteCraft
  • Bitsy
  • Wintermute Engine
  • RPG Maker VX ACE
  • UkiRAD
  • Tombstone Engine
  • 001 Game Creator
  • ZDaemon
  • J.U.R.P.E.
  • Друзья сайта
    Игровой форум GFAQ.ru Перевод консольных игр
    Все права сохранены. GcUp.ru © 2008-2018 Рейтинг