本来在学习 Unity,看到有网友在推荐这款 godot 游戏引擎,听说是跨平台的框架,性能不错,也容易入门,找到官网上的例子试试。
以下是我按照官网上的操作进行编程后的代码:
玩家场景
创建玩家场景,绘制玩家、碰撞区域,设置移动动画及碰撞效果等
extends Area2D
signal hit
@export var speed = 400 # How fast the player will move (pixels/sec). 玩家移动速度
var screen_size # Size of the game window. 游戏窗口大小
# Called when the node enters the scene tree for the first time.
# 节点第一次进入场景树时,调用该函数
func _ready() -> void:
# 获取窗口大小
screen_size = get_viewport_rect().size
# 初次加载时,隐藏玩家
hide()
# Called every frame. 'delta' is the elapsed time since the previous frame.
# 每帧调用一次。'delta' 是自上一帧以来经过的时间。
func _process(delta: float) -> void:
# 按键移动
var velocity = Vector2.ZERO
if Input.is_action_pressed("move_right"):
velocity.x += 1
if Input.is_action_pressed("move_left"):
velocity.x -= 1
if Input.is_action_pressed("move_down"):
velocity.y += 1
if Input.is_action_pressed("move_up"):
velocity.y -= 1
# 运动计算及动画播放
if velocity.length() > 0:
velocity = velocity.normalized() * speed
$AnimatedSprite2D.play()
else:
$AnimatedSprite2D.stop()
position += velocity * delta
position = position.clamp(Vector2.ZERO, screen_size) # 限制玩家在屏幕内活动
# 左右移动动画
if velocity.x != 0:
$AnimatedSprite2D.animation = "walk"
$AnimatedSprite2D.flip_v = false
# 左移水平翻转
$AnimatedSprite2D.flip_h = velocity.x < 0
# 上下移动动画
if velocity.y != 0:
$AnimatedSprite2D.animation = "up"
# 下移竖直翻转
$AnimatedSprite2D.flip_v = velocity.y > 0
# 开始
func start(pos):
# 显示在坐标位置
position = pos
show()
$CollisionShape2D.disabled = false
# 接触动作
func _on_body_entered(body: Node2D) -> void:
# Player disappears after being hit.
# 接触后玩家消失,发出被攻击信号
hide()
hit.emit()
# Must be deferred as we can't change physics properties on a physics callback.
# 当前物理处理完成后,禁用玩家的碰撞检测
$CollisionShape2D.set_deferred("disabled", true)
敌人场景
绘制敌人动画,并在敌人离开屏幕后标记删除节点
extends RigidBody2D
# Called when the node enters the scene tree for the first time.
# 节点第一次进入场景树时,调用该函数
func _ready() -> void:
var mob_types = Array($AnimatedSprite2D.sprite_frames.get_animation_names())
$AnimatedSprite2D.animation = mob_types.pick_random()
# 敌人离开屏幕,标记删除该节点
func _on_visible_on_screen_notifier_2d_screen_exited() -> void:
queue_free()
游戏主场景
开始游戏、生成敌人,以及结束游戏
extends Node
# 导出变量,用于在编辑器中指定 Mob Scene(敌人/移动物体 场景)
@export var mob_scene: PackedScene
var score
# Called when the node enters the scene tree for the first time.
# 节点第一次进入场景树时,调用该函数
func _ready() -> void:
#new_game()
pass # Replace with function body.
# 开始新游戏
func new_game():
score = 0
$Player.start($StartPosition.position)
$StartTimer.start()
$HUD.update_score(score)
$HUD.show_message("Get Ready")
get_tree().call_group("mobs", "queue_free")
$BGMusic.play()
# 游戏结束
func game_over():
$ScoreTimer.stop()
$MobTimer.stop()
$HUD.show_game_over()
$BGMusic.stop()
$DeathSound.play()
# 玩家受到攻击
func _on_player_hit() -> void:
game_over()
# 敌人/移动物体 计时器,用于生成敌人/移动物体
func _on_mob_timer_timeout() -> void:
# 实例化 Mob Scene
var mob = mob_scene.instantiate()
# 根据 Path2D 随机选择生成位置
var mob_spawn_location = $MobPath/MobSpawnLocation
mob_spawn_location.progress_ratio = randf()
# Set the mob's postion to the random location.
mob.position = mob_spawn_location.position
# 将敌人方向设置为与路线垂直
var direction = mob_spawn_location.rotation + PI / 2
# 设置随机方向
direction += randf_range(-PI / 4, PI / 4)
mob.rotation = direction
# 设置随机速度 [150.0, 250.0]
var velocity = Vector2(randf_range(150.0, 250.0), 0.0)
mob.linear_velocity = velocity.rotated(direction)
# 将实例添加到主场景中
add_child(mob)
# 分数定时 +1
func _on_score_timer_timeout() -> void:
score += 1
$HUD.update_score(score)
# 点击开始按钮后,StartTimer开始计时,(2秒)后,启动敌人生成计时器及分数刷新计时器
func _on_start_timer_timeout() -> void:
$MobTimer.start()
$ScoreTimer.start()
游戏信息显示(结算信息等)
游戏的欢迎词及结算信息
extends CanvasLayer
# Notifies `Main` node that the button has been pressed
# 通知主节点:开始游戏
signal start_game
# 提示词
func show_message(text):
$Message.text = text
$Message.show()
$MessageTimer.start()
# 提示游戏结束
func show_game_over():
show_message("Game Over")
# Wait until the MessageTimer has counted down
# 等待消息计时器(用于显示“Game Over”的停顿)
await $MessageTimer.timeout
$Message.text = "Dodge the Creeps!"
$Message.show()
# Make a one-shot timer and wait for it to finish.
# 创建一次性计时器并等待其完成(1秒钟后显示“开始”按钮)
await get_tree().create_timer(1.0).timeout
$StartButton.show()
# 展示分数
func update_score(score):
$ScoreLabel.text = str(score)
# 按下开始按钮
func _on_start_button_pressed() -> void:
$StartButton.hide()
start_game.emit()
# 消息计时器超时即退出消息展示
func _on_message_timer_timeout() -> void:
$Message.hide()
游戏效果
最终的游戏效果如下图:
遇到的问题
1. mob_scene、MobPath、MobSpawnLocation单词不要拼错(比如不要拼成“mod”),同一变量名或相关联的变量名注意在各处保持一致;
2. 如果按照官网的教程一步步进行开发,可能遇到找不到可绑定start_game信号的new_game函数问题(截至2025年3月7日):
a. 点击“Main”节点->左上角初始化子场景->选择HUD场景;
b. 创建完成HUD子场景后,文件系统选中主场景 -> 选中HUD节点 -> 右侧选择Node标签页 -> 点击start_game函数 -> 点击pick按钮选择接收函数 -> 选择new_game函数