Меню эмоций и анимаций FiveM: скрипты, готовые для RP
Добавьте богатые эмоции и анимации на ваш сервер FiveM. RPEmotes, Dpemotes, кастомные стили ходьбы и лучшие меню эмоций для погружения в ролевую игру.
Agency Scripts
Основатель и ведущий разработчик Agency Scripts
Понимание словарей анимаций GTA V
Анимации в GTA V организованы в словари анимаций, которые представляют собой коллекции связанных анимационных клипов. Каждая анимация идентифицируется двумя строками: названием словаря и названием анимации внутри этого словаря. Например, словарь amb@world_human_hang_out_street@female_hold_arm@idle_a содержит анимации бездействия для женских персонажей. Перед воспроизведением любой анимации необходимо запросить словарь с помощью RequestAnimDict и дождитесь, пока он полностью загрузится с помощью HasAnimDictLoaded. Отсутствие запроса словаря анимаций , одна из самых распространенных ошибок новичков, из-за которой анимации не воспроизводятся. GTA V поставляется с тысячами словарей анимаций, охватывающих все от боевых стоек до поз для йоги, и поиск полезных , половина задачи создания полноценного меню эмоций.
Воспроизведение и управление анимациями
Основная функция для воспроизведения анимаций , TaskPlayAnim", принимает ped игрока, словарь, имя анимации, скорость плавного входа, скорость плавного выхода, длительность, флаги и скорость воспроизведения. Флаги анимации контролируют критическое поведение: флаг 1 зацикливает анимацию, флаг 2 останавливает анимацию на последнем кадре, флаг 16 воспроизводит только верхнюю часть тела, позволяя игроку ходить, а флаг 32 разрешает вращение во время анимации. Комбинирование флагов с помощью побитового OR позволяет создавать тонкие поведения. Эмодзи сидения требует флаг 1 для зацикливания и флаг 2 для удержания последнего кадра при прерывании. Эмодзи помахивания лучше всего работает с флагом 49, который сочетает верхнюю часть тела, зацикливание и вращение, позволяя игроку махать, ходя по окружению.
-- Core animation helper functions
local function LoadAnimDict(dict)
if HasAnimDictLoaded(dict) then return true end
RequestAnimDict(dict)
local timeout = GetGameTimer() + 5000
while not HasAnimDictLoaded(dict) do
Wait(10)
if GetGameTimer() > timeout then
return false
end
end
return true
end
local function PlayEmote(dict, anim, flags, duration)
local ped = PlayerPedId()
if IsPedInAnyVehicle(ped, false) then return end
if not LoadAnimDict(dict) then return end
flags = flags or 1 -- default: loop
duration = duration or -1 -- -1 = indefinite
TaskPlayAnim(ped, dict, anim, 2.0, 2.0, duration, flags, 0, false, false, false)
end
local function StopCurrentEmote()
local ped = PlayerPedId()
ClearPedTasks(ped)
-- Also remove any attached props
ClearPedSecondaryTask(ped)
end
-- Export for other resources
exports('PlayEmote', PlayEmote)
exports('StopEmote', StopCurrentEmote)
Организация эмоций по категориям
Хорошо организованное меню эмоций группирует анимации по интуитивным категориям, которые игроки могут быстро просматривать. Общие категории включают Танцы, Приветствия, Реакции, Позы, Спорт, Сценарии (например, сидение, опирание или лежание), Анимации с объектами (включая объекты вроде сигарет или телефонов), Стили ходьбы , и Мимика лица. Каждая запись эмоции должна содержать словарь, имя анимации, флаги, использование реквизита, хэш модели реквизита (если применимо), индекс кости для крепления реквизита, а также значения смещения и вращения для правильного размещения реквизита. Хранение всего этого в структурированной таблице конфигурации облегчает добавление новых эмоций без изменения логики воспроизведения.
-- Emote configuration (config.lua)
Config = {}
Config.Emotes = {
dances = {
{label = 'Dance 1', dict = 'anim@amb@nightclub@dancers@club_dance_bar_1', anim = 'high_center', flags = 1},
{label = 'Dance 2', dict = 'anim@amb@nightclub@mini@dance@dance_solo@female@var_a@', anim = 'high_center', flags = 1},
{label = 'Dance 3', dict = 'anim@amb@nightclub@dancers@crowddance_facedj@', anim = 'hi_dance_facedj_09_v2_female^1', flags = 1},
{label = 'Flamenco', dict = 'special_ped@jessie@idle_latina', anim = 'idle_latina', flags = 1},
},
greetings = {
{label = 'Wave', dict = 'friends@frj@ig_1', anim = 'wave_A', flags = 49, duration = 3000},
{label = 'Salute', dict = 'anim@mp_player_intuppersalute', anim = 'idle_a', flags = 49, duration = 3000},
{label = 'Handshake', dict = 'mp_ped_interaction', anim = 'handshake_guy_a', flags = 49, duration = 4000},
{label = 'Fist Bump', dict = 'mp_ped_interaction', anim = 'fist_bump', flags = 49, duration = 3000},
},
props = {
{label = 'Cigarette', dict = 'amb@world_human_smoking@male@male_a@idle_a', anim = 'idle_c', flags = 49,
prop = 'prop_cs_ciggy_01', bone = 28422, pos = {0.0, 0.0, 0.0}, rot = {0.0, 0.0, 0.0}},
{label = 'Coffee', dict = 'amb@world_human_drinking@coffee@male@idle_a', anim = 'idle_c', flags = 49,
prop = 'p_amb_coffeecup_01', bone = 28422, pos = {0.0, 0.0, 0.0}, rot = {0.0, 0.0, 0.0}},
{label = 'Clipboard', dict = 'amb@world_human_clipboard@male@idle_a', anim = 'idle_c', flags = 49,
prop = 'p_amb_clipboard_01', bone = 36029, pos = {0.16, 0.08, 0.03}, rot = {-100.0, 0.0, 0.0}},
},
scenarios = {
{label = 'Sit Chair', scenario = 'PROP_HUMAN_SEAT_CHAIR', isScenario = true},
{label = 'Lean Wall', scenario = 'WORLD_HUMAN_LEANING', isScenario = true},
{label = 'Guard', scenario = 'WORLD_HUMAN_GUARD_STAND', isScenario = true},
{label = 'Jog', scenario = 'WORLD_HUMAN_JOG_STANDING', isScenario = true},
},
}
Анимации с объектами и прикрепление объектов
Анимации с объектами включают создание игрового объекта и прикрепление его к определенной кости педа игрока. В GTA V есть скелетная система костей, каждая кость имеет уникальный индекс. Распространённые кости для прикрепления включают 57005 для правой руки, 18905 для левой руки, 28422 для области правого пальца (идеально для сигарет), и 31086 для головы. После спавна объекта с CreateObject прикрепите это с помощью AttachEntityToEntity который требует сущность пропа, сущность педа, индекс кости, смещения позиции, смещения вращения и несколько булевых флагов для поведения столкновений и физики. Правильная настройка смещений позиции и вращения требует проб и ошибок, и разные модели педов могут нуждаться в небольших корректировках. Всегда очищайте пропы при отмене эмоции, отслеживая созданный дескриптор сущности и удаляя его.
-- Prop emote system
local currentProp = nil
local function PlayPropEmote(emoteData)
local ped = PlayerPedId()
if IsPedInAnyVehicle(ped, false) then return end
-- Clean up any existing prop
if currentProp and DoesEntityExist(currentProp) then
DeleteEntity(currentProp)
currentProp = nil
end
if not LoadAnimDict(emoteData.dict) then return end
-- Spawn and attach prop
local model = GetHashKey(emoteData.prop)
RequestModel(model)
while not HasModelLoaded(model) do Wait(10) end
local pos = GetEntityCoords(ped)
currentProp = CreateObject(model, pos.x, pos.y, pos.z, true, true, false)
SetModelAsNoLongerNeeded(model)
local boneIndex = GetPedBoneIndex(ped, emoteData.bone)
AttachEntityToEntity(
currentProp, ped, boneIndex,
emoteData.pos[1], emoteData.pos[2], emoteData.pos[3],
emoteData.rot[1], emoteData.rot[2], emoteData.rot[3],
true, true, false, true, 1, true
)
-- Play animation
TaskPlayAnim(ped, emoteData.dict, emoteData.anim,
2.0, 2.0, -1, emoteData.flags or 49, 0, false, false, false)
end
-- Cleanup on emote cancel
local function CleanupProps()
if currentProp and DoesEntityExist(currentProp) then
DeleteEntity(currentProp)
currentProp = nil
end
end
Стили ходьбы и клипы движения
Стили ходьбы изменяют способ передвижения персонажа и применяются с помощью SetPedMovementClipset. В отличие от обычных анимаций, которые заменяют действия, наборы движений сохраняются во всех состояниях локомоции, включая ходьбу, бег и спринт. В GTA V встроено множество наборов движений, таких как MOVE_M@DRUNK@MODERATEDRUNK для пьяной неуклюжести, MOVE_M@GANGSTER@NG для гангстерской походки, MOVE_M@CONFIDENT для уверенной походки, и MOVE_F@HEELS@C для ходьбы на высоких каблуках. Как и словари анимаций, clipsets должны запрашиваться и загружаться перед применением. Предоставьте опцию сброса, чтобы игроки могли вернуться к стандартной ходьбе. Сохраняйте предпочитаемый стиль ходьбы игрока в данных персонажа, чтобы он сохранялся между сессиями и автоматически применялся при входе.
-- Walking style system
local currentWalkStyle = nil
Config.WalkStyles = {
{label = 'Default', clipset = nil},
{label = 'Drunk', clipset = 'MOVE_M@DRUNK@MODERATEDRUNK'},
{label = 'Gangster', clipset = 'MOVE_M@GANGSTER@NG'},
{label = 'Confident', clipset = 'MOVE_M@CONFIDENT'},
{label = 'Tough', clipset = 'MOVE_M@TOUGH_GUY@'},
{label = 'Femme', clipset = 'MOVE_F@FEMME@'},
{label = 'Heels', clipset = 'MOVE_F@HEELS@C'},
{label = 'Injured', clipset = 'move_m@injured'},
{label = 'Hobo', clipset = 'MOVE_M@HOBO@A'},
{label = 'Brave', clipset = 'MOVE_M@BRAVE@A'},
}
local function SetWalkStyle(clipset)
local ped = PlayerPedId()
ResetPedMovementClipset(ped, 0.2)
if not clipset then
currentWalkStyle = nil
return
end
RequestClipSet(clipset)
local timeout = GetGameTimer() + 3000
while not HasClipSetLoaded(clipset) do
Wait(10)
if GetGameTimer() > timeout then return end
end
SetPedMovementClipset(ped, clipset, 0.2)
currentWalkStyle = clipset
end
exports('SetWalkStyle', SetWalkStyle)
exports('GetWalkStyle', function() return currentWalkStyle end)
Мимика лица
Мимика лица добавляет тонкую эмоциональную глубину ролевым взаимодействиям. GTA V поддерживает анимации лица через SetFacialIdleAnimOverride", который изменяет выражение лица ped в состоянии покоя. Доступные выражения включают mood_normal_1, mood_happy_1, mood_angry_1, mood_injured_1, mood_stressed_1, mood_smug_1 , и mood_sulk_1 среди прочего. Эти выражения сохраняются до очистки с помощью ClearFacialIdleAnimOverride. Сочетание выражения лица с анимацией тела и стилем ходьбы дает игрокам полный контроль над тем, как их персонаж себя проявляет. Персонаж, прислонившийся к стене с самодовольным выражением и уверенной походкой, рассказывает совершенно другую историю, чем тот, кто хромает с травмой и напряженным лицом. Эти мелкие детали поднимают ролевую игру с базовых взаимодействий до богатого воплощения персонажа.
-- Facial expression system
local currentExpression = nil
Config.Expressions = {
{label = 'Normal', expression = 'mood_Normal_1'},
{label = 'Happy', expression = 'mood_Happy_1'},
{label = 'Angry', expression = 'mood_Angry_1'},
{label = 'Injured', expression = 'mood_Injured_1'},
{label = 'Stressed', expression = 'mood_Stressed_1'},
{label = 'Smug', expression = 'mood_smug_1'},
{label = 'Sulking', expression = 'mood_sulk_1'},
{label = 'Sleeping', expression = 'pose_injured_1'},
{label = 'Scared', expression = 'mood_scared_1'},
}
local function SetExpression(expression)
local ped = PlayerPedId()
ClearFacialIdleAnimOverride(ped)
if expression then
SetFacialIdleAnimOverride(ped, expression, nil)
currentExpression = expression
else
currentExpression = nil
end
end
exports('SetExpression', SetExpression)
-- Shared emotes: sync with nearby players
RegisterNetEvent('emote:syncShared', function(senderId, dict, anim)
local senderPed = GetPlayerPed(GetPlayerFromServerId(senderId))
if not DoesEntityExist(senderPed) then return end
local myPed = PlayerPedId()
local dist = #(GetEntityCoords(myPed) - GetEntityCoords(senderPed))
if dist > 3.0 then return end
if not LoadAnimDict(dict) then return end
TaskPlayAnim(myPed, dict, anim, 2.0, 2.0, -1, 1, 0, false, false, false)
end)
Создание интерфейса меню эмоций
Меню эмоций можно реализовать либо как радиальное меню на базе NUI, либо как внутриигровое меню с использованием библиотеки вроде ox_lib или qb-menu. Радиальное меню естественно для эмоций, так как игроки могут быстро переходить между категориями и выбирать эмоцию без чтения длинных списков текста. Привяжите меню к клавише, например F4 или настраиваемая клавиша, и предоставьте команду-резерв, например /emote [name] для прямого доступа. Меню должно показывать название эмоции, необязательный превью-миниатюру и указывать, какие эмоции используют реквизит. Включите систему избранного, которая позволяет игрокам закреплять наиболее используемые эмоции в кольце быстрого доступа, избавляя их от необходимости каждый раз просматривать категории для выполнения обычного действия. Сохраняйте избранное в KVP-хранилище игрока с помощью SetResourceKvp чтобы они сохранялись при перезапусках сервера без необходимости записи в базу данных. Хорошо сделанное меню эмоций становится одной из самых используемых функций на любом ролевом сервере, поэтому вложение времени в интерфейс окупается удовлетворенностью игроков.