RS Game Maker Community
Добро пожаловать, Гость. Пожалуйста, войдите или зарегистрируйтесь.
Вам не пришло письмо с кодом активации?

 
Расширенный поиск
  Главная  Форум   Вики Блоги FAQ Игры Статьи Примеры Войти Регистрация  
Вики
Все желающие приглашаются редактировать вики. Можете писать о своих (или чужих) играх, размещать туториалы, постить статьи - главное навесить категорию.
Страниц: 1 2 3   Вниз
  Печать  
Маленькие хитрости GML
0 Пользователей и 2 Гостей смотрят эту тему.
tolich
to ne lich
Ветеран форума
*****

Репутация: 218
Offline Offline

Сообщений: 1 514


moonlite

04 Апрель 2011 в 01:07
Контроль успешности загрузки сохраненной игры.
В чем проблема контроля: даже если файл существует, он может оказаться неверного формата и не загрузиться.
Снимаем галочку "F5 сохраняет, F6 грузит".
Создадём объект controller.
Alarm[0]:
show_message('Игра загружена')
Alarm[1]:
show_message('Игра не загружена')
press F5 key:
var fname; fname=get_save_filename('Любые файлы|*.*','');
if fname!=''{
  alarm[0]=1;
  game_save(fname);
  alarm[0]=-1;
}
press F6 key:
var fname; fname=get_open_filename('Любые файлы|*.*','');
if fname!=''{
  alarm[1]=1;
  game_load(fname);
}
Кстати, alarm[0] может также:
  • вызвать d3d_start() или d3d_end(). Тогда в одной и той же игре можно безопасно смешивать режимы 3d и 2d (для разных объектов-контроллеров, понятно): загрузка игры восстановит тот режим, который был в момент сохранения;
  • запускать подходящую фоновую музыку;
  • уменьшать игровой счет score, делая невыгодным частое сохранение игры;
  • etc

Различение загрузки и перезагрузки игры.
Когда игра выполняет Game Restart, часть игровых данных сбрасывается (например, удаляются сохраненные данные всех неактивных, но когда-либо активированных персистентных комнат), но часть данных: модифицированные ресурсы, структуры данных, системы частиц и модели не сбрасываются.
Конечно, можно это всё удалять в событии Game End, а потом в Game Start пересоздать, но это же время.
В Game Maker 6 я использовал функцию window_get_visible() в Game Start: если она возвращает true, то перезагрузка, если false, то загрузка.
К сожалению, в Game Maker 7 и 8 окно показывается даже до фрагментов кода создания объектов начальной комнаты, и ничего тут не сделать.
Приходится использовать менее изящные способы, например:
if sprite_exists(sprite0){
  sprite_delete(sprite0);
  // тут загрузка
}
else{
  // тут перезагрузка
}
Возможно, другие участники посоветуют более изящное решение.
Различение Escape и Close Button в Game Maker 6.
Начиная с Game Maker 7 появилась возможность обрабатывать нажатие клавиши Escape и закрытие окна игры независимо.
Но как быть тем, кто по каким-либо причинам (скорее всего, религиозным) продолжает использовать Game Maker 6?
При нажатии кнопки закрытия окна (или Alt+F4) имитирует клавиатурное сообщение Escape, но клавиша на самом деле не нажимается.
Флаг escape в примере как раз включается, если событие <Escape> было вызвано нажатием клавиши, а не чем-то ещё.
Create:
escape=0;
release <Escape>:
escape=0;
<Escape>:
if !escape{
  escape=keyboard_check_pressed(vk_escape);
  if !escape{
    keyboard_key_release(vk_escape);
    if show_question('Are you sure?')
      game_end();
  }
}

Назначить направление и скорость движения созданному объекту.
При этом желательно, чтобы это произошло до события Create.
Способ 1. Использовать действие Create object with motion.
Способ 2.
with instance_create(x,y,object) motion_set(direction,speed);
Всё здорово, но вот только движение задаётся уже после события Create.
Способ 3.
action_create_object_motion(object,x,y,speed,direction);
Фактически равнозначно способу 1 (собственно, это и есть функция, вызываемая этим действием).
Способ 4. Создать объект-пустышку, без обработчиков событий и т.д., установить ему нужные параметры движения, а затем преобразовать в нужный объект.
var inst;
inst=instance_create(x,y,obj_empty);
with inst{
  motion_set(тут направление, тут скорость);
  instance_change(object,1);
}

Временно перейти в другую комнату, затем вернуться в прежнюю и чтобы там ничего не поменялось.
Сто раз видел, что для этой цели используют глобальную переменную вроде global.room_return и room_goto(global.room_return). А вдруг нам в той комнате понадобится перейти в третью (опять же, с возвратом во вторую), затем в четвёртую и т.д.?
Будучи противником использования глобальных переменных вообще, изобрёл следующую технику:
1) Создаётся специальный объект-шаттл, в событии Create имеется текст:
{
  room_persistent=true;
  room_goto(room_put_here_roomid);
}
Далее, Room Start:
{
  if(room=room_put_here_roomid){
    // инициализируем вторую комнату
    // используя подготовленные параметры
  }
  else{
    // всё, вернулись, мавр больше не нужен
    // главное гостинцы сохранить
    room_persistent=false;
    instance_destroy();
  }
}
Наконец, Room End:
{
  if(room==room_put_here_roomid){
    // тут мы покидаем вторую комнату, можно набрать гостинцев
  }
  else{
    // тут мы может подготовить параметры для комнаты, в которую переходим
    room_return = room;
  }
}
2) Для перехода в комнату 2 используем instance_create(x,y,obj_shuttle), причем, через x и y можно передавать дополнительные параметры.
3) Для возврата используем room_goto(obj_shuttle.room_return);
Думаю, легко догадаться, как расширить схему на вложенные переходы к комнатам.
Использование констант.
Значение константы вычисляется в момент запуска игры, когда еще не спряталось окно загрузки, но до того, как будут обработаны скрипты. Таким образом можно создавать типы и системы частиц, подключать dll, создавать и грузить модели. Жаль, но нельзя вызывать скрипты и менять порядок определения констант, если, конечно, не пользоваться сторонними программами.
P.S. Как выяснилось, системы частиц создавать как раз нежелательно.
Полоска здоровья от красного к зеленому.
Synopsys. Можно нарисовать полоску здоровья от красного к зеленому вызовом вроде:
draw_healthbar(x1,y1,x2,y2,health,c_black,c_red,c_lime,0,1,1);
Проблема в том, что она выглядит совсем не так, как нарисованная стандартным действием. Решение таково — мешать цвета самостоятельно:
//вначале от желтого к зеленому, затем от красного к желтому
var color;
if health>=50
  color=merge_color(c_yellow,c_lime,min((health-50)/50,1));
else
  color=merge_color(c_red,c_yellow,max(health/50,0));
draw_healthbar(x1,y1,x2,y2,health,c_black,color,color,0,1,1);

Последнее редактирование: 17 Ноябрь 2012 в 15:02 от tolich

 
Огион
Завсегдатай
****

Репутация: 133
Offline Offline

Сообщений: 989


Ответ № 41 19 Июнь 2012 в 02:22
Опробовал на своем псевдотрехмерном движке, в котором активно используются lengthdir'ы. Получил прирост около пяти процентов с выключенной отрисовкой примитовов. С включенной — около двух.
Илья
Ветеран форума
*****

Репутация: 100
Offline Offline

Сообщений: 1 571

Ответ № 42 19 Июнь 2012 в 10:20
А ленгтхдир у нас какими матан-функциями выражается?

Tkachov
Завсегдатай
****

Репутация: 130
Offline Offline

Награды:
За перевод справки Game Maker 8Призер конкурса ''Маляр Ниндзя''
Сообщений: 665


Ответ № 43 19 Июнь 2012 в 10:30
Так ведь синус/косинус направления, умноженный на длину вектора (ну, аргумент length), не?
Райдо
Старожил
******

Репутация: 89
Offline Offline

Награды:
Легенда сайта
Сообщений: 2 639


Ответ № 44 19 Июнь 2012 в 10:34
Таки да, лендиры это попросту адаптация синуса-косинуса к градусам и координатной плоскости.

tolich
to ne lich
Ветеран форума
*****

Репутация: 218
Offline Offline

Сообщений: 1 514


moonlite

Ответ № 45 19 Июнь 2012 в 14:41
lengthdir_x(length,dir)=length*cos(degtorad(dir));
lengthdir_y(length,dir)=-length*sin(degtorad(dir));

Илья
Ветеран форума
*****

Репутация: 100
Offline Offline

Сообщений: 1 571

Ответ № 46 19 Июнь 2012 в 14:48
Знаете, я тут подумал, и пришел к выводу, что Карамак тонко троллит. Он подкупил пользователей гд.ру, чтобы я, в последствии, лоханулся перед вами. Короче, я подаю в суд.

tolich
to ne lich
Ветеран форума
*****

Репутация: 218
Offline Offline

Сообщений: 1 514


moonlite

Ответ № 47 24 Сентябрь 2014 в 11:29
Никогда не думал, что назову это хитростью, но, судя по примерам, которые я вижу в Интернете, описанное ниже не так очевидно для других, как для меня.

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

1) Заводим переменную, скажем canshoot, устанавливаем её в 1.
2) Перед выстрелом проверяем, чтобы canshoot была равна 1. Или просто if canshoot.
3) В момент выстрела делаем canshoot=0; alarm[0]=room_speed;
4) В аларме 0 пишем: canshoot=1; чтобы стрелять снова.

Всё замечательно. А теперь вопрос: какая связь между значениями canshoot и alarm[0]?
Элементарно: значение переменной canshoot всегда соответствует логическому значению условия alarm[0]<0!

Тогда зачем вообще заводить эту переменную? Ведь уже есть переменная, значение которой уже обеспечивается самим Game Makerом.

1) Ничего не делаем.
2) Перед выстрелом проверяем, чтобы alarm[0] был меньше нуля.
3) В момент выстрела делаем alarm[0]=room_speed;
4) В аларме 0 пишем пустые скобки {}. Зачем? Иначе значение alarm[0] не будет уменьшаться.

И это ещё ничего, я видел и такой вариант:

1) Заводим переменную, скажем shoot_timer, устанавливаем её в 0.
2) Перед выстрелом проверяем, чтобы shoot_timer была 0.
3) В момент выстрела делаем shoot_timer=room_speed;
4) В событии Step пишем: if shoot_timer>0 shoot_timer-=1;.

Огион
Завсегдатай
****

Репутация: 133
Offline Offline

Сообщений: 989


Ответ № 48 24 Сентябрь 2014 в 11:48
А чтобы было понятно, какой таймер за что отвечает, можно завести константы и писать, например, alarm[CANSHOOT].
tolich
to ne lich
Ветеран форума
*****

Репутация: 218
Offline Offline

Сообщений: 1 514


moonlite

Ответ № 49 25 Сентябрь 2014 в 10:28
Только надо отслеживать, чтобы алармы с одним свойством имели общий номер, чтобы не надо было вводить константы вроде CANSHOOT_HERO, CANSHOOT_FOE, CANSHOOT_TOWER, и т.д.

tolich
to ne lich
Ветеран форума
*****

Репутация: 218
Offline Offline

Сообщений: 1 514


moonlite

Ответ № 50 16 Октябрь 2014 в 17:17
GMS позволяет создавать некоторые нестандартные события через редактирование файла объекта.

Добавим, например в в список events какого нибудь объекта следующее:

    <event eventtype="9" enumb="19">
      <action>
        <libid>1</libid>
        <id>603</id>
        <kind>7</kind>
        <userelative>0</userelative>
        <isquestion>0</isquestion>
        <useapplyto>-1</useapplyto>
        <exetype>2</exetype>
        <functionname></functionname>
        <codestring></codestring>
        <whoName>self</whoName>
        <relative>0</relative>
        <isnot>0</isnot>
        <arguments>
          <argument>
            <kind>1</kind>
            <string>show_message('Pause!!!');
</string>
          </argument>
        </arguments>
      </action>
    </event>

А теперь откроем проект в Студии. У нашего объекта появилось событие press <unknown> с единственным действием-блоком кода. Запустим проект и нажмем кнопку Pause. Вот оно, сообщение!

tolich
to ne lich
Ветеран форума
*****

Репутация: 218
Offline Offline

Сообщений: 1 514


moonlite

Ответ № 51 01 Июнь 2015 в 15:53
Значение переменной friction может быть отрицательным. Тогда объект будет постоянно ускоряться, если его скорость не 0.

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

tolich
to ne lich
Ветеран форума
*****

Репутация: 218
Offline Offline

Сообщений: 1 514


moonlite

Ответ № 52 15 Январь 2016 в 17:39
Пока студия не обзавелась встроенными конструкторами массивов, можно пользоваться доморощенными.
#define array
var args=undefined;
for(var i=argument_count-1; i>=0; i--)
  args[i]=argument[i];
return args;

#define array2
var args=undefined;
for(var i=argument_count-1; i>=0; i--){
  var arg=argument[i];
  var len=array_length_1d(arg);
  if(len>0){
    for(var j=len-1; j>=0; j--)
      args[i,j]=arg[j];
  }
  else args[i,0]=arg;
}
return args;
Скрипт array создаёт одномерный, а array2 - двухмерный массив из параметров. Параметры array должны быть простыми значениями. Параметры array2 должны быть одномерными массивами - они становятся строками нового массива.

Технически, можно передавать также одномерные и двухмерные массивы в array - получится массив, содержащий массивы. Учтите, что прямой доступ к элементам массива-элемента невозможен, вначале его нужно вначале извлечь в переменную.

var arr=array(10,20,30,array("hehe",4));
var err1=arr[3][0]; // ошибка синтаксиса
var err2=arr[3,0];  // ошибка времени выполнения: массив arr только одна строка
var val1=arr[3];    // == val1=arr[0,3];
var val2=val1[0];   // "hehe"

Также простые значения можно передавать в array2. Они образуют строку из одного элемента, себя. В переданном двухмерном массиве будет воспринята только первая строка. Если нужно зачем-то добавить в виде целой строки весь двухмерный массив, вначале можно передать его в array, а результат в array2.

var test1=array2(array(10,12),array(13),array(9,7,5));
var test2=array2(array(13),array(10,12),array(9,7,5));
var test3=array2(test1,test2); // только первые строки
var test4=array2(array(test1),array(test2)); // целиком в ячейку
Последнее редактирование: 06 Февраль 2016 в 12:36 от tolich

tolich
to ne lich
Ветеран форума
*****

Репутация: 218
Offline Offline

Сообщений: 1 514


moonlite

Ответ № 53 21 Февраль 2016 в 16:05
Пусть у нас есть переменная (скажем, a), значение которой должно постоянно стремиться к значению другой переменной (скажем, b) мелкими шагами (скажем, c).
if a<b { a+=c; }
if a>b { a-=c; }
Всё? Не совсем. Если a равнялось b-c/2, оно так и останется b-c/2.
if a<b { a=min(a+c,b); }
if a>b { a=max(a-c,b); }
Уже лучше. Но недостаточно хорошо. Ведь, если хорошенько подумать, всё сводится к оператору a=b, только b нужно втиснуть в узкие рамки между a-c и a+c.
a=clamp(b,a-c,a+c);
Да, при отсутствии функции clamp её можно заменить median. И добавить ещё один параметр 9000 для GM шестой версии.

input.txt
Не очень
Активный участник
**

Репутация: 19
Offline Offline

Сообщений: 142


12.8 Gb

Ответ № 54 22 Февраль 2016 в 05:22
Некоторые свойства деактивированных экземпляров:
  • Они практически бесплатны в смысле нагрузки на процессор (ради чего, собственно, этот механизм и добавили).
  • К их локальным переменным всё ещё можно обращаться, причём как читать, так и изменять. В сочетании с первым пунктом это позволяет использовать деактивированные экземпляры в качестве структур (как struct в С-подобных языках).
    inst.a = 3;
    show_message( inst.a ); // << 3
    inst.a = "Hello";
    show_message( inst.a ); // << Hello
  • Но стоит иметь в виду, что при переходе в другую комнату все деактивированные объекты удаляются. Чтобы этого избежать (разумеется, только если у них свойство persistent) достаточно просто их активировать перед переходом:
    instance_activate_all();
    room_goto_next();
  • Также instance_exists( inst ) всегда вернёт false, если inst деактивирован, а ещё такие экземпляры не учитываются в instance_count.
  • Как следствие из предыдущего пункта, не работает конструкция with. Все вышеперечисленные проблемы решаются предварительной активацией:
    instance_activate_all();
    if( instance_exists( inst ) ) {   // << true
        // ...
    }
    with( inst ) {
        // ...
    }

Вся информация приведена для GM:Studio и может отличаться в других версиях (ну и в новых обновлениях студии).

P.S. Интересный факт: в GM8 можно создать переменную с именем struct, а в студии - нет. Возможно, у нас когда-нибудь будут нормальные структуры :)
tolich
to ne lich
Ветеран форума
*****

Репутация: 218
Offline Offline

Сообщений: 1 514


moonlite

Ответ № 55 22 Февраль 2016 в 10:00
instance_activate_all();
instance_activate_object(inst);
;)

input.txt
Не очень
Активный участник
**

Репутация: 19
Offline Offline

Сообщений: 142


12.8 Gb

Ответ № 56 22 Февраль 2016 в 17:22
А разве это работает, если inst это id экземпляра, а не индекс объекта?
То есть
instance_activate_object( inst );
эквивалентно
instance_activate_object( inst.object_index );
?

UPD А, понял, так можно активировать только экземпляр inst, не трогая остальные.
Всё-таки полезно иногда читать справку:
instance_activate_object(obj) Activates all instances in the room of the given object. You can also use all to indicate that all instances must be activated or the id of an instance to activate an individual instance.
Последнее редактирование: 22 Февраль 2016 в 17:37 от input.txt
tolich
to ne lich
Ветеран форума
*****

Репутация: 218
Offline Offline

Сообщений: 1 514


moonlite

Ответ № 57 22 Февраль 2016 в 18:53
Интересно, кстати, отличаются ли instance_activate_object(all) и instance_activate_all()… По идее, не должны, но зачем-то эта функция есть…

input.txt
Не очень
Активный участник
**

Репутация: 19
Offline Offline

Сообщений: 142


12.8 Gb

Ответ № 58 23 Апрель 2016 в 11:17
Ещё кое-что:
Если деактивированный объект имеет код в событии, например, Step , и его активировать во время этого события, то его код выполнится сразу же. То есть не на следующем шаге. Также при этом может нарушаться порядок выполнения событий (обычно по возрастанию id). Видимо, проснувшийся объект добавляется в конец какой-нибудь очереди.

Это верно не только для Step, но и для остальных событий. Я наткнулся на этот эффект когда в End Step активировал объект, у которого в End Step же стояло instance_deactivate_object( id ). В том же событии перешёл в следующую комнату и объекта там не обнаружил.
Последнее редактирование: 23 Апрель 2016 в 12:57 от input.txt
Spartan121
Невыспавшийся
Завсегдатай
****

Репутация: 35
Offline Offline

Сообщений: 629


DragonGameStudios

Ответ № 59 23 Апрель 2016 в 14:39
больше похоже на баг, но и это можно обыграть

input.txt
Не очень
Активный участник
**

Репутация: 19
Offline Offline

Сообщений: 142


12.8 Gb

Ответ № 60 20 Февраль 2018 в 16:34
бамп треду

Померил скорость доступа к переменным: глобальным, переменным экземпляра, локальным и переменным других экземпляров (активных и деактивированных).

var N = 10000000;
var t;

globalvar v_global;
v_global = 0;
v_instance = 0;
var v_local = 0;

var active = instance_create(0, 0, o_dummy);
active.v_other = 0;

var inactive = instance_create(0, 0, o_dummy);
instance_deactivate_object( inactive );
inactive.v_other = 0;

t[0] = get_timer();

repeat N {
    v_global++;
}

t[1] = get_timer();

repeat N {
    v_instance++;
}

t[2] = get_timer();

repeat N {
    v_local++;
}

t[3] = get_timer();

repeat N {
    active.v_other++;
}

t[4] = get_timer();

repeat N {
    inactive.v_other++;
}

t[5] = get_timer();

show_message(
    "Global: " + string( ( t[1] - t[0] ) / 1000 ) + "#" +
    "Instance: " + string( ( t[2] - t[1] ) / 1000 ) + "#" +
    "Local: " + string( ( t[3] - t[2] ) / 1000 ) + "#" +
    "Active: " + string( ( t[4] - t[3] ) / 1000 ) + "#" +
    "Inactive: " + string( ( t[5] - t[4] ) / 1000 )
);
game_end();

Как и ожидалось, локальные самые быстрые, а из других объектов самые медленные. Но в целом разница между ними небольшая.
Прикреплённые файлы Графические миниатюры:
Последнее редактирование: 20 Февраль 2018 в 16:46 от input.txt
Страниц: 1 2 3   Вверх
  Печать  
 
Перейти в:  

RSGMC (gmakers.ru) © 2007—2018
Счётчик–@Mail.ru