เอกสาร

X3D ตกแต่งตัวละคร

หน้าตกแต่งตัวละคร 77 ช่อง กล้องหมุนซูม ท่าโพส ไฟส่องหน้า ไม่ต้องลง skinchanger ไม่ต้องลงฐานข้อมูล ต่อกับเฟรมเวิร์กไหนก็ได้

ซื้อ / รับฟรีตั้งค่า config

การติดตั้ง

  1. แตกไฟล์แล้ว เปลี่ยนชื่อโฟลเดอร์เป็น x3d_skin (สคริปต์อื่นเรียก export ด้วยชื่อนี้)
  2. วางใน resources/
  3. เพิ่ม 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 ออกให้ลงได้โดยไม่ error
  • esx_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 แทน:

  1. รัน install.sql ผ่าน oxmysql
  2. เปิด 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

ดูหน้าสินค้า