X3D ตกแต่งตัวละคร
หน้าตกแต่งตัวละคร 77 ช่อง กล้องหมุนซูม ท่าโพส ไฟส่องหน้า ไม่ต้องลง skinchanger ไม่ต้องลงฐานข้อมูล ต่อกับเฟรมเวิร์กไหนก็ได้
ซื้อ / รับฟรีตั้งค่า configการติดตั้ง
- แตกไฟล์แล้ว เปลี่ยนชื่อโฟลเดอร์เป็น
x3d_skin(สคริปต์อื่นเรียก export ด้วยชื่อนี้) - วางใน
resources/ - เพิ่ม
ensure x3d_skinในserver.cfg
จบ — ไม่ต้องลงฐานข้อมูล ไม่ต้องลงอะไรเพิ่ม ไม่ต้องแตะ resource เดิมของเซิร์ฟ ใช้ได้ทั้ง ESX, QBCore และเซิร์ฟเปล่า
ผู้เล่นเปิดด้วย /skin · แอดมินเปิดให้คนอื่นด้วย /skin <id>
ให้สิทธิ์แอดมิน:
add_ace group.admin x3d_skin.admin allow
⚠ เซิร์ฟที่มี esx_skin อยู่แล้ว มันจด /skin ไว้เหมือนกัน — ตัวที่ ensure ทีหลังเป็นคนตอบ วาง ensure x3d_skin ไว้หลัง ensure esx_skin หรือเปลี่ยน Config.Command
ช่องที่แก้ได้
77 ช่องในโหมดปกติ แบ่งเป็น 6 หมวดในแถบล่าง:
| หมวด | มีอะไร |
|---|---|
| พื้นฐาน | เพศ · ใบหน้า · ผิว |
| ผมและดวงตา | ทรงผม สีผม ไฮไลท์ · สีตา · ทรงคิ้ว สีคิ้ว ความเข้ม |
| เสื้อผ้า | เสื้อชั้นใน เสื้อ ลวดลาย แขนเสื้อ เกราะ กางเกง รองเท้า (พร้อมช่องสีของแต่ละชิ้น) |
| เครื่องประดับ | หน้ากาก สร้อยคอ หมวก แว่นตา นาฬิกา กำไล ต่างหู |
| แต่งหน้า | เมกอัพ ลิปสติก ปัดแก้ม เครา |
| รายละเอียด | อายุ ตำหนิ สีผิวไม่สม่ำเสมอ รอยแดดเผา ไฝ ขนหน้าอก ตำหนิร่างกาย |
เพดานของแต่ละช่องถามจากเกมตอนรัน (GetNumberOfPedDrawableVariations และเพื่อน ๆ) ไม่ได้ฮาร์ดโค้ดไว้ — ลงชุด addon เพิ่มเมื่อไหร่ ผู้เล่นเลือกได้ครบทันทีโดยไม่ต้องแก้ config
เปลี่ยน Config.AdvancedFace = true เพื่อสลับจาก "เลือกหน้าสำเร็จรูป 46 แบบ" ไปเป็นระบบพ่อ/แม่ + น้ำหนักหน้า + ดัดจมูก แก้ม คาง ทีละจุดแบบ GTA Online
⚠ สลับค่านี้หลังเปิดเซิร์ฟแล้ว หน้าที่ผู้เล่นเซฟไว้จะเพี้ยน เพราะเป็นคนละชุดค่ากัน
การควบคุมในพาเนล
| ปุ่ม | ทำอะไร |
|---|---|
| LMB + ลาก | หมุนตัวละคร |
| RMB + ลาก | เลื่อนมุมกล้อง (มีขอบเขต ส่องไปที่อื่นไม่ได้) |
| Scroll | ซูมเข้า/ออก |
| Q / E | หมุนตัวละครทีละองศา |
| คลิกช่อง + Scroll | ปรับค่าช่องนั้น |
| Shift + Scroll | ปรับทีละ ±10 |
| ลูกศร | ปรับละเอียด |
ปุ่มซ้ายล่าง: โฟกัสหัว · โฟกัสตัว · รีเซ็ตกล้อง · ไฟส่องหน้า · ท่าโพส (4 แบบ) · ซ่อนผู้เล่นคนอื่นในรัศมี 50 เมตร
กล้องเลื่อนไปยังจุดที่กำลังแก้เอง แต่พอผู้เล่นหมุน/ซูมเองแล้ว กล้องจะหยุดขยับเองภายในหมวดนั้น จนกว่าจะเลือกช่องในหมวดอื่น
เปิดจากร้าน (ร้านตัดผม ร้านเสื้อผ้า)
exports['x3d_skin']:OpenEditor({
title = 'ร้านตัดผม',
price = 500,
restrict = { 'hair_1', 'hair_2', 'hair_color_1', 'hair_color_2',
'beard_1', 'beard_2', 'beard_3', 'beard_4' },
onSubmit = function() TriggerServerEvent('myshop:charge', 500) end,
onCancel = function() print('ยกเลิก') end,
})
restrict จำกัดช่องที่แก้ได้ · price ทำให้ปุ่มยืนยันกลายเป็น "ซื้อ" พร้อมราคา
⚠ ทางนี้ (OpenEditor เอง) ไม่หักเงินให้ — ตัดเงินใน onSubmit ของร้านคุณเอง · ร้านที่อยู่ใน Config.Shops คนละทาง ตรงนั้นเซิร์ฟเวอร์หักเงินและบันทึกให้ในก้าวเดียว
เข้ากับ skinchanger / esx_skin ที่มีอยู่แล้ว
ไม่ต้องตั้งอะไร สคริปต์ดูเองตอนเปิดเซิร์ฟ:
- มี
skinchangerอยู่แล้ว → ใช้ของเดิมใส่เสื้อผ้าลงร่าง และให้esx_skinเก็บชุดทางเดิมของมัน x3d_skin เป็นแค่ UI + ร้าน + ตู้เสื้อผ้า - ไม่มี → x3d_skin ใส่เองและเก็บเองในไฟล์ JSON
ทั้งสองทางไม่ต้อง stop อะไร ไม่ต้องย้ายข้อมูล ตัวละครเก่าอยู่ครบ
บังคับทางใดทางหนึ่งได้ที่ Config.Backend ('skinchanger' / 'self') ถ้าจำเป็น
มากับซิปด้วย: optional/skinchanger + optional/esx_skin
เซิร์ฟ ESX ส่วนใหญ่มีสองตัวนี้อยู่แล้ว — มีอยู่แล้วก็ข้ามโฟลเดอร์ optional/ ไปเลย x3d_skin ทำงานกับของเดิมได้อยู่แล้ว
ยังไม่มี? ย้ายออกมาไว้ใน resources/ แล้ว ensure ก่อน x3d_skin:
ensure skinchanger
ensure esx_skin
ensure x3d_skin
skinchanger= ของ ESX Legacy เดิม ตัดการ@includeไฟล์ locale ของ es_extended ออกให้ลงได้โดยไม่ erroresx_skin= เหลือเฉพาะส่วน API (เก็บคอลัมน์skin+ รับ event ชื่อเดิม) ไม่มี UI ซ้อนกับพาเนล และตั้งชื่อตารางผู้เล่นได้ใน config
สองตัวนี้เป็นของ ESX Legacy สัญญาอนุญาต GPL v3 รายละเอียดอยู่ใน optional/README.md
esx_skin ที่แถมมายังมี esx_skin:save, callback esx_skin:getPlayerSkin และ event เปิดเมนูแบบเก่า — event พวกนี้จะเปิดพาเนลของ x3d_skin ให้ สคริปต์เดิมที่ยังเรียกอยู่จึงใช้ต่อได้
ร้าน (Config.Shops)
ร้านเสื้อผ้า 14 จุด ร้านเสริมสวย 7 จุด ร้านหน้ากาก 1 จุด อยู่ในสคริปต์นี้เลย ไม่ต้องลงร้านแยก
- หมุดบนแผนที่โผล่เมื่อเข้าใกล้ 120 เมตร (
blipDistance = 0= โชว์ตลอด) - วงกลมบนพื้น + ป้าย
[E] ร้านเสื้อผ้า ($500)ภาษาไทยอ่านออก - แต่ละประเภทกำหนดราคาและช่องที่แก้ได้เองใน
Config.ShopTypesพร้อมแท็บย่อย (เสื้อผ้า / เครื่องประดับ ฯลฯ) Config.Shops.enabled = false= ปิดร้านทั้งหมด เหลือแค่/skin- ตั้ง
price = 0= เข้าใช้ฟรี ไม่แตะระบบเงินเลย
เงินถูกหักและชุดถูกบันทึกในก้าวเดียวกันฝั่งเซิร์ฟเวอร์ ตอนกดยืนยัน เซิร์ฟเวอร์เช็กใหม่หมดว่ายังยืนอยู่ที่ร้านจริง เงินยังพอจริง แล้วค่อยหักและบันทึก หักไม่ผ่าน = ไม่บันทึกอะไรเลย · เขียนไม่ลง = คืนเงินทันที
⚠ ร้านเสื้อผ้าแบบเดิม (สคริปต์ร้านเสื้อผ้าแยก + esx_skin) คิดเงินคนละทางกับที่บันทึกชุด ผู้เล่นที่บล็อกข้อความคิดเงินจะได้ชุดฟรี — ตัวนี้ปิดช่องนั้นแล้ว
ต่อระบบเงินของเซิร์ฟตัวเองที่ addons/framework/server.lua (ESX มาให้แล้ว QBCore คอมเมนต์รออยู่) ถ้ายังไม่ต่อ ร้านที่มีราคาจะไม่เปิดและบอกให้แจ้งแอดมิน — ไม่แจกฟรี
ตู้เสื้อผ้า (Config.Wardrobe)
เซฟชุดที่ใส่อยู่พร้อมตั้งชื่อ กดใส่ทีเดียวทั้งชุด เปลี่ยนชื่อ ทับชุดเดิม และลบได้ เก็บได้คนละ maxPerType ชุด (ค่าเริ่มต้น 20) ชื่อซ้ำกันไม่ได้
ชุดที่ใส่จากตู้จะทับเฉพาะเสื้อผ้า หน้าตาไม่ขยับ — เก็บหน้าเดิมไว้เสมอ
รายการชุดเป็นของเซิร์ฟเวอร์ ส่งลงมาให้พาเนลทุกครั้งที่มีอะไรเปลี่ยน พาเนลไม่ได้เป็นคนตัดสินว่ามีชุดอะไรบ้าง
exports ฝั่งเซิร์ฟเวอร์
exports['x3d_skin']:GetSkin(src) --> table หน้าที่เซฟไว้ หรือ nil
exports['x3d_skin']:SetSkin(src, skin) --> boolean (ตรวจแบบเดียวกับที่ผู้เล่นเซฟเอง)
exports['x3d_skin']:OpenEditor(src) --> boolean
GetSkin รับได้ทั้ง server id และ identifier · SetSkin ไม่ติด rate limit ระบบยูนิฟอร์มเปลี่ยนชุดให้หลายคนพร้อมกันได้
exports ฝั่ง client
exports['x3d_skin']:GetSkin()
exports['x3d_skin']:GetMaxValues()
exports['x3d_skin']:LoadSkin(skin)
exports['x3d_skin']:LoadClothes(playerSkin, clothesSkin) -- ทับเฉพาะเสื้อผ้า หน้าไม่ขยับ
exports['x3d_skin']:Change('torso_1', 5)
exports['x3d_skin']:OpenEditor(opts)
exports['x3d_skin']:CloseEditor()
exports['x3d_skin']:IsOpen()
hook
-- server-internal ไม่ใช่ net event — client ยิงเองไม่ได้
AddEventHandler('x3d_skin:onSave', function(src, identifier, skin)
print(('%s เซฟหน้าใหม่'):format(identifier))
end)
ฝั่ง client มี x3d_skin:opened กับ x3d_skin:closed ให้สคริปต์อื่นซ่อน HUD ของตัวเองตอนพาเนลเปิด
ที่เก็บข้อมูล
หน้าตาของผู้เล่นเก็บฝั่งเซิร์ฟเวอร์เป็นไฟล์ JSON เดียว (Config.Storage.File ค่าเริ่มต้น data/skins.json) โหลดครั้งเดียวตอน resource เริ่ม เขียนกลับทุก Config.Storage.FlushSeconds วินาที ตอนผู้เล่นออก และตอนปิด resource แบบปกติ คีย์ที่ใช้คือ identifier ถาวร (license/discord/steam) ไม่ใช่ server id
ไฟล์เสียจะถูกเก็บไว้เป็น data/skins.broken.json แล้วเริ่มใหม่จากว่าง พร้อมเตือนใน console ไม่ทับทิ้งเงียบ ๆ
ใช้ MySQL แทน:
- รัน
install.sqlผ่าน oxmysql - เปิด
addons/storage/server.luaเลื่อนไปท้ายไฟล์ คัดลอกบล็อก oxmysql ทับสี่ฟังก์ชันStorage.Get/Set/All/Flushด้านบน
ความปลอดภัย (มีเทสคุมทุกข้อ)
- ค่าที่พาเนลส่งมาไม่เชื่อสักตัว — ชื่อช่องต้องอยู่ใน
components.luaค่าต้องเป็นตัวเลข ถูก floor และ clamp เข้าช่วง คีย์แปลกปลอมถูกทิ้ง - คีย์เก็บเป็น identifier เสมอ ไม่ใช่ server id ที่ถูกใช้ซ้ำกับคนอื่นได้
- เซฟได้ 1 ครั้ง/วินาที/คน cooldown ถูกล้างทันทีที่ผู้เล่นออก
/skin <id>ต้องมี ace เสมอ แม้เปิดConfig.PlayersCanEditให้ทุกคนแก้ตัวเองได้- เลขที่เกินช่วงถูก clamp ตอนใส่ลงร่างเท่านั้น ไม่เขียนกลับลงชุดที่เซฟ — ชุด addon ที่โหลดไม่ทันจะไม่ล้างเสื้อผ้าผู้เล่นเป็น 0 ถาวร
- พาเนลปิดเองตอนตายและตอน resource หยุด คืนเมาส์และคืนการควบคุมทุกครั้ง
pip install lupa
python tools/lua-test/x3d_skin_check.py
config (config.lua)
| คีย์ | ทำอะไร |
|---|---|
Config.Locale |
ภาษาข้อความฝั่ง Lua (th/en) — พาเนลมีปุ่มสลับภาษาของตัวเอง |
Config.Command / Config.Key |
คำสั่งเปิด (/skin) และปุ่มลัด (ว่าง = ไม่ผูกปุ่ม) |
Config.AdminAce |
ace ที่ต้องมีถึงจะเปิดให้คนอื่นได้ |
Config.PlayersCanEdit |
false = แก้ได้เฉพาะคนมี ace |
Config.AdvancedFace |
ระบบหน้าแบบง่าย หรือแบบพ่อ/แม่ |
Config.ApplyOnSpawn / Config.FirstTimeSetup |
ใส่หน้าที่เซฟไว้ตอนเกิด · เปิดพาเนลให้คนที่ยังไม่เคยเซฟ |
Config.Storage.File / .FlushSeconds |
ไฟล์ที่เก็บและความถี่ในการเขียน |
Config.Camera |
ระยะซูม ขอบเขตการเลื่อนกล้อง ไฟส่องตัวละคร |
Config.HideHud |
ซ่อนเรดาร์/HUD ระหว่างเปิดพาเนล |
Config.Shops / Config.ShopTypes / Config.ShopLocations |
ร้าน — เปิดปิด ราคา ช่องที่แก้ได้ และที่ตั้ง |
Config.Wardrobe |
ตู้เสื้อผ้า — เปิดปิด จำนวนชุดสูงสุดต่อคน |
Config.Backend |
auto/skinchanger/self — ใครใส่ค่าลงร่างและใครเก็บชุด |
ช่วยเหลือ
Discord: discord.gg/x3d-developer · เว็บ: x3d-developer.com