Explore all courses in this category.
Explore all courses in this category.
Nguyễn Đình Cường
0 comment
09 Nov, 2026
Sau khi làm được nhân vật platformer, hãy thử một thể loại khác: game nhìn từ trên xuống, nơi nhân vật đi tự do bốn phía và nhặt vật phẩm. Bài này dạy bạn di chuyển 8 hướng với Godot 4 và dùng signal, cơ chế giao tiếp rất đặc trưng của Godot, để cập nhật điểm số.
Ta cần ba scene nhỏ rồi ghép lại, đúng tinh thần của Godot:
player.tscn: CharacterBody2D, Sprite2D, CollisionShape2D, Camera2D.gem.tscn: Area2D, Sprite2D, CollisionShape2D (vật phẩm cần thu thập).main.tscn: Node2D gốc, chứa Player, nhiều Gem và một Label hiển thị điểm bên trong CanvasLayer.Trong game top-down không có trọng lực, vì vậy hãy vào Project Settings > Physics hoặc đơn giản là không áp trọng lực trong script. Nhớ đặt nhân vật vào nhóm (group) tên player bằng tab Node > Groups để các vật phẩm nhận ra.
Dùng Input.get_vector để lấy vector hướng từ bốn hành động. Hàm này tự chuẩn hóa nên đi chéo không nhanh hơn đi thẳng.
extends CharacterBody2D
@export var speed: float = 220.0
func _physics_process(_delta):
var direction = Input.get_vector("ui_left", "ui_right", "ui_up", "ui_down")
velocity = direction * speed
move_and_slide()Từ khóa @export hiển thị biến trong Inspector để bạn chỉnh tốc độ mà không sửa code. Dấu gạch dưới trước _delta báo rằng tham số này chưa dùng đến, tránh cảnh báo.
Area2D phát hiện vật thể chồng lên nó mà không chặn đường. Đây là lựa chọn hợp lý cho đồng xu, gem, vùng kích hoạt. Gắn script cho gốc của gem.tscn:
extends Area2D
signal collected(value: int)
@export var value: int = 1
func _ready():
body_entered.connect(_on_body_entered)
func _on_body_entered(body):
if body.is_in_group("player"):
collected.emit(value)
queue_free()Ở đây xuất hiện khái niệm signal. Gem không cần biết ai đang quản lý điểm, nó chỉ "phát tín hiệu" rằng mình đã bị nhặt kèm giá trị. Ai quan tâm thì lắng nghe. Cách này giữ cho các scene độc lập, dễ tái sử dụng, đúng nguyên tắc "gọi xuống, báo hiệu lên" của Godot. Hàm queue_free() xóa node an toàn vào cuối khung hình.
Gắn script cho node gốc của main.tscn. Ta duyệt các Gem trong một group và kết nối signal của chúng:
extends Node2D
@onready var score_label: Label = $CanvasLayer/ScoreLabel
var score: int = 0
func _ready():
for gem in get_tree().get_nodes_in_group("gems"):
gem.collected.connect(_on_gem_collected)
_update_label()
func _on_gem_collected(value: int):
score += value
_update_label()
func _update_label():
score_label.text = "Điểm: %d" % scoreĐể cách trên hoạt động, hãy thêm node gem vào group gems (đặt group ngay trong scene gem.tscn để mọi bản sao đều có). Từ khóa @onready gán biến sau khi cây node đã sẵn sàng, tránh lỗi tham chiếu rỗng. Đường dẫn $CanvasLayer/ScoreLabel phải đúng với tên node thực tế của bạn.
Bạn có thể kéo nhiều bản gem.tscn vào scene chính bằng tay. Nếu muốn tạo ngẫu nhiên bằng code, hãy thêm đoạn sau vào _ready của scene chính, trước vòng lặp kết nối signal:
@export var gem_scene: PackedScene
func _spawn_gems(count: int):
for i in count:
var gem = gem_scene.instantiate()
gem.position = Vector2(randf_range(50, 1100), randf_range(50, 600))
add_child(gem)Kéo gem.tscn vào ô Gem Scene trong Inspector, và nhớ gọi _spawn_gems(10) trước khi duyệt group để các gem mới sinh cũng được kết nối.
player, và collision layer/mask của hai bên có tương tác với nhau.$... hoặc dùng node trước khi cây sẵn sàng.Với CharacterBody2D, Area2D và signal, bạn nắm được bộ ba công cụ cốt lõi của Godot 2D. Hãy thử thêm kẻ địch, đồng hồ đếm ngược và màn hình thắng thua để biến nó thành game hoàn chỉnh.
Nếu muốn học làm game bài bản trên Godot hoặc Unity, hãy khám phá các khóa học tại codebem.com.
Nguyễn Đình Cường
0 comment