Jump to content

Patrego

Users
  • Content Count

    2
  • Joined

  • Last visited

About Patrego

  • Rank
    Newbie

Recent Profile Visitors

The recent visitors block is disabled and is not being shown to other users.

  1. @BendeR Спасибо за ответ, натолкнуло на некоторые мысли. Мне не очень подходит модуль Civilian_Presence, т.к. он создаёт практически неуправляемых цивилов, которых сложно настраивать под себя. К тому же, как я понял из описания, их также нужно синхронизировать с триггерами для спавна/деспавна в конкретном радиусе от позиции игрока (для каждого создавать свой триггер). Я сменил подход и решил вопрос иначе: Мы расставляем LOGIC-объекты в поселениях, как и раньше: Теперь у нас всего один триггер, прикрепленный к игроку. Этот триггер постоянно собирает все LOGIC-и, находящиеся в триггере, в массив "активных" LOGIC-ов. С этим массивом мы и работаем с помощью цикла while. Цикл while "следит" за массивом активных LOGIC-ов, спавня/деспавня цивов. Код: repeatFlag = true; tr_playerOne = createTrigger ["EmptyDetector", position player]; tr_playerOne attachTo [player, [0,0,0],"Pelvis"]; // настраиваем размер триггера (кол-во метров от игрока до LOGIC-а для спавна/деспавна цивов) tr_playerOne setTriggerArea [250, 250, 0, false, 1000]; tr_playerOne setTriggerActivation ["LOGIC", "PRESENT", true]; tr_playerOne setTriggerStatements ["this && repeatFlag", "pointsArray = [];{if (['point_', (str _x)] call BIS_fnc_inString) then {pointsArray pushBackUnique _x;};} forEach thisList; repeatFlag = false;", "repeatFlag = true;"]; // Если UNIT - то создаёт цивов как юнитов; Если AGENT - как агентов civType = 'UNIT'; // civType = 'AGENT'; // Массивы с модельками,координатами,направлением цивов для каждого LOGIC-а // LOGIC-у с именем point_1 соответствует массив civArray_1 и т.д. civArray_1 = [ ["C_CUPAFRCIV_Civ_1_01", [1170.57,2716.23,0], 124] ]; civArray_2 = [ ["C_CUPAFRCIV_Civ_2_01", [1164,2714.12,0], 20] ]; civArray_3 = [ ["C_CUPAFRCIV_Civ_3_01", [1165.31,2720.09,0], 221] ]; pointsArray = []; activePoints = []; spawnCivFnc = { { if (!(_x in activePoints)) then { activePoints pushBack _x; _pointStr = format ["%1", _x]; _pointNum = parseNumber (_pointStr trim ['point_', 1]); _code = ""; _code = compile (format [" [civArray_%1, %1] call createCivFnc; ", _pointNum]); call _code; }; } forEach pointsArray; { if (!(_x in pointsArray)) then { _pointStr = format ["%1", _x]; _pointNum = parseNumber (_pointStr trim ['point_', 1]); _code = ""; _code = compile (format [" { deleteVehicle _x; } forEach point_%1_activeCivs; ", _pointNum]); call _code; activePoints deleteAt (activePoints find _x); }; } forEach activePoints; }; createCivFnc = { params ["_civArray", "_pointNum"]; { switch (civType) do { case "AGENT": { _civ = createAgent [ format ["%1", ((_civArray select _forEachIndex) select 0)], ((_civArray select _forEachIndex) select 1), [], 0, "NONE" ]; _civ setDir ((_civArray select _forEachIndex) select 2); _code = compile (format [" if (isNil 'point_%1_activeCivs') then { point_%1_activeCivs = []; }; point_%1_activeCivs pushBack _civ; ", _pointNum]); call _code; }; case "UNIT": { _gr = createGroup civilian; _civ = _gr createUnit [ format ["%1", ((_civArray select _forEachIndex) select 0)], ((_civArray select _forEachIndex) select 1), [], 0, "FORM" ]; _civ setFormDir ((_civArray select _forEachIndex) select 2); _code = compile (format [" if (isNil 'point_%1_activeCivs') then { point_%1_activeCivs = []; }; point_%1_activeCivs pushBack _civ; ", _pointNum]); call _code; }; }; } forEach _civArray; }; while {true} do { call spawnCivFnc; // sleep 2; - интервал в секундах, для проверки LOGIC-ов в триггере // можно поставить меньше, если размер триггера будет меньше sleep 2; }; С таким подходом нам не важно, сколько LOGIC-ов будет накладываться друг на друга. Всё должно работать корректно. Демонстрация работы с тремя, наложенными друг на друга, LOGIC-ами: Это можно использовать, если вам нужно расставить конкретных цивилов на конкретные места и иметь возможность делать что-то с ними дальше из скрипта (добавить поведение или навесить addAction-ы и прочее). Цивы будут спавниться и деспавниться в зависимости от расстояния от LOGIC-ов до игрока. По идее это более оптимизированный способ, чем создавать 30+ триггеров, которые будут обновляться каждые 0.5 секунд. Возможно можно что-то улучшить, но я хз :)
  2. *UPD: Рабочий вариант в комментариях внизу. Дисклеймер: я только начал изучать скриптовый язык SQF. Если у вас есть способ сделать это проще и эффектней, прошу поделитесь мыслями/кодом. Это не пример рабочего кода, это описание проблемы, с которой я столкнулся. Цель: написать скрипт, который будет отслеживать передвижение игрока по карте и спавнить цивилов на заранее обозначенных позициях в поселениях, как только игрок приблизится к поселению на 250 метров, а также удалять этих цивилов, когда игрок от поселения на 250 метров отойдёт. Первое, что приходит в голову - создать в каждом поселении по триггеру и повесить код с респавном цивилов на них. В чём проблема: на карте около 40 поселений - это 40 триггеров, каждый из которых обновляется раз в 0.5 секунд. Это слишком затратно. Что я хочу, так это создать триггер и прикрепить его к игроку, чтобы триггер перемещался вместе с ним. А в каждом поселении мы разместим объекты типа LOGIC (почему именно их, напишу дальше). Как только игрок приблизится к объекту LOGIC на 250 метров - триггер сработает (сработает условие: объект LOGIC внутри триггера) и сработает наш код по спавну цивилов. Код: // Создаём триггер, настраиваем размер, прикрепляем триггер к игроку tr_playerOne = createTrigger ["EmptyDetector", position player]; tr_playerOne attachTo [player, [0,0,0],"Pelvis"]; tr_playerOne setTriggerArea [250, 250, 0, false, 1000]; // Почему LOGIC? У нас на выбор есть: "EAST", "WEST", "GUER", "CIV", "LOGIC", "ANY", "ANYPLAYER", // "STATIC", "VEHICLE", "GROUP", "LEADER", "MEMBER". // Из всего этого мы можем использовать только LOGIC, // ведь всё остальное и так будет присутствовать на карте и ошибочно триггерить наш тригер. tr_playerOne setTriggerActivation ["LOGIC", "PRESENT", true]; // при активации вызываем функцию спавнющую цивилов, а при деактивации - удаляющую заспавненных цивилов. tr_playerOne setTriggerStatements ["this", "[thisList, 'one'] call respawnCivReadyFnc", "['one'] call deleteCivFnc"]; // сюда добавятся активные цивилы (которые были заспавненны, но ещё не были удалены) activeCivArrayOne = []; // это массивы с цивилами. Каждый массив содержит цивилов из одного конкретного поселения. civArray_1 = [ // "название модельки, позиция, азимут на который ориентирован цив (его direction)" ["1", [883.64,2749.38,0], 337] ]; civArray_2 = [ ["2", [879.059,2739.4,0], 20] ]; respawnCivReadyFnc = { // список наших LOGIC-ов, которые попадают в триггер; название триггера (в нашем случае просто 'one') params ["_pointsList", "_trigger"]; // для всех LOGIC-ов попавших в триггер: { // узнаём название LOGIC-а, попавшего в триггер. Все LOGIC-и имеют названия типа: point_*номер* _pointStr = format ["%1", _x]; // узнаём номер поинта _pointNum = parseNumber (_pointStr trim ['point_', 1]); // вызываем след. функцию передавая ей номер поинта и название триггера [_pointNum, 'one'] call respawnCivCompileFnc; } forEach _pointsList; }; respawnCivCompileFnc = { params ["_civArrayNum", "_trigger"]; _code = ""; // вызываем функцию спавнющую цивилов, передавая ей в аргументы массив с цивилами определённого поинта и имя триггера // все массивы с цивилами имеют названия типа: civArray_*номер*. civArray_1 будет принадлежать point_1, civArray_2 - point_2 и т.д. if (_trigger == "one") then { _code = compile (format [" [civArray_%1, 'one'] call respawnCivFnc; ", _civArrayNum]); }; call _code; }; respawnCivFnc = { params ["_civArray", "_trigger"]; // спавним цивов из переданного массива и заносим их в массив activeCivArrayOne //(это нужно, чтобы знать каких именно цивов нужно удалить, когда произойдет деактивация триггера) { _gr = createGroup civilian; _civ = _gr createUnit [ format ["C_CUPAFRCIV_Civ_%1_01", ((_civArray select _forEachIndex) select 0)], ((_civArray select _forEachIndex) select 1), [], 0, "FORM" ]; _civ setFormDir ((_civArray select _forEachIndex) select 2); if (_trigger == 'one') then { activeCivArrayOne pushBack _civ; }; } forEach _civArray; }; // При деактивации триггера (когда игрок отходит на 250 метров от LOGIC-объекта) - удаляем всех цивов из массива activeCivArrayOne deleteCivFnc = { params ["_trigger"]; if (_trigger == 'one') then { { deleteVehicle _x; } forEach activeCivArrayOne; // после удаления обнуляем массив активных цивов activeCivArrayOne = []; }; }; Это работает. Но есть проблема: иногда случается такая ситуация, когда один LOGIC-объект расположен слишком близко к другому (это случается когда поселения находятся рядом друг с другом). В таком случае наш триггер перекрывается полем действия сразу двух LOGIC-объектов, или другими словами, наш триггер триггерится сразу на два LOGIC-а. Что происходит в таком случае? Триггер срабатывает на один из них, а на другой - нет. Точнее он срабатывает на оба. Но условие активации (респавн цивов) происходит только один раз - для одного LOGIC-а. Другой же LOGIC просто игнорируется. И даже больше. Если был активирован левый (на картинке) LOGIC, а правый был проигнорирован, то даже если игрок сместится вправо - на территорию правого LOGIC-а, полностью уйдя с территории левого - то ничего не произойдёт. Точнее не произойдет спавна цивов на правом LOGIC-е, произойдёт лишь удаление цивов с территории левого. Что же делать? Ну, мне пришла идея - вместо одного триггера, висячего на игроке, сделать два триггера, висящих на игроке. И тут главный вопрос: как заставить эти два триггера работать обособленно друг от друга. Чтобы первый триггер стриггерился на левый LOGIC, а второй - на правый? Я много вариантов перебрал и не нашёл ни одного рабочего. Точнее, я нашел один вариант, но его сложно считать рабочим. Код: // Итак, что изменилось? tr_playerOne = createTrigger ["EmptyDetector", position player]; tr_playerOne attachTo [player, [0,0,0],"Pelvis"]; tr_playerOne setTriggerArea [250, 250, 0, false, 1000]; tr_playerOne setTriggerActivation ["LOGIC", "PRESENT", true]; tr_playerOne setTriggerStatements ["this", "[thisList, 'one'] call respawnCivReadyFnc", "['one'] call deleteCivFnc"]; // теперь у нас два триггера tr_playerTwo = createTrigger ["EmptyDetector", position player]; tr_playerTwo attachTo [player, [0,0,0],"Pelvis"]; tr_playerTwo setTriggerArea [250, 250, 0, false, 1000]; // Единственный "рабочий" способ, который я нашёл, - задать второму триггеру что-то другое, чем LOGIC. // Например WEST (любой юнит стороны синих). tr_playerTwo setTriggerActivation ["WEST", "PRESENT", true]; tr_playerTwo setTriggerStatements ["this", "[thisList, 'two'] call respawnCivReadyFnc", "['two'] call deleteCivFnc"]; activeCivArrayOne = []; // У нас теперь два активных массива - каждый для своего триггера. activeCivArrayTwo = []; civArray_1 = [ ["1", [883.64,2749.38,0], 337] ]; civArray_2 = [ ["2", [879.059,2739.4,0], 20] ]; respawnCivReadyFnc = { params ["_pointsList", "_trigger"]; _pointStr = format ["%1", _pointsList select 0]; _pointNum = parseNumber (_pointStr trim ['point_', 1]); // теперь мы проверяем, какой триггер вызвал функцию и передаём имя этого триггера дальше // (чтобы знать, в какой массив заносить активных цивов) if (_trigger == "one") then { [_pointNum, 'one'] call respawnCivCompileFnc; }; if (_trigger == "two") then { [_pointNum, 'two'] call respawnCivCompileFnc; }; }; respawnCivCompileFnc = { params ["_civArrayNum", "_trigger"]; _code = ""; // тут тоже передаём имя массива if (_trigger == "one") then { _code = compile (format [" [civArray_%1, 'one'] call respawnCivFnc; ", _civArrayNum]); }; if (_trigger == "two") then { _code = compile (format [" [civArray_%1, 'two'] call respawnCivFnc; ", _civArrayNum]); }; call _code; }; respawnCivFnc = { params ["_civArray", "_trigger"]; { _gr = createGroup civilian; _civ = _gr createUnit [ format ["C_CUPAFRCIV_Civ_%1_01", ((_civArray select _forEachIndex) select 0)], ((_civArray select _forEachIndex) select 1), [], 0, "FORM" ]; _civ setFormDir ((_civArray select _forEachIndex) select 2); // после спавна цива, заносим его в массив активных цивов, в зависимости от имени триггера if (_trigger == 'one') then { activeCivArrayOne pushBack _civ; }; if (_trigger == 'two') then { activeCivArrayTwo pushBack _civ; }; } forEach _civArray; }; deleteCivFnc = { params ["_trigger"]; // при удалении также проверяем имя триггера, чтобы знать из какого активного массива удалять цивов при деактивации триггера. if (_trigger == 'one') then { { deleteVehicle _x; } forEach activeCivArrayOne; activeCivArrayOne = []; }; if (_trigger == 'two') then { { deleteVehicle _x; } forEach activeCivArrayTwo; activeCivArrayTwo = []; }; }; С одной стороны это работает, но с другой - полностью лишает нас возможности использовать WEST (синих) юнитов на карте, ведь на них триггер также будет ошибочно срабатывать. Возможно есть смысл заменить WEST на CIV. Но я надеюсь, что найдётся знаток, который подскажет как сделать это всё "по уму". Мне кажется, что решение кроется в настройке условия триггера: tr_playerOne setTriggerStatements ["ВОЗМОЖНО РЕШЕНИЕ КРОЕТСЯ ЗДЕСЬ?", "[thisList, 'one'] call respawnCivReadyFnc", "['one'] call deleteCivFnc"]; Я много чего перепробовал, клепал различные переменные, пытался запоминать "активный триггер" и т.п. Но ничего не помогло. Подскажите, ребята, как сделать респавн цивов, на заданных позициях, при подходе к поселению на ~200-250 метров, с возможным перекрытием этих областей (поселений) друг-другом, а также удаление заспавленных цивов при отходе игрока от поселения. И всё это, используя 1-2 триггера, прикреплённых к игроку.
×

Important Information

We have placed cookies on your device to help make this website better. You can adjust your cookie settings, otherwise we'll assume you're okay to continue.