[Roblox Scripting | vi] Debounce technique on a Touched Event
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.