Huricane, только сейчас увидел твой комментарий. В принципе, ничего сложного, я в начале подумал, что тебе нужны динамические имена переменных. Здесь же достаточно массива-инвентаря в каждом из персонажей. Персонажи - отдельные объекты, созданные заранее. Все наследуются от объекта, например, characters. Предметы - отдельные объекты, наследуемые от объекта, например, staff. У каждого предмета в событии Create описываем общие для Staff переменные: Name, Weight, Durablity, Cost and etc.
Дальнейшее действие таково: если нам надо будет для всех "Меч обыкновенный" поменять цену - мы просто заходим в данный объект, открываем событие Create и меняем нужное значение. Подробней допишу вечером.
Добавлено: В общем, набросал сегодня проектик, в котором реализовал некоторого рода инвентарь и случайную выдачу объектов юнитам. Скриншот:
Смысл какой: есть группа персонажей (в моем случае, из двух юнитов), которые случайным образом получают заданные предметы из сундука. Чтобы это осуществить, нам необходимо: доступ отдельно к каждому персонажу (или массивом), отдельный инвентарь (в моем случае, список DS List) для каждого персонажа, список (массив) объектов, которые необходимо выдать.
В моём примере, все юниты являются отдельными объектами и наследуются от общего объекта - characters. Вся общая реализация происходит именно там. Главное, что нас сейчас интересует - инвентарь. В этом примере, я его реализовал с помощью списка - DS_List.
Код
// Событие Create // У каждого экземпляра свой id списка, так называемый инвентарь invtr = ds_list_create();
Теперь нам надо как-то дать этим юнитам предметы. В качестве пула, будем использовать сундук, который открывается, когда к нему подходит юнит и выдаёт предметы. Сундук имеет две специальные переменные, которые помогут нам выдать предметы:
Код
// Массив предметов, которые может выдать сундук aItems[0] = stf_mace; aItems[1] = stf_sword; aItems[2] = stf_crook; aItems[3] = stf_dagger; aItems[4] = stf_gloves; // Шанс выдачи каждого предмета rate = 3;
Теперь, когда какой-либо из юнитов подходит близко к сундуку, он открывается и срабатывает ранее написанная функция
Код
GiveItem (char, items, rate)
Первый аргумент - персонаж, которому будут выданы предметы Второй аргумент - массив этих самых предметов Третий аргумент - шанс выпадения предметов.
Два последних аргумента хранятся у сундука, юнита(ов) можно получить различным способом, в моём случае, для теста, ссылки на них уже были заготовлены заранее. Так как юнитов у меня два, то и функцию я вызывал два раза, каждый раз передавая в аргументе ссылку на нужного юнита. Что из себя представляет функция GiveItem():
Код
var _char = argument0; // Получаем персонажа var _items = argument1; // Получаем массив предметов var _rate = argument2; // Получаем шанс выпадения var itemsCount = array_length_1d(_items); // Переменная для удобства - размер массива с предметами
randomize();
with (_char) { if (is_undefined(invtr)) // Если вдруг у юнита нет ссылки на инвентарь, return false; // то выходим из функции и возвращаем False
show_debug_message("Random loot for " + sprName); show_debug_message("--------------------------------");
for (var i = 0; i < itemsCount; i++) // Перебираем все предметы в цикле { if (irandom(10) > _rate) // Если наш рейт меньше, чем выпавшее случайное число, то continue; // переходим к следующей итерации цикла (следующем предмету)
// Дальнейшее происходит, если рейт оказался больше ds_list_add(invtr, instance_create(-100, -100,_items[i])); // Добавляем в список (инвентарь юнита) новый предмет, // экземпляр которого разместили за пределами комнаты show_debug_message(_items[i].name); } } show_debug_message("--------------------------------");
Всё достаточно просто. Теперь у каждого юнита есть отдельный инвентарь со своим набором предметов. Экземпляры предметов разные. То есть, несмотря на то, что, например, Sword у мага и рыцаря создан из одного объекта (stf_sword), который имеет параметры:
Код
name = "Sword"; cost = 200; durability = 100;
В дальнейшем, обратившись к инвентарю одного из персонажа, параметры ЕГО меча можно изменить на желаемые. Так, например, у рыцарского меча со временем будет уменьшаться durability (если это реализовать, конечно. Как вариант - после каждой удачной атаки мечом, прочность уменьшается на -5, для примера). В общем, как-то так. Если нужен будет проект - могу куда-нибудь скинуть, хотя, думаю, и так понятен принцип. Если есть какие-то вопросы по реализации - постараюсь ответить.
Сообщение отредактировал Rean - Среда, 16 Ноября 2016, 01:09
Zazaza, а каким образом будет определятся "идти прямо" или "идти наверх"?
Наипростейшее решение видится таким: "кидаем" два невидимых триггера по обеим концам лестницы и при пересечении, например, с нижним триггером, если игрок нажал "UP + LEFT", то ставим флаг isOnLeftStairs (у нас же лестницы будут не только в одну сторону, на карте могут быть и наоборот). И уже в Step персонажа обрабатываем движение при флаге isOnLeftStairs (или флаге isOnRightStairs), меняя анимацию на анимацию подъёма/спуска и увеличивая/уменьшая координату y.
Это если решение с ходу. Если подумать, то, возможно, где-то можно упростить/что-то добавить. Но общая идея такова.
Добавлено: забыл сказать - при повторном пересечении с одним из триггеров, флаги (isOn..Stairs) убираем, чтобы игрок мог двигаться дальше прямо.
Сообщение отредактировал Rean - Вторник, 15 Ноября 2016, 12:21
EnviyngEarth, прошу прощения, не увидел новой ссылки - смотрел только в нулевом посте. Прошел версию 0.2 два раза подряд - новые уровни уже интересней. Проблемы с управлением как в старой версии нет, но вот с досрочным завершением уровня - осталось. Последний уровень на втором круге прохождения завершился, как только я взял звезду.
EnviyngEarth, небольшой баг-репорт: - в версии "Alfa ver. 0.1", если пройти игру и после попадания в главное меню, снова начать игру, то любой уровень заканчивается, как только подберешь звезду (появляется менюшка перехода на новый уровень). - в версии под спойлером "Старая версия", также если начать новую игру, то после прохождения первого уровня по второму кругу, на втором уровне мышь не реагирует на управление.
Решил обратить на это внимание, чтобы в дальнейшем не получился "снежный ком".
EnviyngEarth, поиграл в игрушку - достаточно занимательно. Нюансы: - немного не хватает динамики: в прохождении уровней с поднимающимися-опускающимися платформами нет никакой сложности, только приходится ждать эти самые платформы. Немного тормозит процесс. - что в самой игре, что на последней картинке (та что "new skin") практически отсутствует контраст между героем и фоном. Всё же, надо как-то выделить и подчеркнуть протагониста. Ну и с другими динамичными объектами та же проблема (за исключением сыра и звёзд - они хорошо заметны) - Проблема многих платформеров, сделанных на конструкторах - игрок может зацепится за "вертикальный" торец платформы. Было бы неплохо попытаться это исправить.
В остальном, желаю удачно довести проект до финальной стадии!
Сообщение отредактировал Rean - Воскресенье, 13 Ноября 2016, 20:44
Объектов также n кол-во, но для примера пусть будет 3. В одном объекте должно быть от 1 до 3х сущностей.
Вот здесь поподробней: какие именно подразумеваются объекты? GameMaker'ские? Или это абстрактный объект? В общем, разложи конкретно на примере что и как должно выглядеть, прям по типам данных. Пока что это настолько абстрактно, что я не представляю в каком ключе это должно использоваться - от этого и сложность реализации. К примеру, если там подразумеваются какие-то новые GM-омвские объекты, то, думаю, это вряд ли возможно реализовать стандартными средствами конструктора. Если под "объектом" подразумевается, например, массив с ссылками на экземпляры - это уже совсем другое и, соответственно, реализовать в разы проще. Если же там должна хранится ссылка на существующий GM-овский объект, у которого либо есть, либо нет экземпляров - здесь тоже ничего сложного.
Далее, сущности, которые должны быть у объекта - какие-то новые сущности или одни из тех, что указаны в таблице? Пока не ясна конкретная задача с конкретными определениями - сложно что-то конкретизировать. Пока представляется следующий алгоритм:
1. Формируем список (например, массив) сущностей, отобранных по определенному критерию 2. Запускаем цикл, в котором перебираем все отобранные сущности 3. Передаем ссылку на сущность в заранее подготовленную функцию SetProperties()
Функция SetProperties(): 1. Определяем переменную iGroup и присваиваем ей случайное значение (например, из заранее подготовленного enum) для полученной сущности. С объектами не всё очевидно, как я уже писал выше, поэтому здесь требуется уточнение 2. Определяем переменную iObject, которой присваиваем ссылку на один из объектов (если объекты могут повторяться, то ссылки на них могут хранится в перечислении enum, если нет - то в стеке, к примеру). Для объекта создаем случайно до 3-х сущностей (опять же, не ясно - сущности УЖЕ должны существовать или нет). 3. В цикле создания сущностей, для каждой новой созданной сущности определяем, к примеру, массив properties[] из двух элементов, либо две отдельные переменные - как душа просит. В момент определения массива или переменных - присваиваем необходимые случайные значения, границы диапазона можно передать в аргументах функции SetProperties, либо исходя из глобальных "констант".
В общем, всё дело в конкретных нуждах и конкретной реализации. Если это реализация некоторого рода искусственного интеллекта масс, то можно посмотреть в сторону одно- и двусвязных списков, членами которого являются экземпляры заранее подготовленного объекта с необходимыми переменными.
Huricane, тогда подробней: по какому критерию надо отобрать объекты? Объекты какие-то определенные или случайные? Тоже самое касается свойств и значений: имена свойств заранее известны и как должно определятся какое имя свойству дать? Лучше, конечно, если будет какой-нибудь наглядный пример использования данной конструкции.
В любом случае, если нужны динамические, например заданные пользователем, имена свойств, к которым нужно будет обращаться, то я бы копал в сторону словарей - DS Map:
Код
// Объявляем массив с объектами для наглядности objects[0] = obj_knight; objects[1] = obj_mage; objects[2] = obj_thief;
// Создаём стэк и заполняем его нужными нам свойствами attributes_stack = ds_stack_create(); ds_stack_push( attributes_stack, "Health", "Stamina", "Mana", "Stealth", "Science", "Intellect", "Luck" );
// Распихиваем свойства do { var obj = irandom( array_length_1d( objects ) - 1) // Получаем id случайного объекта из массива выше var inst = irandom( instance_number( objects[obj] ) - 1) // Получаем id случайного экземпляра объекта, выбранного выше var inst_temp = instance_find( objects[obj], inst ); // Находим экземпляр
if (is_undefined( inst_temp.map_id )) // Если у экземпляра ещё нет "карты"... inst_temp.map_id = ds_map_create() // ...то создаём "карту" и присваиваем идентификатор экземпляру
ds_map_add( inst_temp.map_id, ds_stack_pop( attributes_stack ), irandom(10) ); // Добавляем в "карту" свойство из стека. // Вместо стека можно использовать и очередь, и пронумерованную очередь... // ...в общем, как захочется. } until (!ds_stack_empty( attributes_stack )); // Повторяем действия до тех пор, пока стек не закончится
// Достаём свойства в специальной функции, которая возвращает false, если у экземпляра нет карты { var obj = irandom( array_length_1d( objects ) - 1) // var inst = irandom( instance_number( objects[obj] ) - 1) // Как и выше - получаем случайный экземпляр случайного объекта var inst_temp = instance_find( objects[obj], inst ); //
if (is_undefined( inst_temp.map_id )) // Если "карты" для этого объекта нет, то выходим из функции return false;
var ID, key, value; // Объявляем временные переменные, в которых будем хранить идентификатор, ключ и значение "карты" ID = inst_temp.map_id; // Получаем id "карты" - чисто для краткости написания key = ds_map_find_first(ID); // Получаем первый ключ "карты"
for (var i = 0; i < ds_map_size(ID); i++) // Перебираем все ключи в "карте" { value = ds_map_find_value(ID, key); // Получаем значение по ключу show_debug_message( key + " : " + value ); // Выводим сообщение с "КЛЮЧ : ЗНАЧЕНИЕ" в консоле отладки key = ds_map_find_next(ID, key); // Получаем следующий ключ } }
По хорошему, надо бы проверок добавить: пустая карта или нет, имеет ли значение данный ключ или нет и т.д.
Задачка достаточно нетривиальная. Можно попробовать менее красивый, топорный, но более наглядный путь: использовать одномерный массив вместо DS MAP. В массиве, например, каждый нечётный элемент хранит ключ, а каждый чётный - значение:
Использование так или иначе будет похожим. Вместо номера идентификатора карты присваиваем экземпляру непосредственно массив. В общем, вариантов много - всё зависит от конкретных задач.
Сообщение отредактировал Rean - Суббота, 12 Ноября 2016, 01:13
Huricane, речь идёт о разных объектах или о разных экземпляров одного объекта? Если про экземпляры (instance), то можно попробовать перебрать с помощью цикла for:
Код
// Пример из справки var i; for (i = 0; i < instance_number(obj_Enemy); i += 1) { enemy[i] = instance_find(obj_Enemy,i); }
Если разные объекты, то, думаю, массив будет наиболее предпочтителен.
Добавлено: "случайное название из пула" - это типа, переменные с динамическим именем?
Сообщение отредактировал Rean - Пятница, 11 Ноября 2016, 22:22
Вообще не судите строго, я бы всё с нуля переписал, если бы было не лень ибо щас смотрю на старый код и охереваю, насколько всё можно было проще и лучше написать.
Всё окей, главное развиваться и попутно развивать свой код. Проблема "временных" костылей в том, что с ростом проекта они становятся фундаментом. Очень ненадёжным фундаментом.
Проблема стрельбы в том, что у тебя событие "Left Button" в объекте obj_plane, что означает - срабатывать при нажатии на объект. Поменяй событие на "Global Left Button". Ну и причеши код в присвоение позиции объекта. Вообще, у тебя origin у спрайта смещён?
Сообщение отредактировал Rean - Пятница, 11 Ноября 2016, 17:51
dildo_bomber, в таком коде, без обид, но сам чёрт ногу сломит. Не забывайте комментировать переменные и блоки программы.
Значит, во-первых:
Код
// Код в STEP объекта obj_stats if pause = 0 then { x_origin = obj_plane.x y_origin = obj_plane.y } else { x_delta = mouse_x - x_origin // Расстояние между самолётом и курсором по Х y_delta = mouse_y - y_origin // Расстояние между самолётом и курсором по У }
//---------------------------
// Код в STEP объекта obj_plane x = mouse_x - obj_stats.x_delta // Х приравнивается позиция левее курсора - зачем?? y = mouse_y - obj_stats.y_delta // У приравнивается позиция выше курсора - тот же вопрос
Вообще, какие изначальные значения у переменных: pause, x_origin, x_delta ? Они формируют дальнейшее положение объекта, но при этом сами имеют не всегда очевидное значение.
Код
// Зачем здесь в коде нули? if (y < 0 + sprite_get_yoffset(spr_plane)) { y = 0 + sprite_get_yoffset(spr_plane) } if (x < 0 + sprite_get_xoffset(spr_plane)) { x = 0 + sprite_get_xoffset(spr_plane) }
// Что за константа 125? Можно ли получить её динамически? if (y > (room_height) - sprite_get_height(spr_plane) + sprite_get_yoffset(spr_plane)) { y = (room_height) - sprite_get_height(spr_plane) + sprite_get_yoffset(spr_plane) }
// Для подобного рода присвоений, используй функцию clamp(int value, int min, int max)
x = clamp( x, 0, room_width - sprite_get_width(plane)); // x += sprite_get_xoffset(spr_plane);
y = clamp( y, 0, room_height - sprite_get_height(plane)) y += sprite_get_yoffset(spr_plane); // И то, я не уверен, что это оптимальный вариант. Возможно стоит "покурить" мануал в поисках более пригодных функций
Ну и наконец, стрельба:
Код
if can_shoot = 1 // Где формируется значение переменной can_shoot ДО этого места? { instance_create(obj_plane.x - 15, obj_plane.y - 16, obj_fire1) can_shoot = 0 alarm[0] = obj_control_vars.fire_rate }
Пока мы не узнаем, при каких условиях can_shoot получает значение "1", мы не сможем понять, почему "не стреляет".
Сообщение отредактировал Rean - Пятница, 11 Ноября 2016, 16:54
А что, геймдизы у нас только виденьем и концепцией занимаются? И помимо "дизайна 3d-графики" там ничего нет? Или один человек должен делать всю игру? Баба ещё будет учить мужика как делать игры.
Ох-хо-хо, что-то мне подсказывает, что долго здесь такие на задерживаются... С таким походом, вряд ли можно найти путного сотрудника.
Сообщение отредактировал Rean - Понедельник, 07 Ноября 2016, 21:02
smile196, в этой теме на форуме YoYoGames был задан подобный вопрос. Участники, сославшись на лицензионное соглашение, сказали, что ты в праве делать со standalone версией своей игры, что хочешь, в том смысле - публиковать, продавать и т.д.
С другой стороны, на страницы сравнения версий, в колонке Studio FREE отсутствует галочка в строке Marketplace Selling, что как бы заставляет задуматься. Marketplace Selling - это, как я понял, имеется ввиду их торговая площадка, так что скорее всего первый вариант с форума - верный.
Сообщение отредактировал Rean - Понедельник, 07 Ноября 2016, 20:51
SovaDeveloper, Леонид правильно сказал по поводу глобальных переменных. Достаточно создать глобальную переменную
Код
globalvar Money;
ИЛИ
global.Money = 0;
и дальше уже использовать по необходимости. Глобальные переменные сохраняют значения при переходе из комнаты в комнату, а также имеют зону видимости в любом месте программы. Либо, как альтернатива, можно создать один невидимый объект и дать ему свойство Persistent. Назвать его, к примеру, GameStates и хранить все необходимые глобальные данные в нём. Это не самый оптимальный вариант, при небольшом количестве глобальных переменных они будут предпочтительней. Но вот есть и такой вариант.
Данный кусок кода действует по окружности, то есть если Игрок будет ниже, но направление Врага будет в его сторону, то Враг заметит Игрока. Тут уже надо допиливать под конкретные нужды: добавлять проверки по Y
Код
( abs(Игрок.X - Враг.X) <= Враг.ЗонаВидимости ) И (Игрок.Y <= Враг.Y + некоторое_значение ) И (Игрок.Y >= Враг.Y - некоторое значение)
в таком случае Игрок будет замечен, если попадёт в некоторый прямоугольник зрения Врага.
Сообщение отредактировал Rean - Понедельник, 07 Ноября 2016, 01:59