– Это совсем не важно, капитан. Мы дали ему все, что необходимо… В любом случае всегда лучше предложить чуть–чуть меньше, чем чуть–чуть больше. Некоторые автоматически подвергают сомнению чересчур открытую информацию.
Гранд адмирал Траун в разговоре с Пеллаэоном.
Тимоти Зан, Трилогия Трауна





Сколько глобус ни крути, там Fess-Style не найти...
Сайт Fess'a
Главная » 2016 » Январь » 1 » Snow Weather Script \ Делаем снежную погоду в ArmA 3
14:08
Snow Weather Script \ Делаем снежную погоду в ArmA 3
Всем привет! Сегодня 1 января 2016, и пока большинство людей спит или отходит от вчерашнего праздника, я решил поделиться с вами скриптом на снег, который я использую в своей миссии VRFortress.



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



Не смотря на то, что в серии ArmA на данный момент снежная погода полностью отсутствует, вот видео, в котором можно увидеть, что работы в данном направлении всё-таки велись :


Начинаем спавнить снег



В своем скрипте я прикрепляю к игроку несколько источников частиц, которые расположены следующим образом :



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

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

Код
FS_spawnSnowCube = {
  private ["_step", "_height", "_RelativePositions", "_source"];
   
  FS_SnowSources = [];
  _step = 20;
  _height = 7;
  _RelativePositions = [
  [0, 0, _height],
  [_step, 0, _height], [-_step, 0, _height], // left and right
  [_step / 2, _step, _height], [-_step / 2, _step, _height], // front : left and right
  [_step / 2, -_step, _height], [-_step / 2, -_step, _height], // back : left and right
  [0, 0, _step / 2 + _height], [0, 0, -_step / 2 + _height] // up and down
  ];

  for [{private _i = 0}, {_i < count _RelativePositions}, {_i = _i + 1}] do  
  {
  _source = "#particlesource" createVehicleLocal getpos player;
  _source setDropInterval FS_SnowDensity;  
  _source setParticleCircle [0.0, [0, 0, 0]];
  _source setParticleRandom [0, [10, 10, 7], [0, 0, 0], 0, 0.01, [0, 0, 0, 0.1], 0, 0];
  _source setParticleParams [
  /*Sprite*/ ["\A3\data_f\ParticleEffects\Universal\Universal", 16, 12, 13,1], "",// [File,Ntieth,Index,Count,Loop(Bool)], "?"
  /*Type*/ "Billboard",  
  /*TimmerPer*/ 1,
  /*Lifetime*/ 6,  
  /*Position*/ _RelativePositions select _i,  
  /*MoveVelocity*/ [0,0,0],  
  /*Simulation*/ 1,0.0000001,0.000,1.7, //rotationVel,weight,volume,rubbing
  /*Scale*/ [0.07],  
  /*Color*/ [[1,1,1,1]],
  /*AnimSpeed*/ [0,1],  
  /*randDirPeriod*/ 0.2,  
  /*randDirIntesity*/ 1.2,
  /*onTimerScript*/ "",
  /*DestroyScript*/ "",
  /*Follow*/ player  
  /*Angle*/ //0,
  /*onSurface*/ //true,
  /*bounceOnSurface*/ //0.5,
  /*emissiveColor*/ //[[0,0,0,0]]
  ];
   
  FS_SnowSources pushBack _source;
  };  
   
};  

Так как в поле /*Follow*/ мы указываем player, то все созданные кубы будут следовать за игроком по мере его движения, в добавок кубы будут поворачиваться вместе с ним. Так что осталось только предусмотреть разные ситуации (когда игрок садится в машину или попадает под воду), и с созданием снега можно закончить.

Код

FS_SnowPersistence = {
  private["_vel","_dir","_attached"];
   
  while {alive player} do  
  {
  _attached = player;
  if (vehicle player != player) then { _attached = vehicle player; } else { _attached = player; };
   
  _vel = speed _attached;
  _dir = getdir _attached;
  {
  _x attachto [_attached, [0, _vel * 0.5, 8]];
  _x setDropInterval FS_SnowDensity;
  }
  forEach FS_SnowSources;
   
  // Disable snow underwater. On by Default
  if (underwater player) then { snowEmitter setDropInterval 0; };

  sleep 1;
  };
  {
  deleteVehicle _x;
  }  
  forEach FS_SnowSources;
   
  waitUntil {alive player};
  [] spawn FS_spawnSnowCube; // restarts snow
};


Теперь сделаем дыхание паром



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

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

Итак... Зеленый круг - Игрок, красный круг - невидимая игровая логика, которая должна поворачиваться вслед за головой, серый треугольник - направление выдыхаемого пара.





Направление поворота головы (направо мы повернули башню или же налево) мы выясним через знание векторного произведения. Если результирующий вектор будет направлен вверх (z-компонента > 0), то мы поворачиваем голову направо, иначе налево.

Код
_v1 = vectorDir _unit;
  _v2 = eyeDirection _unit;
  _cosA = [0,1,0] vectorCos _v1; // cos between north and body direction
  _cosB = _v1 vectorCos _v2; // cos between head- and body- directions
  _sinA = sqrt(1 - _cosA^2);
  _sinB = sqrt(1 - _cosB^2);
   
  /* initial direction */
  _velocity = [0, 1.2, -0.2];
   
  _volume = (_v1 select 0)*(_v2 select 1)-(_v1 select 1)*(_v2 select 0);
  // Which is equal to "(_v1 vectorCrossProduct _v2) select 2", but FASTER
  // We can find out which vector is on the right by calculating the volume of cross product
  // Volume will be negative if we turn head to the left and positive if turn to the right


Все повороты мы будем осуществлять домножением на матрицу поворота, при этом мы будем игнорировать z-составляющую для того, чтобы упростить расчёт (будем использовать поворот вектора в 2D пространстве вместо поворота вектора в 3D пространстве). Z-компонента у нас всегда будет равна -0.2, что означает, что пар всегда будет выдыхаться немного вниз.

Код
/* 1. Turn due to body direction */
  if (getDir _unit > 180 && getDir _unit <= 360) then {
  _velocity = [
  (_velocity select 0) * _cosA - (_velocity select 1) * _sinA,  
  (_velocity select 0) * _sinA + (_velocity select 1) * _cosA,  
  -0.2
  ];
  } else {
  _velocity = [
  (_velocity select 0) * _cosA + (_velocity select 1) * _sinA,  
  -(_velocity select 0) * _sinA + (_velocity select 1) * _cosA,  
  -0.2
  ];
  };
   
  /* 2. Turn due to head direction */
  if (_volume <= 0) then { _sinB = -1 * _sinB; };
   
  _velocity = [
  (_velocity select 0) * _cosB - (_velocity select 1) * _sinB,  
  (_velocity select 0) * _sinB + (_velocity select 1) * _cosB,  
  -0.2
  ];


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

Код
_fog setParticleParams [
  /*Sprite*/ ["\A3\data_f\ParticleEffects\Universal\Universal", 16, 12, 13,0], "",
  /*Type*/ "Billboard",
  /*TimmerPer*/ 0.5,
  /*Lifetime*/ 0.5,
  /*Position*/ [0,0,0],
  /*MoveVelocity*/ _velocity,
  /*Simulation*/ 1, 1.275, 1, 0.2, //rotationVel,weight,volume,rubbing
  /*Scale*/ [0, 0.2,0],
  /*Color*/ [[1,1,1, 0.001], [1,1,1, 0.01], [1,1,1, 0]],
  /*AnimSpeed*/ [1000],
  /*randDirPeriod*/ 1,
  /*randDirIntesity*/ 0.04,
  /*onTimerScript*/ "",
  /*DestroyScript*/ "",  
  /*Follow*/ _source
  ];


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



При реализации пара изо рта я решил привязать каждый момент создания пара к реально сделанному выдоху, то есть игрок будет выдыхать горячий воздух только в те моменты, когда вы будете слышать его дыхание. Для этого я использовал addEventHandler ["SoundPlayed", {..}]

Весь скрипт полностью будет выглядеть так :


Категория: Arma 3 | Просмотров: 5911 | Добавил: Fess
Всего комментариев: 1
Комментарий № 1 написал: 7oundabout
09.08.2021 | Понедельник | 11:49

1 коммент 9 августа 2021 года

Добавлять комментарии могут только зарегистрированные пользователи.
[ Регистрация | Вход ]

Форма входа

Категории раздела

Arma 3 [11]
Сайт [2]
Мысли и обзоры [5]
Minecraft [1]

Поиск

Наш опрос

Лучшая игра серии ...?

Архив | Результаты

Мини-чат

200



Статистика


Онлайн всего: 1
Те, кому лень регаться: 1
Пользователей: 0