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

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

Popular posts from this blog

FiveM ยังน่าเล่นไหม? Enhanced เปลี่ยน FiveM แค่ไหน

FiveM คืออะไร เล่นอย่างไร สำหรับมือใหม่ เริ่มต้นตั้งแต่ศูนย์

วิธีตั้ง Admin Permission ด้วย add_ace และ add_principal FiveM แบบละเอียด