提示:B站链接:Godot Zelda教程练习
资产链接:项目资产
Godot版本:4.3
文章目录
- 一、新建项目
- 1、创建项目
- 2、设置项目标签
- 3、项目基本设置
- 4、导入资产
- 二、图块集和自动平铺
- 1、创建TileMapLayer
- 2、创建Terrian Set
- 1、Match Siders(边缘匹配)
- 2、Match Corners(角落匹配)
- 3、Match Corners and Siders(角落匹配和边缘匹配)
- 4、图块概率设置
- 三、水的瓦片集(water TileSet)
- 1、加入TileSetWater.png图块
- 2、创建WaterLightGrass
- 3、创建Water的瓦片
- 四、桥梁的瓦片集(Bridge TileSet)
- 五、雪、高山以及楼梯
- 1、添加瓦片集
- 2、添加地形元素
- 六、简单关卡和装饰
- 1、绘制草
- 2、添加水
- 3、移动桥
- 4、其它装饰
- 5、添加TilesetNature装饰
- 七、基本角色移动
- 1、新建场景
- 2、创建动画
- 1、定义其他帧
- 2、创建Camera2D
- 3、角色移动
- 八、添加碰撞以及移动动画
- 1、添加碰撞
- 1、为water设置碰撞
- 2、为桥设置碰撞
- 3、为楼梯设置碰撞器
- 4、为其他装饰物设置碰撞器
- 2、移动动画
- 九、添加碰撞 - 拾取物品和库存物品
- 1、新建文件夹Resources
- 2、新建资源文件gold_coin.tres
- 3、编写inventory_item.gd
- 4、新建PickUpItem场景
- 5、设置金币和玩家碰撞
- 6、为PickUpItem创建脚本
- 7、实例化子场景
- 十、库存槽
一、新建项目
1、创建项目
项目名称:ZeldaCourse
点击创建并编辑即可。
2、设置项目标签
可以为项目设置标签,便于管理
3、项目基本设置
设置默认纹理过滤为Linear
4、导入资产
新建Assets文件夹,将资产解压导入到该文件夹中。
二、图块集和自动平铺
1、创建TileMapLayer
- 创建Scenes文件夹,并且创建一个node空节点,命名为main,保存场景到Scenes文件夹。
- 为main节点创建一个child node,命名为TileLayers
- 为TileLayers创建一个TileMapLayers节点,取名为GroundTileLayer
- 为GroundTileLayer创建Tile Set
- 点击TileSet ,将res://Assets/Sprites/Backgrounds/Tilesets/TilesetFloor.png拖动到图快区域
2、创建Terrian Set
如下图,在GroundTileLayer节点上,创建Terrian Set,名称为LightGrass,Color为0cc214
完成地形集创建之后,需要在绘制中添加对应的图块。
1、Match Siders(边缘匹配)
模式选择Match Siders,然后我们选择两个图块,然后进入TileMap查看有两个图块。可以绘制。
2、Match Corners(角落匹配)
使用矩形绘制如下:
两个矩形叠加的时候,会出现如上图所示的凸出的部分,这是因为边缘和角落冲突了,所以我们修改模式为Match Corners and Siders
3、Match Corners and Siders(角落匹配和边缘匹配)
重新绘制之后如下图所示:
还是发现了一些其他的问题,如下:
我们再加上如下设置
修复角落:
我们最终的图块如下设置:
设置纹理渲染为Nearest
4、图块概率设置
选择这两个土块,绘制后,上面有路的纹理,现在我们不需要这两个图块的频率很大,可以设置概率。
现在很稀疏了
三、水的瓦片集(water TileSet)
1、加入TileSetWater.png图块
将TileSetWater.png加入到图块。
2、创建WaterLightGrass
颜色设置为3070c5
3、创建Water的瓦片
如下设置:
添加一些装饰,并且设置概率是0.1
绘制完成如下:
四、桥梁的瓦片集(Bridge TileSet)
在Terrian Sets添加Bridge地形,颜色设置为c05505
设置如下规则:
设置概率:
1、为TileLayers节点新建DecorationTileMapLayers节点(TileMapLayers)
2、将原来的TileSet另存为。保存的位置是res://TileSets,当然要新建TileSets文件夹,并且命名为tileset.tres
3、选择DecorationTileMapLayers节点,然后TileSet选择刚才保存的tileset.tres
4、绘制桥梁
五、雪、高山以及楼梯
1、添加瓦片集
将res://Assets/Sprites/Backgrounds/Tilesets/TilesetRelief.png添加到瓦片集中。
2、添加地形元素
设置地形元素名称为SnowRidge,颜色为5c544d
设置瓦片集
创建SnowRidgeWall地形元素,颜色设置为:5c544d。图块设置如下:
创建Staris地形元素,颜色设置为:c2008c。然后将res://Assets/Sprites/Backgrounds/Tilesets/TilesetHouse.png添加到TileSet中,图中存在楼梯。
在GroundTileLayer绘制SnowRidgeWall和SnowRidge,在DecorationTileMapLayers绘制Stairs
效果如下:
六、简单关卡和装饰
1、绘制草
按照如下步骤绘制:
然后再使用LightGrass地形修正:
2、添加水
3、移动桥
选择图块,选择桥,然后就可以移动。
4、其它装饰
添加这个墩子
上述绘制完成之后,在图案选择这个墩子,直接移动到图案中,后续使用。
还有一个洞穴的方式同样如此操作。
5、添加TilesetNature装饰
res://Assets/Sprites/Backgrounds/Tilesets/TilesetNature.png,将这个图块加入进来。
选择图块->选择照片->TileMap->选择树木->选择绘制
同样拖到图案中。
最后绘制如下:
七、基本角色移动
1、新建场景
1、创建CharacterBody2D节点
2、添加子节点AnimatedSprite2D
2、创建动画
选择AnimatedSprite2D节点,在检查器中新建Sprites Frames,然后选择这里
选择:res://Assets/Sprites/Actor/Characters/FighterWhite,然后打开SpriteSheet.png
设置垂直为7,和图片匹配
选择第一幅,添加帧
创建节点CollisionShape2D,作为CharacterBody2D的子节点。
设置碰撞器的形状为原型,半径为7,然后调整碰撞器,保存场景为player.tscn
修改名称为font_idle,然后点击自动播放。
切换到main场景
实例化子场景
1、定义其他帧
分别是back_idle、left_idle、right_idle
2、创建Camera2D
设置Zoom为3
3、角色移动
1、切换到main场景
2、打开项目设置
3、选择输入映射
在后面的+号绑定键盘
修改节点名称为Player
Player节点添加脚本:保存到res下面的Scripts文件夹(需要创建),名称为player.gd
脚本如下:
extends CharacterBody2Dconst SPEED = 5000.0func _physics_process(delta: float) -> void:#获取向量var direction=Input.get_vector("left","right","up","down")if direction :velocity=direction * SPEED * delta;else :# float move_toward(from: float, to: float, delta: float)# 将 from 向 to 移动,移动的长度是 delta。不会超过 to。velocity.x= move_toward(velocity.x, 0, SPEED * delta)velocity.y= move_toward(velocity.y, 0, SPEED * delta)# 根据velocity速度移动该物体move_and_slide()
八、添加碰撞以及移动动画
1、添加碰撞
切换到main场景,在项目设置中,设置如下的Layer
1、为water设置碰撞
切换到Player场景,添加碰撞
为GroundTileLayer添加物理层碰撞
选择展开,栅格吸附为8
完成之后运行游戏,发现角色不能进入水的范围。
设置水的土块的物理层:
设置另外一块水的物理层:
带来了新的问题,角色无法跨越过桥
在选择中,对如下的两个图块添加备选图块
然后删除这些物理层:
将备选图块放入桥的边缘。
运行游戏,角色可以穿过桥了。
带来新的问题,角色可以从桥进入水中了。
2、为桥设置碰撞
为桥设置碰撞器,首先要点击TileSet才会出现物理层。
为桥设置栏杆物理层:
运行游戏,发现角色不能从桥进入水中,带来的新问题是:角色不能从桥的侧面上桥。
桥的六个图块添加备选
对桥的角落重新绘制即可。
此时运行游戏发现正常上桥。
3、为楼梯设置碰撞器
为了更加清晰的看到碰撞区域,我们在调试中勾选如下:
为楼梯设置物理层:
运行游戏,发现碰撞层生效,但是无法上楼梯:
操作方式还是创建备选图块,重新绘制楼梯口。
此时楼梯的栏杆失去了作用:
同理,加碰撞层。
优化下边缘:
发现楼梯那里有点缝隙,可以调整粉色框和格子纵向对齐:
4、为其他装饰物设置碰撞器
1、大树根
2、树块
还有装饰一些其他,不再赘述。
我设置的有问题,这些应该在物理层1中完成绘制。
2、移动动画
修改节点CharacterBody2D为Player
创建脚本:animation_controller.gd
extends AnimatedSprite2D
class_name AnimationControllerfunc _ready() -> void:pass func _process(delta: float) -> void:pass
然后打开play.gd
extends CharacterBody2Dclass_name Player#@onready 注解:确保变量在节点准备好后才被赋值。这意味着即使在 _ready() 函数之前调用这个变量,也不会导致 null 或未初始化的错误。
#变量类型:animated_sprite_2d 被声明为 AnimationPlayer 类型。这是 Godot 中用于播放动画的节点类型。但是,根据您的描述,
# $AnimatedSprite2D 应该是一个 AnimatedSprite2D 节点,而不是 AnimationPlayer。因此,变量类型应该改为 AnimatedSprite2D。
#路径表达式:$AnimatedSprite2D 是一个路径表达式,表示从当前节点开始查找名为 AnimatedSprite2D 的子节点。@onready var animated_sprite_2d:AnimationController = $AnimatedSprite2Dconst SPEED = 5000.0func _physics_process(delta: float) -> void:#获取向量var direction=Input.get_vector("left","right","up","down")if direction :velocity=direction * SPEED * delta;else :# float move_toward(from: float, to: float, delta: float)# 将 from 向 to 移动,移动的长度是 delta。不会超过 to。velocity.x= move_toward(velocity.x, 0, SPEED * delta)velocity.y= move_toward(velocity.y, 0, SPEED * delta)# 根据velocity速度移动该物体move_and_slide()
1、新建动画,命名为front_walk,添加帧。
2、重复上述动作,新建其他的动画,加帧。
编写脚本animation_controller.gd
extends AnimatedSprite2Dclass_name AnimationControllerconst MOVEMENT_TO_IDLE={"back_walk" : "back_idle","front_walk" : "front_idle","right_walk" : "right_idle","left_walk" : "left_idle"
}func play_movement_animation(velovity:Vector2):if(velovity.x>0):play("right_walk")elif (velovity.x<0):play("left_walk")if(velovity.y>0):play("front_walk")elif (velovity.y<0):play("back_walk")func play_idle_animation():if MOVEMENT_TO_IDLE.keys().has(animation):play(MOVEMENT_TO_IDLE[animation])
编写play.gd
func _physics_process(delta: float) -> void:#获取向量var direction=Input.get_vector("left","right","up","down")if direction :velocity=direction * SPEED * delta;else :# float move_toward(from: float, to: float, delta: float)# 将 from 向 to 移动,移动的长度是 delta。不会超过 to。velocity.x= move_toward(velocity.x, 0, SPEED * delta)velocity.y= move_toward(velocity.y, 0, SPEED * delta)# 根据velocity速度移动该物体# 动画处理if velocity != Vector2.ZERO:animated_sprite_2d.play_movement_animation(velocity)else :animated_sprite_2d.play_idle_animation()move_and_slide()
运行游戏即可运动。
九、添加碰撞 - 拾取物品和库存物品
1、新建文件夹Resources
res://Resources/
2、新建资源文件gold_coin.tres
3、编写inventory_item.gd
创建脚本:res://Scripts/inventory_item.gd
inventory_item.gd
extends Resourceclass_name InventoryItemvar stacks=1@export_enum("Right_Hand","Left_Hand","Potions","NotEquipable")
var slot_type:String="NotEquipable"@export var ground_collision_shape:RectangleShape2D
@export var name:String=""
@export var texture:Texture2D
@export var side_texture:Texture2D
@export var max_stacks:int
@export var price:int
然后gold_coin.tres快速加载这个脚本:
4、新建PickUpItem场景
新建场景,类型为Area2D,
重命名为PickUpItem,保存为:
pickup_item.tscn
为PickUpItem新建如下的两个子节点:
选择Sprite2D节点,设置金币纹理:
把res://Assets/Sprites/Items/Treasure/GoldCoin.png图片托入进去。
选择CollisionShape2D节点,然后选择新建RectangleShape2D
设置碰撞如下:
然后如下:
保存到res://Resources/GoldCoin/目录下,GoldCoin需要新建。名称为:gold_coin_clollision_shape.tres。并且把gold_coin.tres移动到该文件夹下。
为gold_coin.tres设置属性:
5、设置金币和玩家碰撞
在项目设置中设置2D物理:
设置碰撞:
6、为PickUpItem创建脚本
名称为pickup_item.gd
编写代码:
extends Area2Dclass_name PickUpItem
@export var inventory_item:InventoryItemfunc _ready() -> void:$Sprite2D.texture=inventory_item.texture$CollisionShape2D.shape=inventory_item.ground_collision_shape
7、实例化子场景
加载资源:
跳转到Player场景,新建Area2D节点,为其创建子节点CollisionShape2D:
然后选择图中的节点,另存为资源res://Resources/Player/player_collision_shape.tres
Player文件夹需要新建。
在节点CollisionShape2D中加载保存的资源:
调整碰撞:
选择第一个函数,右键连接:
函数编写:
func _on_area_2d_area_entered(area: Area2D) -> void:if area is PickUpItem:area.queue_free()
Player:碰撞设置如下:
Area2D如下:
运行游戏可以见到金币。
十、库存槽
1、新建场景VBoxContainer
2、在Scenes新建UI文件夹
3、保存场景:res://Scenes/UI/inventory_slot.tscn
4、在VBoxContainer新建子节点NinePatchRect
5、将FacesetBox.png拖入到NinePatchRect的纹理中
6、为NinePatchRect的Layout设置尺寸为60
7、为NinePatchRect新建子节点MenuButton,并且锚点预设为整个矩形。
8、为MenuButton新建子节点CenterContainer,并且锚点预设为整个矩形。
9、为CenterContainer新建子节点TextureRect
10、为TextureRect设置属性
11、在NinePatchRect节点下新建子节点Button,命名为OnClickButton
12 、为OnClickButton设置属性:
13、为NinePatchRect节点新建Label标签节点,拖到如下位置:
重命名Label为StacksLabel
14、VBoxContainer节点新建子节点Label,命名为NameLabel
15、VBoxContainer节点新建子节点Label,命名为PriceLabel,并且隐藏
16、重命名VBoxContainer为InventorySlot
17、新建脚本inventory_slot.gd
extends VBoxContainerclass_name InventorySlotvar is_empty=true
var is_selected=false@export var single_button_press=false
@export var starting_texture:Texture
@export var start_label:String@onready var texture_rect:TextureRect= $NinePatchRect/MenuButton/CenterContainer
@onready var name_label:Label= $NameLabel
@onready var stacks_label:Label= $NinePatchRect/StacksLabel
@onready var on_click_button:Button=$NinePatchRect/OnClickButton
@onready var price_label:Label=$PriceLabel
@onready var menu_button:MenuButton=$NinePatchRect/MenuButtonvar slot_to_equip="NotEquipable"func _ready() -> void:if starting_texture!=null:texture_rect.texture=starting_textureif start_label!=null:name_label.text=start_labelmenu_button.disabled=single_button_presson_click_button.disabled=!single_button_presson_click_button.visible=single_button_pressvar popup_menu=menu_button.get_popup()popup_menu.id_pressed.connect(on_popup_menu_item_pressed)func on_popup_menu_item_pressed(id:int):print_debug(id)
为MenuButton设置属性