FiveM Wait คืออะไร ใช้อย่างไรไม่ให้ Script กิน CPU
FiveM Wait() คือ Function ของ CfxLua ที่ใช้พักการทำงานของ Coroutine หรือ Thread ปัจจุบันตามจำนวน Milliseconds ที่กำหนด ก่อนให้ FiveM Scheduler กลับมาทำ Code ต่ออีกครั้ง
ตัวอย่าง
CreateThread(function()
print('Start')
Wait(2000)
print('2 seconds later')
end)
Wait(2000) หมายถึงพัก Thread ประมาณ 2 วินาที
ส่วน
Wait(0)
ไม่ได้หมายถึง “ไม่รอ” แต่หมายถึง Yield Coroutine แล้วกลับมาทำงานอีกครั้งใน Game Tick ถัดไป
ความเข้าใจเรื่อง Wait() สำคัญมาก เพราะ FiveM Script ที่เขียน Loop ผิด เช่น
while true do
-- code
end
โดยไม่มี Wait() อาจทำให้ Client ค้างหรือ Crash ได้
ในทางกลับกัน หากใส่
Wait(0)
ทุก Loop โดยไม่คิด ก็อาจทำให้ Resource ทำงานทุก Tick และกิน CPU โดยไม่จำเป็น
① FiveM Wait คืออะไร
รูปแบบเต็มคือ
Citizen.Wait(milliseconds)
และสามารถเขียนแบบสั้นได้ว่า
Wait(milliseconds)
ตัวอย่าง
Wait(1000)
หมายถึงพัก Coroutine ปัจจุบันประมาณ 1,000 Milliseconds หรือประมาณ 1 วินาที
Wait() ใช้ Lua Coroutine อยู่เบื้องหลัง
จึงไม่ได้เป็นการหยุด FiveM Server หรือหยุดเกมทั้งหมด
มันพักเฉพาะ Execution Flow ปัจจุบันที่กำลัง Yield ได้
② Citizen.Wait กับ Wait ต่างกันไหม
ใน FiveM Lua คุณอาจพบทั้ง
Citizen.Wait(1000)
และ
Wait(1000)
รูปแบบสั้นคือ Alias ที่ช่วยให้เขียน Code กระชับขึ้น
ดังนั้นสองตัวอย่างนี้ใช้แนวคิดเดียวกัน
Citizen.Wait(1000)
กับ
Wait(1000)
Resource สมัยใหม่จำนวนมากนิยมใช้ Wait() เพราะอ่านง่ายกว่า
③ Wait ใช้ทำอะไร
ใช้กับงาน เช่น
Delay
Loop
Polling
รอโหลด Model
รอ Animation Dictionary
ตรวจ Player State
Marker
Controls
Periodic Task
Retry
Cooldown Logic บางประเภท
Background Thread
ตัวอย่าง
CreateThread(function()
while true do
Wait(1000)
print('Checking player...')
end
end)
Script จะทำงานเป็นช่วงแทนที่จะวนอย่างต่อเนื่องโดยไม่พัก
④ Wait ใช้กับ CreateThread อย่างไร
Pattern ที่พบบ่อยที่สุดคือ
CreateThread(function()
while true do
Wait(1000)
-- logic
end
end)
Flow คือ
CreateThread
↓
while true
↓
Wait
↓
Yield
↓
Scheduler
↓
กลับมาทำงาน
↓
วนรอบใหม่
นี่เป็นโครงสร้างพื้นฐานของ Lua FiveM Script จำนวนมาก
⑤ ทำไม while true ต้องมี Wait
ตัวอย่างที่อันตรายคือ
CreateThread(function()
while true do
print('running')
end
end)
Loop ไม่มีจุด Yield
จึงพยายามทำงานต่อเนื่องไม่คืน Execution ให้ Scheduler อย่างเหมาะสม
Cfx.re เตือนชัดเจนว่า while true do ที่ไม่มี Wait() สามารถทำให้ Client Game ค้างหรือ Crash ได้
ดังนั้นควรจำว่า
Infinite Loop
+
ไม่มี Wait
=
อันตราย
⑥ Wait(0) คืออะไร
Wait(0) หมายถึง
Yield Coroutine ปัจจุบันจนถึง Game Tick ถัดไป
ตัวอย่าง
CreateThread(function()
while true do
Wait(0)
print('Next tick')
end
end)
Logic จะถูกประเมินประมาณทุก Tick
จึงเหมาะกับสิ่งที่ต้องทำต่อเนื่องระดับ Frame/Tick จริง ๆ
⑦ Wait(0) ไม่ได้หมายถึง 0 Milliseconds แบบธรรมดา
นี่เป็นจุดที่มือใหม่เข้าใจผิดบ่อย
Wait(0)
ไม่ได้หมายความว่า Loop ไม่พักเลย
มัน Yield ให้ Scheduler แล้วรอจน Tick ถัดไป
แตกต่างจาก
while true do
end
ซึ่งไม่มี Yield
ดังนั้น
Wait(0)
≠
ไม่มี Wait
⑧ Game Tick คืออะไร
Game Tick คือรอบการทำงานของระบบเกมและ Scheduler
บน Client Tick มีความสัมพันธ์กับ Frame Rate
ตัวอย่างที่ Cfx.re ยกไว้:
60 FPS
≈ 16.6 ms ต่อ Tick
180 FPS
≈ 5.5 ms ต่อ Tick
ดังนั้น Tick ไม่ได้มีระยะเวลาเท่ากันทุกเครื่อง
Player ที่ FPS สูงอาจมี Game Tick ถี่กว่า Player ที่ FPS ต่ำ
⑨ Wait(0) ทำงานกี่ครั้งต่อวินาที
ขึ้นอยู่กับ Game Tick และ FPS
เช่นเครื่องที่ประมาณ 60 FPS Logic แบบ
while true do
Wait(0)
-- code
end
อาจถูกประเมินประมาณ 60 รอบต่อวินาที
ในเครื่อง FPS สูงอาจมากกว่านั้น
ดังนั้น Logic ที่อยู่ใน Wait(0) ต้องเบาและจำเป็นจริง
⑩ Wait(1) เหมือน Wait(0) ไหม
ไม่ควรถือว่าเหมือนกัน
Wait(0) มีความหมายชัดเจนว่า
กลับมาที่ Game Tick ถัดไป
ส่วน Wait(1) ขอพักตามเวลา Milliseconds และการกลับมาทำงานยังขึ้นกับ Scheduler
ถ้าระบบต้องทำทุก Frame Cfx.re แนะนำให้ใช้
Wait(0)
แทนค่าอย่าง Wait(5) หรือ Wait(10)
⑪ ทำไม Wait(5) อาจพลาด Frame
เพราะเครื่อง FPS สูงอาจมี Tick ถี่กว่า 5 ms
ตัวอย่าง 180 FPS มี Frame Time ประมาณ
5.5 ms
และเครื่อง FPS สูงกว่านั้นอาจมี Tick สั้นลงอีก
หากต้องทำทุก Frame การกำหนด Milliseconds เองอาจทำให้บาง Tick ไม่ถูกประมวลผล
จึงควรใช้
Wait(0)
เมื่อ Requirement คือ “ทุก Frame”
⑫ Wait(1000) คืออะไร
Wait(1000)
หมายถึงพักประมาณ 1 วินาที
ตัวอย่าง
CreateThread(function()
while true do
Wait(1000)
print('Every second')
end
end)
เหมาะกับงานที่ไม่ต้องตอบสนองระดับ Frame
เช่น
Update Cache
ตรวจ State
Cleanup
Periodic Check
⑬ Wait(5000) คือกี่วินาที
5000 milliseconds
=
5 seconds
ดังนั้น
Wait(5000)
คือพักประมาณ 5 วินาที
ค่าที่ใช้บ่อย เช่น
Wait(0) → Tick ถัดไป
Wait(100) → ~0.1 วินาที
Wait(250) → ~0.25 วินาที
Wait(500) → ~0.5 วินาที
Wait(1000) → ~1 วินาที
Wait(5000) → ~5 วินาที
Wait(60000) → ~1 นาที
แต่เวลาจริงควรมองเป็น “ประมาณ” ตาม Scheduler
⑭ Wait แม่นยำ 100% ไหม
ไม่
Wait(1000) ไม่ควรถูกมองเป็นระบบ Hard Real-time Timer
Thread จะสามารถกลับมาทำงานได้หลังเวลาที่กำหนดและตามการ Scheduling ของ Runtime
ดังนั้นถ้าต้องการจับเวลาระยะยาวหรือคำนวณ Cooldown สำคัญ ควรอิง Timestamp/Game Timer หรือ Server-side Time Logic ตาม Use Case แทนการนับจำนวนรอบ Wait() อย่างเดียว
⑮ Wait ใช้นอก CreateThread ได้ไหม
Wait() ต้องอยู่ใน Coroutine Context ที่สามารถ Yield ได้
Cfx.re ระบุว่า Citizen.Wait ใช้ Lua Coroutines อยู่เบื้องหลัง
จึงไม่สามารถนำไปใช้ใน Context ใดก็ได้แบบ Function ธรรมดาโดยไม่คำนึงถึง Coroutine
Pattern ที่แน่นอนคือ
CreateThread(function()
Wait(1000)
end)
Event Handler บางประเภทก็สามารถ Yield ได้ตาม Runtime แต่ต้องเข้าใจ Context ก่อนใช้งาน
⑯ Wait ทำให้ FiveM ทั้ง Server หยุดไหม
ไม่
สมมติมี
CreateThread(function()
Wait(10000)
print('done')
end)
Thread นี้พัก 10 วินาที
แต่ Thread และ Resource Logic อื่นยังสามารถทำงานต่อได้
นี่เป็นข้อดีของ Coroutine-based Scheduling
⑰ Wait กับ Sleep เหมือนกันไหม
Developer FiveM มักเรียกค่า Wait ว่า
sleep
เช่น
local sleep = 1000
Wait(sleep)
คำว่า sleep ในที่นี้เป็นเพียง Variable Name
Function จริงยังเป็น
Wait()
แนวคิดคือกำหนดเวลาพักแบบ Dynamic
⑱ Dynamic Wait คืออะไร
แทนที่จะใช้ค่าเดิมตลอด
Wait(1000)
สามารถสร้าง
local sleep = 1000
แล้วปรับตามสถานการณ์
if playerNearShop then
sleep = 0
end
Wait(sleep)
นี่เรียกกันบ่อยว่า Adaptive Sleep หรือ Dynamic Wait
⑲ Adaptive Wait ช่วยอะไร
สมมติ Player อยู่ห่าง Shop หลายกิโลเมตร
ไม่จำเป็นต้อง Draw Marker และตรวจ Keyboard ทุก Frame
สามารถใช้
อยู่ไกล
→ Wait(1000)
เข้าใกล้
→ Wait(0)
เมื่อ Player เข้าใกล้จึงเพิ่มความถี่ในการตรวจ
ช่วยลด Client CPU โดยไม่ทำให้ Interaction ช้าเมื่อ Player อยู่หน้า Shop
⑳ ตัวอย่าง Adaptive Wait
local shopCoords =
vector3(
25.7,
-1345.2,
29.5
)
CreateThread(function()
while true do
local sleep = 1000
local ped =
PlayerPedId()
local coords =
GetEntityCoords(ped)
local distance =
#(coords - shopCoords)
if distance < 20.0 then
sleep = 0
DrawMarker(
1,
shopCoords.x,
shopCoords.y,
shopCoords.z - 1.0,
0.0,
0.0,
0.0,
0.0,
0.0,
0.0,
1.0,
1.0,
1.0,
255,
255,
255,
150,
false,
false,
2,
false,
nil,
nil,
false
)
end
Wait(sleep)
end
end)
เมื่ออยู่ไกล Resource ไม่ต้องทำงานทุก Tick
เมื่อเข้ามาใกล้จึงเปลี่ยนเป็น Wait(0)
㉑ ทำไม local sleep ควรอยู่ใน Loop
ควรทำแบบนี้
while true do
local sleep = 1000
-- logic
Wait(sleep)
end
เพราะทุก Loop จะ Reset ค่าเริ่มต้นใหม่
ถ้าเขียน
local sleep = 1000
while true do
if condition then
sleep = 0
end
Wait(sleep)
end
แล้วลืมปรับกลับ ค่า Sleep อาจค้างอยู่ที่ 0 ตลอดหลัง Condition เคยเป็นจริง
㉒ Wait เท่าไรดีที่สุด
ไม่มีตัวเลขเดียวที่ดีที่สุด
ต้องขึ้นกับ Requirement
ตัวอย่างคร่าว ๆ:
ต้อง Render ทุก Frame
→ Wait(0)
Controls สำคัญขณะอยู่ใน Zone
→ Wait(0)
ตรวจ Player อยู่ใกล้พื้นที่หรือไม่
→ 250–1000 ms ตามระบบ
Refresh Data ที่เปลี่ยนไม่บ่อย
→ หลายวินาที
Cleanup
→ หลายสิบวินาทีหรือหลายนาที
ต้องเลือกจากความเร็วที่ Feature ต้องตอบสนองจริง
㉓ Wait(0) ใช้กับ Marker ทำไม
Native อย่าง DrawMarker() ต้องถูกเรียกต่อเนื่องเพื่อแสดง Marker
ดังนั้นเมื่อ Player อยู่ใกล้จุดที่ต้องแสดง มักใช้
Wait(0)
เพื่อ Draw ทุก Frame
แต่ไม่จำเป็นต้องทำ DrawMarker() ทุก Frameเมื่อ Player อยู่คนละฝั่งของแผนที่
จึงควรใช้ Distance Check + Adaptive Sleep
㉔ Wait(0) ใช้กับ Controls ทำไม
การตรวจ Keyboard/Controller เช่น
IsControlJustReleased(
0,
38
)
มักต้องตรวจต่อเนื่องในช่วงที่ Interaction Active
เช่น
if distance < 2.0 then
if IsControlJustReleased(
0,
38
) then
-- open
end
end
ถ้าใช้ Wait(1000) Player อาจกดปุ่มระหว่างช่วงที่ Thread กำลังหลับและ Interaction ตอบสนองช้า
㉕ แต่ต้องตรวจ Controls ทุกเวลาหรือไม่
ไม่
สามารถทำ
Player อยู่ไกล
→ ไม่ตรวจ E ทุก Frame
Player เข้า Interaction Zone
→ Wait(0)
→ ตรวจ E
นี่เป็นแนวทางที่ดีกว่า
Player อยู่ที่ไหนก็ได้
→ ตรวจ E ทุก Frame
ตลอด Session
㉖ Wait กับ Native Call
สมมติ Thread ทำ
local ped =
PlayerPedId()
local coords =
GetEntityCoords(ped)
ทุก Tick
ถ้า Requirement ไม่ต้องละเอียดระดับ Frame สามารถเพิ่ม Wait เป็น
Wait(500)
ลดจำนวน Native Calls ต่อวินาทีลงอย่างมาก
Cfx.re แนะนำให้หลีกเลี่ยง Heavy Natives แบบ Per-tick เมื่อไม่จำเป็น
㉗ Native ไหนหนักต้องรู้ทั้งหมดไหม
ไม่จำเป็นต้องจำ Cost ของ Native ทุกตัว
หลักที่ดีกว่าคือ
เรียก Native นี้บ่อยแค่ไหน?
×
จำนวน Entity เท่าไร?
×
จำนวน Thread เท่าไร?
จากนั้นวัดจริงด้วย Resource Monitor หรือ Profiler
อย่า Optimize จากการเดาเพียงอย่างเดียว
㉘ Cache Native Result ช่วยได้ไหม
ได้ในบางกรณี
สมมติ Resource เรียก
PlayerPedId()
หลายจุดถี่มาก
สามารถ Cache ค่าที่เปลี่ยนไม่บ่อยใน Thread ที่ช้ากว่าได้ตาม Architecture
Cfx.re ยกการ Cache Native Results ที่เปลี่ยนไม่บ่อยเป็นหนึ่งใน Best Practices ของ Wait()
แต่ Cache ต้องไม่เก่าจน Logic ผิด
㉙ ตัวอย่าง Cache Thread
local playerPed = 0
CreateThread(function()
while true do
playerPed =
PlayerPedId()
Wait(1000)
end
end)
Resource อื่นภายใน Script สามารถใช้
playerPed
แทนการเรียก Native ซ้ำบางกรณี
แต่ถ้า Feature ต้องรู้ Ped ที่ถูกต้องทันทีหลัง Ped เปลี่ยน ต้องออกแบบ Update ให้เร็วพอ
㉚ Wait กับ Distance Check
ระบบ Nearby Interaction มักมี
local coords =
GetEntityCoords(
PlayerPedId()
)
แล้วคำนวณ Distance
หากมี 1 จุดไม่หนักมาก
แต่ถ้ามี
500 Shops
300 Garages
200 Interaction Points
การ Loop ทุกจุดทุก Frameอาจแพง
แค่เพิ่ม Wait() อย่างเดียวอาจไม่พอ ต้อง Optimize Data Structure และ Zone System ด้วย
㉛ Wait ไม่สามารถแก้ Algorithm ที่แย่ได้ทั้งหมด
สมมติ Code ทุก 1 วินาทีทำ
Loop 100,000 Records
แม้มี
Wait(1000)
Script ก็ยังอาจกระตุกทุกครั้งที่ Loop ทำงาน
ดังนั้น Performance ต้องดูทั้ง
Frequency
+
Work per iteration
ไม่ใช่แค่ค่า Wait
㉜ Wait กับ Event-driven Design
สมมติ Job เปลี่ยนเพียงตอน Server ส่ง Event
แทน
CreateThread(function()
while true do
Wait(1000)
-- ask job
end
end)
อาจใช้
Job Changed Event
↓
Update cached job
Event-driven Design ทำงานเฉพาะเมื่อข้อมูลเปลี่ยน
จึงช่วยลด Polling ที่ไม่จำเป็น
㉝ Polling ยังจำเป็นไหม
จำเป็นในบาง Use Case
เช่น
Nearby Detection
Controls
Rendering
State ที่ไม่มี Event ให้ใช้
Retry/Health Check บางประเภท
ไม่ได้หมายความว่า Thread/Loop เป็น Design ที่ไม่ดี
สิ่งสำคัญคือเลือก Frequency ตาม Requirement
㉞ Wait กับ Model Loading
ตัวอย่างจากเอกสาร FiveM
RequestModel(model)
while not HasModelLoaded(model) do
Wait(500)
end
Loop จะตรวจ Model เป็นช่วงจนโหลดสำเร็จ
นี่เป็นตัวอย่างที่ดีของการใช้ Wait() เพื่อไม่ให้ Loop Busy-spin ระหว่างรอ Resource
㉟ ใช้ Wait(0) ตอนโหลด Model ได้ไหม
ได้ใน Context ที่เหมาะสม
เช่น
while not HasModelLoaded(model) do
Wait(0)
end
จะตรวจทุก Tick
แต่ถ้าไม่จำเป็นต้องตอบสนองเร็วระดับ Frame สามารถใช้ค่า Delay มากขึ้น
เลือกตาม UX และ Cost
㊱ ควรมี Timeout ตอนรอ Model ไหม
สำหรับ Resource ที่ต้อง Robust มากขึ้น ควรพิจารณา Timeout
แทนที่จะ
while not HasModelLoaded(model) do
Wait(0)
end
วนตลอดหากมีปัญหา
สามารถออกแบบ
Start Time
↓
Request Model
↓
Wait
↓
ถ้านานเกินกำหนด
→ Abort
ช่วยป้องกัน Thread รอไม่จบใน Edge Case
㊲ Wait กับ Animation Dictionary
หลักเดียวกัน
RequestAnimDict
↓
ยังไม่โหลด
↓
Wait
↓
ตรวจใหม่
↓
โหลดแล้ว
↓
เล่น Animation
อย่าเขียน Busy Loop โดยไม่มี Wait()
เพราะ Resource กำลังรอข้อมูลอยู่ ไม่มีเหตุผลต้องยึด Execution ตลอดเวลา
㊳ Wait กับ Server Script
Wait() ใช้ฝั่ง Server Lua ได้เช่นกัน
ตัวอย่าง
CreateThread(function()
while true do
Wait(60000)
print('Server task')
end
end)
นี่คือ Server Periodic Task ทุกประมาณ 1 นาที
แต่ต้องระวังงานภายใน Thread เพราะ Server CPU มีผลกับผู้เล่นทั้งหมด
㊴ Server ควรใช้ Wait(0) ไหม
มี Use Case ที่ใช้ได้ แต่โดยทั่วไปต้องถามให้หนักกว่าฝั่ง Clientว่า Logic จำเป็นต้องทำทุก Server Tickจริงหรือไม่
ตัวอย่างที่ควรระวัง
CreateThread(function()
while true do
Wait(0)
for _, playerId in ipairs(
GetPlayers()
) do
-- heavy logic
end
end
end)
Player มากขึ้น Cost จะเพิ่มขึ้นตาม
㊵ อย่า Loop Player ทุก Tick ถ้าไม่จำเป็น
ถ้ามี 200 Players และ Thread ตรวจทุก Player ทุก Tick
จำนวน Iterations สามารถสูงมาก
หากระบบยอมรับ Delay
Wait(1000)
หรือ
Wait(5000)
อาจเหมาะกว่า
หรือใช้ Event เมื่อ Player State เปลี่ยน
㊶ Wait กับ Database Query
ตัวอย่างที่ควรหลีกเลี่ยง
CreateThread(function()
while true do
Wait(100)
-- database SELECT
end
end)
Query Database 10 ครั้งต่อวินาทีโดยไม่มีเหตุผลอาจสร้าง Load สูง
ควรใช้
Cache
Events
Query เมื่อจำเป็น
Scheduled Batch
ตาม Architecture
㊷ Wait ไม่ทำให้ Database Query เร็วขึ้น
การเพิ่ม
Wait(1000)
ก่อน Query ไม่ได้ Optimize SQL Query ตัวนั้น
ถ้า Query ช้า ต้องดู
Index
SQL Structure
จำนวน Rows
Database Server
Query Frequency
Wait ช่วยควบคุม Frequency แต่ไม่แก้ Query ที่ไม่มีประสิทธิภาพ
㊸ Wait กับ Network Event
ไม่ควรทำ
CreateThread(function()
while true do
Wait(0)
TriggerServerEvent(
'player:update'
)
end
end)
เพราะจะสร้าง Network Event ทุก Tick
ถ้าข้อมูลไม่จำเป็นต้องส่งทุก Frame ควร
ส่งเมื่อข้อมูลเปลี่ยน
หรือเพิ่ม Interval ให้เหมาะสม
㊹ Wait(1000) + TriggerServerEvent ดีเสมอไหม
ไม่
แม้เปลี่ยนเป็นทุกวินาที ก็ยังต้องถามว่า Event จำเป็นหรือไม่
Player 500 คน × 1 Event/วินาที
ยังเท่ากับ Event จำนวนมากต่อเนื่อง
บางข้อมูล FiveM/OneSync หรือ State Bags อาจจัดการได้เหมาะกว่า
㊺ Wait กับ NUI Message
ไม่ควรส่ง
SendNUIMessage(...)
ทุก Frame หากข้อมูลไม่เปลี่ยน
ตัวอย่าง HUD Money
ไม่จำเป็นต้องส่ง Balance เดิม 60 ครั้งต่อวินาที
ควรส่งเฉพาะเมื่อ Money เปลี่ยน
Money Changed
↓
SendNUIMessage
ช่วยลดทั้ง Lua และ Browser Processing
㊻ Wait กับ Resmon
Resource ที่ใช้ Thread จำนวนมากและมี Wait(0) พร้อม Heavy Logic อาจปรากฏ Cost สูงใน Resource Monitor
แต่ไม่ควรเห็น Resource สูงแล้วเปลี่ยนทุก Wait(0) เป็น Wait(1000) ทันที
เพราะบางระบบต้องทำทุก Frameจริง
ต้องหาว่า
Thread ไหน
Native ไหน
Loop ไหน
Function ไหน
เป็นต้นเหตุ
㊼ Resource Monitor คืออะไร
Resource Monitor หรือ Resmon ใช้ดู Performance ของ Resource ฝั่ง Client เช่น CPU Time และ Memory
ช่วยดูว่า Resource ไหนอาจใช้เวลา Processing สูง
ถ้า Resource ของคุณสูงผิดปกติ ให้ตรวจ
Wait(0)LoopsHeavy Native Calls
Entity Loops
NUI
Thread Count
แต่ Resmon เป็นจุดเริ่มต้น ไม่ใช่คำตอบสุดท้ายว่า Line ไหนผิด
㊽ Profiler ช่วยอะไร
FiveM Profiler สามารถใช้ทั้ง Client และ Server เพื่อช่วยหา Thread หรือ Code Path ที่ใช้เวลามาก
ถ้าพบ Resource กิน CPU อย่าเดาอย่างเดียว
ใช้ Workflow
พบปัญหา
↓
Resmon / Hitch
↓
Profiler
↓
หา Thread/Function
↓
Optimize
↓
วัดใหม่
จะมีประสิทธิภาพกว่าแก้ตามความรู้สึก
㊾ Wait ต่ำ = Script เร็วกว่าเสมอไหม
ไม่
Wait(0) ทำให้ Logic ถูกตรวจถี่ขึ้น แต่ก็เพิ่ม Work
ความเร็วที่ผู้เล่นรู้สึกขึ้นกับ Requirement
เช่นระบบ Cleanup ไม่มีเหตุผลต้องทำ 60 ครั้งต่อวินาที
ดังนั้น
Wait ต่ำ
≠
Script ดี
และ
Wait สูง
≠
Script Optimize
ต้องเลือกให้เหมาะกับงาน
㊿ Wait สูงเกินไปมีผลอะไร
หาก Interaction ต้องตอบสนองเร็ว แต่ใช้
Wait(5000)
Player อาจต้องรอหลายวินาทีกว่าระบบจะตรวจพบ
ตัวอย่าง Nearby Shop
Player เดินถึง Shop แล้ว Thread กำลังหลับ 5 วินาที
UX จะดูเหมือน Script ไม่ทำงาน
ดังนั้น Optimization ต้องไม่ทำลาย Responsiveness
51 ตัวอย่าง Wait ที่ไม่เหมาะ
CreateThread(function()
while true do
Wait(5000)
if IsControlJustReleased(
0,
38
) then
print('E pressed')
end
end
end)
การตรวจปุ่มทุก 5 วินาทีทำให้แทบจับ Input ไม่ได้
นี่แสดงว่าไม่สามารถเพิ่ม Wait สูง ๆ กับทุกระบบได้
52 ตัวอย่างแยก Slow Check กับ Fast Check
Architecture ที่ดีกว่าอาจแยก
Slow Thread
→ ตรวจว่า Player เข้าใกล้ Zone
Fast Thread
→ ทำงานเมื่อ nearZone = true
ตัวอย่าง
local nearShop = false
Slow Thread:
CreateThread(function()
while true do
Wait(500)
local coords =
GetEntityCoords(
PlayerPedId()
)
nearShop =
#(coords - shopCoords) < 10.0
end
end)
Fast Thread:
CreateThread(function()
while true do
if nearShop then
if IsControlJustReleased(
0,
38
) then
TriggerEvent(
'shop:open'
)
end
Wait(0)
else
Wait(500)
end
end
end)
ช่วยจำกัด Per-frame Work เฉพาะช่วงที่ต้องใช้
53 Thread เยอะกับ Wait สูง ยังหนักได้ไหม
ได้
สมมติมี 1,000 Threads แต่ละตัว
Wait(1000)
ก็ไม่ได้หมายความว่า Architecture ดี
Scheduler ยังต้องจัดการ Threads และแต่ละรอบอาจมี Logic หนัก
จึงควรหลีกเลี่ยงการสร้าง Thread แยกให้ทุก Item/Shop/Entity โดยไม่มีเหตุผล
54 ควรรวม Interaction Points หรือไม่
หลายระบบสามารถใช้ Thread กลางจัดการ Interaction Points
แทน
Shop 1 → Thread
Shop 2 → Thread
Shop 3 → Thread
...
Shop 100 → Thread
อาจใช้
Interaction Manager
↓
ตรวจจุดที่เกี่ยวข้อง
↓
เลือกจุดใกล้ Player
Architecture ที่ดีมักสำคัญกว่าเพียงปรับ Wait
55 Wait กับ Thread Leak
ถ้า Event สร้าง Infinite Thread ทุกครั้ง
RegisterNetEvent(
'system:start',
function()
CreateThread(function()
while true do
Wait(1000)
-- logic
end
end)
end
)
และ Event ถูก Trigger ซ้ำ 20 ครั้ง
ก็อาจมี 20 Threads แม้แต่ละ Thread จะ Wait(1000)
ต้องป้องกันการสร้าง Thread ซ้ำ
56 วิธีป้องกัน Thread ซ้ำ
ใช้ State เช่น
local running = false
local function startSystem()
if running then
return
end
running = true
CreateThread(function()
while running do
Wait(1000)
-- logic
end
end)
end
เมื่อเรียก startSystem() ซ้ำจะไม่สร้าง Thread ใหม่ถ้าตัวเดิมยังทำงานอยู่
57 Wait กับ source ใน Server Event
นี่เป็นรายละเอียดที่สำคัญมาก
หาก Server Event Handler มี
local src = source
ควรเก็บ source ไว้ ก่อน Wait()
ตัวอย่างที่ดี
RegisterNetEvent(
'example:test',
function()
local src = source
Wait(1000)
print(src)
end
)
Cfx.re ระบุว่า Global source รับประกันค่าเฉพาะ Event Call เริ่มต้น และการเรียก Wait() จะทำให้คุณเข้าสู่ Async/Yield Scope ซึ่งไม่ควรพึ่งค่า Global source เดิมต่อไป
58 อย่าเขียนแบบนี้เมื่อใช้ source
RegisterNetEvent(
'example:test',
function()
Wait(1000)
print(source)
end
)
หากต้องการ Player Source เดิมหลังรอ
ควรเป็น
local src = source
Wait(1000)
print(src)
หลักนี้สำคัญกับ Server Events, Database Async Logic และ Callback ที่มี Yield
59 Checklist เลือกค่า Wait
ก่อนใส่ Wait() ถามว่า
① Logic ต้องทำทุก Frameไหม?
② ถ้าไม่ ต้องตอบสนองภายในกี่ ms?
③ ใช้ Event แทน Polling ได้ไหม?
④ Native ที่เรียกหนักไหม?
⑤ Loop Entity/Player จำนวนมากไหม?
⑥ Cache ค่าได้หรือไม่?
⑦ ใช้ Adaptive Wait ได้ไหม?
⑧ Thread ถูกสร้างซ้ำหรือไม่?
⑨ มี Network Event อยู่ใน Loop หรือไม่?
⑩ มี Database Query อยู่ใน Loop หรือไม่?
⑪ มี NUI Message ทุก Tickหรือไม่?
⑫ while true มีเส้นทางที่ไม่เรียก Wait หรือไม่?
⑬ Wait สูงจน UX ช้าหรือไม่?
⑭ วัดด้วย Resmon/Profiler แล้วหรือยัง?
อย่าเลือกค่า Wait จากการ Copy Script คนอื่นเพียงอย่างเดียว
⑥⓪ สูตรจำ FiveM Wait แบบง่ายที่สุด
จำ 4 หลักนี้
Wait(0)
→ Tick ถัดไป
Wait(1000)
→ ประมาณ 1 วินาที
while true
→ ต้องมี Yield/Wait ที่แน่นอน
ไม่ต้องทำทุก Frame
→ อย่าใช้ Wait(0) โดยไม่มีเหตุผล
เพียงเข้าใจสี่ข้อนี้ก็ช่วยลดปัญหา Performance ได้มาก
ตัวอย่าง FiveM Wait แบบเหมาะสม
local location =
vector3(
215.8,
-810.1,
30.7
)
CreateThread(function()
while true do
local sleep = 1000
local ped =
PlayerPedId()
local coords =
GetEntityCoords(ped)
local distance =
#(coords - location)
if distance < 20.0 then
sleep = 0
DrawMarker(
1,
location.x,
location.y,
location.z - 1.0,
0.0,
0.0,
0.0,
0.0,
0.0,
0.0,
1.0,
1.0,
1.0,
255,
255,
255,
150,
false,
false,
2,
false,
nil,
nil,
false
)
if distance < 2.0
and IsControlJustReleased(
0,
38
) then
TriggerEvent(
'garage:open'
)
end
end
Wait(sleep)
end
end)
หลักของ Code นี้คือ
อยู่ไกล
↓
Wait(1000)
↓
ลดการทำงาน
อยู่ใกล้
↓
Wait(0)
↓
Draw Marker + รับ Input ได้ทุก Tick
นี่เป็น Pattern ที่เหมาะกว่าการใช้ Wait(0) ตลอดเวลาสำหรับ Interaction ที่ไม่ได้อยู่ใกล้ Player ตลอด Session
คำถามที่พบบ่อยเกี่ยวกับ FiveM Wait
FiveM Wait คืออะไร
คือ Function สำหรับพัก Coroutine ปัจจุบันตามจำนวน Milliseconds ที่กำหนดก่อนให้ Scheduler กลับมาทำงานต่อ
Citizen.Wait กับ Wait ต่างกันไหม
Wait เป็น Alias แบบสั้นของ Citizen.Wait ใน CfxLua
Wait(0) คืออะไร
Yield Coroutine จนถึง Game Tick ถัดไป
Wait(1000) คือกี่วินาที
ประมาณ 1 วินาที
while true ต้องมี Wait ไหม
ต้องมีเส้นทางที่ Yield อย่างแน่นอน Cfx.re เตือนว่า Infinite Loop ที่ไม่มี Wait() สามารถทำให้ Client ค้างหรือ Crash ได้
Wait(0) ทำให้ Script กิน CPU ไหม
ตัว Wait(0) ไม่ใช่ปัญหาในตัวเอง แต่ทำให้ Logic ถูกประเมินทุก Tick ดังนั้นถ้า Handler หนักหรือมี Native จำนวนมาก CPU Usage สามารถสูงได้
ควรใช้ Wait เท่าไรดีที่สุด
ไม่มีค่าตายตัว ต้องเลือกตามความถี่ที่ Feature ต้องตอบสนอง
Marker ควรใช้ Wait(0) ไหม
ในช่วงที่ต้อง Draw Marker ต่อเนื่อง มักต้องทำทุก Frame แต่สามารถใช้ Adaptive Wait เมื่อ Player อยู่ไกล
ตรวจปุ่มควร Wait เท่าไร
ถ้าต้องตรวจ Input ต่อ Frameในช่วงที่ Interaction Active ใช้ Wait(0) เหมาะกว่า
Query Database ใช้ Wait(0) ได้ไหม
ไม่ควร Query Database ทุก Tick โดยไม่มีเหตุผล ควรใช้ Event, Cache หรือ Interval ที่เหมาะสม
Wait ทำให้ Server Lag ได้ไหม
Wait เองมีไว้ช่วย Yield แต่ Logic ที่อยู่ระหว่างแต่ละ Wait ยังสามารถทำให้ Server หนักได้หากมี Loop, SQL หรือ Calculation จำนวนมาก
Wait ใช้ฝั่ง Server ได้ไหม
ได้ใน CfxLua Coroutine/Thread Context
Wait ใช้นอก Coroutine ได้ไหม
Cfx.re ระบุว่า Citizen.Wait ใช้ Lua Coroutines และไม่สามารถเรียกจาก Context ที่ไม่รองรับ Coroutine/Yield ได้
Wait(5) ดีกว่า Wait(0) ไหม
ไม่เสมอ ถ้าต้องทำทุก Frame Cfx.re แนะนำ Wait(0) เพราะค่าอย่าง 5 หรือ 10 ms สามารถทำให้พลาด Tick บนเครื่อง FPS สูงได้
Resmon สูงแก้ด้วยเพิ่ม Wait อย่างเดียวได้ไหม
ไม่เสมอ ต้องตรวจ Thread, Native Calls, Loops, NUI, Entities และ Algorithm แล้วใช้ Resource Monitor/Profiler หาต้นเหตุจริง
สรุป FiveM Wait คืออะไร ใช้อย่างไรไม่ให้ Script กิน CPU
FiveM Wait() คือเครื่องมือควบคุมการ Yield และความถี่ของ Lua Coroutine ซึ่งมีผลโดยตรงต่อทั้ง Responsiveness และ Performance ของ Resource
สิ่งที่ต้องจำคือ
Wait(0)
หมายถึงกลับมาทำงานใน Game Tick ถัดไป
ส่วน
Wait(1000)
หมายถึงพักประมาณ 1 วินาที
และหากมี
while true do
ต้องแน่ใจว่า Loop มี Wait() หรือ Yield ที่ถูกเรียกอย่างแน่นอน ไม่เช่นนั้นสามารถทำให้เกมค้างได้
แต่การ Optimize ที่ถูกต้องไม่ใช่การเปลี่ยนทุกอย่างเป็น
Wait(1000)
เพราะ Marker, Rendering และ Controls บางระบบจำเป็นต้องทำทุก Frame
แนวทางที่ดีกว่าคือ
ต้องทุก Frame
→ Wait(0)
ไม่ต้องทุก Frame
→ เพิ่ม Interval
ข้อมูลเปลี่ยนเฉพาะเมื่อมีเหตุการณ์
→ ใช้ Event
อยู่ไกล Feature
→ Sleep นาน
เข้าใกล้ Feature
→ Sleep สั้น
Resource ยังหนัก
→ วัดด้วย Resmon / Profiler
สำหรับคนที่กำลังเรียน FiveM Developer กับ comsiam การเลือก Wait() ให้ถูกเป็นหนึ่งในพื้นฐานสำคัญที่สุดของการเขียน Script ที่ลื่น เพราะ Resource จำนวนมากไม่ได้ช้าเพราะ Lua อย่างเดียว แต่ช้าเพราะ Loop ทำงานถี่เกินความจำเป็น
หลักที่ควรติดเป็นนิสัยจาก comsiam คือ อย่าเริ่มจากถามว่า Wait เท่าไรดีที่สุด แต่ให้ถามก่อนว่า Logic นี้จำเป็นต้องทำบ่อยแค่ไหน แล้วจึงเลือก Wait(), Event, Cache หรือ State Management ให้เหมาะกับงาน
Comments
Post a Comment