Уроки Коммент.

Создание игр » Featured, Уроки » Создание игры. Игровой уровень.

Создание игры. Игровой уровень.

Игровой Уровень, уровни игрыИгровой уровень это небольшая часть игры, которая, обычно, является небольшой частью всего игрового мира. Практически все игры, в том или ином виде, используют игровые уровни. Разбиение создаваемой игры на уровни позволяет: разбить игру на отдельные независимые части-уровни, уменьшить время загрузки игры, точнее контролировать перемещение игрока по общему игровому миру, выполнять независимый дизайн каждого уровня, контролировать сложность игры (повышение от уровня к уровню), посадить за работу одновременно несколько дизайнеров игровых уровней (каждый дизайнер работает над своим уровнем игры), уменьшить потребление ресурсов игрой (каждый игровой уровень грузит только те данные, которые нужны для этого уровня игры и не более), упростить АИ (требуется обрабатывать только тех монстров, которые находятся на текущем игровом уровне). Раз игровые уровни так удобны – давайте добавим их и в нашу игру.

Из чего состоит игровой уровень

Из каких же компонент состоит игровой уровень? Обычно как минимум из следующих компонент:

  • Описание расположения игровых объектов – враги на уровне, стены и прочие препятствия, игровые бонусы и т.д.
  • Описание целей уровня – как правило, это условия при которых игровой уровень считается завершённым (пройденным).
  • Описание логики работы игрового уровня – чаще всего это скрипты, определяющие игровую логику данного уровня

Естественно, помимо всего этого частью игрового уровня так же считаются разного рода игровые модели, текстуры, звуки и т.д. – в общем всё то, что составляет визуальную и звуковую часть любой игры. Эти компоненты могут быть как уникальными для текущего уровня игры (скажем, уникальный босс в конце уровня), так и общими с другими уровнями (скажем, обычная стенка, астероид, спрайт выстрела или рядовой враг).

Создание игрового уровня

Обычно игровые уровни создаются в специальных программах-редакторах, которые называются редакторами игровых уровней или просто редакторы уровней. Обычно редактирование уровня в таких программах максимально упрощено и автоматизированно, для того, что бы максимально упросить и ускорить создание очередного уровня для игры. Однако, редактора у нас пока нет (позже будет урок по созданию редактора игровых уровней), потому придётся обходиться подручными средствами. Наиболее удобным из таких средств являются те самые xml-файлы, работу с которыми мы рассмотрели в нашем предыдущем уроке “PugiXML: XML-файлы”.

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

Поскольку наш корабль летит “сквозь” игровой уровень, наверное, самым логичным и правильным будет решение размещать врагов, просто задавая их координаты, т.е. положение в пространстве игрового уровня. Получится xml-файл примерно вот такого вида:

<enemy x="100" y="100">Enemy01</enemy>
<enemy x="700" y="100">Enemy01</enemy>
<enemy x="400" y="150">Enemy02</enemy>
// и так далее

Всё, что нам нужно будет сделать для создания игрового уровня – это записать в этот файл координаты всех врагов.

Класс игрового уровня

В данный момент наш игровой уровень должен содержать лишь минимальный набор данных, а именно: данные о расположении наших противников на уровне. Код совсем не сложен:

class CGameLevel
{
	struct LevelObject
	{
		D3DXVECTOR2 m_pos;
		std::string m_strType;
		LevelObject(const std::string& sType, D3DXVECTOR2 pos);
	};
	//! Что бы было удобнее использовать - новый тип
	typedef std::vector<LevelObject> LevelObjectsArray;
	//! вектор со списком объектов на уровне
	LevelObjectsArray m_vecObjects;
 
public:
	CGameLevel(void);
	~CGameLevel(void);
	//! Функция загрузки уровня из файла
	bool Load(const std::string& strFileName);
	//! Получить список объектов в заданном интервале Y
	void GetObjectsInYRange(float fYStart, float fYEnd, 
		LevelObjectsArray& arrOut);
};

Загрузка игрового уровня в игру

Поскольку в предыдущем уроке мы уже научились читать xml-файлы, загрузка уровня из xml-файла не представляет из себя никакой проблемы:

bool CGameLevel::Load( const string& strFileName )
{
	// удаляем всё, что было загружено ранее
	m_vecObjects.clear();
 
	// резервируем место - так быстрее потом будет добавление работать
	m_vecObjects.reserve(4096);
 
	// читаем сожержимое файла
	const string strContents = GetFileAsString(strFileName);
	if (strContents.length()==0)
		return false; // что-то пошло не так
 
	pugi::xml_document doc;
	pugi::xml_parse_result result = doc.load_buffer_inplace(
		(void*)strContents.c_str(), strContents.length());
	if (!result)
		return false; // Ошибка парсинга данных XML
 
	// Парсим документ
	for (pugi::xml_node item = doc.first_child(); item; 
		item = item.next_sibling())
	{
		const char* name = item.name();
		if (strcmp(name, "enemy")==0)
		{
			D3DXVECTOR2 pos(0,0);
 
			// читаем координаты
			stringstream(item.attribute("x").value()) >> pos.x;
			stringstream(item.attribute("y").value()) >> pos.y;
 
			// добавляем врага в список
			m_vecObjects.push_back(
				LevelObject(item.child_value(), pos) );
		}
	}
 
	// функтор сортировки
	struct  sort_by_y
	{
		bool operator() (const CGameLevel::LevelObject& a,
			const CGameLevel::LevelObject& b)
		{ return (a.m_pos.y<b.m_pos.y); }
	} sort_object;
 
	// на всякий случай сортируем объекты по возрастанию Y
	// если они вдруг не были отсортированы в файле
	sort(m_vecObjects.begin(), m_vecObjects.end(), sort_object);
 
	return true;
}

Получение объектов из игрового уровня

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

Каждый кадр игровой уровень смещается в на некоторое расстояние по оси Y, точнее это наша “игровая камера” смещается. Если на предыдущем кадре мы видели часть уровня, которая начинается в какой-то позиции Y_пред_кадра, то в следующем кадре эта позиция будет уже Y_пред_кадра+дельта, где дельта обычно равна скорости нашего корабля.

Итак, напишем функцию получения списка тех врагов, которые станут видимы (появятся в пределах экрана) благодаря этому смещению, она получилась вообще элементарная:

void CGameLevel::GetObjectsInYRange( float fYStart, float fYEnd, 
		LevelObjectsArray& arrOut )
{
	struct filter_by_y
	{
		float m_fYstart, m_fYEnd;
 
		filter_by_y(float fYStart, float fYEnd)
			: m_fYstart(fYStart), m_fYEnd(fYEnd) {}
 
		bool operator() (const CGameLevel::LevelObject& a)
		{ return (a.m_pos.y>=m_fYstart && a.m_pos.y<m_fYEnd); }
	} filter_object(fYStart, fYEnd);
 
	// копируем с использованием бэк-инсёртера
	// без него будет ошибка, т.к. целевой массив,
	// по идее должен быть пустым и там нет места
	// для записи новых элементов
	copy_if(m_vecObjects.begin(), m_vecObjects.end(), 
		back_inserter(arrOut), filter_object);
}

Я хочу сразу уточнить, что этот код будет работать, но при больших размерах уровня (тысячи/десятки тысяч объектов и более) – она начнёт тормозить, т.к. её придётся каждый кадр проверять все объекты в уровне и смотреть, попали ли они в заданный нами промежуток. Пока объектов на уровне не много – нас это мало волнует, но в будущем нам придётся оптимизировать эту функцию.

И алгоритм copy_if не относится (уж не знаю по каким причинам) к стандартным алгоритмам STL, потому я написал его сам:

template<typename ForwardIter,
typename OutputIter, typename UnaryPred>
OutputIter copy_if(ForwardIter begin, ForwardIter end,
				   OutputIter dest, UnaryPred f)
{
	while(begin != end)
	{
		if(f(*begin))
			*dest++ = *begin;
		++begin;
	}
	return dest;
}

На этом с загрузкой самого игрового уровня мы закончили. Исходники пока не прилагаю – они будут в след. уроках, пока же попробуйте сами разобраться в том, что я описал в этой статье – если поймёте, то легко сможете сами внедрить этот код в наши предыдущие исходники и посмотреть как оно будет работать.

»crosslinked«

Ещё по этой теме:




Раздел: Featured, Уроки · Теги: XML, Создание игр

23 комментариев на "Создание игры. Игровой уровень."
  1. Easy пишет:

    Спасибо за уроки! Как раз начал изучать Direct3D
    Но хотелось бы увидеть уроки и 3d :)
    Например мультитекстурирование. Как использовать альфа карты или маски, как их там точно не знаю :)
    А то статьи по ландшафту на русском нашел, но про то как его сделать красивым нет :)
    Ну и по шейдерам.

    1. Вячеслав пишет:

      Спасибо за отзыв! Статьи по 3д скоро будут. И, как раз вместе с ними, будут и статьи и по мульти-текстурированию, и по ландшафтам и т.д. Просто сейчас, к сожалению, очень мало свободного времени и потому писать уроки ежедневно просто нет возможности (((

  2. L-ee-X пишет:

    Хотелось бы увидеть уроки написание 3D движка, на подобие Blitz3D Xors3D, хотя бы увидеть основы как это пишется и реализуется в общем :)

  3. L-ee-X пишет:

    Буду весьма благодарен, если таковы уроки появятся здесь :) Очень хороший сайт, очень хорошие уроки, и все внятно описано… По крайней мере я понял все сразу… :) Спасибо за уроки… :)

    1. Вячеслав пишет:

      Рад, что смог помочь! Уроки по 3д постараюсь сделать как можно раньше – Вы уже не первый, кто пишет мне о их необходимости.

      1. L-ee-X пишет:

        Спасибо!

  4. Вячеслав пишет:

    Спасибо за уроки, только начинаю изучать , очень интересна зd графика-создания движка- структура , как программирования так и взаимосвязь железа, если таковое возможно.Изучаю ZBrush, Max, собираю свою игру.Удачи!!!

  5. Станислав пишет:

    При компиляции Урока-21 вылезает error C3861: ‘back_inserter': identifier not found

    как раз в строке copy_if(…) что-бы посоветовали?

    1. Вячеслав пишет:

      Студия 2010?
      надо сделать: #include <iterator>

      1. Станислав пишет:

        если добавить “Итератор”, то вообще компилятор жалуется на copy_if:
        1>d:\programming\tutorial-21\tutorial2d\gamelevel.cpp(89): error C2668: ‘std::copy_if’ : ambiguous call to overloaded function
        1> c:\program files\microsoft visual studio 10.0\vc\include\algorithm(317): could be ‘_OutIt std::copy_if<std::_Vector_iterator,std::back_insert_iterator,CGameLevel::GetObjectsInYRange::filter_by_y>(_InIt,_InIt,_OutIt,_Pr)’
        1> with
        1> [
        1> _OutIt=std::back_insert_iterator,
        1> _Myvec=std::_Vector_val<CGameLevel::LevelObject,std::allocator>,
        1> _Container=CGameLevel::LevelObjectsArray,
        1> _InIt=std::_Vector_iterator<std::_Vector_val<CGameLevel::LevelObject,std::allocator>>,
        1> _Pr=CGameLevel::GetObjectsInYRange::filter_by_y
        1> ]
        1> d:\programming\tutorial-21\engine\utils.h(21): or ‘OutputIter copy_if<std::_Vector_iterator,std::back_insert_iterator,CGameLevel::GetObjectsInYRange::filter_by_y>(ForwardIter,ForwardIter,OutputIter,UnaryPred)’
        1> with
        1> [
        1> OutputIter=std::back_insert_iterator,
        1> _Myvec=std::_Vector_val<CGameLevel::LevelObject,std::allocator>,
        1> _Container=CGameLevel::LevelObjectsArray,
        1> ForwardIter=std::_Vector_iterator<std::_Vector_val<CGameLevel::LevelObject,std::allocator>>,
        1> UnaryPred=CGameLevel::GetObjectsInYRange::filter_by_y
        1> ]
        1> while trying to match the argument list ‘(std::_Vector_iterator, std::_Vector_iterator, std::back_insert_iterator, CGameLevel::GetObjectsInYRange::filter_by_y)’
        1> with
        1> [
        1> _Myvec=std::_Vector_val<CGameLevel::LevelObject,std::allocator>
        1> ]
        1> and
        1> [
        1> _Myvec=std::_Vector_val<CGameLevel::LevelObject,std::allocator>
        1> ]
        1> and
        1> [
        1> _Container=CGameLevel::LevelObjectsArray
        1> ]

        Думаю обсуждение можно перенести в почту…

  6. Felix пишет:

    Только сегодня наткнулся на данный ресурс, перечитал все, сильно понравилось.
    Хотелось бы узнать, будет ли развитие данной темы (той в которой пишу :)), или не стоит ждать?

    1. Вячеслав пишет:

      Кокретно этой – это какой?
      2д-игр? Будет.
      игровых уровней? Тоже будет )

      1. Алексей пишет:

        Спасибо за урок! Будут ли уроки по созданию файтингу?

  7. Alex-dragon_X пишет:

    Спасибо за урок! Будут ли у вас уроки по созданию 2D файтингов? Есть ли какая-нибудь литература по этой теме?

    1. Вячеслав пишет:

      Насчёт литературы – не знаю, годной не встречал. Но файтинг мало отличается от любой другой игры. Что именно по этой теме Вас интересует?

  8. Алексей пишет:

    Я бы хотел создать игру похожую на Mugen или Mortal Kombat на языке С++, но не знаю, как пошагово её сделать. Совсем недавно начал изучать программирование, благодаря вашему уроку я понял как поэтапно сотздаются игры, но по файтингу остались вопросы: создание меню настроек, анимация бойцов, реакция бойцов на нанесённые им удары и физика.

    1. Вячеслав пишет:

      В принципе, можно попробовать сделать несколько уроков и по файтингу.

  9. Вячеслав пишет:

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

    1. Вячеслав пишет:

      Большое спасибо за ваш отзыв! Я рад, что уроки получились понятными. Новые уроки, надеюсь, скоро появятся…

  10. ALEXANDER пишет:

    Здравствуйте! Сайт жив?что то давно ничего ни кого не видать((а сайт очень даже с полезным материалом))

  11. sooqua пишет:

    Я тоже надеюсь увидеть продолжение @_@

  12. Иван пишет:

    Спасибо большое!!!Подскажите пожалуйста как можно конвертировать const pugi::char_t в int.

Оставить комментарий

*

Вы можете использовать это HTMLтеги и атрибуты: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>