Level 1 • Day 2 Hands-on

GDScript 2.0 Type Safety & Editor Annotations

Day 2 of 7-Day Foundation

ยินดีต้อนรับสู่ Day 2 ครับ!
Type Safety & Annotations

วันนี้เราจะลงลึกเรื่อง GDScript 2.0 Static Typing และ Editor Annotations (@export, @onready) ซึ่งเป็นหัวใจสำคัญในการเขียนโค้ด Godot 4 ให้ทำงานได้รวดเร็ว ลดความผิดพลาดขณะรันเกม (Runtime Crash) และเปิดโอกาสให้ Game Designer ปรับแต่งค่าได้สะดวกผ่าน Inspector!

8m

1. Micro-Concept

Static Types & Annotations

15m

2. StatsController

สร้าง Component แบบ Type-Safe

5m

3. Test & Validate

เชื่อมต่อ & รันจริงใน Output

D2

คีย์เวิร์ดสำคัญประจำวันนี้

"ระบุชนิดตัวแปรให้ครบทุกตัวแปรและฟังก์ชัน เพื่อให้คอมไพเลอร์ช่วยดักจับบั๊กแทนผู้เล่นของคุณ"

Strict Static Typing (: Type, -> void)
@export_group & @export_range
@onready ป้องกัน Null Reference
Step 1 • 5–10 Minutes Breakdown

1. Micro-Concept Breakdown: 3 เสาหลักของความปลอดภัยใน GDScript

Core Architecture

1) Static Typing

การระบุชนิดตัวแปรอย่างชัดเจน เช่น var health: int = 100 หรือ func take_damage(...) -> void: ช่วยให้ Godot ตรวจสอบข้อผิดพลาดตั้งแต่ตอนเขียนโค้ด (Compile Time) ป้องกันเกมแครชกลางคัน (Runtime Crash)

ข้อดี: Autocomplete แม่นยำ 100% + Compiler เร่งความเร็วโค้ด

2) Editor Annotations

  • • @export: Expose ตัวแปรไปยัง Inspector ให้แก้ค่าได้โดยไม่ต้องเปิดโค้ด
  • • @export_range(min, max, step): สร้างแถบ Slider จำกัดขอบเขตค่าตัวเลข
  • • @export_group("..."): จัดหมวดหมู่ใน Inspector ให้เรียบร้อยสวยงาม
ประโยชน์: Game Designer ทำงานร่วมกับโปรแกรมเมอร์ได้ทันที

3) Safe Node Access (@onready)

@onready ชะลอการดึงค่าตัวแปรจนกว่า Node นั้นและลูกๆ ทั้งหมดจะก้าวเข้าสู่ SceneTree เสร็จสมบูรณ์ (หลังจากเหตุการณ์ _ready())

แก้ปัญหา: ขจัดบั๊ก Null-Pointer Crash เมื่ออ้างอิงโหนดลูก
Step 2 • 15–20 Mins Drill

2. Hands-on Drill: สร้าง StatsController.gd

เราจะสร้าง Component สำหรับจัดการค่าพลังและสถานะตัวละคร ที่ระบุชนิดข้อมูลแบบ Static Typing พร้อมใช้ @export_group และ @export_range จัดหมวดหมู่ใน Inspector

โครงสร้าง Node Tree ใน Godot Editor
Node2D (Character) ── [โหนดแม่ตัวละคร]
└── StatsController (Node) [แนบสคริปต์ StatsController.gd]
ไฟล์: StatsController.gd Strict Static Typing + Clamped Annotations
class_name StatsController
extends Node

## Controller สำหรับจัดการค่าพลังตัวละครด้วย GDScript 2.0 Type Safety

# จัดกลุ่มตัวแปรพื้นฐานใน Inspector
@export_group("Base Stats")
@export_range(1, 1000, 1) var max_health: int = 100
@export_range(0.0, 500.0, 10.0) var movement_speed: float = 250.0
@export var character_name: String = "Hero"

# จัดกลุ่มการต่อสู้ใน Inspector
@export_group("Combat")
@export var attack_power: int = 15

# ตัวแประดับ Class ระบุ Type ชัดเจน (ยังไม่กำหนดค่าเริ่มต้น)
var current_health: int

func _ready() -> void:
    current_health = max_health
    # ใช้ print_rich เพื่อพิมพ์ข้อความแบบมีสีลงใน Console Output
    print_rich("[color=green]%s initialized with %d HP.[/color]" % [character_name, current_health])

## ฟังก์ชันรับความเสียหาย (ระบุชนิด Parameter และ Return Type เป็น void)
func apply_damage(amount: int) -> void:
    current_health = clampi(current_health - amount, 0, max_health)
    print_rich("[color=yellow]%s took %d damage! Remaining HP: %d[/color]" % [character_name, amount, current_health])
Step 3 • Hands-on Workflow & Testing

3. ขั้นตอนการลงมือทำและทดสอบใน Godot Engine 4

สเต็ปสร้างและตรวจสอบใน Editor:

  1. เพิ่ม Component: คลิกขวาที่ Root Node ของคุณ (เช่น Node2D หรือ CharacterBody2D) เลือก Add Child Node > เลือกประเภท Node ตั้งชื่อว่า StatsController
  2. แนบสคริปต์: คลิกขวาที่ StatsController เลือก Attach Script ตั้งชื่อว่า StatsController.gd แล้ววางโค้ดด้านบนลงไป
  3. ตรวจสอบความเปลี่ยนแปลงใน Inspector: คลิกที่โหนด StatsController แล้วสังเกตหน้าต่าง Inspector ด้านขวา:
    • จะเห็นแถบหมวดหมู่ Base Stats และ Combat ถูกแบ่งส่วนอย่างเป็นระเบียบ
    • ตัวแปร max_health และ movement_speed จะกลายเป็น Slider Bar ให้ลากปรับค่าได้อย่างปลอดภัย
  4. สร้าง Script ทดสอบที่ Root Node: คลิกขวาที่ Root Node แล้วแนบสคริปต์ทดสอบเพื่อจำลองการรับความเสียหาย:
extends Node2D

# ใช้ @onready ผูกกับ StatsController อย่างปลอดภัย
@onready var stats_controller: StatsController = $StatsController

func _ready() -> void:
    # ทดสอบเรียกใช้ฟังก์ชันรับความเสียหาย
    stats_controller.apply_damage(25)
ทดสอบการรัน: กด Play Scene (F6) แล้วสังเกตข้อความแจ้งเตือนสีเขียว (Initialized) และสีเหลือง (Took damage) ในแท็บ Output ด้านล่างของ Godot Editor
Reflect & Validate

4. 🎯 Mini-Check สำหรับ Day 2

ลองทดสอบความเข้าใจสั้นๆ เพื่อปิดท้ายการฝึกของวันนี้ครับ:

คำถาม: ถ้าเราเขียนโค้ด @onready var sprite: Sprite2D = $Sprite2D ไว้ที่ส่วนหัวของ Script กับการเขียน var sprite: Sprite2D = $Sprite2D เฉยๆ (ไม่มี @onready) สองแบบนี้ให้ผลลัพธ์ต่างกันอย่างไร และแบบไหนจะเกิด Error?
กดดูเฉลยและคำอธิบายทางสถาปัตยกรรม

คำตอบ: แบบที่ไม่มี @onready จะทำให้เกิด Runtime Null-Pointer Error ทันที!

เหตุผลเชิงลึก: ตัวแปรระดับคลาสทั่วไปที่ไม่มี @onready จะถูกประมวลผลตั้งแต่ช่วงที่วัตถุกำลังจัดสรรพื้นที่ใน RAM (ช่วง _init()) ซึ่งในขณะนั้น Node ลูก ($Sprite2D) ยังไม่ได้ถูกสร้างขึ้นและยังไม่ได้เชื่อมต่อเข้ากับ SceneTree ตัวแปรจึงมีค่าเป็น null

การใส่ @onready เป็นการบอก Godot ให้ "รอจนกระทั่งทั้งตัวมันเองและ Node ลูกทั้งหมดก้าวเข้าสู่ SceneTree และรัน _ready() เสร็จสมบูรณ์ก่อน" จึงค่อยค้นหาพาทและกำหนดค่าให้ตัวแปร ทำให้ปลอดภัย 100%!

เตรียมตัวสำหรับวันถัดไป

พร้อมต่อ Day 3 แล้วหรือยังครับ?

วันพรุ่งนี้เราจะเจาะลึก Day 3: Node Lifecycles & Execution Order (_init, _enter_tree, _ready, _process, _physics_process) ทำความเข้าใจลำดับการเกิดและตายของ Node อย่างลึกซึ้ง!

คัดลอก GDScript ไปยังคลิปบอร์ดแล้ว!