godot4 游戏篇之官方2d教程 - 2024年亲测有效

471 阅读6分钟

嘿, 一碰到这godot游戏引擎啊,就觉得这开发思维真他x的不一样. 跟着官方教程走一遍, 走着走着, 也就入乡随俗了.反正走一步看一步,说不定最后还真能搞出点啥名堂来. 你说有官方教程了,还做一遍是不是有毛病? 话不能这么说, 官方教程你可能还看不懂呢! 细节解释部分看官网,咱们只负责能够实现游戏项目.

image.png

官方教程传送门

下载篇⬇\color{red}{下载篇⬇}

游戏引擎 ⬇️

官网传送门 ⬅️

image.png

游戏素材⬇️

素材传送门 ⬅️

image.png

准备篇⬇\color{red}{准备篇⬇}

创建项目⬇️

image.png

输入素材⬇️

解压后的内容拖到左下角(资管管理区)

image.png

项目设置⬇️

修改窗口宽高, 以及模式

image.png

玩家篇⬇\color{red}{玩家篇⬇}

玩家场景⬇️

创建玩家⬇️

点击+ ➡️ 搜索ar ➡️ 选择Area2D ➡️ 创建节点

image.png

节点重命名⬇️

image.png

添加动画⬇️

image.png

创建动画控制界面⬇️

image.png

制作序列动画⬇️

image.png

image.png

添加碰撞区域⬇️

image.png

创建碰撞区域⬇️

image.png

调整碰撞区域⬇️

鼠标滚轮按下: (不要松开), 此时可以拖动精灵

鼠标滚轮滑动: 缩放精灵

image.png

玩家代码⬇️

创建脚本文件⬇️

image.png

创建控制按键⬇️

image.png

以此类推,创建上下左右⬇️

image.png

绑定键盘按钮⬇️

image.png

以此类推,绑定上下左右⬇️

也可以同时绑定wasd

image.png

代码知识⬇️

image.png

用键盘移动palyer⬇️

image.png

extends Area2D

var 速度 := 300
var 方向 := Vector2(0,0)

func _ready() -> void:
	pass 

func _process(delta: float) -> void:
	
	方向 = Vector2(0,0)
	
	if Input.is_action_pressed("move_right") :
		方向.x = 方向.x + 1
	if Input.is_action_pressed("move_left") :
		方向.x = 方向.x - 1
	if Input.is_action_pressed("move_down") :
		方向.y = 方向.y + 1
	if Input.is_action_pressed("move_up") :
		方向.y = 方向.y - 1
	
	if 方向.length() > 0 :
		方向 = 方向.normalized() * 速度
	
	position += 方向  * delta
	
	pass

运行游戏测试⬇️

image.png

设置玩家不能跑出屏幕⬇️

image.png

extends Area2D

var 速度 := 300
var 方向 := Vector2(0,0)

func _ready() -> void:
	pass 


func _process(delta: float) -> void:
	
	方向 = Vector2(0,0)
	
	if Input.is_action_pressed("move_right") :
		方向.x = 方向.x + 1
	if Input.is_action_pressed("move_left") :
		方向.x = 方向.x - 1
	if Input.is_action_pressed("move_down") :
		方向.y = 方向.y + 1
	if Input.is_action_pressed("move_up") :
		方向.y = 方向.y - 1
	
	if 方向.length() > 0 :
		方向 = 方向.normalized() * 速度
	
	position += 方向  * delta
        
        position = position.clamp(Vector2(0,0), 屏幕宽高)
	
	pass

方向有变化时,播放动画⬇️

image.png

extends Area2D

var 速度 := 300
var 方向 := Vector2(0,0)

func _ready() -> void:
	pass 


func _process(delta: float) -> void:
	
	方向 = Vector2(0,0)
	
	if Input.is_action_pressed("move_right") :
		方向.x = 方向.x + 1
	if Input.is_action_pressed("move_left") :
		方向.x = 方向.x - 1
	if Input.is_action_pressed("move_down") :
		方向.y = 方向.y + 1
	if Input.is_action_pressed("move_up") :
		方向.y = 方向.y - 1
	
	if 方向.length() > 0 :
		方向 = 方向.normalized() * 速度
		$AnimatedSprite2D.play()
	else:
		$AnimatedSprite2D.stop()
	
	position += 方向  * delta
        
        position = position.clamp(Vector2(0,0), 屏幕宽高)
	
	pass

播放指定动画⬇️

image.png

extends Area2D

var 速度 := 300
var 方向 := Vector2(0,0)
var 屏幕宽高 := Vector2(0,0)

func _ready() -> void:
	屏幕宽高 = get_viewport_rect().size
	pass 


func _process(delta: float) -> void:
	
	方向 = Vector2(0,0)
	
	if Input.is_action_pressed("move_right") :
		方向.x = 方向.x + 1
	if Input.is_action_pressed("move_left") :
		方向.x = 方向.x - 1
	if Input.is_action_pressed("move_down") :
		方向.y = 方向.y + 1
	if Input.is_action_pressed("move_up") :
		方向.y = 方向.y - 1
	
	if 方向.length() > 0 :
		方向 = 方向.normalized() * 速度
		$AnimatedSprite2D.play()
	else:
		$AnimatedSprite2D.stop()
		
	position += 方向  * delta
	
	position = position.clamp(Vector2(0,0), 屏幕宽高)
	
	if 方向.x != 0 :
		$AnimatedSprite2D.animation = "walk"
		$AnimatedSprite2D.flip_h = (方向.x < 0)
		$AnimatedSprite2D.flip_v = false
		
	if 方向.y != 0 :
		$AnimatedSprite2D.animation = "up"
		$AnimatedSprite2D.flip_v = (方向.y > 0)
	pass

实现隐藏玩家 ⬇️

image.png

extends Area2D

var 速度 := 300
var 方向 := Vector2(0,0)
var 屏幕宽高 := Vector2(0,0)

func _ready() -> void:
	屏幕宽高 = get_viewport_rect().size
	
	hide()
	pass 


func _process(delta: float) -> void:
    ....内容保持不变

准备碰撞敌人功能⬇️

创建自定义信号方法

image.png

extends Area2D

signal 受伤

var 速度 := 300
var 方向 := Vector2(0,0)
var 屏幕宽高 := Vector2(0,0)

func _ready() -> void:
	屏幕宽高 = get_viewport_rect().size
	
	hide()
	pass 


func _process(delta: float) -> void:
    ....内容保持不变
    

制作碰撞信号 ⬇️

image.png

碰撞时发生事件 ⬇️

image.png

extends Area2D

signal 受伤

var 速度 := 300
var 方向 := Vector2(0,0)
var 屏幕宽高 := Vector2(0,0)

func _ready() -> void:
	屏幕宽高 = get_viewport_rect().size
	
	hide()
	pass 


func _process(delta: float) -> void:
	
	方向 = Vector2(0,0)
	
	if Input.is_action_pressed("move_right") :
		方向.x = 方向.x + 1
	if Input.is_action_pressed("move_left") :
		方向.x = 方向.x - 1
	if Input.is_action_pressed("move_down") :
		方向.y = 方向.y + 1
	if Input.is_action_pressed("move_up") :
		方向.y = 方向.y - 1
	
	if 方向.length() > 0 :
		方向 = 方向.normalized() * 速度
		$AnimatedSprite2D.play()
	else:
		$AnimatedSprite2D.stop()
		
	position += 方向  * delta
	
	position = position.clamp(Vector2(0,0), 屏幕宽高)
	
	if 方向.x != 0 :
		$AnimatedSprite2D.animation = "walk"
		$AnimatedSprite2D.flip_h = (方向.x < 0)
		$AnimatedSprite2D.flip_v = false
		
	if 方向.y != 0 :
		$AnimatedSprite2D.animation = "up"
		$AnimatedSprite2D.flip_v = (方向.y > 0)
	pass


func _on_body_entered(body: Node2D) -> void:
	hide()
	$CollisionShape2D.set_deferred("disabled", true)
	受伤.emit()
	pass 
       

玩家初始化 ⬇️

image.png

extends Area2D

signal 受伤

var 速度 := 300
var 方向 := Vector2(0,0)
var 屏幕宽高 := Vector2(0,0)

func _ready() -> void:
	屏幕宽高 = get_viewport_rect().size
	
	hide()
	pass 


func _process(delta: float) -> void:
	
	方向 = Vector2(0,0)
	
	if Input.is_action_pressed("move_right") :
		方向.x = 方向.x + 1
	if Input.is_action_pressed("move_left") :
		方向.x = 方向.x - 1
	if Input.is_action_pressed("move_down") :
		方向.y = 方向.y + 1
	if Input.is_action_pressed("move_up") :
		方向.y = 方向.y - 1
	
	if 方向.length() > 0 :
		方向 = 方向.normalized() * 速度
		$AnimatedSprite2D.play()
	else:
		$AnimatedSprite2D.stop()
		
	position += 方向  * delta
	
	position = position.clamp(Vector2(0,0), 屏幕宽高)
	
	if 方向.x != 0 :
		$AnimatedSprite2D.animation = "walk"
		$AnimatedSprite2D.flip_h = (方向.x < 0)
		$AnimatedSprite2D.flip_v = false
		
	if 方向.y != 0 :
		$AnimatedSprite2D.animation = "up"
		$AnimatedSprite2D.flip_v = (方向.y > 0)
	pass


func _on_body_entered(body: Node2D) -> void:
	hide()
	$CollisionShape2D.disabled = true
	
	受伤.emit()
	pass 


func start(pos) :
	position = pos 
	show() 
	$CollisionShape2D.disabled = false	 
       

敌人篇⬇️

敌人场景⬇️

添加刚体⬇️

image.png

创建节点⬇️

image.png

设置刚体⬇️

image.png

设置动画⬇️

image.png

设置碰撞⬇️

image.png

npc代码⬇️

创建脚本⬇️

image.png

随机播放动画⬇️

image.png

extends RigidBody2D

func _ready() -> void:
	
	var npcnames = $AnimatedSprite2D.sprite_frames.get_animation_names()
	var npc = randi() % npcnames.size()
	$AnimatedSprite2D.play( npcnames[npc] )
	pass 

func _process(delta: float) -> void:
	pass
    

销毁屏幕外npc⬇️

image.png

image.png

extends RigidBody2D

func _ready() -> void:
	
	var npcnames = $AnimatedSprite2D.sprite_frames.get_animation_names()
	var npc = randi() % npcnames.size()
	$AnimatedSprite2D.play( npcnames[npc] )
	pass 

func _process(delta: float) -> void:
	pass


func _on_visible_on_screen_notifier_2d_screen_exited() -> void:
	queue_free()
	pass 
        

主场景篇⬇️

主场景⬇️

创建node节点⬇️

image.png

实例化玩家场景⬇️

image.png

创建4个节点⬇️

image.png

设置时间细节⬇️

image.png

image.png

设置玩家初始位置

image.png

生成npc范围⬇️

image.png

image.png

image.png

主场景脚本⬇️

image.png

image.png

extends Node

@export var npc : PackedScene 
var score

func _ready() -> void:
	pass 


func _process(delta: float) -> void:
	pass
        

加载npc场景⬇️

image.png

玩家受伤信号 ⬇️

image.png

image.png

extends Node

@export var npc : PackedScene 
var score

func _ready() -> void:
	pass 


func _process(delta: float) -> void:
	pass

func game_over() -> void:
	$Timer_npc.stop()
	$Timer_score.stop()
	pass 
        

重新开始⬇️

image.png

extends Node

@export var npc : PackedScene 
var score

func _ready() -> void:
	pass 


func _process(delta: float) -> void:
	pass

func game_over() -> void:
	$Timer_npc.stop()
	$Timer_score.stop()
	pass 
	
func newgame() :
	score = 0
	$player.start( $Marker2D_startpos.position )
	$Timer_start.start()
        

分数计时⬇️

image.png

extends Node

@export var npc : PackedScene 
var score

func _ready() -> void:
	pass 


func _process(delta: float) -> void:
	pass

func game_over() -> void:
	$Timer_npc.stop()
	$Timer_score.stop()
	pass 
	
func newgame() :
	score = 0
	$player.start( $Marker2D_startpos.position )
	$Timer_start.start()
	

func _on_timer_score_timeout() -> void:
	score += 1
	pass
        

开始计时⬇️

image.png

extends Node

@export var npc : PackedScene 
var score

func _ready() -> void:
	pass 


func _process(delta: float) -> void:
	pass

func game_over() -> void:
	$Timer_npc.stop()
	$Timer_score.stop()
	pass 
	
func newgame() :
	score = 0
	$player.start( $Marker2D_startpos.position )
	$Timer_start.start()
	

func _on_timer_score_timeout() -> void:
	score += 1
	pass 


func _on_timer_start_timeout() -> void:
	$Timer_npc.start()
	$Timer_score.start()
	pass 
        

npc计时⬇️

image.png

extends Node

@export var npc : PackedScene 
var score

func _ready() -> void:
	pass 


func _process(delta: float) -> void:
	pass

func game_over() -> void:
	$Timer_npc.stop()
	$Timer_score.stop()
	pass 
	
func newgame() :
	score = 0
	$player.start( $Marker2D_startpos.position )
	$Timer_start.start()
	

func _on_timer_score_timeout() -> void:
	score += 1
	pass 


func _on_timer_start_timeout() -> void:
	$Timer_npc.start()
	$Timer_score.start()
	pass 


func _on_timer_npc_timeout() -> void:
    # 获取NPC路径上的PathFollow2D节点
    var npc_follow = $Path2D_npc/PathFollow2D
    # 随机设置PathFollow2D在路径上的位置
    npc_follow.progress_ratio = randf()
    
    # 计算NPC的方向
    var direction = npc_follow.rotation + PI/2
    # 添加一些随机偏移到方向上
    direction += randf_range(-PI/4 , PI/4)
    
    # 设置随机速度
    var velocity = Vector2(randf_range(155.0,250.0), 0.0)
    
    # 实例化新的NPC
    var new_npc = npc.instantiate()
    # 设置NPC的位置、旋转和线性速度
    new_npc.position = npc_follow.position
    new_npc.rotation = direction
    new_npc.linear_velocity = velocity.rotated(direction)
    
    # 将新的NPC添加到场景中
    add_child(new_npc)

        

ui篇⬇️

ui场景搭建⬇️

canvas节点⬇️

image.png

创建子节点⬇️

image.png

分数ui⬇️

image.png

信息ui⬇️

image.png

按钮ui⬇️

手动调整按钮位置和大小

image.png

计时器⬇️

image.png

ui代码

gui节点添加脚本⬇️

image.png

新增信号节点⬇️

image.png

extends CanvasLayer

signal start_game

func _ready() -> void:
	pass  

func _process(delta: float) -> void:
	pass
        

显示文字 ⬇️

image.png

image.png

extends CanvasLayer

signal start_game

func _ready() -> void:
	pass  

func _process(delta: float) -> void:
	pass

func show_message(text) :
	$Label_message.text = text
	$Label_message.show()
	$Timer_message.start()


func show_gameover() :
	show_message("游戏结束!")
	
	await $Timer_message.timeout
	
	$Label_message.text = "行不行啊?"
	$Label_message.show()
	
	await  get_tree().create_timer(1.0).timeout
	$Button_start.show()
        

更新分数⬇️

image.png

extends CanvasLayer

signal start_game

func _ready() -> void:
	pass  

func _process(delta: float) -> void:
	pass

func show_message(text) :
	$Label_message.text = text
	$Label_message.show()
	$Timer_message.start()


func show_gameover() :
	show_message("游戏结束!")
	
	await $Timer_message.timeout
	
	$Label_message.text = "行不行啊?"
	$Label_message.show()
	
	await  get_tree().create_timer(1.0).timeout
	$Button_start.show()
        
func up_score(score) :
	$Label_score.text = str( score )
        

按钮按下⬇️

image.png

extends CanvasLayer

signal start_game

func _ready() -> void:
	pass  

func _process(delta: float) -> void:
	pass

func show_message(text) :
	$Label_message.text = text
	$Label_message.show()
	$Timer_message.start()


func show_gameover() :
	show_message("游戏结束!")
	
	await $Timer_message.timeout
	
	$Label_message.text = "行不行啊?"
	$Label_message.show()
	
	await  get_tree().create_timer(1.0).timeout
	$Button_start.show()
	
func up_score(score) :
	$Label_score.text = str( score )
	

func _on_button_start_pressed() -> void:
	$Button_start.hide()
	start_game.emit()
	pass 
        

隐藏文字⬇️

image.png

extends CanvasLayer

signal start_game

func _ready() -> void:
	pass  

func _process(delta: float) -> void:
	pass

func show_message(text) :
	$Label_message.text = text
	$Label_message.show()
	$Timer_message.start()


func show_gameover() :
	show_message("游戏结束!")
	
	await $Timer_message.timeout
	
	$Label_message.text = "行不行啊?"
	$Label_message.show()
	
	await  get_tree().create_timer(1.0).timeout
	$Button_start.show()
	
func up_score(score) :
	$Label_score.text = str( score )
	

func _on_button_start_pressed() -> void:
	$Button_start.hide()
	start_game.emit()
	pass 

func _on_timer_message_timeout() -> void:
	$Label_message.hide()
	pass 
        

返回主场景⬇️

image.png

添加gui的新游戏信号⬇️

image.png

image.png

extends Node

@export var npc : PackedScene 
var score

func _ready() -> void:
	pass 


func _process(_delta: float) -> void:
	pass

func game_over() -> void:
	$Timer_npc.stop()
	$Timer_score.stop()
	
	$gui.show_gameover()
	pass 
	
func newgame() :
	score = 0
	$player.start( $Marker2D_startpos.position )
	$Timer_start.start()
	
	$gui.up_score( score )
	$gui.show_message("准备开始!")
	

func _on_timer_score_timeout() -> void:
	score += 1
	
	$gui.up_score(score)
	pass 


func _on_timer_start_timeout() -> void:
	$Timer_npc.start()
	$Timer_score.start()
	pass 


func _on_timer_npc_timeout() -> void:
	
	var npc_follow = $Path2D_npc/PathFollow2D
	npc_follow.progress_ratio = randf()
	
	var direction = npc_follow.rotation + PI/2
	direction += randf_range(-PI/4 , PI/4)
	
	var velocity = Vector2(randf_range(155.0,250.0), 0.0)
	
	var new_npc = npc.instantiate()
	new_npc.position = npc_follow.position
	new_npc.rotation = direction
	new_npc.linear_velocity = velocity.rotated( direction )
	
	add_child( new_npc )
	
	pass 
        

游戏结束内容显示 ⬇️

image.png

extends Node

@export var npc : PackedScene 
var score

func _ready() -> void:
	pass 


func _process(_delta: float) -> void:
	pass

func game_over() -> void:
	$Timer_npc.stop()
	$Timer_score.stop()
	
	$gui.show_gameover()
	pass 
	
func newgame() :
	score = 0
	$player.start( $Marker2D_startpos.position )
	$Timer_start.start()
	

func _on_timer_score_timeout() -> void:
	score += 1
	pass 


func _on_timer_start_timeout() -> void:
	$Timer_npc.start()
	$Timer_score.start()
	pass 


func _on_timer_npc_timeout() -> void:
	
	var npc_follow = $Path2D_npc/PathFollow2D
	npc_follow.progress_ratio = randf()
	
	var direction = npc_follow.rotation + PI/2
	direction += randf_range(-PI/4 , PI/4)
	
	var velocity = Vector2(randf_range(155.0,250.0), 0.0)
	
	var new_npc = npc.instantiate()
	new_npc.position = npc_follow.position
	new_npc.rotation = direction
	new_npc.linear_velocity = velocity.rotated( direction )
	
	add_child( new_npc )
	
	pass 


func new_game() -> void:
	$gui.up_score( score )
	$gui.show_message("准备开始!")
	pass 

gui分数更新⬇️

image.png

extends Node

@export var npc : PackedScene 
var score

func _ready() -> void:
	pass 


func _process(_delta: float) -> void:
	pass

func game_over() -> void:
	$Timer_npc.stop()
	$Timer_score.stop()
	
	$gui.show_gameover()
	pass 
	
func newgame() :
	score = 0
	$player.start( $Marker2D_startpos.position )
	$Timer_start.start()
	

func _on_timer_score_timeout() -> void:
	score += 1
	
	$gui.up_score(score)
	pass  


func _on_timer_start_timeout() -> void:
	$Timer_npc.start()
	$Timer_score.start()
	pass 


func _on_timer_npc_timeout() -> void:
	
	var npc_follow = $Path2D_npc/PathFollow2D
	npc_follow.progress_ratio = randf()
	
	var direction = npc_follow.rotation + PI/2
	direction += randf_range(-PI/4 , PI/4)
	
	var velocity = Vector2(randf_range(155.0,250.0), 0.0)
	
	var new_npc = npc.instantiate()
	new_npc.position = npc_follow.position
	new_npc.rotation = direction
	new_npc.linear_velocity = velocity.rotated( direction )
	
	add_child( new_npc )
	
	pass 


func new_game() -> void:
	$gui.up_score( score )
	$gui.show_message("准备开始!")
        

优化篇⬇️

npc场景⬇️

为npc添加组

image.png

主场景代码⬇️

销毁上一局中的npc

image.png

extends Node

@export var npc : PackedScene 
var score

func _ready() -> void:
	pass 


func _process(_delta: float) -> void:
	pass

func game_over() -> void:
	$Timer_npc.stop()
	$Timer_score.stop()
	
	$gui.show_gameover()
	pass 
	
func newgame() :
	score = 0
	$player.start( $Marker2D_startpos.position )
	$Timer_start.start()
	
	$gui.up_score( score )
	$gui.show_message("准备开始!")
	
	get_tree().call_group("npcs", "queue_free")
	

func _on_timer_score_timeout() -> void:
	score += 1
	
	$gui.up_score(score)
	pass 


func _on_timer_start_timeout() -> void:
	$Timer_npc.start()
	$Timer_score.start()
	pass 


func _on_timer_npc_timeout() -> void:
	
	var npc_follow = $Path2D_npc/PathFollow2D
	npc_follow.progress_ratio = randf()
	
	var direction = npc_follow.rotation + PI/2
	direction += randf_range(-PI/4 , PI/4)
	
	var velocity = Vector2(randf_range(155.0,250.0), 0.0)
	
	var new_npc = npc.instantiate()
	new_npc.position = npc_follow.position
	new_npc.rotation = direction
	new_npc.linear_velocity = velocity.rotated( direction )
	
	add_child( new_npc )
	
	pass 
        

修改背景颜色⬇️

image.png

添加音频⬇️

image.png

image.png

代码播放音乐⬇️

image.png

extends Node

@export var npc : PackedScene 
var score

func _ready() -> void:
	pass 


func _process(_delta: float) -> void:
	pass

func game_over() -> void:
	$Timer_npc.stop()
	$Timer_score.stop()
	
	$gui.show_gameover()
	
	$AudioStreamPlayer2D_1.stop()
	$AudioStreamPlayer2D_2.play()
	pass 
	
func newgame() :
	score = 0
	$player.start( $Marker2D_startpos.position )
	$Timer_start.start()
	
	$gui.up_score( score )
	$gui.show_message("准备开始!")
	
	get_tree().call_group("npcs", "queue_free")
	
	$AudioStreamPlayer2D_1.play()
	

func _on_timer_score_timeout() -> void:
	score += 1
	
	$gui.up_score(score)
	pass 


func _on_timer_start_timeout() -> void:
	$Timer_npc.start()
	$Timer_score.start()
	pass 


func _on_timer_npc_timeout() -> void:
	
	var npc_follow = $Path2D_npc/PathFollow2D
	npc_follow.progress_ratio = randf()
	
	var direction = npc_follow.rotation + PI/2
	direction += randf_range(-PI/4 , PI/4)
	
	var velocity = Vector2(randf_range(155.0,250.0), 0.0)
	
	var new_npc = npc.instantiate()
	new_npc.position = npc_follow.position
	new_npc.rotation = direction
	new_npc.linear_velocity = velocity.rotated( direction )
	
	add_child( new_npc )
	
	pass 

通过按空格开始游戏⬇️

image.png

切换到gui场景

image.png

点击shortchut文件

image.png

完结⬇️

运行游戏后, 可以点击按钮开始,也可以用空格开始, 音乐播放没问题, 游戏可以玩了

可以继续打磨完善该游戏

祝大家游戏开发找到快乐!!!

image.png