ลุย Day 1 กันเลยครับ!
Object Composition & Scene Tree
วันนี้เราจะใช้เวลา 20–30 นาที ปูพื้นฐานสถาปัตยกรรมที่สำคัญที่สุดของ Godot Engine 4 เน้นการเปลี่ยนผ่านจากแนวคิดคลาสแม่ผูกแน่น (Inheritance) สู่การใช้ Component Nodes อิสระ พร้อมเปิด Strict Type Safety ใน Editor ตั้งแต่นาทีแรก!
1. Editor Setup
เปิด Strict Typing
2. Micro-Concept
Composition vs Tree
3. Rotator Drill
โค้ด Component แรก
กฎสำคัญของวันนี้
"เขียนสคริปต์ให้เหมือน LEGO ตัวต่อ ไม่ต้องรู้ว่าใครเป็นตัวต่อข้างเคียง แต่ทำหน้าที่หมุนวัตถุให้ดีที่สุด"
1. การตั้งค่า Editor สำหรับ Strict Type Safety
ก่อนเริ่มเขียนโค้ด แนะนำให้เปิดใช้งานการแจ้งเตือน (Warnings) ของ GDScript ใน Godot 4 เพื่อให้ Editor ช่วยตรวจสอบชนิดข้อมูลทันที และช่วยให้ระบบ Autocomplete แนะนำฟังก์ชันได้แม่นยำ 100%:
เปิด Project Settings
ไปที่เมนูด้านบนของ Godot 4:
Project > Project Settings
ค้นหาหมวด GDScript
เลือกแท็บ General แล้วพิมพ์คำว่า GDScript ในช่องค้นหาด้านบน
ปรับค่า Warnings
ในส่วน Warnings ให้ตั้งค่าดังนี้:
- • Untyped Declaration: Warn / Error
- • Unsafe Property Access: Warn
2. Micro-Concept Breakdown: Composition vs. Inheritance
แนวคิดแบบเดิม (Deep Inheritance)
สร้าง Class แม่ขนาดใหญ่ เช่น Character > Player > WarriorPlayer
ส่งผลให้โค้ดผูกติดกันแน่น (Tight Coupling) หากต้องการสร้างหุ่นโดรนบินที่หมุนตัวได้เหมือนดาวเคราะห์ เราต้องเขียนซ้ำหรือดึงโครงสร้างคลาสแม่ขนาดใหญ่มาโดยไม่จำเป็น
แนวคิดของ Godot (Pure Composition)
มองทุกอย่างเป็น Node เดี่ยวๆ ที่มีหน้าที่เฉพาะทาง (Single-responsibility) เช่น หมุนตัว (Rotator), เลือด (Health), ตัวยิง (Spawner) แล้วนำมาประกอบเป็นกิ่งก้าน (Node Tree) ใต้โหนดอะไรก็ได้ที่ต้องการฟังก์ชันนั้น
SceneTree คือศูนย์กลางการมีชีวิตของ Node
Node ทุกตัวที่จะทำงานได้ (เช่น ได้รับรอบการประมวลผล _process(delta) หรือ _physics_process(delta)) จะต้องถูกเพิ่มเข้าไปอยู่ใน SceneTree เท่านั้น หาก Node ถูกสร้างขึ้นด้วย Node.new() แต่อยู่เฉยๆ ในหน่วยความจำโดยไม่ได้สั่ง add_child() มันจะอยู่นิ่งๆ ใน RAM และไม่ถูกรันเฟรมใดๆ ทั้งสิ้น
3. Hands-on Drill: สร้าง RotatorComponent
เราจะสร้าง Component สำหรับหมุน Node แม่ (Parent Node) โดยที่ตัว Component ไม่จำเป็นต้องรู้ว่า Node แม่คืออะไร ขอแค่เป็นวัตถุ 2D (Node2D) ก็พอครับ
└── RotatorComponent (Node) [แนบสคริปต์ RotatorComponent.gd]
class_name RotatorComponent
extends Node
## RotatorComponent
## ใช้สำหรับหมุน Parent Node (Node2D) ด้วยความเร็วที่กำหนด
# @export ช่วยให้ปรับค่าความเร็วในการหมุนได้ผ่าน Inspector
@export var rotation_speed: float = 2.0
func _process(delta: float) -> void:
# ดึง Node แม่ที่เป็น Node2D มาใช้งาน
var parent := get_parent() as Node2D
# ตรวจสอบเพื่อความปลอดภัยว่า Parent มีอยู่จริงและเป็น Node2D
if parent:
# หมุน Node แม่ตามเวลาที่ผ่านไป (delta) เพื่อให้ความเร็วคงที่ทุก Frame Rate
parent.rotation += rotation_speed * delta
ขั้นตอนการลงมือทำจริงใน Godot Engine 4:
- สร้าง Scene ใหม่: เลือก Root Node เป็น
Sprite2D(ใส่รูปโลโก้ Godot หรือรูปอะไรก็ได้ลงในช่อง Texture ของ Inspector) - เพิ่ม Component: คลิกขวาที่
Sprite2Dเลือก Add Child Node > เลือกประเภทNodeแล้วเปลี่ยนชื่อโหนดเป็นRotatorComponent - สร้างและแนบสคริปต์: คลิกขวาที่
RotatorComponentเลือก Attach Script ตั้งชื่อไฟล์ว่าRotatorComponent.gdแล้วพิมพ์หรือวางโค้ดด้านบนลงไป - ทดสอบการรัน: ลองกด Play Scene (F6) แล้วสังเกตว่าภาพ Sprite2D จะเริ่มหมุนตัวอย่างนุ่มนวล
- ทดลองปรับค่าแบบ No-Code: ลองคลิกที่
RotatorComponentแล้วเปลี่ยนค่า Rotation Speed ในหน้าต่าง Inspector ด้านขวาดูว่าความเร็วเปลี่ยนตามไหมโดยไม่ต้องแก้ไขโค้ดแม้แต่บรรทัดเดียว!
4. 🎯 Mini-Check สำหรับ Day 1
ก่อนจะจบการฝึกของวันนี้ ลองตอบคำถามนี้สั้นๆ เพื่อทดสอบความเข้าใจครับ:
parent.rotation += rotation_speed * delta ทำไมเราถึงต้องคูณด้วย delta เสมอ?
กดดูเฉลยและคำอธิบายเชิงสถาปัตยกรรม
คำตอบ: เพื่อให้ความเร็วในการหมุน "คงที่เท่าเดิมทุกเครื่อง โดยไม่ขึ้นกับ Frame Rate" (Frame-rate Independence)
ค่า delta คือระยะเวลาจริงที่ผ่านไปของแต่ละเฟรม (เป็นวินาที เช่น ที่ 60 FPS delta จะประมาณ 0.0166 วินาที) หากไม่คูณ delta หากเกมรันบนหน้าจอ 144Hz วัตถุจะหมุนเร็วกว่าบนหน้าจอ 60Hz ถึงกว่า 2 เท่าตัว!
พร้อมต่อ Day 2 แล้วหรือยังครับ?
วันพรุ่งนี้เราจะเจาะลึก Day 2: GDScript 2.0 Type Safety & Editor Annotations (@export, @onready) เพื่อสร้างระบบสถิติตัวละครที่ปลอดภัย 100%!