Онлайн всего: 1 Гостей: 1 Пользователей: 0 |
|
Главная » Полезные статьи » Руководства по NPC |
Читайте предыдущую часть статьи по ссылке: Разбор скрипта "NPC боксёр в SAMP" (Часть 6) switch(random(3)) выбираем из трех вариантов, либо NPC нажмет правую кнопку, а затем левую, попробуйте сделать так сами, когда противоположный игрок находится вблизи от вас, что выполнит комбо 1, либо ох, вообщем запутаешься в этих кнопках, дело в том, что все их переназначают обычно, мало у кого на пробел спринт, а на шифт прыжок. Поэтому вот, что я Вам скажу, потестите на Вашем друге какие кнопки Вы нажимаете, затем гляньте, если пробел это уже прыжок, а не бег, как в оригинале, то она будет KEY_JUMP, иначе KEY_JUMP будет шифт, думаю разберетесь, здесь правда ничего сложного, а домашнее задание всем сделать 3е комбо, в кунг-фу, например, это удар с ноги в прыжке. Итак, что у нас получилось на данные момент – 2 комба и блок, всё используется случайно каждые 250 секунд, то ударим, то заблокируем, не предугадать ведь в схватке) if(dist> 0.8 && dist<15.0 && GetPVarInt(pid, "box")== 1) { SetPVarInt(npc[npcid][npc_ID], "Condition",0); } Если игрок далее чем в 0.8 метров от нас, но не далее 15, то ставим состояние 0, и теперь выполняется оно. if(dist>26.0) { for (new i = 0; i < 499; i++) { if(GetPVarInt(i, "box")== 1){ DeletePVar(i,"box");}} KillTimer(boxertimer); FCNPC_Destroy(npc[npcid][npc_ID]); ringused=0; SetTimer("Renewbox",15000,0); } если же дистанция между игроком, вызвавшим на бой NPC и NPC более 26 метров, удаляем NPC, можете и бан сразу вписать, ибо ринг, в который портает, из него вообщем нереально выбраться, только с читами, ну может кто-то баги знает, лично я нет, а так если вдруг так получилось, ну вдруг админ портнул Вас далеко, то удаляем NPC и обновляем пикап, что из-за одного другим-то мучиться? public OnPlayerCommandText(playerid, cmdtext[]) { if(!strcmp(cmdtext, "/bc", true)) { SetPlayerInterior(playerid, 5); SetPlayerPos(playerid,767.5128,6.9371,1000.7142); SetPlayerFacingAngle(playerid,90.9281); } return 0; } Пишем команду /bc и нас портает в спортзал. Проблемы скрипта Итак, здесь мы обсудим, чем же неидеален наш скрипт, ничто не идеально, наверное, но тем не менее. Про дистанцию, я подбирал такую дистанцию, когда NPC ещё не врезается в игрока, ведь если тупо писать goto в игрока, то он будет бежать и бежать в игрока, расстояние 0.8 по практическим исследования показалось оптимальным, дело в том, что если мы будет стоять далеко от нашего соперника и жать кнопки, то мы его даже в фокус не возьмем, данном расстояние позволяет брать игрока в фокус, можно же было бы как-то получать угол, или, для продвинутых кватернион, ну точнее изменять кватернион NPC, чтобы он плавно поворачивался к игроку и дрался с ним, но я не знаю как это делается, кватернионы – это типа повороты в пространстве, но плавные, а не сразу с 45, например, градусов перескачить в 180. Идея типа получать зеркальный угол от игрока – бред, если игрок стоит не лоб в лоб, то NPC будет бить по воздуху, вообщем работает очень не круто, если у кого-то есть способы оптимизации скрипта, значительной оптимизации, пишите в комментариях. Вернуться в начало статьи: Разбор скрипта "NPC боксёр в SAMP" (Часть 1)
|
Читайте предыдущую часть статьи по ссылке: Разбор скрипта "NPC боксёр в SAMP" (Часть 5) Паблик для вычисления ближайшего игрока – примем как данное, это математика. forward boxerfight(); public boxerfight() { начинается самое интересное, готовы? Поехали! for (new npcid = 0; npcid < 1; npcid++) { new Float:dist; new pid = GetClosestPlayer(npc[npcid][npc_ID],dist); для NPC-боксера задаем переменную дистанция, через переменную pid мы получаем ближайшего игрока к NPC на какой-либо дистанции. if(dist<15.0 && GetPVarInt(pid, "box")== 1) { SetPVarInt(npc[npcid][npc_ID], "Condition",0); } Собственно сама дистанция, если дистанция менее 15 и этот человек именно тот, кто бросил вызов NPC, задать состояние NPC равное нулю. Так как таймер каждые 250 миллисекунд всё проверяет, то просканировав в следующий раз он такой смотрит if(GetPVarInt(pid, "box")== 1 && GetPVarInt(npc[npcid][npc_ID], "Condition")==0) { new Float:xnc, Float:ync, Float:znc; GetPlayerPos(pid,xnc,ync,znc); FCNPC_GoTo(npc[npcid][npc_ID],xnc,ync,znc,MOVE_TYPE_RUN,0,0); } Игрок, мол есть, идентификатор у него бокс, а теперь и у NPC ещё состояние нулевое, что означает состояние “беги” в данном случае. Задаем локальные координаты, получаем координаты ближайшего игрока через pid, ну и, конечно же, функцию GetPlayerPos, фишка в том, что pvar нам нужен для того, чтобы NPC не бил просто ближайшего игрока, он будет бить именно того, что бросил вызов, надеюсь теперь немного понятно зачем они здесь. Далее говорим NPC беги в игрока. Таймер все также по 250 миллисекунд сканирует, вот подбегает он к игроку и тут if(dist < 0.8 && dist<15.0 && GetPVarInt(pid, "box")== 1) { FCNPC_Stop(npc[npcid][npc_ID]); SetPVarInt(npc[npcid][npc_ID], "Condition",1); } Опа, дистанция то у нас теперь менее 0.8 и не более 15, а игрок наш тот, что вызов бросил, говорим NPC прекратить бег, бегая удар-то не нанесешь, нормальный по крайней мере, хотя есть такая функция для FCNPC, но лучше её не использовать, итак остановили NPC и перевели его в состояние 1. Таймер снова сканирует всё if(GetPVarInt(pid, "box")== 1 && GetPVarInt(npc[npcid][npc_ID], "Condition")==1) { switch(random(3)) { case 0: FCNPC_SetKeys(npc[npcid][npc_ID],KEY_HANDBRAKE+KEY_FIRE); case 1: FCNPC_SetKeys(npc[npcid][npc_ID],KEY_HANDBRAKE+KEY_SECONDARY_ATTACK); case 2: FCNPC_SetKeys(npc[npcid][npc_ID],KEY_HANDBRAKE+KEY_JUMP); } } Теперь у нас выполняется условие, что игрок с индентификатором бокс рядом, в пределах от 0.8 до 15, а состояние NPC равно единичке. Про то, почему такие дистанции, напишу позже. Читай скорее далее: Разбор скрипта "NPC боксёр в SAMP" (Заключение и выводы)
|
Читайте предыдущую часть статьи по ссылке: Разбор скрипта "NPC боксёр в SAMP" (Часть 4) SetTimer("spawnNPC", 500, 0); - феил, не стер со старого скрипта, он ничего не делает, раньше NPC вызывался через паблик, паблик удалил, а вот то, что его вызывать один раз не удалил, вообщем он ни на что не влияет можете смело удалить эту строчку. } – закрываем пикап. Return 1; - ну обычно его ставят, пусть постоит, он нужен не везде, например, вы вызывали выдачу какого-то сообщения через таймер, таймер крайне мал по времени, в том плане, что вызывается каждые 200 секунд, скажем, чтобы Вам 2 раза не выдало одно и то же, ставим в конце return 0, типа не повторять и удаляем его, это как пример, подробнее на samp wiki) }Закрываем паблик скобочкой. forward destroyNPC(npcid); public destroyNPC(npcid) { FCNPC_Destroy(npcid); } Удаляем NPC, используется при смерти NPC. forward Renewbox(npcid); public Renewbox(npcid) { pickupkase = CreatePickup(1314,2, 757.4380,5.8088,1000.7014, -1); SendClientMessageToAll(-1,"Боксерский ринг обновлен!"); } Обновляем боксерский ринг и пишем об этом всем public FCNPC_OnDeath(npcid, killerid, weaponid) вызывается когда NPC умер { Проверяем если скин NPC 81 и его состояния равно 0, либо 1, либо 2, то if(FCNPC_GetSkin(npcid)==81 && GetPVarInt(npcid, "Condition")== 0 || GetPVarInt(npcid, "Condition")== 1 || GetPVarInt(npcid, "Condition")== 2) { if(ringused==1) если стоит, что ринг используется, перевести в не используется { ringused=0; SetTimer("Renewbox",15000,0); обновляем ринг } DeletePVar(npcid,"Condition"); удаляем Pvar для NPC SetTimerEx("destroyNPC",5000,0,"dd",npcid); удаляем конкретного NPC KillTimer(boxertimer); убиваем таймер if(GetPVarInt(killerid, "box")== 1){ если игрок убивший NPC имел идентификатор box, выдадим ему стиль и напишем об этом SetPlayerFightingStyle(killerid,FIGHT_STYLE_BOXING); SendClientMessage(killerid,COLOR_GREEN,"Вы овладели боксом! ");} for (new i = 0; i < 499; i++) { для всех игроков, если у них идентификатор 1, то зададим виртуальный мир, позицию и угол, а так же удалим идентификатор. if(GetPVarInt(i, "box")== 1){ SetPlayerInterior(i, 5); SetPlayerPos(i, 766.2117,6.1029,1000.7167); SetPlayerFacingAngle(i, 170.4782); DeletePVar(i,"box");}} } return 1; } static stock GetClosestPlayer(playerid,&Float:cdist) { new cid = INVALID_PLAYER_ID; new Float:dist; new Float:x,Float:y,Float:z; new Float:mx,Float:my,Float:mz; cdist = 65000.0; GetPlayerPos(playerid,mx,my,mz); for(new i = GetMaxPlayers();i >= 0;i--) { if(playerid == i) continue; if(!IsPlayerConnected(i)) continue; if (IsPlayerNPC(i)) continue; GetPlayerPos(i,x,y,z); x -= mx; y -= my; z -= mz; dist = floatsqroot(x*x + y*y + z*z); if(dist < cdist) { cdist = dist; cid = i; } } return cid; } Читай далее: Разбор скрипта "NPC боксёр в SAMP" (Часть 6)
|
Читайте предыдущую часть статьи по ссылке: Разбор скрипта "NPC боксёр в SAMP" (Часть 3) ringused=1; - в принципе необязательно это использовать было, можно было бы как-то по-другому сделать, но пусть будет так. Мы сделаем так, что при ringused=1 ринг будет занят, а при ringused=0 ринг свободен, опять же говорю, здесь можно было продумать и по-другому, но когда я создавал какие-то арены, для всех пользователей это было критично, потому что вход туда производился через команду рядом с NPC, которая проверяла, есть ли кто сейчас на арене или нет, чтобы не писать проверки по всем игрокам или какие-то Pvar’ы сканировать, просто одна переменная и проблема решена. for (new npcid = 0; npcid < 1; npcid++) { открываем наш цикл, для самого первого нашего NPC имеющего идентификатор в цикле 0 (да-да всё тут начинается на с единички, а с нуля), и говорим, что же от него хотим. npc[npcid][npc_ID] = FCNPC_Create(npc[npcid][npc_Name]); Об этом написано в прошлом уроке, вместо имени для каждого NPC из enum’а берем его. FCNPC_Spawn(npc[npcid][npc_ID], npc[npcid][npc_Skin], npc[npcid][npc_X], npc[npcid][npc_Y], npc[npcid][npc_Z]); - спавним NPC на ринге с такими-то координатами и скином, указанном в enum’е. FCNPC_SetAngle(npc[npcid][npc_ID], npc[npcid][npc_A]); - угол поворота задаем из enum’а. FCNPC_SetInterior(npc[npcid][npc_ID], npc[npcid][npc_Interior]); - задаем интерьер, я написал в комментариях, но напишу и тут, извиняюсь, пока не делал этот скрипт, забыл, что виртуальные миры и интерьеры разные вещи, интерьер – это место где находится наш зал, который тоже в данном случае берется из enum’а, а виртульных миров может быть очень и очень много, но в данном случае нам не нужны виртуальные миры, а вот интерьер, для зала бокса в ЛС нужен. SetPlayerFightingStyle(npc[npcid][npc_ID], npc[npcid][npc_FightStyle]); - зададим для NPC стиль боя, указанный в enum’е. SetPVarInt(npc[npcid][npc_ID], "Condition",0); - задаем для NPC состояние равном нулю, про смену Pvar’ов и их наглядное действие вы увидите позже в цикле самого боя для NPC. boxertimer = SetTimer("boxerfight", 250, 1); - чтобы действия не выполнялись разово мы говорим, что каждые 250 миллисекунд (1/4 секунды) выполнять таймер, который мы обозначили как boxertimer. Таймеры работают и без обозначений, но имея обозначения таймера, позже его можно удалить, это важно, чтобы не было нагрузок, да и багов, чтобы не было, а то по 100 раз будете один и тот же таймер вызывать, даже не знаю, что будет, он ведь не разовый, а постоянный о чём свидетельствует цифра 1, а “boxerfight” – это тот паблик, куда нам надо перейти, то есть тот паблик, который выполнять каждые 250 миллисекунд. } Далее закрываем цикл для данного NPC, больше нам от него здесь ничего не надо. Читаем далее: Разбор скрипта "NPC боксёр в SAMP" (Часть 5)
|
Читайте предыдущую часть статьи по ссылке: Разбор скрипта "NPC боксёр в SAMP" (Часть 2) Начнем с функции public OnPlayerPickUpPickup(playerid, pickupid) { - открываем паблик Функция вызывается тогда, когда игрок подбирает какой-либо пикап, наиболее яркие примеры пикапов в сингле это броня, аптечки, оружие. if (pickupid == pickupkase) в этом строке мы указываем, что будет если игрок подберет пикап с идентификатором pickupkase. { - открываем всё, что нам надо сделать при подборе этого пикапа. SendClientMessage(playerid,COLOR_GREEN,"Сражайтесь с мастером, чтобы овладеть стилем!"); - Посылаем игроку, подобравшему пикап текстовое сообщение зеленым цветом. SetPlayerPos(playerid, 762.9995,2.5302,1001.5942); - задаем игроку позицию, точнее мы портаем его на ринг, данная функция выглядит как playerid – это тот, кого портать, а дальше через запятую идут некие цифры, которые являются координатами по x,y,z. SetPlayerFacingAngle(playerid, 138.2046); - телепортировав игрока на ринг зададим ему некий угол поворота, чтобы он смотрел в центр ринга. DestroyPickup(pickupkase); - уничтожаем пикап, на самом деле эту строчку можно было бы написать выше всех, возможно, так было бы логичней, но вследствие того, что данный процесс происходит очень быстро, то мы не почувствуем разницы, в данном случае это не принципиально, но бывают случаи, когда это критично, если такое встретиться в моих скриптах, мы обязательно это обсудим. Зачем уничтожать пикап, спросите Вы? Для того, чтобы не было такого, что 2 и более игроков так же телепортируются на ринг – одного портировали и хватит, умрет или выйдет с сервера – удалим боксера и обновим пикап, но об этом далее. SetPVarInt(playerid, "box",1); - что за ужасы пошли, «пвары» какие-то? На самом деле это «АРХИполезная» функция. «Пвары» это такие «присваиватели». Если так можно сказать. В данном случае здесь обозначается игрок ,находящийся на ринге. Для NPC Pvar’ы очень и очень важны, с помощью них мы можем производить контроль по принципу state_machine, об этом позже. Здесь же у нас всё просто: для игрока взявшего пикап, мы говорим -этот человек на ринге! Ну, как говорим, NPC-то пока об этом ничего не знает, об это знаем только мы, но, вскоре, мы расскажем ему этот секрет) Читаем далее: Разбор скрипта "NPC боксёр в SAMP" (Часть 4)
|