【Godot 保存和加载游戏-01:简单方法介绍】 https://www.bilibili.com/video/BV19r421H7kM/?share_source=copy_web&vd_source=210b0b0bede87c353444923de149ef38
以下是该B站视频的笔记。
1-简单方法
节点排序
Container
SaveButton
LoadButton
SaverLoader
挂载脚本,连接按钮
SaverLoader.gd
func save_game():
var file=FileAccess.open("res://savegame.data",FileAccess.WRITE)
file.store_var(player.global_position)
file.store_var(player.health)
file.close()
func load_game():
var file=FileAccess.open("res://savegame.data",FileAccess.READ)
player.global_position=file.get_var()
player.health=file.get_var() #写入与读取的顺序要一致
file.close()
#认识Vector2,人类不可读
2-格式选择JSON int、float、String、Array、Dictionary
SaverLoader.gd
func save_game():
var file=FileAccess.open("user://savegame.json",FileAccess.WRITE)
var saved_data={}
saved_data['player_global_position']=player.global_position
saved_data['player_health']=player.health
var json=JSON.stringify(saved_data)
file.store_string(json)
file.close()
func load_game():
var file=FileAccess.open("user://savegame.json",FileAccess.READ)
var json=file.get_as_text()
var saved_data=JSON.parse_string(json)
player.health=saved_data['player_health']
player.global_position=saved_data['player_global_position']
file.close()
#JSON不认识Vector2,会把position变成字符串,所以须把它拆分成xy
#人类可读
3-格式选择字典
在简单方法中,把字典写入.data文件中
4-格式选择自定义资源
saved_game.gd
class_name SavedGame
extends Resource
@export var player_position:Vector2
@export var player_health:float
SaverLoader.gd
func save_game():
var saved_game:SavedGame=SavedGame.new()
saved_game.player_position=player.global_position
saved_game.player_health=player.health
ResourceSaver.save(saved_game,'user://save_game.tres')
func load_game():
var saved_game:SavedGame=load('user://save_game.tres') as SavedGame
player.global_position=saved_game.player_position
player.health=saved_game.player_health
5-动态游戏元素的处理
saved_game.gd
@export var fish_positions:Array[Vector2]=[]
SaverLoader.gd
保存
for fish in get_tree().get_nodes_in_group('fish'):
saved_game.fish_positions.append(fish.global_position)
加载
#先清除当前的鱼
for fish in get_tree().get_nodes_in_group('fish'):
fish.get_parent.remove_child(fish)
fish.queue_free()
#若直接用queue,则节点会在树里待到最后一帧才山删除,可能有副作用
#再读取
var fish_scene=preload('')
for position in saved_game.fish_positions:
var new_fish=fish_scene.instantiate()
world_root.add_child(new_fish)
new_fish.global_position=position
6-通用方法的构建
保存加载器不用知道节点内部信息
将保存分为两个阶段,收集和保存
加载同上,鱼、水雷要清除,而入口等静态物不要
saved_data.gd
class_name SavedData
extends Resource
@export var position:Vector2
@export var scene_path:String
saved_game.gd
class_name SavedGame
extends Resource
@export var player_position:Vector2
@export var player_health:float
@export var saved_data:Array[SavedData]=[]
fish.gd
func on_save_game(saved_data:Array[SavedData]):
#让节点决定自己是否被保存
if _dying:
return
var my_data=SavedData.new()
my_data.position=global_position
my_data.scene_path=scene_file_path
saved_data.append(my_data)
func on_before_load_game():
get_parent().remove_child(self)
queue_free()
func on_load_game(saved_data:SavedData):
global_position=saved_data.position
SaverLoader.gd
func save_game():
var saved_game:SavedGame=SavedGame.new()
saved_game.player_position=player.global_position
saved_game.player_health=player.health
var saved_data:Array[SavedData]=[] #game_events分组管理
get_tree().call_group('game_events','on_save_game',saved_data)
saved_game.saved_data=saved_data
ResourceSaver.save(saved_game,'user://save_game.tres')
func load_game():
var saved_game:SavedGame=load('user://save_game.tres') as SavedGame
player.global_position=saved_game.player_position
player.health=saved_game.player_health
get_tree().call_group('game_events','on_before_load_game')
for item in saved_game.saved_data:
var scene=load(item.scene_path) as PackedScene
var restored_node=scene.instantiate()
world_root.add_child(restored_node)
if restored_node.has_method('on_load_game'):
restored_node.on_load_game(item)
7-扩展通用方法
新建脚本
class_name SavedThisData
extends SavedData
增加属性
8-存档兼容性
机制更新后,加载旧存档可能报错
比如鱼被攻击后变小,二次则死。
新建的SavedFishData,旧版本的SavedGame没有它
报错 var my_data:SavedFishData=saved_data as SavedFishData
纠正
func on_load_game(saved_data:SavedData):
global_position=saved_data.position
if saved_data is SavedFishData:
scale=saved_data.scale
#旧存档不应用新属性
9-存档安全性
玩家修改血量为一万
检查血量在合理范围内
在存档中添加代码,存档被加载时,代码会自动执行,恶意发布植入代码的存档
插件SafeResourceLoader
取消选择示例文件夹,项目设置里勾选并重加载项目
func load_game():
var saved_game:SavedGame=SafeResourceLoader.load()
if saved_game==null:
print('Saved game was unsafe!')
return