godot4 web 文件上传和下载
关于这个需求,之前在itch.io玩过一个web端游戏,可以导入导出存档,并且游戏同样是godot开发的
这次gamejam在确定了核心机制之后,就在思考如何构建编辑器,因为我个人设计能力偏弱,内置编辑器让玩家来参与设计是一个取巧的方法,玩家在设计出关卡之后,如何分享是一个问题,而由于之前活动租的云服务器早已到期,况且接入服务端是一件很费心力的事情,为了简化需求,在网页端导入导出关卡便是最后确定的设计。虽然说没有玩家来分享关卡也是很正常的结果,不过“可以不用,但是要有”这也是个人秉持的理念之一,谁知道冗余的设计在什么时候连成一个体系了呢
说归正题,在实现核心模块之后,我开了一个测试项目用来测试web段导入导出,测试项目和jam项目的版本都是godot4.4,在测试项目最开始,就是先问ai,如何实现,而ai给出的代码中,下载可以直接用,上传就是无法使用,我先给出下载的部分
func download_file(path: String, filename: String):
var file = FileAccess.open(path, FileAccess.READ)
var bytes = file.get_buffer(file.get_length())
JavaScriptBridge.eval("""
var blob = new Blob([new Uint8Array(%s)], {type: 'application/octet-stream'});
var link = document.createElement('a');
link.href = URL.createObjectURL(blob);
link.download = '%s';
link.click();
""" % [str(bytes), filename])
func _on_download_button_pressed():
download_file("user://savegame.json", "savegame.json")
下载部分很简单,读取user://savegame.json,转换为bytes,使用 JavaScriptBridge.eval生成html的下载链接,把数据写入blob并触发下载
关键在于上传,该部分调试了很久,主要是回调问题不好解决
上传的代码有2部分
一部分是在html中,这个部分建议修改导出模板将代码直接嵌入,否则就需要每次导出时都需要导入一次,我是将该段代码放在加载状态代码的前面
<input id="godot_upload" type="file" accept=".json" style="display: none;">
<script>
window.ret_uint_array = []
document.getElementById('godot_upload').addEventListener('change', function(e) {
const file = e.target.files[0];
const reader = new FileReader();
reader.onload = function() {
const uintArray = new Uint8Array(reader.result);
window.ret_uint_array = uintArray
window.on_receive_save_data(uintArray, file.name);
};
reader.readAsArrayBuffer(file);
});
</script>
简单来说就是一个隐藏的上传input,和一个回调函数,回调函数可将上传数据生成 Uint8Array传入window.on_receive_save_data
而另一部分代码,作用一个是触发上传input,另一个是将godot的回调函数挂载到web部分的window变量中
var receive_save_data_ref
func _ready():
receive_save_data_ref = JavaScriptBridge.create_callback(_on_save_received)
var window = JavaScriptBridge.get_interface("window")
window.on_receive_save_data = receive_save_data_ref
func _on_save_received(args):
var arr = JavaScriptBridge.get_interface("ret_uint_array")
var byte_array = args[0] # 接收 JavaScript 的 Uint8Array
var file_name = args[1]
# 转换为 PackedByteArray
var bytes = PackedByteArray()
for i in byte_array.length:
bytes.append(byte_array[i])
# 保存到用户目录
var save_path = "user://%s" % file_name
var file = FileAccess.open(save_path, FileAccess.WRITE)
if file:
file.store_buffer(bytes)
print("存档已加载至:", save_path)
#load_game_data(save_path) # 调用你的存档加载逻辑
else:
push_error("文件写入失败")
func _on_upload_button_pressed():
JavaScriptBridge.eval("""
document.getElementById('godot_upload').click();
""", true)
在_ready中将_on_save_received挂载到window.on_receive_save_data,引用地址为var receive_save_data_ref,同时也方便对于不同环境做差异化,如果不是web环境就不加载_ready处的内容
_on_save_received的部分,args为js回调函数传过来的参数
实现这个功能主要是使用ai + 文档,ai可以把第一步做好,但ai很容易胡说,所以要注意对照文档,要不然就被ai带进沟里了
Web - The JavaScriptBridge Singleton - 《Godot 游戏引擎 v4.3 中文文档》 - 书栈网 · BookStack
Leave a comment
Log in with itch.io to leave a comment.