DeatHSoul
GM Pro user
Репутация: 236
Offline
Пол: Награды:
API: GameMaker Studio Standard
Сообщений: 478
|
|
« Ответ #21 : Апрель 26, 2011, 00:10:38 » |
|
Привет всем! ^_^ Сегодня состоялось очередное обновление FAQ. Был добавлен только один новый вопрос, добавлено два, которые ранее были в составе третьего, и, что главное, исправлено двадцать три вопроса. Это обновление и так небольшое, а с этого дня обновления будут содержать в себе всего 2-3 новых вопроса. Статьи, которые я обещал написать, я до сих пор не начал писать, и вряд ли они появятся в скором времени. Но появятся обязательно, я не вхожу в число людей, которые не могут держать свое слово. С каждым обновлением будет поставляться подробная информация обо всех изменениях и нововведениях в FAQ, и это обновление - не исключение. Далее вы можете видеть подробный список всех исправленных / дополненных / переписанных вопросов: Обновлён. I.1.3. Какая последняя версия Game Maker? Ответ: На данный момент, последняя версия GM - 8.1. Впрочем, постоянно выходят обновления, исправляющие баги текущей версии и добавляющие новые возможности. Обновлён. I.1.4. На каких платформах работают GM-игры? Ответ: На данный момент существует версия Game Maker для Windows и Mac. У YoYo Games так же имеется конвертер для портирования GM игр на PSP, iOS (iPhone, iPad), Android и HTML5. Так же, в планах у YoYo Games выпустить продукт под названием "GameMaker Studio", некую особую версию GameMaker, включающую возможность портирования игр на различные платформы. Обновлён и дополнен. I.1.5. Что такое Pro и Lite версии Game Maker? Какая стоимость Game Maker? Ответ: Начиная с GameMaker версии 8.1, обычная Pro версия стала называться Standart (а Lite сохранила свое название). До первого июня 2011 года GameMaker Standart можно купить за $25, однако после этого стоимость GameMaker повыситься до $39.99. Покупка GameMaker 8.x будет действительна для всех версий 8.х Информация из русифицированного справочного руководства к Game Maker 8.0: Game Maker предоставляется в двух версиях, Lite Edition (облегченная редакция) и Pro Edition (профессиональная редакция). Lite Edition подходит для тех, кто только делает первые шаги в разработке игр. Она совершенно бесплатна, но имеет ограниченную функциональность. Также при запуске игр показывается логотип, а при использовании программы будет постоянно выходить напоминание о возможности обновления. Если Вы собираетесь пользоваться Game Maker регулярно, Вам настойчиво рекомендуется обновить программу до Pro Edition. В Pro Edition значительно расширена функциональность и не показываются логотипы и сообщения. Конкретно в Pro Edition доступны следующие возможности: - Не показывается логотип Game Maker при запуске игр.
- Не показываются всплывающие сообщения о возможности обновления.
- Возможность отрисовывать повернутые, окрашенные и прозрачные спрайты.
- Дополнительные опции в редакторах спрайтов и изображений.
- Дополнительные действия, такие как проигрывание CD музыки, рисование повернутого текста и окрашенных форм.
- Использование звуковых эффектов и позиционирования источников звука.
- Использование загрузочных экранов с фильмами, изображениями, веб страницами, текстом и т. д.
- Система частиц для эффектов взрыва, салюта, пламени, снега, дождя и других.
- Дополнительные продвинутые функции рисования, например, для отрисовки окрашенного текста и текстурированных полигонов.
- Возможность создание 3D игр, при использовании функций для 3D графики.
- Возможность создания многопользовательских проектов для игры через интернет или локальную сеть.
- Возможность задавать свои собственные эффекты перехода между комнат.
- Вы можете использовать функции для создания, загрузки и изменения ресурсов (спрайтов, фонов и т.д.) "на лету", прямо во время игры.
- Доступны функции для работы с различными структурами данных.
- Функции для планирования движения.
- Возможность вложения файлов в исполняемые файлы игр, которые можно использовать во время запуска игр.
- Функциональные возможности можно улучшать пакетами расширений. Их может сделать любой человек и распространять, как правило, совершенно бесплатно.
- Три таких пакета расширения уже включены в дистрибутив, они добавляют новые эффекты перехода между комнатами, диалоги windows и функции для принтера.
- Вы можете определять свои собственные события триггера.
- Вы можете импортировать и экспортировать ресурсы, что поможет Вам легко совмещать ваши проекты.
Обновление с версии Lite Edition до Pro Edition стоит 20 Евро или US $25. Этот взнос делается один раз и будет действительным, как минимум, для всех версий Game Maker 8.x. Информация из официального справочного руководства к Game Maker 8.1.69:
GameMaker comes in two editions, the Lite Edition and Standard Edition. The Lite Edition is meant for those that take their first steps on the path of developing games. It can be used for free but is limited in its functionality. Also it shows a popup logo when running games and will regularly remind you of upgrading the program. When you are using GameMaker regularly you are strongly recommended to upgrade from the Lite Edition. Standard Edition contains considerably more functionality and does not display any logos or popup messages. More precisely, Standard Edition has the following additional functionality: - No GameMaker logo is shown when running a game.
- No GameMaker TV logo in the corner of a game.
- No GameMaker advert screen on exit.
- No regular popups remind you of upgrading.
- You can use color blended sprites, which can be used for many special effects and easy shadows.
- There are additional options in the sprite and image editors.
- There are additional actions for e.g. CD music, rotated text, and colorized shapes.
- You can use special sound effects and positional sound.
- You can create splash screens with movies, images, webpages, texts, etc.
- There is a particle system to create explosions, fireworks, flames, rain, and other effects.
- A number of advanced drawing functions are available, for example colorized text and textured polygons.
- It is possible to create 3D games using functions for 3D graphics.
- It is possible to create multiplayer games that can be played over a network.
- You can define your own room transitions.
- You can use functions to create, load, and modify resources (sprites, backgrounds, etc.) while the game is running.
- There is a collection of functions to create and use data structures.
- There are functions for motion planning.
- You get the possibility to include additional files in the game executables that can be used when the game is run.
- Standard Edition can be easily extended using extension package. These can be made by everybody and will in general be provided free of
- charge.
- Three such extension packages are included adding many room transitions, windows dialogs, and printing facilities.
- You can define your own trigger events.
- You can export and import resources, which makes it easier to collaborate on games.
Upgrading from Lite Edition costs only $39.99 (subject to change). This is a one-time fee that will be valid for all versions 8.x of GameMaker. When you are running the Lite Edition, you can upgrade by going to the Help menu, and selecting Upgrade from Lite Edition. If you purchased GameMaker before (and hence, have an license key), you can go to the Help menu, and pick Enter License Key. Once you have entered a valid, your copy of GameMaker will be upgraded. You must be connected to the internet for the upgrade to work. While GameMaker itself does not require an internet connection, it will require occasional access to maintain the license. If you can not connect your computer to the internet, you can download the license check file from YoYo Games website, and point the auto update system to it. if you fail to provide a valid file, or an internet connection when requested, your copy of GameMaker will be downgraded to Lite until such time as you do. Слегка исправлен. I.2.1. У меня появились / я скачал файлы с расширением *.gb1 - *.gb9. Что это? Ответ: Это бэкап-файлы игр, сделанных на GM. Каждый раз когда вы сохраняетесь - бэкап файлы обновляются. В случае, если сохранили игру с критической ошибкой - вы можете удалить текущий исходник и работать с бэкап файлом. Для этого просто откройте его с помощью GM (ПКМ -> Открыть с помощью -> Game Maker 8). Выбрав пункт Preferences в меню File - вы увидите окно настроек GM, где во вкладке General вы можете либо вовсе отключить создание бэкапов, убрав флажок с "Keep backup copies of files", либо изменить их количество, изменив значение "Maximum number of backups". Дополнен. I.2.2. Текст в редакторе кода стал меньше / больше. Как восстановить нормальный размер? Ответ: Используйте функциональные клавиши F7 и F8 (только для Game Maker 8). Дополнен. I.2.3. Меню-помощи в редакторе кода перестало появляться. Что делать? Ответ: Нажмите F11 (только для Game Maker 8). Слегка исправлен и дополнен. I.2.7. Как использовать функции пакета расширения? Ответ: Перед тем как добавить пакет расширения в ваш проект - вам нужно установить его в Game Maker. Выберете из меню Resources пункт Select Extension Packages (Shift+Ctrl+E). Перед вами появится окно, в котором вы можете добавить какой-то пакет расширения в вашу игру или просмотреть информацию о нём / прочитать справочное руководство. Жмите кнопку "Install". В открывшемся окне вы можете удалить из Game Maker выбранный пакет расширения, нажав кнопку "Uninstall" и установить новый, нажав кнопку "Install" и выбрав *.gex файл. После установки пакетов расширений вы можете добавить какие-то из них себе в проект. Для этого вернитесь в первое окно (нажмите "OK"), выберите имя пакета в списке справа, и нажмите на появившуюся кнопку между списками. Убрать пакет расширения из вашего проекта вы можете аналогичным образом. Функции пакетов расширений в данном проекте можно посмотреть, выбрав из меню Scripts пункт Show Extension functions. Подробнее здесь: http://gmakers.ru/gamemaker_help/source/files/215_00_extensions.php Переписан. I.3.1: Что такое шаг, что такое скорость комнаты? Ответ: В Game Maker за секунду проходит определённое количество шагов. Это количество определяет переменная room_speed (её можно задать в свойствах комнаты: settings -> Speed). Событие step event называется событием шага именно потому, что оно выполняется каждый шаг. К примеру, если в событии step event объекта вы напишете код "score += 1", и установите скорость комнаты на 30 шагов, то за секунду переменная score увеличится на 30. Таким же образом, если вы напишите "image_speed = 1", то за секунду у спрайта сменится 30 кадров, потому что переменная image_speed хранит в себе значение, на которое изменяется кадр спрайта каждый шаг. То же относится и ко времени. Если вы напишете "alarm[0] = 1", то событие таймера произойдёт через 1/30 секунды (для общего случая - 1/room_speed). Переписан. II.1.1. Как ограничить значение переменной? Ответ: Для начала, следует учесть, что вы можете получить небольшой, но всё же выигрыш в производительности, проверяя значение переменной исключительно фактически после её изменения. То есть, если жизни игрока уменьшаются при столкновении с монстром и с огнём - немного производительней будет поставить две проверки в эти события, чем писать одну в step event, которая будет выполнятся каждый шаг. Однако, не стоит злоупотреблять этим в ущерб сопровождаемости кода. К примеру, если вам понадобится как-то изменить событие смерти игрока - вам придётся менять его в двух событиях (в лучшем случае), а не в одном (в случае со step event). Чтобы этого не происходило, лучше выносите повторяющиеся куски кода в скрипты или user events. Последние можно выполнить с помощью команды event_user(номер_события). Итак, при увеличении или уменьшении вы можете использовать такие простые проверки: variable += x; if variable > variable_max // Если переменная больше указанного значения, то... variable = variable_max; // Устанавливаем переменной максимальное значение variable -= x; if variable < variable_min variable = variable_min; Слегка понизив читабельность можно добится большей компактности кода, используя функции min и max: variable = min(variable + x, variable_max); // Если variable_min + x больше variable_max - выбираем меньшее, то есть, variable_max. variable = max(variable - x, variable_min); Часто в различных примерах вы можете встретить такую запись: variable = max(variable_min, min(variable, variable_max)); Однако, есть способ лучше, который я и предпочитаю использовать: variable = median(variable_min, variable, variable_max); // Находим среднее из значений, если variable < variable_min - средним становится variable_min и аналогично для variable_max. Например, если вы хотите ограничить передвижение игрока некоторыми рамками, скажем, сделать, чтобы он не выходил за границы комнаты - вы можете написать так: x = median(0, x, room_width - sprite_width); y = median(0, y, room_height - sprite_height); Переписан. II.1.4. Как запросить имя игрока, и в последствии использовать его в сообщениях и диалогах? Ответ: Запросить имя можно таким образом: global.Hero_name = get_string('Как вас зовут?', 'DeatHSoul'); После этого введённое имя игроком будет хранится в глобальной переменной Hero_name, которая будет сохранять свое значение во всех комнатах, и вы можете использовать её, чтобы получить имя игрока. Например, так: show_message('Привет ' + global.Hero_name + '!'); Дополнен. II.1.6. Как повернуть комнату? Ответ: Используйте view_angle[0..7], и убедитесь, что у вас включен указанный вид. Если вы хотите, чтобы вид поворачивался вместе с игроком - учтите, что его нужно поворачивать в противоположную сторону. Вместо: view_angle[0] = direction; пишите: view_angle[0] = -direction; Исправлен. II.1.10. Как сделать простую паузу одной кнопкой? Ответ: Один из самых простых алгоритмов состоит в использовании функции keyboard_wait (код нужно вставить в событие нажатия соответствующей клавиши): /// Отрисовка сообщения о паузе игры draw_set_color(c_black); draw_set_alpha(0.3); draw_rectangle(0, 0, view_wview, view_hview, 0); // Отрисовка фона. Если у вас не используются виды - замените view_wview и view_hview на room_width и room_height соответственно. draw_set_alpha(1); draw_set_halign(fa_center); draw_set_valign(fa_middle); draw_text(view_xview + view_wview/2, view_yview + view_hview/2, 'PAUSE'); // Отрисовка текста. Если у вас не рисует русский текст - посмотрите вопрос II.4.6.
// Обновляем экран screen_refresh(); // Эта функция в действительности отображает на экране всё то, что мы нарисовали ранее
// Останавливаем игру до нажатия какой-либо клавиши keyboard_wait(); Если вы хотите, чтобы игра продолжалась только при нажатии какой-то конкретной клавиши, к примеру, клавиши P (или, скажем, клике мыши) - замените последнюю строку скрипта (keyboard_wait()) на: io_clear(); // Очищаем состояние клавиатуры
while true // Запускаем вечный цикл { if keyboard_check_pressed(ord('P')) break; // Если игрок нажал Р - выходим из цикла keyboard_wait(); // Ожидаем нажатия какой-нибудь клавиши }
io_clear(); // Снова очищаем состояние клавиатуры, чтобы пауза не влючилась сразу же после выключения Переписан. II.2.4. Как сделать разгон для машины, как сделать чтобы игрок постепенно разгонялся и тормозил? Ответ: На самом деле, на разгон и торможение машины влияет масса показателей. Для общего случая - нужно задать скорость разгона, силу торможения, силу трения и максимальную скорость движения (это касается любого абстрактного игрока, как машини так и реального героя). Простая схема механизма разгона / торможения для машины будет выглядеть примерно так: if keyboard_check(vk_up) { скорость += скорость_разгона; // Увеличиваем скорость машины. } else if keyboard_check(vk_down) { скорость -= сила_торможения; // Уменьшаем её скорость. } else { скорость -= сила_трения; }
скорость = median(0, скорость, максимальная_скорость); // Ограничиваем скорость, чтобы она не могла быть отрицательной и не превышала максимальный порог. Управление игроком в плафтормере будет выглядеть несколько иначе, хотя и сохраняет основную схему: // В платформере скорость может принимать отрицательное значение - движение влево (соответственно положительное - движение вправо). if keyboard_check(vk_left) spd = median(-speed_max, spd - 0.5 , speed_max); else if keyboard_check(vk_right) spd = median(-speed_max, spd + 0.5 , speed_max); else spd = max(abs(spd) - 0.2, 0) * sign(spd); // Короткая запись довольно большого куска кода. Если вы не можете понять его значение - вы можете увидеть расшифровку чуть ниже.
if place_free(x + spd, y) // Если ничто не преграждает путь игроку { x += spd; // Двигаем игрока } else { var dir; if spd > 0 // Самый простой код, позволяющий определить направление движения. dir = 0; else dir = 180; move_contact_solid(dir, speed_max); // Пододвигаем игрока вплотную к стене. spd = 0; // Обнуляем скорость. } speed_max в этом коде - максимальная скорость игрока по горизонтали. spd - текущая скорость игрока. Эта строка: spd = max(abs(spd) - 0.2, 0) * sign(spd); Скрывает в себе весь этот код: if spd > 0 { spd -= 0.2; if spd < 0 spd = 0; } else { spd += 0.2; if spd > 0 spd = 0; } То есть, она уменьшает скорость передвижения героя, сохраняя направление (знак числа). Исправлена картинка. II.2.10. Как сделать движение по неровной поверхности в платформере? Ответ: Здесь тоже нет ничего сложного. Проблема в том, что иногда игроку может препятствовать всего один пиксель, и чтобы на него взобраться - приходится прыгать. Потому, нужно создать некоторую имитацию "ног человека" - за один шаг игрок может взобраться на несколько пикселей, без прыжка. Итак, сначала задаётся максимальная высота, на которую может взобраться игрок - обычно 4-8 пикселей, во время движения в цикле проверяется сначала позиция прямо возле игрока, потом на один пиксель выше, на два пикселя выше, и так до максимальной высоты, на которую может взобраться игрок за один шаг. Эта картинка наглядно демонстрирует метод: http://savepic.net/1109905.jpgВ коде это будет выглядеть примерно так: step event: var i; if keyboard_check(vk_left) // Если игрок нажал стрелку влево { for (i = 0; i < vdis_max; i += 1) // В цикле проверяем все позиции: с высотой как у игрока, на 1 пиксель выше, на 2 и т.д. { if place_free(x - spd, y - i) // Если данная позиция свободна, то... { x -= spd; // Двигаем игрока y -= i; break; // Завершаем цикл } } }
//... Где vdis_max - максимальная высота, на которую может взобраться игрок за один шаг, spd - скорость передвижения игрока. В случае, если нужно реализовать ещё и быстрый спуск по ступенькам вниз - код будет выглядеть примерно так: var i; if keyboard_check(vk_left) // Если игрок нажал стрелку влево { for (i = 0; i < vdis_max; i += 1) // В цикле проверяем все позиции: с высотой как у игрока, на 1 пиксель выше, на 2 и т.д. if place_free(x - spd, y - i) // Если данная позиция свободна { x -= spd; // Двигаем игрока y -= i; if place_free(x, y + 1) // Если игрок не стоит на платформе, if !place_free(x, y + vdis_max2 + 1) // Но она под ним есть! for (i = 0; i < vdis_max2 + 1; i += 1) // Проверяем все позиции немного ниже его { if !place_free(x, y + i) // Если в данная позиция занята твёрдым объектом { y += i - 1; // Двигаем игрока вплотную к этому объекту break; // Завершаем цикл } } break; // Завершаем цикл } } Собственно, эти циклы - самописные аналоги функций move_outside_solid и move_contact_solid. При желании их можно заменить встроенными функциями. Слегка исправлен. II.2.16. Как сделать, чтобы герой в игре жанра "лабиринт" (в игре с видом сверху) мог толкать ящики? Ответ: Реализация возможности двигать ящики зависит от того, как реализовано управление игроком. Если он двигается за счёт speed и direction, можно поставить такой код в событие столкновения ящика с игроком: if place_free(x + lengthdir_x(other.speed, other.direction), y + lengthdir_y(other.speed, other.direction)) // Если ящик можно сдвинуть (позиция свободна) { x += lengthdir_x(other.speed, other.direction); // Двигаем ящик по направлению движения игрока y += lengthdir_y(other.speed, other.direction); } else // Если ящик нельзя сдвинуть { with other // Возвращаем игрока на предыдущую позицию, чтобы он не застрял в ящике { x = xprevious; y = yprevious; speed = 0; } } Если игрок двигается за счёт vspeed и hspeed, можно использовать такой код (практически аналогичный предыдущему): if place_free(x + other.hspeed, y + other.vspeed) { x += other.hspeed; y += other.vspeed; } else { with other { x = xprevious; y = yprevious; vspeed = 0; hspeed = 0; } } В случае, если игрок двигается непосредственно через x и y - можно использовать такой код: var dx, dy; // Объявляем временные переменные dx = other.x - other.xprevious; // Вычисляем dy = other.y - other.yprevious;
if place_free(x + dx, y + dy) // Если ящик можно сдвинуть { x += dx; // Двигаем ящик по направлению движения игрока y += dy; } else // Если ящик нельзя сдвинуть { with other // Возвращаем игрока на предыдущую позицию { x = xprevious; y = yprevious; } } Можно усовершенствовать код, используя функцию move_contact_solid, для того, чтобы придвинуть игрока к ящику. Ещё можно вычислять скорость игрока и сдвигать ящик на вдвое меньшее расстояние, чтобы создать эффект тяжести. Переписан (дополнен). II.4.4. Как определить наивысший объект под мышкой? Ответ: Скрипт опеределения наивысшего объекта в конкретной позиции выглядит так: /*************************************************** Скрипт определяет наивысший экземпляр объекта в указанной позиции. Возвращает id экземпляра объекта, если он существует, в противном случае - noone. argument0 - x; argument1 - y; argument2 - объект; ***************************************************/ var high_object; high_object = noone; with argument2 // Обращаемся ко всем экземплярам объекта { if position_meeting(argument0, argument1, id) // Если объект находится в нужной позиции, то... { if other.high_object = noone // Если мы ещё не выбрали ни один объект, то... { other.high_object = id; // Запоминаем текущий объект. } else { if depth < other.high_object.depth // Если текущий объект выше чем выбранный, то... { other.high_object = id; // Запоминаем объект. } } } }
return high_object; // Возвращаем id найденного объекта. В этом скрипте идёт обращение ко всем экземплярам искомого объекта. Вначале каждый из них проверяется на столкновение с указанной позицией, то есть, проверяется, находится ли он в ней. Если да, тогда его глубина сравнивается с глубиной записанного объекта (тут и далее подразумевается экземпляр объекта). Если его глубина меньше - этот объект заменяет ранее записанный. Конечно, если ни один объект ещё не был записан - глубина не проверяется, а объект сразу записавается. Скачать пример. Переписан. II.4.6. Как мне нарисовать русский текст? Почему я его не вижу? Ответ: В Game Maker по умолчанию встроен шрифт Arial 12, с ограниченным набором символов: начиная от 32 до 127. Русский алфавит в этот диапазон не входит, поэтому, чтобы рисовать русский текст, вам понадобится добавить свой шрифт, в котором нужно выставить максимальный диапазон символов, для поддержки кириллицы. Итак, выберите пункт "Create Font" из меню "Resources", или просто нажмите соответствующую кнопку на панели инструментов. Затем дайте имя своему шрифту в игре (я лично предпочитаю что-то вроде font_menu_button или font_arial10b), выберите сам шрифт, установите нужный размер и нажмите на кнопку "All" (Все), которая устанавливает максимальный диапазон символов. Наконец, вам нужно установить для использования этот шрифт в игре, для этого перед рисованием текста вызовите функцию draw_set_font: draw_set_font(имя_вашего_шрифта_в_игре); draw_text(...); Если вы всё равно не увидили русские буквы - значит выбранный вами шрифт не поддерживает кириллицу. Переписан. II.4.8:Вопрос 1: Как запросить у игрока код, к примеру, для открытия двери? Вопрос 2: Как сделать чит-коды? Ответ: Обе задачи решаются очень просто, с помощью функции get_string. Запросить код можно следующим образом: var text; text = get_string('Введите код для открытия двери:', '****'); // Запрашиваем у игрока код. if text = '1234' // Сравниваем его с правильным кодом. { // Действия, которые выполняются, если игрок ввёл правильный код. } else { show_message('Код неверен.'); // Выводим сообщение об ошибке. } Чит-коды можно организовать следующим образом: var text; text = get_string('Введите чит-код:', ''); // Запрашиваем у игрока чит-код. switch (text) // Сравнивем введённый игроком текст со всеми чит-кодами. Не забудьте break в конце каждой проверки! { case 'lives': lives = 100; break; case 'gold': score += 1000000; break; default: show_message('Такого чит-кода не существует!'); } Дополнен. II.4.10. Как определить время компьютера? Как отобразить текущее время на экране? Ответ: Все функции можно посмотреть в справке: GML -> Игровой процесс -> Синхронизация: current_time* Количество миллисекунд, которые прошли с тех пор, как система была запущена. current_year* Текущий год. current_month* Текущий месяц. current_day* Текущий день. current_weekday* Текущий день недели (1=воскресенье, ..., 7=суббота). current_hour* Текущий час. current_minute* Текущая минута. current_second* Текущая секунда. Отобразить дату и время компьютера можно следующим образом: draw_text(x, y, string(current_hour) + ':' + string(current_minute) + ':' + string(current_second) + ' / ' + string(current_day) + '.' + string(current_month) + '.' + string(current_year)); Слегка исправлен. II.5.2. Что быстрее выполняется, функции lengthdir_x(len, dir) и lengthdir_y(len, dir), или cos(degtorad(dir))*len и -sin(degtorad(dir))*len? Ответ: Как и в случае с point_distance, lengthdir_x и lengthdir_y будет работать быстрее. Слегка исправлен. II.5.3. Что выполняется быстрее, стандартные проверки GM в объекте (на подобии add event -> Keyboard -> <Left>), или собственные проверки в step event (вроде keyboard_check)? Ответ: Стандартные события GM выполняются немного быстрее проверок в шаге. Вероятно, из-за того, что интерпретация кода, каким-бы простым он ни был - занимает довольно много времени. То есть, использование стандартных GM событий покажет большую производительность, чем многочисленные собственные проверки в step event. Однако, разница в скорости выполнения не столь велика, чтобы злоупотреблять этим. P.S: я лично, например, чаще всего вручную пишу большинство проверок в step event объекта. Слегка исправлен. II.5.4. Если одно из выражений в условии ложно, будет ли проверяться остальная часть условия? Ответ: В отличии от большинства языков программирования, в GML проверяется всё условие, вне зависимости, является ли одно из выражений ложным. То есть, условие написанное так: if is_picture_grayscale(temp) { if is_picture_fractal(temp) { // ... } } Будет выполнятся быстрее условия, записанного так: if is_picture_grayscale(temp) and is_pisture_fractal(temp) { // ... } (названия скриптов взяты наобум) Слегка дополнен. II.5.5. Почему не стоит выносить действия в событиях step/draw в скрипты? Ответ: Механизм вызова скриптов в GM невероятно медленный. Допустим, у нас есть два куска кода: a = b; И script0(); script0 такого содержания: a = b; Первый код, повторенный 100 раз выполнялся 0.044 миллисекунды, второй (скрипт) - 0.341 миллисекунды (то есть, примерно в семь раз дольше!). Первый код, повторенный 1000000 раз выполнялся 5401 миллисекунду, второй (скрипт) - 9864 миллисекунды. Очевидно, что вызов скрипта занимает довольно много времени. На деле, при большом количестве использования скриптов в постоянно повторяющихся событиях (как step и draw) - fps значительно падает. Был удалён следующий вопрос, из-за ошибочного предположения в разной скорости выполнения операторов <> и !=: II.5.5. Что работает быстрее, <> или != ? Ответ: На практике разницы практически нет. Тем не менее, я бы всё равно советовал вам использовать !=. И новые вопросы: II.01.15. Я устанавливаю image_speed на единицу, но кадр спрайта меняется мгновенно, а не один раз в секунду! Почему? Ответ: Присвоив переменной image_speed единицу, вы устанавливает смену кадра спрайта каждый шаг. То есть, если у вас скорость комнаты - 30 шагов, то за секунду у спрайта сменится 30 кадров. Если вам нужно, чтобы за секунду менялся один кадр, пишите: image_speed = 1/room_speed; Тогда каждую секунду кадр будет изменяться на 1/30 (если у вас скорость комнаты - 30 шагов), и за секунду кадр всего изменится на 30/30, то есть, на 1. II.01.16. Я устанавливаю таймер на единицу, но действие таймера происходит мгновенно, а не через одну секунду! Почему? Ответ: Таймеры измеряется в шагах, и установив таймеру время в единицу - он выполнится через один шаг, то есть, через 1/room_speed секунды. Для того, чтобы таймер выполнился через одну секунду, нужно писать так: image_speed = alarm[0] = room_speed;; Таким образом, событие таймера выполнится через room_speed шагов, а room_speed шагов состовляют одну сенкунду. II.4.24. Как вычислить разницу между направлениями? Ответ: Пожалуй, стоит разобраться с этим на примере, шаг за шагом разрабатывая искомый скрипт. 1. Предположим, у нас есть три направления: ссылка на изображение, размер: 34.0 кбайт, 192 x 192 точекОчевидно, что для вычисления разницы между синим и красным направлениями нужно от синего отнять красное: (135° - 45°) = 90°. Как может показаться вначале, разница между красным и синим направлениями равна разнице между синим и красным. Однако, в нашем случае, разница может принимать отрицательно значение. Поэтому, разница между красным и синим направлениями равна -90°, а не 90°. На самом деле, вы получаете значение, которое нужно добавить ко второму направлению, чтобы получить первое. Добавляя 90 градусов к 45° получается 135°, ровно как и вычитая 90 градусов из 135° получается 45°. 2. Однако с зелёным и красным направлениями такая простая операция не даст желаемого результата. Скрипт должен возвращать "ближайшую разницу", не 270 градусов (которые можно получить, вычитая 45 градусов из 315°), а 90°: ссылка на изображение, размер: 31.3 кбайт, 192 x 192 точекТребуется вычислить угол a° - он обозначен оранжевым цветом на картинке. Очевидно, что его можно получить, вычитая из 360 345° (зелёное направление), и прибавив к нему 45° (красное направление): dir = dir1 + (360 - dir2); // 45 + (360 - 315) = 90 Где dir - угол a°, dir1 - меньший угол (45° на картинке) и dir2 - больший угол (315 градусов на картинке). Итак, если разность направлений меньше 180 - можно просто отнять от одного направления другое, как в случае с синим и красным направлениями. Если же разность больше или равна 180 - нужно вычислять направление посредством вышеприведённой формулы, или вот так: dir = 360 - (dir2 - dir1); Этот код аналогичен предыдущему, в нём лишь переставлены значения, хотя логика сохранена. 3. Итого, скрипт вычисления разницы между двумя направлениями можно записать так: var diff; diff = argument1 - argument0; // Вычисляем разность между направлениями if abs(diff) > 180 // Если разность по модулю больше 180 return 360 - diff; // Возвращаем разность "через ноль" else return diff; // Возвращаем обычную разность Мы уже вплотную приблизились к решению задачи. Однако, этот скрипт полностью рабочий только когда argument1 больше argument0. К примеру, если argument1 = 15°, а argument0 = 215°, получим соответственно 360° - (15° - 215°) = 560 градусов. Потому, нужно изменить скрипт так, чтобы этой ошибки не возникало: // argument0 - первое направление // argument1 - второе направление var diff; diff = argument1 - argument0; if abs(diff) > 180 return (360 - abs(diff)) * -sign(diff); // Сначала находим разность между направлениями "через ноль", а потом устанавливаем нужный знак else return diff; Этот скрипт полностью рабочий и готов к использованию. Он возвращает значение, на которое больше / меньше направление argument1 чем направление argument0. Если argument1 = 135, а argument0 = 45, то получим 90. Если же argument1 = 315, а argument0 = 45, получим -90, так как 315 на 90 градусов меньше чем 45, считая "через ноль" (извините за каламбур, но это так): ссылка на изображение, размер: 38.1 кбайт, 192 x 192 точек4. Есть однако ещё одна проблема. Иногда направления бывают отрицательные, и часто превышают 360. Поэтому, желательно обработать входящие данные должным образом, чтобы не возникало непредвиденных ошибок: dir = dir mod 360; Эта строка вычисляет остаток от деления указанного направления на 360. То есть, если направление 370, то остаток от деления на 360 будет 10, если направление -740, то получим -20 и т.п. Таким образом, направление будет всегда меньше 360. После этого отрицательное направление нужно преобразовать в положительное. Сделать это, очевидно, можно так: if dir < 0 dir += 360; 5. К слову, все три вышенаписаные строки можно записать всего одной: dir = (dir mod 360 + 360) mod 360; Поясню этот код. Вначале направление приводится к виду от -360 до 360 (не включительно), с помощью оператора mod: "dir mod 360". Далее, если направление отрицательное - она преобразовывается в положительное, после прибавления к нему 360 градусов. К примеру: "-20 + 360" это тоже самое что "360 - 20", то есть, 340 - получаем нормальное положительное направление. Далее вычисляется остаток деления направление на 360, если направление было отрицательным - ничего не изменится, так как 340 mod 360 = 340, а если положительным - оно вернётся к изначальному: (20 + 360) mod 360 = 380 mod 360 = 20. Если вас эта сокращённая запись чем-то смущает, либо вы не можете с нею разобраться, либо же вам просто лень морочить себе голову, то используйте обычный код: dir = dir mod 360; if dir < 0 dir += 360; 6. В результате всех манипуляций с направлениями, скрипт вычисления разницы между двумя направлениями приобрёл следующий вид: /*************************************************** Скрипт возвращает разницу между двумя направлениями. argument0 - первое направление; argument1 - второе направление направление; ***************************************************/ var diff; diff = (argument1 - argument0) mod 360; if diff < 0 diff += 360; if abs(diff) > 180 return (360 - abs(diff)) * -sign(diff); else return diff; 7. На этом можно было бы закончить, однако есть способ записать весь этот скрипт в одну строку, только для самых "крутых" программистов. Я хочу вам рассказать про этот способ. Идея, на самом деле, довольно простая, только очень запутанная. Для начала нужно вычислить разницу двух направлений, просто вычитая одно из другого. Далее, нужно добавит 180 к полученному направлению. Если разность была меньше 180 - она так и останется меньше 360, а если больше - мы получим направление большее 360. После этого находим остаток от деления на 360, и снова отнимаем 180. Когда это написано сухими словами - понять это довольно сложно, так что снова попробуем разобраться на примере: ссылка на изображение, размер: 35.6 кбайт, 192 x 192 точекИтак, мы "поворачиваем" наше направление (225 на рисунке) на 180 градусов, после чего вычисляем, на сколько полученное направление больше нуля (mod 360), в результате чего, получаем 45 градусов (серая стрелка на рисунке, a°). После этого вычитаем из полученного направления 180 градусов, вроде бы как "возвращая" направление назад, получая тоже, что и -(360 - dir) или dir - 360. На рисунке: ссылка на изображение, размер: 28.2 кбайт, 192 x 192 точекЕсли dir (разность направлений) меньше 180 - в результате всех операций получаем тоже направление, так как (dir + 180) mod 360 - 180 = dir (при условии, что dir меньше 180). Итого, получаем вот такую страшную строку: return (((argument1 - argument0) mod 360 + 360) mod 360 + 180) mod 360 - 180; "((argument1 - argument0) mod 360 + 360) mod 360" - приводим разность направлений к виду 0 - 360, чтобы избежать ошибок. "(... + 180) mod 360 - 180" - если направление меньше 180, то ничего не меняется, если больше - получаем отрицательную разность (то, что я описывал выше). Можно заметить, что в этой строке есть пара повторяющихся действий, которые можно было бы объединить в одно: "((... + 360) mod 360 + 180) mod 360" тоже что и "(... + 540) mod 360" Итого, получаем такой скрипт: return ((((argument1 - argument0) mod 360) + 540) mod 360) - 180; и выглядит он именно так, как на сайте www.gmlscripts.com: /* ** usage: ** diff = angle_difference(angle1,angle2); ** ** given: ** angle1 first direction in degrees, real ** angle2 second direction in degrees, real ** ** returns: ** difference of the given angles in degrees, -180 to 180 ** ** GMLscripts.com */ { return ((((argument0 - argument1) mod 360) + 540) mod 360) - 180; } Вы можете использовать уже готовый скрипт в одну строку - а можете использовать более простой скрипт для понимания, составленный нами выше. Ах да, совсем забыл! Был полностью переписан раздел искусственного интеллекта, и, мало того, он ещё находится в разработке. Потому, среди готовых вопросов вы можете видеть странные надписи "Зарезервировано", они означают, что новый вопрос скоро будет добавлен на это место. Или не скоро, When It's Done, в общем. По правде говоря, и те вопросы, которые я переписал следовало бы полностью переписать ещё два-три раза как минимум. Не говоря уж о тех, которые я оставил в покое. Но, как известно, лучшее - враг хорошего, верно? Я не буду зацикливаться на доведении FAQ до идеала, а займусь добавлением вопросов. Обязательно будет переписан вопрос про lengthdir'ы, как я и обещал, но чуть позже. Не избегут такой участи и два вопроса, ему предшествующие. Так же планирую слегка переформировать FAQ, потому что подраздел "Другое" содержит в себе слишком большое кол-во вопросов, которые можно было бы как-то разделить на несколько категорий. Впрочем, никому не интересны эти скучные детали, кроме меня, так что просто ждите улучшений в ближайшем будущем! ^_^ Ещё я готовлю кое-что интересное, но увидите вы это не скоро, потому что с каждым днём свободного времени у меня становится всё меньше. Скорее всего, релиз будет летом. До новых встреч. С уважением, DeatHSoul.
|