บทความ › พัฒนาเกม

Roblox EP2 โค้ด Luau แรก: พื้นเปลี่ยนสี พื้นลาวา และพื้นที่หายไปเมื่อยืนนาน

เขียนโค้ด Luau ครั้งแรกใน Roblox Studio รู้จัก Script, Touched, Humanoid และ debounce ผ่านตัวอย่างสามแบบที่ใช้ใน obby จริง พร้อมอธิบายทุกบรรทัด

เริ่มต้น อ่าน 8 นาที 16 ตุลาคม 2569 ทำเกม Roblox เกมแรก · บทที่ 2/5

อ่านจบแล้วจะรู้
  • โค้ดใน Roblox ทำงานตาม "เหตุการณ์" เช่น Touched เกิดเมื่อมีอะไรมาแตะชิ้นส่วน แล้วฟังก์ชันที่เราผูกไว้จะทำงาน
  • ต้องเช็กว่าสิ่งที่มาแตะเป็นตัวละครจริงไหม ด้วยการหา Humanoid ก่อนทำอะไรเสมอ
  • Touched เกิดซ้ำหลายสิบครั้งในการเหยียบครั้งเดียว ต้องมี debounce กันโค้ดทำงานซ้ำ

EP1 เราสร้างฉากที่เดินกระโดดได้แล้ว แต่ทุกอย่างยังนิ่ง ตอนนี้จะเขียนโค้ดครั้งแรก ด้วยตัวอย่างสามแบบที่ obby ทุกเกมต้องมี ทุกบรรทัดมีคำอธิบาย ไม่ต้องเคยเขียนโปรแกรมมาก่อน

เพิ่ม Script แรก

  1. สร้าง Part ใหม่ ตั้ง Anchored ตั้งชื่อ ColorPad
  2. ใน Explorer เอาเมาส์ไปชี้ที่ ColorPad จะมีปุ่ม + ขึ้นมา กดแล้วเลือก Script
  3. Studio เปิดหน้าเขียนโค้ด มีบรรทัดเดียวมาให้แล้ว
print("Hello world!")
  1. กด Play แล้วดูแผง Output จะเห็นข้อความ Hello world!

print คือการพิมพ์ข้อความออกมาที่ Output ใช้บ่อยมากเวลาอยากรู้ว่าโค้ดทำงานถึงตรงไหนแล้ว

Script ที่อยู่ข้างใน Part จะทำงานเองตอนเกมเริ่ม ไม่ต้องสั่ง และรู้ว่าตัวเองอยู่ในอะไรผ่านคำว่า script.Parent

ตัวอย่างที่ 1: เหยียบแล้วเปลี่ยนสี

ลบบรรทัดเดิม แล้วพิมพ์ใหม่ทั้งหมด

local pad = script.Parent

pad.Touched:Connect(function(otherPart)
	local humanoid = otherPart.Parent:FindFirstChildWhichIsA("Humanoid")
	if humanoid then
		pad.BrickColor = BrickColor.random()
	end
end)

ทีละบรรทัด

  • local pad = script.Parent สร้างตัวแปรชื่อ pad เก็บ Part ที่ script นี้อยู่ข้างใน คำว่า local แปลว่าตัวแปรนี้ใช้ได้ในไฟล์นี้เท่านั้น
  • pad.Touched คือ เหตุการณ์ (event) ที่เกิดทุกครั้งที่มีอะไรมาแตะ pad
  • :Connect(function(otherPart) ... end) ผูกฟังก์ชันไว้กับเหตุการณ์ ทุกครั้งที่มีการแตะ ฟังก์ชันนี้จะทำงาน และได้รับ otherPart คือชิ้นส่วนที่มาแตะ
  • otherPart.Parent:FindFirstChildWhichIsA("Humanoid") ส่วนที่มาแตะคือเท้าหรือขาของตัวละคร ตัวละครทั้งตัวคือ Parent ของมัน และตัวละครทุกตัวมี Humanoid อยู่ข้างใน ถ้าหาเจอ แปลว่าสิ่งที่มาแตะคือตัวละครจริง ไม่ใช่ลูกบอลหรือก้อนหินที่กลิ้งมา
  • if humanoid then ... end ทำเฉพาะเมื่อหาเจอ
  • pad.BrickColor = BrickColor.random() สุ่มสีใหม่

กด Play แล้วเดินขึ้นไปเหยียบ สีจะเปลี่ยน

โค้ดใน Roblox ส่วนใหญ่มีรูปแบบเดียวกันนี้: รอเหตุการณ์ → เช็กว่าใช่สิ่งที่สนใจไหม → ทำงาน

ปัญหาแรก: Touched เกิดถี่มาก

ลองเพิ่มบรรทัดนี้ไว้บรรทัดแรกในฟังก์ชัน

	print("touched by", otherPart.Name)

กด Play แล้วเดินขึ้นไปยืนนิ่ง ๆ ดู Output จะเห็นข้อความพรั่งพรูหลายสิบบรรทัด เพราะเท้าซ้าย เท้าขวา และขาแต่ละท่อน แตะพื้นแยกกัน และแตะซ้ำทุกครั้งที่ขยับนิดเดียว สีจึงเปลี่ยนรัวจนดูไม่ออก

ทางแก้เรียกว่า debounce คือมีตัวแปรบอกว่า “กำลังทำงานอยู่ ห้ามซ้ำ”

local pad = script.Parent
local cooling = false

pad.Touched:Connect(function(otherPart)
	if cooling then
		return
	end
	local humanoid = otherPart.Parent:FindFirstChildWhichIsA("Humanoid")
	if not humanoid then
		return
	end

	cooling = true
	pad.Color = Color3.fromRGB(255, 90, 90)  -- แดง
	task.wait(1)
	pad.Color = Color3.fromRGB(90, 200, 120) -- เขียว
	cooling = false
end)
  • cooling เริ่มเป็น false พอเริ่มทำงานตั้งเป็น true ระหว่างนั้นการแตะครั้งใหม่จะเจอ if cooling then return end แล้วออกทันที
  • return คือออกจากฟังก์ชันตรงนั้นเลย เขียนแบบนี้แทนการซ้อน if หลายชั้น อ่านง่ายกว่า
  • task.wait(1) รอหนึ่งวินาที
  • Color3.fromRGB(r, g, b) กำหนดสีแบบแดง เขียว น้ำเงิน ค่า 0 ถึง 255
  • ข้อความหลัง -- คือคอมเมนต์ โปรแกรมไม่อ่าน เขียนไว้ให้คนอ่าน
Touched เกิดมีอะไรมาแตะชิ้นส่วนกำลังทำงานอยู่? (debounce)ใช่ → ออกมี Humanoid ไหมไม่มี → ออก (ลูกบอล ก้อนหิน)ทำงานเปลี่ยนสี / ลาวา / จางหายtask.wait แล้วปลดล็อกพร้อมรับการแตะครั้งใหม่
รูปแบบเดียวที่ใช้ทุกตัวอย่าง: รอเหตุการณ์ เช็กให้ชัว แล้วค่อยทำ

ตัวอย่างที่ 2: พื้นลาวา

obby ทุกเกมมีพื้นที่เหยียบแล้วต้องเริ่มใหม่ สร้าง Part ใหม่ชื่อ Lava ตั้ง Anchored ใน Properties ตั้ง Material เป็น Neon และ Color เป็นสีส้มแดง แล้วใส่ Script นี้

local lava = script.Parent

lava.Touched:Connect(function(otherPart)
	local humanoid = otherPart.Parent:FindFirstChildWhichIsA("Humanoid")
	if humanoid then
		humanoid.Health = 0
	end
end)

humanoid.Health = 0 คือตั้งพลังชีวิตเป็นศูนย์ ตัวละครจะล้มแล้วเกิดใหม่ที่จุดเกิด ตัวนี้ไม่ต้องมี debounce เพราะตั้งพลังชีวิตเป็นศูนย์ซ้ำกี่ครั้งก็ได้ผลเหมือนเดิม

เวลาตัดสินใจว่าต้องมี debounce ไหม ให้ถามว่า “ถ้าทำซ้ำสิบครั้งติด ผลต่างจากทำครั้งเดียวไหม” ถ้าต่าง ต้องมี

ตัวอย่างที่ 3: พื้นที่หายไปเมื่อยืนนาน

พื้นที่ค่อย ๆ จางหายเมื่อเหยียบ แล้วกลับมาใหม่ บังคับให้ผู้เล่นต้องรีบ สร้าง Part ชื่อ FadePad แล้วใส่ Script นี้

local pad = script.Parent
local busy = false

pad.Touched:Connect(function(otherPart)
	if busy then
		return
	end
	if not otherPart.Parent:FindFirstChildWhichIsA("Humanoid") then
		return
	end

	busy = true
	for i = 1, 10 do
		pad.Transparency = i / 10   -- จางลงทีละ 10%
		task.wait(0.1)
	end
	pad.CanCollide = false          -- ไม่มีพื้นให้ยืนแล้ว
	task.wait(3)
	pad.CanCollide = true
	pad.Transparency = 0
	busy = false
end)
  • for i = 1, 10 do ... end ทำซ้ำ 10 รอบ โดย i มีค่า 1, 2, 3 ไปจนถึง 10
  • Transparency ความโปร่งใส 0 คือทึบ 1 คือมองไม่เห็นเลย
  • CanCollide = false ทำให้ชิ้นส่วนไม่มีตัวตนทางกายภาพ ตัวละครจะร่วงทะลุ
  • ทั้งหมดใช้เวลาจางหาย 1 วินาที หายไป 3 วินาที แล้วกลับมา

ลองปรับตัวเลขให้ยากง่ายตามใจ เช่น task.wait(0.05) จะจางเร็วขึ้นสองเท่า

อ่านข้อผิดพลาดเป็น

ลองเปลี่ยนบรรทัดที่หา Humanoid ในพื้นลาวาเป็นแบบนี้ดู

local humanoid = otherPart.Parent.Humanoid  -- ไม่ใช้ FindFirstChild

พอมีอะไรที่ไม่ใช่ตัวละครมาแตะ เช่น ลูกบอลที่ไม่ได้ Anchored กลิ้งมาโดน Output จะขึ้นตัวแดงประมาณว่า Humanoid is not a valid member of ... เพราะเราสั่งให้ไปเอา Humanoid จากสิ่งที่ไม่มี Humanoid

หรือถ้าเจอ attempt to index nil with ... แปลว่ากำลังใช้งานของที่เป็น nil (ว่างเปล่า ไม่มีอยู่จริง) เหมือนพยายามเปิดลิ้นชักของตู้ที่ไม่มี

ทั้งสองแบบแก้ด้วยหลักเดียวกัน หาให้เจอก่อน แล้วเช็กว่าเจอจริง ค่อยใช้ ซึ่งก็คือ FindFirstChildWhichIsA กับ if humanoid then ที่เราเขียนมาตลอด

ลองเองใน 10 นาที

  1. ทำ ColorPad แบบมี debounce แล้วลองเปลี่ยนให้กลับเป็นสีเดิมหลัง 2 วินาที
  2. วาง Lava สองสามแผ่นระหว่างขั้นบันไดจาก EP1
  3. ทำ FadePad เป็นสะพานข้ามลาวา แล้วกด Play ลองเล่น

ข้อผิดพลาดที่เจอบ่อย

  • ไม่เช็ก Humanoid ทำให้ลูกบอลหรือชิ้นส่วนที่กลิ้งมา ทำให้โค้ดทำงานเอง
  • ลืม debounce ในงานที่ทำซ้ำแล้วผลต่าง
  • ลืมตั้ง busy = false ตอนจบ ทำให้ทำงานได้แค่ครั้งเดียวตลอดเกม
  • ใส่ Script ผิดที่ เช่น ไว้นอก Part ทำให้ script.Parent ไม่ใช่ชิ้นที่ตั้งใจ

ตอนหน้า

ตอนนี้เรามี Script หลายตัวกระจายอยู่ในแต่ละชิ้นส่วน และทุกตัวทำงานบน เซิร์ฟเวอร์ EP3 จะอธิบายว่าเกม Roblox มีสองฝั่ง คือเซิร์ฟเวอร์กับเครื่องของผู้เล่น ทำไมต้องรู้ว่าโค้ดไหนทำงานฝั่งไหน และทำไมเรื่องนี้คือหัวใจของการกันคนโกงเกม

คำถามที่พบบ่อย

ทำไมใช้ task.wait ไม่ใช้ wait

wait แบบเก่ายังใช้ได้ แต่ Roblox แนะนำ task.wait แทนแล้ว เพราะแม่นยำกว่าและเป็นส่วนหนึ่งของไลบรารี task ที่ออกแบบมาใหม่ บทความหรือคลิปเก่า ๆ ยังใช้ wait อยู่เยอะ ถ้าเจอให้เปลี่ยนเป็น task.wait ได้เลย

Output ขึ้นตัวแดง แปลว่าอะไร

แปลว่าโค้ดมีข้อผิดพลาด ข้อความบอกชื่อ script และเลขบรรทัด คลิกที่ข้อความแล้ว Studio จะพาไปที่บรรทัดนั้น ข้อผิดพลาดที่มือใหม่เจอบ่อยที่สุดคือ attempt to index nil ซึ่งอธิบายไว้ในบทความนี้

คำสั่งและค่าตั้งในบทความทดสอบบนระบบตัวอย่าง ลองบนเครื่องทดสอบก่อนใช้กับระบบจริงเสมอ

อ่านต่อ

#เกมกลาง

Roblox EP5 เกมลาวาขึ้น: สร้างทั้งด่านด้วยโค้ด และทดสอบกฎเกมนอก Studio

เกมที่สองของซีรีส์ เกมเป็นรอบที่ลาวาค่อย ๆ ขึ้น ใครถึงยอดหอคอยได้แต้ม ทั้งด่านสร้างจากโค้ดด้วย Rojo และทดสอบกฎว่ากระโดดถึงทุกขั้นด้วย Lune

อ่าน 13 นาที
#เกมเริ่มต้น

Roblox EP1 เริ่มทำเกมแรก: ติดตั้ง Roblox Studio และสร้างฉากแรกที่กด Play ได้

ทำเกม Roblox ตั้งแต่ศูนย์ ติดตั้ง Roblox Studio รู้จักหน้าจอ Explorer และ Properties วางพื้น ทำบันได กด Play ทดสอบ และเข้าใจว่าเกมหนึ่งเกมประกอบด้วยอะไรบ้าง

อ่าน 6 นาที
#เกมเริ่มต้น

Roblox EP4 ประกอบ obby ให้ครบเกม: จุดเซฟ กระดานด่าน ลาวาทั้งเกม และเส้นชัย

รวมทุกอย่างเป็นเกม obby ที่เล่นได้จริง จุดเซฟด้วย SpawnLocation กระดานคะแนน leaderstats ลาวาทั้งเกมคุมด้วย Script เดียวผ่าน Tag และเส้นชัยที่นับครั้งชนะ พร้อมโค้ดเต็ม

อ่าน 13 นาที