Bloga Geri Dön
Tutorial8 dk. okuma

FiveM Emote ve Animasyon Menüleri: Rol Yapmaya Hazır Scriptler

FiveM sunucunuza zengin animasyonlar ve emotelar ekleyin. RPEmotes, Dpemotes, özel yürüyüş stilleri ve roleplay derinliği için en iyi emote menüleri.

Agency Scripts

Agency Scripts Kurucusu ve Baş Geliştiricisi

GTA V Animasyon Sözlüklerini Anlamak

GTA V içerisindeki animasyonlar, birbiriyle ilişkili animasyon kliplerinin koleksiyonu olan animasyon sözlükleri (animation dictionaries) halinde düzenlenmiştir. Her animasyon iki dize ile tanımlanır: sözlük adı ve bu sözlük içindeki animasyon adı. Örneğin sözlük amb@world_human_hang_out_street@female_hold_arm@idle_a kadın pedler için bekleme duruşu animasyonlarını içerir. Herhangi bir animasyonu oynatmadan önce, sözlüğü şu fonksiyonla istemelisiniz: RequestAnimDict ve şununla yüklenmesi tamamlanana kadar bekleyin: HasAnimDictLoaded. Öncesinde dictionary istememek, yeni geliştiricilerin en sık yaptığı hatalardan biridir ve animasyonların sessizce oynatılamamasına neden olur. GTA V, dövüş duruşlarından yoga pozlarına kadar her şeyi kapsayan binlerce animasyon dictionary'si ile birlikte gelir ve kullanışlı olanları keşfetmek, kapsamlı bir emote menüsü oluşturma sürecinin yarısını oluşturur.

Animasyonları Oynatma ve Yönetme

Animasyonları oynatmak için kullanılan temel fonksiyon şudur: TaskPlayAnim, oyuncu ped'i, dictionary, animasyon adı, blend-in hızı, blend-out hızı, süre, flag'ler ve oynatma hızını kabul eder. Animasyon flag'leri kritik davranışları kontrol eder: flag 1 animasyonu döngüye alır, flag 2 animasyonu son karede durdurur, flag 16 oyuncunun yürümesine izin vererek yalnızca üst gövdeyi oynatır ve flag 32 animasyon sırasında dönmeye izin verir. Flag'leri bit düzeyinde OR ile birleştirmek ayrıntılı davranışlar oluşturmanızı sağlar. Oturma emote'u döngü için flag 1 ve kesintiye uğrarsa son kareyi tutmak için flag 2 gerektirir. El sallama emote'u en iyi, yalnızca üst gövdeyi döngü ve dönme ile birleştiren ve oyuncunun dolaşırken el sallamasına olanak tanıyan flag 49 ile çalışır.

-- 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)

Animasyonları Kategorilere Ayırma

İyi organize edilmiş bir emote menüsü, animasyonları oyuncuların hızla göz atabileceği sezgisel kategorilere ayırır. Yaygın kategoriler şunları içerir: Danslar, Selamlar, Tepkiler, Pozlar, Spor, Senaryolar (oturma, yaslanma veya uzanma gibi), Prop Emote'ları (sigara veya telefon gibi nesneleri içeren), Yürüyüş Stilleri, ve Yüz İfadeleri. Her emote girişi dictionary, animasyon adı, flag'ler, prop kullanıp kullanmadığı, varsa prop model hash'i, prop takma için bone index'i ve doğru prop yerleşimi için offset ve rotation değerlerini saklamalıdır. Tüm bunların yapılandırılmış bir yapılandırma tablosunda saklanması, oynatma mantığını değiştirmeden yeni emote'lar eklemeyi kolaylaştırır.

-- 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},
    },
}

Prop Emote'ları ve Nesne Bağlama

Prop emote'ları, bir oyun nesnesi oluşturmayı ve bunu oyuncunun ped'i üzerindeki belirli bir kemiğe bağlamayı içerir. GTA V, her kemiğin benzersiz bir dizine sahip olduğu bir iskelet kemik sistemine sahiptir. Yaygın bağlantı kemikleri şunları içerir: 57005 sağ el için, 18905 sol el için, 28422 sağ parmak bölgesi için (sigaralar için ideal) ve 31086 kafa için. Prop'u şununla oluşturduktan sonra: CreateObject, şunu kullanarak bağlayın: AttachEntityToEntity bu da prop entity, ped entity, bone index, pozisyon ofsetleri, rotasyon ofsetleri ile çarpışma ve fizik davranışı için birkaç boolean flag gerektirir. Pozisyon ve rotasyon ofsetlerini doğru ayarlamak deneme yanılma gerektirir ve farklı ped modelleri küçük ayarlamalar gerektirebilir. Emote iptal edildiğinde oluşturulan entity handle değerini takip edip silerek propları her zaman temizleyin.

-- 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

Yürüyüş Stilleri ve Hareket Klipleri

Yürüyüş stilleri oyuncu karakterinin nasıl hareket ettiğini değiştirir ve şu kullanılarak uygulanır: SetPedMovementClipset. Eylemleri geçersiz kılan normal animasyonların aksine, hareket clipset'leri yürüme, koşma ve depar atma dahil tüm hareket durumlarında kalıcıdır. GTA V, şunlar gibi onlarca yerleşik hareket clipset'i içerir: MOVE_M@DRUNK@MODERATEDRUNK sarhoş sendelemesi için, MOVE_M@GANGSTER@NG gangster yürüyüşü için, MOVE_M@CONFIDENT kendinden emin bir yürüyüş için ve MOVE_F@HEELS@C yüksek topuklu ayakkabılarla yürümek için. Animasyon sözlükleri gibi, clipset'ler de uygulanmadan önce talep edilmeli ve yüklenmelidir. Oyuncuların varsayılan yürüyüşe dönebilmesi için bir sıfırlama seçeneği sunun. Oyuncunun tercih ettiği yürüyüş stilini oturumlar arasında korunması için karakter verilerinde saklayın ve giriş yaptıklarında otomatik olarak yeniden uygulayın.

-- 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)

Yüz İfadeleri

Yüz ifadeleri, roleplay etkileşimlerine ince bir duygusal derinlik katar. GTA V, yüz animasyonlarını şu yolla destekler: SetFacialIdleAnimOverride, ped'in dinlenme anındaki yüz ifadesini değiştirir. Kullanılabilir ifadeler şunları içerir: mood_normal_1, mood_happy_1, mood_angry_1, mood_injured_1, mood_stressed_1, mood_smug_1, ve mood_sulk_1 ve diğerleri. Bu ifadeler şununla temizlenene kadar geçerliliğini korur: ClearFacialIdleAnimOverride. Bir yüz ifadesini vücut animasyonu ve yürüme tarzıyla birleştirmek, oyunculara karakterlerinin kendilerini nasıl sunduğu konusunda tam kontrol sağlar. Kendini beğenmiş bir ifade ve kendinden emin bir yürüyüşle duvara yaslanan bir karakter, yaralı şekilde topallayan ve stresli bir yüze sahip olan bir karaktere kıyasla tamamen farklı bir hikaye anlatır. Bu küçük detaylar, roleplay deneyimini basit etkileşimlerden zengin bir karakter tasvirine yükseltir.

-- 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)

Emote Menüsü Arayüzünü Geliştirme

Emote menüsü, NUI tabanlı bir radial menü veya ox_lib ya da qb-menu gibi bir kütüphane kullanan oyun içi bir menü olarak uygulanabilir. Radial menü animasyonlar için doğal hissettirir, çünkü oyuncular uzun metin listelerini okumak zorunda kalmadan kategoriler arasında hızla gezinebilir ve bir animasyon seçebilir. Menüyü şu şekilde bir tuşa atayın: F4 veya yapılandırılabilir bir tuş ataması ve şu şekilde bir komut yedeği sağlayın: /emote [name] doğrudan erişim içindir. Menü, emote adını, isteğe bağlı bir önizleme küçük resmini göstermeli ve hangi emote'ların prop kullandığını belirtmelidir. Oyuncuların en sık kullandıkları emote'ları hızlı erişim çarkına sabitlemelerine olanak tanıyan bir favoriler sistemi ekleyin; böylece yaygın bir eylemi gerçekleştirmek istedikleri her seferinde kategorilere göz atmak zorunda kalmazlar. Favorileri oyuncunun KVP depolamasında şununla kalıcı hale getirin: SetResourceKvp böylece veritabanı yazma işlemlerine gerek kalmadan sunucu yeniden başlatmalarından sonra da korunurlar. İyi yapılandırılmış bir emote menüsü, herhangi bir roleplay sunucusunda en çok kullanılan özelliklerden biri haline gelir; bu nedenle arayüze zaman ayırmak oyuncu memnuniyeti olarak geri döner.

Başlamaya Hazır mısınız?

Scriptleri mağazamızdan edinin veya destek, güncellemeler ve gelecek yeniliklere göz atmak için Discord'a katılın.