Game Maker: делаем игры своими руками Глава 9-12

Posted by Anuriel on декабря 30, 2003 - 00:00

Глава 9: Создание образцов объектов
Наконец Мышь, к которой все относились с почтением, закричала:
— Садитесь, все садитесь и слушайте. Вы у меня вмиг высохнете.
Все послушно уселись в круг, а Мышь стала посередине.
Л. Кэролл "Алиса в стране Чудес". 

Мы уже научились производить сложные (и не очень) математические расчеты. Пришло время показать, зачем же они нужны. Первым делом узнаем (или вспомним, тут уж все зависит от вашего опыта), как создавать образцы объектов в комнате. Заодно мы выучим, как делать проверку нажатия клавиши, и узнаем, что такое таймер. За дело! 
Идея следующая: мы создадим объект, который будет создавать одни объекты через определенные промежутки времени и другие объекты, если мы будем нажимать на определенную клавишу. Объяснения я буду давать по ходу. 

Запускаем GM, загружаем два спрайта (какие вам понравятся). Называем их s_sprite1 и s_sprite2 соответственно. Создаем комнату с именем r_room и три объекта: o_controller, o_object1 (для него назначаем спрайт s_sprite1), o_object2 (для него назначаем спрайт s_sprite2). Помещаем объект o_controller в комнату r_room. Все, приготовления закончены — переходим к написанию кода.
Сперва сделаем так, чтобы при нажатии на пробел в комнате создавался объект o_object1 в точке с произвольными координатами. Для этого в событие шага (step) объекта o_controller помещаем следующий блок кода:

{
	if (keyboard_key=vk_space)
	{
		xa = random(room_width);
	ya = random(room_height);
		instance_create(xa,ya,o_object1);
		io_c
lear();
	}
 }

Объясняю, как это работает. Объект o_controller непрерывно проверяет, нажата клавиша "пробел" или нет. Если нажата, то переменной xa присваивается случайное значение, лежащее в промежутке от 0 до значения ширины комнаты. То же самое происходит для переменной ya, только значение выбирается по вертикали. Таким образом, с помощью переменных xa и ya мы определяем координату точки, которая лежит в пределах видимости комнаты. Затем мы непосредственно создаем в этой точке объект o_object1, а после этого с помощью процедуры io_clear() очищаем состояние клавиши "пробел", т.е. при нажатии на клавишу в текущий момент создается только один объект. Вот, однако, работает. Теперь сделаем так, чтобы в комнате через равные промежутки времени создавался объект o_object2. Для этого в событие создания объекта o_controller поместим следующий код:

{
	alarm[0] = 30;
 }

А в событие Alarm0, т.е. активизации первого таймера, вот такой блок кода:

{
		xb = random(room_width);
		yb = random(room_height);
		in
stance_create(xb,yb,o_object2);
		alarm[0] = 30;
 }

Работает это следующим образом. При создании объекта o_controller активизируется его первый таймер. Для того чтобы было понятнее, представьте себе будильник, на котором есть только секундная стрелка и стрелка звонка. Вы заводите его — и через определенное время он звонит. По такому же принципу работает таймер. Строка кода "alarm[0] = 30;" означает, что для первого таймера объекта o_controller мы устанавливаем значение 30 — по прошествии 30 шагов в игре он "зазвонит", т.е. активизируется. Действия, которые при этом выполнятся, содержатся в событии Alarm0, а именно: по аналогии с объектом o_object1 переменным xb и yb присваиваются случайные значения, а затем в точке с этими координатами появляется объект o_object2. С помощью строки "alarm[0] = 30;" мы заново активизируем первый таймер, т.е. через 30 шагов эти действия снова выполнятся. По сути, с помощью этой строки мы просто зацикливаем таймер, и действия, определенные для него, будут происходить бесконечно. Если бы в событии Alarm0 мы не прописали строку "alarm[0] = 30;", то объект o_object2 создался бы только один раз.

Глава 10: Настройка внешнего вида сообщений
— Ах, все равно, — быстро сказала Алиса. — Только, знаете, так неприятно все время меняться...
— Не знаю, — отрезала Гусеница.
Л. Кэролл "Алиса в стране Чудес".

Вы, наверное, замечали, что иногда внешний вид стандартных сообщений просто не вписывается в игру. Чтобы исправить это, существует ряд функций, которые мы сейчас и рассмотрим. Итак, создаем новый файл. Загружаем в него спрайт, который будет выполнять роль кнопки, а также картинку для заднего фона сообщения. Называем их соответственно s_button и b_back. Далее создаем объект o_object и помещаем его в созданную комнату r_room. Теперь в событии создания объекта o_object пишем следующее:

{
	message_text_font('Arial',14,c_blue, 
fs_normal);
	message_button_font('Ar
ial',10,c_black, fs_bold);
	message_mouse_color(c_blue);
	message_size(320,240);
	message_caption(1,'Это наше сообщение!');
	message_background(b_back);
	m
essage_button(s_button);
	show_message('Вот и пример!');
 }

Что же мы здесь сделали? А вот что. С помощью процедуры message_text_font() мы определили, что текст сообщения будет написан шрифтом Arial размером кегля, равным четырнадцати, синим цветом и нормальным стилем. Далее, с помощью процедуры message_button_font () мы определили шрифт и его параметры для текста, который будет написан на кнопке. С помощью процедуры message_mouse_ color() мы указали, какого цвета должен стать шрифт текста, написанного на кнопке, когда мы наводим на кнопку курсор. Процедура message_size() позволила нам задать размер появляющегося сообщения, т.е. оно оказалось 320 пикселей в ширину и 240 в высоту. Процедура message_caption() помогла установить заголовок для окна сообщения. Единица в качестве параметра определила, что заголовок следует показывать. Процедуры message_background() и message_button() определили спрайты, использующиеся для заднего фона и для кнопки сообщения соответственно. Ну, а затем с помощью процедуры show_message() мы сделали тест, т.е. показали подобающе настроенное сообщение.

Глава 11: Сохранение и загрузка игры в\из определенного файла
— Держи! — крикнула вдруг Герцогиня и швырнула Алисе младенца. Л. Кэролл "Алиса в стране Чудес".

Что ж, иногда бывает полезным осуществить не просто сохранение игры, а сохранить игру в файл с определенным именем, например, для того, чтобы разные игроки могли использовать лично свои прохождения. Для этих целей мой партнер Карл Густаффсон написал два скрипта, которые я и хочу продемонстрировать вам. 
Скрипт, который сохраняет игру:

{
	SaveGameName = "";
	SaveGameName = get_save_filename("Sa-ve Game
s (*.svg)|*.svg", "TestSave.svg");
	if (SaveGameName != "")
<tab>{
		if (string_copy(SaveGameName,string_ length(SaveGameName) — 3, 4) != ".svg
")
		{
			SaveGameName = SaveGameName + ".svg";
	}
		game_save(SaveGameName);
	 }
 }

Объясню, как он работает. Переменная SaveGameName содержит имя файла, в который следует сохранить текущую игру. Для начала присваиваем ей пустое значение. Затем с помощью процедуры get_save_filename() запрашиваем у пользователя имя файла, в который мы хотим сохранить нашу игру. В данном примере в качестве расширения сохраняемого файла используется svg. Естественно, вы можете заменить это расширение своим. Далее с помощью конструкции if мы проверяем, содержит ли переменная SaveGameName значение. Если нет, то ничего не происходит, поскольку пользователь не ввел имя сохраняемой игры. Если переменная все же содержит значение, то перед тем, как сохранить игру в указанный файл, надо проверить, правильно ли указано его расширение. Для этого мы используем вторую вложенную конструкцию if. С помощью функции string_copy () мы проверяем, равняются ли последние четыре символа переменной SaveGameName строке ".svg", то есть, указано ли соответствующее расширение. Если нет, то к строке, содержащейся в переменной SaveGa-meName, добавляется расширение файла, а затем игра сохраняется в файл с именем, идентичным тому, что содержится в строковой переменной SaveGameName.
Когда вы помещаете данный скрипт в раздел Scripts, то вы даете ему имя, например SaveGameSript. Чтобы запустить его, просто наберите в соответствующем блоке кода SaveGameScript(); — и он будет выполнен.
Теперь скрипт, который позволит загрузить игру из сохраненного ранее файла:

{
	LoadGameName = "";
	LoadGameName = get_open_filename("Sa-ve Game
s (*.svg)|*.svg", "TestSave.svg");
	if (LoadGameName != "")
<tab>{
		if (file_exists(LoadGameName))
		{
			game_load(L
oadGameName);
		}
	}
 }

Аналогично предыдущему примеру, здесь переменная LoadSaveGame содержит имя файла, из которого мы будем загружать игру. Так, сначала ее содержимое очищается, а затем с помощью функции get_open_filename() мы запрашиваем у пользователя файл с расширением svg, из которого должна быть загружена наша игра. Соответственно, конструкция if проверяет, является ли содержимое переменной LoadSaveGame непустым после закрытия диалога, и если оно содержит значение, движок попытается загрузить сохраненную игру с именем, которое содержится в переменной LoadSaveGame.
Вызывается скрипт аналогично предыдущему.

Глава 12: Чтение уровня из файла
— Если вам нечего делать, — сказала она с досадой, — придумали бы что-нибудь получше загадок без ответа. А так только попусту теряете время! Л. Кэролл "Алиса в стране Чудес".

Давайте теперь разберем скрипт, который читает уровень нашей игры из файла. Идея: уровень состоит из поля, размером 10 клеток в ширину и 10 клеток в высоту. Размер клетки будет 32*32 пикселя. Клетки могут содержать либо зеленый квадрат (objGreenBlock), либо синий квадрат (objBlueBlock). Файл, из которого мы будем читать уровень, будет иметь следующую структуру:

начало файла
// Все строки, написанные после пары символов //, являются комментариями.
// 
Это тоже комментарий.
// Тут мы определяем структуру уровня
###...####
#.#.####.#
<n>#...###.##
.#######.#
#####.####
.#######.#
#.#.####.#
#####.####
##
###.####
#####.####
конец файла

Таким образом мы имеем файл, в котором можно писать комментарии и который будет содержать "карту" нашего будущего уровня. После прочтения файла мы должны получить уровень, в котором блоки расставлены соответственно тому, как их позиции расставлены в файле при условии, что символ "#" соответствует зеленому блоку, а символ "." — синему. В качестве аргумента скрипта мы будем передавать имя файла, в котором содержится уровень. То есть, если мы загрузим наш скрипт в игру под именем ReadFile, то кусок кода вида:

{
ReadFile('level.lvl');
}

загрузит наш уровень из файла с именем level.lvl. Собственно, вот текст самого скрипта, где я разъясню его работу, используя комментарии.

{
// начало скрипта
/* используем в качестве имени открываемого файла передаваемый в функц
ия аргумент...*/
	levelFileName = argument0;
/*... и проверяем, существует ли файл с т
аким именем. Если нет, то необходимо вывести соответствующее сообщение и выйти из скрипта*/
if (not file_exists(levelFileName)) 
	{
		show_message("Level file '&qu
ot; + levelFileNa-me + "' does not exist.");
		exit;
	}
/*если 
файл с таким именем все же существует, то открываем его для чтения и инициализируем переменную curre
ntLevelLine, которая содержит текущую строку ЧИТАЕМОГО УРОВНЯ (но не файла!!!)*/
	else 
<n>	{
		file_open_read(levelFileName);
		currentLevelLine = 0;
/*д
алее определяем, что скрипт будет выполняться либо пока мы не прочитаем все 10 строк, либо пока файл
 не закончится*/
		while (currentLevelLine < 10 and not file_eof()) 
		
{
			yPos = currentLevelLine * 32;
/*переменная yPos содержит номер рисуемой к
летки по вертикали. Умножая его на 32, получим значение по оси Y, в котором в нашем уровне будет рис
оваться текущий блок в текущей комнате.*/
			levelString = file_read_string();
			file_readln();
/*читаем строку из файла и заносим ее в переменную levelString, 
а затем с помощью процедуры file_readln() переводим курсор на новую строку файла и проверяем, являет
ся ли строка пустой (т.е. ничего не содержит) либо комментарием (начинается с группы символов "
//"). Если так оно и есть, то не выполняем никаких действий, в противном случае...*/
	
		if (string_copy(levelString, 1, 2) = "//" or string_length(levelString) = 0) 
			{
			}
			else 
/*инициализируем переменную
 currentObj, которая будет содержать номер текущего рисуемого объекта в строке. Если номер будет бол
ьше длины строки, т.е. строка закончится, данной переменной заново будет присвоено значение 1, а ном
ер текущей строки, содержащейся в переменой currentLevelLine, увеличиться на 1*/
		{
			currentObj = 1
			while (currentObj <= string_length(le-v
elString)) 
			{
				xPos = (currentObj — 1) * 32;
/*пере
менная xPos содержит номер рисуемой клетки по горизонтали. Умножая его на 32, получим значение по ос
и X, в котором в нашем уровне будет рисоваться текущий блок в текущей комнате*/
			
	currentChar = string_copy(level String, currentObj, 1);
/*здесь из строки levelString, сод
ержащей индексы 10 объектов строки уровня, выбирается один символ с номером currentObj и заносится в
 переменную currentChar. Далее следует проверка: если currentChar равняется символу "#", т
о в точке с координатами (xPos,yPos) рисуется зеленый блок, если символу "." — то синий*/<r>
				if (currentChar = "#") 
				{
				instance_create(xPos, yPos, obj GreenBlock);
				}
			else 
				if (currentChar = ".") 
			{
					instance_create(xPos, yPos, obj BlueBlock);
			<tab>}
/*увеличиваем значение currenObj на 1, то есть читаем содержимое следующей клетки в текущ
ей строке*/
			currentObj += 1;
			}
/*увеличиваем значение c
urrenLevelLine на 1, то есть переходим на новую строку файла, содержащего "карту" уровня*/

		currentLevelLine += 1;
		}
	}
// конец скрипта
 }

Вот и все на этот раз. Понимаю, что уже начался материал, более сложный для понимания, поэтому если кто не разберется — пишите письма или заходите на мой сайт http://www.cps.fatal.ru,  который посвящен Game-Maker'у — с удовольствием помогу вам.
P.S. Выражаю признательность Карлу Густаффсону за предоставленные примеры.

Воронецкий "AlienRaven" Михаил

№ 48

Zebrazavr

Яндекс.Метрика