[Roblox Scripting | vi] Debounce technique on a Touched Event

20 Th04 2022

Bạn viết một chương trình đơn giản mục đích tăng một điểm cho người chơi nếu người ta chạm vào một vật thể xác định (như "đồng xu", "tinh thể" hay "kim cương",...) trong trò chơi, như hình dưới.

-- KHAI BÁO SERVICE
local Players = game.Players

-- KHAI BÁO ĐƯỜNG DẪN ĐẾN VẬT THỂ
local Crystal = script.Parent

-- KHAI BÁO FUNCTION ONTOUCH
local function onTouch(touchedPart)	
    print("Touched: " .. touchedPart.Name)
    local character = touchedPart.Parent
    
    -- Xác định xem vật thể chạm vào có phải là character của player nào đó không
    if touchedPart and character and character:FindFirstChild("Humanoid") then
    
        -- Tìm player có character đã chạm vào Crystal
        local player = Players:GetPlayerFromCharacter(character)

        --Khai báo đường dẫn đến Score thuộc leaderstats trong player
        local ScoreObject = player.leaderstats.Score

        print("Touched player " .. player.Name)
        
        -- Khiến Crystal biến mất, người chơi không thể chạm vào nữa
        Crystal.Transparency = 1
        Crystal.Anchored = true
        Crystal.CanCollide = false
        Crystal.Position = Crystal.Position + Vector3.new(0,-10,0)

        -- Tăng điểm cho người chơi
        ScoreObject.Value = ScoreObject.Value + 1

        -- Khiến Crystal hiện lại ở một chỗ khác ngẫu nhiên sau 5s.
        wait(5)
        Crystal.Transparency = 0
        Crystal.CanCollide = true
        Crystal.Position = Crystal.Position + Vector3.new(math.random(-20,20),10,math.random(-20,20))
    end
end

-- KẾT NỐI SỰ KIẾN TOUCH VỚI FUNCTION ONTOUCH
Crystal.Touched:Connect(onTouch)

Và bạn chợt nhận ra trong quá trình kiểm thử rằng: Dù người chơi chỉ chạm vào vật thể một lần duy nhất, và bạn cũng đã cố tính giấu vật thể đi chỗ khác để nó không chạm vào người chơi nữa, trước cả khi tiến hành cộng điểm, nhưng thay vì được cộng 1 điểm như mục tiêu thì người chơi được cộng đến 5 hay 7 điểm, mà không rõ lý do! Trong trường hợp này, bạn cần biết thêm một kỹ thuật mới trong lập trình có tên là Debounce. Kỹ thuật này không chỉ sẽ giúp bạn giải quyết vấn đề tăng điểm không đúng như trong ví dụ trên mà sẽ giúp bạn chạy đúng mong muốn với tất cả các dạng sự kiện xảy ra liên tiếp trong Roblox nói riêng và lập trình nói chung.

Debounce là gì?

Debounce là một kỹ thuật được áp dụng rộng rãi để tối ưu hóa hiệu năng (performance) của ứng dụng bằng cách giảm thiểu số lần ứng dụng phải xử lý những sự kiện (event) mang tính liên tục. Trong trường hợp đoạn lệnh trên, chính Touched Event là một dạng sự kiện được Roblox xử lý liên tục. Bạn đoán xem trong 1s, nếu bạn còn chạm vật thể, Roblox phát ra bao nhiêu sự kiện dạng này nào, 1 - 2 hay 3 sự kiện? Câu trả lời là hàng trăm sự kiện luôn nhé!
Việc phát tín hiệu "quá liên tiếp" không những dẫn đến tiêu tốn tài nguyên mà còn dẫn đến kết quả không như mong muốn. Trong đoạn code trên, dù bạn thực hiện quá trình thay đổi vị trí vật thể để chống không cho vật thể va chạm vào người chơi, từ đó ngừng phát sự kiện chạm đi nữa, thì trong thời gian vị trí thực sự thay đổi, dù chỉ rất ngắn, vẫn có 5-7 sự kiện đã được phát ra và cũng có 5-7 onTouch function đã được thực hiện dẫn đến người chơi bị cộng sai điểm.

Debounce hoạt động thế nào?

Debounce là một hàm bậc cao (high-order function) dùng để [1] làm trì hoãn việc xử lý event nào đó cho đến khi không có event nào được emitted (phát) ra (nữa) trong một khoảng thời gian (delay) đã được định trước, hoặc trong trường hợp khác, là [2] chỉ thực hiện xử lý đối với sự kiện đầu tiên diễn ra trong một chuỗi sự kiện ứng với thời gian đã định. Nói thì dài dòng nhưng bạn cứ nhìn đoạn lệnh sau sẽ dễ hình dung hơn. Đoạn lệnh bên dưới thực hiện chức năng [2].

-- KHAI BÁO CÁC BIẾN SỬ DỤNG 
local allowTouching = true	-- Biến sử dụng cho kỹ thuật debounce 	

-- KHAI BÁO FUNCTION DEBOUNCE
local function debouncedOnTouch(touchedPart)
	-- Trong thời gian function onTouch đang xử lý
	-- thì không tiếp nhận thêm các event Touched khác.
	if allowTouching then
		allowTouching = false
		onTouch(touchedPart)
		allowTouching = true
	end
end

-- KẾT NỐI SỰ KIẾN TOUCH VỚI FUNCTION ONTOUCH
Crystal.Touched:Connect(debouncedOnTouch)

Đoạn lệnh trên tái sử dụng function onTouch() đã khai báo trước đó. Tuy nhiên, thay vì thực thi onTouch() ngay khi có sự kiện Touched ứng với vật thể Crystal, thì onTouch() được đặt trong function mới là debouncedOnTouch() và chỉ được thực thi khi function onTouch() trước đó đã thực thi xong - mất khoảng 5s - đây cũng là thời gian vật thể biến mất.

Đoạn Code đầy đủ

Tags

Tony Phạm

Là một người thích vọc vạch và tò mò với tất cả các lĩnh vực từ khoa học tự nhiên, lập trình, thiết kế đến ... triết học. Luôn mong muốn chia sẻ những điều thú vị mà bản thân khám phá được.