System tatuaży i fryzjera w FiveM: skrypty personalizacji
Dodaj salony tatuażu i barber shopy do swojego miasta FiveM. Kąty kamery, interfejsy UI, system wizyt i najlepsze skrypty personalizacji dla roleplay.
Agency Scripts
Założyciel i główny deweloper w Agency Scripts
Zrozumienie komponentów wyglądu pedów
Stworzenie systemu salonu tatuażu i fryzjera w FiveM wymaga solidnego zrozumienia tego, jak GTA V obsługuje wygląd peda (postaci). System wyglądu jest podzielony na kilka kategorii komponentów: head overlays dla cech takich jak niedoskonałości, zarost, brwi i makijaż, komponenty drawable dla ubrań i fryzur oraz dekoracje tatuaży nakładane na model peda. Każdy typ komponentu używa innych funkcji natywnych i posiada własny system indeksowania. Head overlays używają indeksów 0-12 do kontrolowania cech takich jak niedoskonałości (0), zarost (1), brwi (2), starzenie się (3), makijaż (4), róż (5) i szminka (8). Fryzury są ustawiane za pomocą systemu komponentów drawable pod indeksem 2 dla włosów na głowie. Tatuaże używają całkowicie oddzielnego systemu dekoracji z nazwami kolekcji i wartościami hash. Przed zbudowaniem UI musisz zmapować wszystkie dostępne opcje dla każdego komponentu, w tym liczbę wariantów dostępnych dla bieżącego modelu peda, ponieważ postacie męskie i żeńskie mają różne zestawy dostępnych stylów. Użyj GetNumHeadOverlayValues(overlayIndex) aby sprawdzić w czasie działania, ile opcji istnieje dla każdego typu overlay.
System kamery do podglądu postaci
Świetny system fryzjera i tatuaży zależy od tego, czy gracz widzi na bieżąco to, co wybiera. Zbuduj niestandardowy system kamer, który skupia się na odpowiedniej części ciała podczas przeglądania opcji przez gracza. Podczas edycji fryzury przybliż kamerę do głowy. Wybierając tatuaże na klatce piersiowej, oddal kamerę, aby pokazać górną część ciała. Pracując nad tatuażami na nogach, ustaw kamerę tak, aby kadrowała dolną część ciała. Użyj natywnych funkcji kamery, aby stworzyć płynne przejścia między punktami widzenia, gdy gracz przełącza się między kategoriami. Oto system zarządzania kamerami, który obsługuje wiele poziomów przybliżenia i obszarów ciała:
local customCam = nil
local camActive = false
local CameraPresets = {
head = {offset = vector3(0.0, 0.65, 0.65), fov = 35.0},
face = {offset = vector3(0.0, 0.45, 0.62), fov = 25.0},
torso = {offset = vector3(0.0, 1.2, 0.2), fov = 45.0},
legs = {offset = vector3(0.0, 1.5, -0.5), fov = 50.0},
full = {offset = vector3(0.0, 2.5, 0.2), fov = 50.0},
}
function SetupPreviewCamera(preset)
local ped = PlayerPedId()
local pedCoords = GetEntityCoords(ped)
local pedHeading = GetEntityHeading(ped)
local headingRad = math.rad(pedHeading)
local cam = CameraPresets[preset] or CameraPresets.full
local camX = pedCoords.x + cam.offset.y * math.sin(-headingRad)
local camY = pedCoords.y + cam.offset.y * math.cos(-headingRad)
local camZ = pedCoords.z + cam.offset.z
if customCam then
local newCam = CreateCam('DEFAULT_SCRIPTED_CAMERA', true)
SetCamCoord(newCam, camX, camY, camZ)
PointCamAtCoord(newCam, pedCoords.x, pedCoords.y, pedCoords.z + cam.offset.z)
SetCamFov(newCam, cam.fov)
SetCamActiveWithInterp(newCam, customCam, 800, 1, 1)
Wait(800)
DestroyCam(customCam, false)
customCam = newCam
else
customCam = CreateCam('DEFAULT_SCRIPTED_CAMERA', true)
SetCamCoord(customCam, camX, camY, camZ)
PointCamAtCoord(customCam, pedCoords.x, pedCoords.y, pedCoords.z + cam.offset.z)
SetCamFov(customCam, cam.fov)
SetCamActive(customCam, true)
RenderScriptCams(true, true, 500, true, false)
end
camActive = true
end
function DestroyPreviewCamera()
if customCam then
RenderScriptCams(false, true, 500, true, false)
DestroyCam(customCam, false)
customCam = nil
camActive = false
end
end
Dodaj obsługę obracania myszą lub gałką analogową, aby gracze mogli obracać swoją postać podczas podglądu zmian. Przechwytuj ruch myszy, gdy NUI go nie przetwarza, i stosuj go jako obrót heading dla peda gracza. Pozwala to graczom obejrzeć tatuaż pod różnymi kątami przed sfinalizowaniem zakupu, co znacząco poprawia wrażenia z zakupów. Zamroź peda gracza w miejscu podczas sesji personalizacji i odtwórz animację idle, aby postać wyglądała naturalnie podczas przeglądania.
Personalizacja włosów i brody
Personalizacja włosów i brody korzysta odpowiednio z komponentów drawable oraz systemu head overlay. Fryzury ustawia się za pomocą SetPedComponentVariation przy użyciu indeksu komponentu 2 (włosy) i indeksu 1 (wariant tekstury/koloru). Kolor włosów jest nakładany osobno za pomocą SetPedHairColor który przyjmuje indeks koloru głównego oraz indeks koloru pasemek. Style brody wykorzystują system head overlay o indeksie 1 z wartością przezroczystości kontrolującą gęstość zarostu. Zbuduj NUI tak, aby pokazywało miniaturki lub nazwy dla każdego dostępnego stylu, aktualizując peda w czasie rzeczywistym podczas klikania kolejnych opcji. Oto logika Lua do wprowadzania zmian włosów i brody z podglądem w czasie rzeczywistym:
function ApplyHairStyle(ped, styleIndex, textureIndex, primaryColor, highlightColor)
SetPedComponentVariation(ped, 2, styleIndex, textureIndex or 0, 0)
SetPedHairColor(ped, primaryColor, highlightColor)
end
function ApplyBeardStyle(ped, styleIndex, opacity, color)
SetPedHeadOverlay(ped, 1, styleIndex, opacity or 1.0)
SetPedHeadOverlayColor(ped, 1, 1, color, color)
end
function ApplyEyebrows(ped, styleIndex, opacity, color)
SetPedHeadOverlay(ped, 2, styleIndex, opacity or 1.0)
SetPedHeadOverlayColor(ped, 2, 1, color, color)
end
function GetAvailableHairStyles(ped)
local styles = {}
local numStyles = GetNumberOfPedDrawableVariations(ped, 2)
for i = 0, numStyles - 1 do
local numTextures = GetNumberOfPedTextureVariations(ped, 2, i)
table.insert(styles, {
index = i,
textures = numTextures,
label = 'Style ' .. (i + 1)
})
end
return styles
end
function GetAvailableBeardStyles(ped)
local styles = {}
local numStyles = GetNumHeadOverlayValues(1)
for i = 0, numStyles - 1 do
table.insert(styles, {
index = i,
label = 'Beard ' .. (i + 1)
})
end
return styles
end
Interfejs NUI powinien prezentować kolory włosów jako paletę wizualną, a nie indeksy numeryczne, mapując wewnętrzne indeksy kolorów GTA na ich rzeczywiste odpowiedniki RGB. Dodaj suwak krycia zarostu, który pozwoli graczom precyzyjnie dostosować gęstość zarostu: od lekkiego zarostu po pełną, gęstą brodę. Zapisz poprzedni wygląd gracza przed otwarciem menu fryzjera, aby móc cofnąć wszystkie zmiany, jeśli kliknie Anuluj, chroniąc go przed niepotrzebną utratą pieniędzy przez pomyłkę.
System nakładania tatuaży
Tatuaże w GTA V korzystają z systemu dekoracji, który nakłada tekstury na model peda za pomocą par hashy kolekcji i nakładki. Każdy tatuaż jest zdefiniowany przez nazwę kolekcji (np. "mpairraces_overlays") oraz nazwę nakładki (np. "MP_Airraces_Tattoo_000_M"), które wspólnie identyfikują konkretny zasób tatuażu. Tatuaże aplikuje się za pomocą AddPedDecorationFromHashes i usuń wszystkie tatuaże za pomocą ClearPedDecorations. Trudność polega na tym, że nie ma funkcji natywnej do usuwania pojedynczego tatuażu, więc gdy gracz chce usunąć jeden tatuaż, musisz wyczyścić wszystkie dekoracje i nałożyć ponownie wszystko oprócz usuniętego. Oznacza to, że musisz utrzymywać pełną listę aktualnych tatuaży gracza w swoim modelu danych. Oto jak zarządzać kolekcją i nakładaniem tatuaży:
-- Tattoo database structure
local TattooZones = {
head = {label = 'Head', camera = 'face'},
torso = {label = 'Torso', camera = 'torso'},
left_arm = {label = 'Left Arm', camera = 'torso'},
right_arm = {label = 'Right Arm', camera = 'torso'},
left_leg = {label = 'Left Leg', camera = 'legs'},
right_leg = {label = 'Right Leg', camera = 'legs'},
back = {label = 'Back', camera = 'torso'}
}
-- Available tattoos per zone (abbreviated, real list has hundreds)
Config.Tattoos = {
torso = {
{collection = 'mpairraces_overlays', overlay = 'MP_Airraces_Tattoo_000_M',
label = 'Wing Design', price = 500, zone = 'torso'},
{collection = 'mpbiker_overlays', overlay = 'MP_MP_Biker_Tat_000_M',
label = 'Flame Skull', price = 750, zone = 'torso'},
},
left_arm = {
{collection = 'mpchristmas2_overlays', overlay = 'MP_Xmas2_M_Tat_000',
label = 'Sleeve Art', price = 600, zone = 'left_arm'},
}
}
function ApplyAllTattoos(ped, tattooList)
ClearPedDecorations(ped)
for _, tattoo in ipairs(tattooList) do
local collectionHash = GetHashKey(tattoo.collection)
local overlayHash = GetHashKey(tattoo.overlay)
AddPedDecorationFromHashes(ped, collectionHash, overlayHash)
end
end
function PreviewTattoo(ped, tattoo, currentTattoos)
local previewList = {}
for _, t in ipairs(currentTattoos) do
table.insert(previewList, t)
end
table.insert(previewList, tattoo)
ApplyAllTattoos(ped, previewList)
end
function RemoveTattoo(ped, tattooIndex, currentTattoos)
local newList = {}
for i, t in ipairs(currentTattoos) do
if i ~= tattooIndex then
table.insert(newList, t)
end
end
ApplyAllTattoos(ped, newList)
return newList
end
Katalog tatuaży w GTA V zawiera setki pozycji rozproszonych w wielu kolekcjach DLC. Uporządkuj je według stref ciała w konfiguracji, aby NUI mogło filtrować według obszaru. Dla każdego tatuażu sprawdź, czy należy użyć wariantu męskiego czy damskiego na podstawie modelu ped gracza, ponieważ większość tatuaży ma oddzielne nazwy assetów kończące się na _M lub _F. Pokazuj podgląd, gdy gracz najedzie kursorem na tatuaż w katalogu, i pobieraj opłatę dopiero po potwierdzeniu wyboru.
System makijażu i malowania twarzy
Makijaż korzysta z systemu head overlay w kilku indeksach: róż (5), szminka (8) oraz owłosienie klatki piersiowej lub nakładki cery dla dodatkowych efektów. Każdy typ makijażu oferuje wiele stylów oraz suwak przezroczystości kontrolujący intensywność. Zbuduj sekcję makijażu w NUI barber shopu, która prezentuje każdą kategorię z wizualnymi próbkami dostępnych kolorów. System kolorów dla makijażu wykorzystuje SetPedHeadOverlayColor z parametrem typu koloru: typ 0 dla kolorów domyślnych, typ 1 dla kolorów włosów (używany do brody i brwi) oraz typ 2 dla kolorów makijażu. Makijaż wykorzystuje konkretnie typ koloru 2, który zapewnia paletę kolorów kosmetycznych, w tym różne odcienie skóry, czerwienie, róże i wyraziste barwy. Dodaj suwak przezroczystości dla każdego elementu makijażu, aby gracze mogli uzyskać subtelny, naturalny wygląd lub odważne, wyraziste stylizacje. Przechowuj wszystkie ustawienia makijażu wraz z danymi wyglądu gracza, aby zachowały się między sesjami, oraz zaoferuj interakcję z lustrem w domu gracza, gdzie może poprawić makijaż bez wizyty w sklepie.
System płatności i cennik
System płatności musi śledzić zmiany dokonane przez gracza podczas sesji i obliczać łączny koszt na podstawie wyświadczonych usług. Gdy gracz otwiera menu sklepu, zapisz zrzut jego obecnego wyglądu. W miarę wprowadzania zmian rejestruj każdą modyfikację w koszyku sesji, który sumuje łączną kwotę. Gdy gracz zatwierdzi i wyjdzie, pobierz od niego pełną opłatę za wszystkie wprowadzone zmiany. Jeśli anuluje, przywróć stan ze zrzutu. Wprowadź cennik wielopoziomowy, w którym podstawowe fryzury kosztują mniej niż style premium, a ceny tatuaży zależą od rozmiaru i stopnia skomplikowania. Oto system śledzenia sesji i płatności:
local sessionChanges = {}
local originalAppearance = nil
function StartCustomizationSession()
local ped = PlayerPedId()
originalAppearance = CaptureFullAppearance(ped)
sessionChanges = {}
end
function TrackChange(category, item, price)
-- Remove previous change in same category if exists
for i = #sessionChanges, 1, -1 do
if sessionChanges[i].category == category then
table.remove(sessionChanges, i)
end
end
table.insert(sessionChanges, {
category = category,
item = item,
price = price
})
UpdateCartUI()
end
function UpdateCartUI()
local total = 0
local items = {}
for _, change in ipairs(sessionChanges) do
total = total + change.price
table.insert(items, {
label = change.category .. ': ' .. change.item,
price = change.price
})
end
SendNUIMessage({
action = 'updateCart',
items = items,
total = total
})
end
function ConfirmAndPay()
local total = 0
for _, change in ipairs(sessionChanges) do
total = total + change.price
end
TriggerServerEvent('appearance:server:pay', total, sessionChanges)
end
function CancelSession()
if originalAppearance then
RestoreFullAppearance(PlayerPedId(), originalAppearance)
end
sessionChanges = {}
DestroyPreviewCamera()
end
Po stronie serwera sprawdź, czy gracz ma wystarczające środki przed zatwierdzeniem zmian wyglądu. Jeśli płatność się nie powiedzie, powiadom klienta o konieczności cofnięcia zmian. Rozważ zaoferowanie pakietów, w których strzyżenie włosów i przycięcie brody w pakiecie kosztuje mniej niż suma pojedynczych usług, co zachęci graczy do skorzystania z pełnego zakresu usług podczas jednej wizyty.
Zapisywanie strojów i integracja z garderobą
Rozbuduj system fryzjera i tatuaży o funkcję zapisywania strojów, która pozwala graczom zapisywać kompletne konfiguracje wyglądu jako nazwane presety. Każdy preset zawiera pełny stan wyglądu gracza, w tym styl i kolor włosów, zarost, makijaż oraz wszystkie aktywne tatuaże. Gracze mogą następnie przełączać się między zapisanymi stylizacjami w domowej szafie lub ponownie odwiedzając salon. Przechowuj stroje w bazie danych powiązane z citizen ID gracza za pomocą obiektu JSON zawierającego wszystkie dane wyglądu. Ogranicz liczbę zapisanych strojów na gracza lub zaoferuj dodatkowe sloty jako funkcję premium. Interakcja z szafą w domu gracza powinna wyświetlać prosty panel NUI z listą zapisanych strojów oraz funkcją podglądu, która tymczasowo nakłada dany wygląd, aby gracz mógł go zobaczyć przed zatwierdzeniem. Łączy to system wyglądu postaci z systemem housing, sprawiając, że obie funkcje stają się razem bardziej wartościowe niż osobno.
Lokalizacje sklepów i integracja fryzjera NPC
Zdefiniuj wiele lokalizacji sklepów na mapie, z których każda może oferować inne usługi i mnożniki cen. Budżetowy fryzjer w południowej części miasta może oferować podstawowe strzyżenia w niskich cenach, podczas gdy ekskluzywny salon w Vinewood pobiera wysokie opłaty, ale oferuje unikalne fryzury i kolory. Użyj systemu target lub markerów zbliżeniowych, aby wywołać menu sklepu, gdy gracz zbliży się do fotela fryzjerskiego lub stanowiska tatuażu. Posadź gracza na fotelu za pomocą TaskStartScenarioInPlace ze scenariuszem siedzenia dla salonów fryzjerskich lub ustaw ich w określonym miejscu na czas sesji tatuażu. Spawnuj fryzjerów lub tatuażystów NPC w każdej lokalizacji przy użyciu odpowiednich modeli pedów i przypisz im animacje bezczynności, dzięki którym wyglądają, jakby pracowali. Kiedy gracz wchodzi w interakcję ze sklepem, NPC może odtworzyć animację pracy w pobliżu gracza, aby zasymulować wykonywanie usługi, co dodaje wizualnej immersji. Po sesji NPC wraca do pozycji bezczynności. Zapisuj przychody każdego lokalu w bazie danych, aby wspierać rozgrywkę ze sklepami prowadzonymi przez graczy, gdzie przedsiębiorcy kupują i zarządzają własnymi salonami fryzjerskimi lub studiami tatuażu, ustalając ceny i zatrudniając personel NPC lub innych graczy jako pracowników.