Godot(4.X): 外接Python处理Excel数据: Godot全局登录管理器部分
前言:本文是 Godot(4.X): 外接Python处理Excel数据: 账号管理系统实现-CSDN博客 中Godot内 全局登录管理器AutoLoad部分 的补充完整实现,在阅读本文前,若有需要,请先阅读
Godot(4.X): 外接Python处理Excel数据: 账号管理系统实现-CSDN博客
Godot(2D)架构细分4:编辑器全局设置解释-CSDN博客
这些文字提供了阅读本文需要了解的内容。本文将会解释Godot内全局登录管理系统的主管理器,子管理器,登录界面 的实现方式以及演示
1. Godot内登录管理器概述
本文篇章使用的登录管理器的主要功能是对接Python,向游戏内其他逻辑部分返回经过处理后的统一返回值。注意 本登录管理器本身并不实现任何逻辑 ,只负责接收Python后向其他需要登录信息的场景或者脚本提供信息。
这里需要注意的是,登录管理器还存储当前登录玩家ID,以及一些登录信息,这部分逻辑由登录系统简单判断,登录后更新。
这里以登录界面为解释流程(登录界面本身为单独实现的场景)
1. 登录界面将玩家输入的信息存入,并发送至登录系统
2. 登录系统接收信息,将信息转化为传参传递给Python判断,Python返回判断值
3. 登录系统将判断值处理成统一格式后返回给登录界面,同时更新当前登录状态
2. Godot内登录管理器具体实现
实现将从AutoLoad登录管理器开始解释,再解释登录界面的实现逻辑
先给出主管理器的节点样式

以及管理器场景文件结构

1. AutoLoad登录管理器
Login子管理器和Register子管理器实现的原理一致,但是函数名和开放的API区分,可以使代码清晰易懂
先给出全局AutoLoad状态如下

1. 子登录管理器(Login)
负责接收Python的登录返回值
extends Node2D
"""
注意传输路径
Godot传出去的参数到Python接收,全部会被转化为字符串类型
这里给python传入的脚本格式为:
[python主脚本地址, str Excel请求路径, int 请求操作, 账户ID, 账户密码]
且传递内容全部统一为 str 类型
"""
## 登录信息验证(阻塞线程,运行的时候主程序会暂停)
## 这里的 input_info 传入的是[ID, Password]
func login_info_verify(input_info: Array[String]) -> String:
var receive_return: Array[String] = [] # 接收Python的返回值
var transmit_argv: Array = [] # Godot传给Python的参数
# 补全玩家输入的信息
while len(input_info) < 2:
input_info.append("None")
## 转化传输标准格式
## ProjectSettings.globalize_path()是将Godot的相对文件路径转化为系统路径
var python_path: String = ProjectSettings.globalize_path(LoginSystem.get_python_processing_path())
## 这里是先将传入的参数放进一个数组
## [Python程序路径, Excel文件路径, Godot人为规定的给Python指令, 账户ID, 账户密码]
transmit_argv = [python_path,
LoginSystem.excel_path,
str(0),
input_info[0],
input_info[1]]
## 向python传输基本信息
## 格式 OS.execute(程序运行编译环境(没有会从环境变量自动找),
## 传入参数(第一个是程序路径,给Godot找脚本同时也会被作为参数被传过去),
## 用于接收返回值的数组
## 决定是否接收没被Python收走的异常报错(try 模型收走报错所以不会被接收)
## 如果程序是命令终端类的,可以为True打开一个命令终端运行脚本,为False则后台静默运行
OS.execute("python",
transmit_argv,
receive_return,
true,
true)
## 由于接收为数组,需要使用 join 函数将数组拼起来转化为 string, 前面 "" 代表每一个元素之间连接为空字符串
## 由于Python返回值中包含转义符,使用strip_escapes()删除所有转义符
var result_output: String = "".join(receive_return).strip_escapes()
# 返回Python处理结果
return result_output
2. 子注册管理器
负责接收Python的注册返回值(实现原理同登录一致)
extends Node2D
"""
注意传输路径
这里给python传入的脚本格式为:
[python主脚本地址, str Excel请求路径, int 请求操作, 账户ID, 账户密码]
且传递内容全部统一为 str 类型
"""
# 注册信息验证(阻塞线程)
func register_info_verify(input_info: Array[String]) -> String:
var receive_return: Array[String] = []
var transmit_argv: Array = []
# 防止数组越界
while len(input_info) < 2:
input_info.append("None")
# 转化传输标准格式
var python_path: String = ProjectSettings.globalize_path(LoginSystem.get_python_processing_path())
transmit_argv = [python_path,
LoginSystem.excel_path,
str(1),
input_info[0],
input_info[1]]
# 向python传输基本信息
OS.execute("python",
transmit_argv,
receive_return,
true,
true)
# 返回结果
var result_output: String = "".join(receive_return).strip_escapes()
return result_output
3. 主管理器
主管理器负责使用子管理器的接收功能,后主动更新当前基本登录信息(玩家ID等),主要用于向外开放API接口
以下为示例代码
extends Node2D
"""
需要注意的是,下文的所有返回值
均可以换成全局枚举类型
本文使用的是字符串比对
使用全局枚举的在登录界面演示中会使用
"""
# 登录信息信号
signal login_success # 登录成功信号
signal login_out # 账户登出信号
# 登录和注册两个子管理器
@export var Login : Node2D
@export var Register : Node2D
# 存储当前账号状态
var current_account_id: String = "" # 当前成功登录玩家ID
var is_Login: bool = false # 当前是否登录
# 主管理器提供统一 Excel 文件地址,这里使用绝对地址,当然也可以使用相对Godot的相对地址
# 主管理器同样要提供 Python脚本 的地址
var excel_path = "D:/GodotProject/Godot Excel Spreadsheet/godot-excel-spreadsheet/06_GameData/ServerData/GameDataExcel.xlsx"
var python_script = "res://06_GameData/ServerData/FileProcessingMain.py"
# 获得默认python脚本路径
func get_python_processing_path() -> String:
return python_script
# 获得默认excel路径
func get_excel_path() -> String:
return excel_path
"""
以下登录返回值类型
返回 0 登录成功
返回 1 账户信息不存在
返回 2 账户信息验证错误(密码错误)
"""
# 进行登录验证
func login_verify(name: String, password: String) -> String:
# 将获得的输入改为标准格式: ["名字", "密码"]
var input_info: Array[String] = [name, password]
# 调用子登录管理器验证并获得返回值
var verify_result = Login.login_info_verify(input_info)
# 登录器自验证注册是否成功,成功则更改登录服务器存储信息
# 登录自验证函数在下文
verify_login_success(verify_result, name)
return verify_result
"""
以下注册返回值类型
返回 0 注册成功
返回 1 账户注册出现问题(非法字符/账户密码问题)
返回 2 账户已被注册
"""
# 进行注册验证
func register_verify(name: String, password: String) -> String:
# 将获得的输入改为标准格式: ["名字", "密码"]
var input_info: Array[String] = [name, password]
var verify_result = Register.register_info_verify(input_info)
# 登录器自验证注册是否成功
verify_login_success(verify_result, name)
return verify_result
# 获得当前登录玩家ID
func get_current_id() -> String:
return current_account_id
# 更改当前玩家登录ID
func change_current_id(new_id: String) -> void:
current_account_id = new_id
# 获得登录状态
func get_login_state() -> bool:
return is_Login
# 登录成功验证
func verify_login_success(verify_result: String, verift_account_name: String) -> void:
# 验证结果为 "0"(通过) 则更新当前登录系统登录状态
if verify_result == "0":
is_Login = true
login_success.emit() # 发出登录信号
# 更新服务器信息
if is_Login:
current_account_id = verift_account_name
# 退出登录,同时更新服务器信息
func exit_login() -> void:
is_Login = false # 登录状态为 未登录
current_account_id = "" # 重置当前登录ID
login_out.emit() # 发送登出信号
这里就是本文章使用的全局登录系统的全部代码
2. 登录界面
登录界面是一个 独立的场景,同时 与登录系统保持独立 ,使用登录系统所提供的API。
登录界面要做以下流程:
1. 用户提供登录或者注册信息
2. 登录界面将信息转化为标准格式,将信息传输至全局登录管理器
3. 接收登录管理器返回的校验结果,弹出对应提示,完成对应逻辑
先给出登录界面的结构如下
1. 节点结构:


2. 显示界面:
1. 操作

2. 登录界面和注册界面复用一个窗口

文章将会解释登录注册窗口的代码。
全局类枚举管理器
extends Node
class_name GlobalEnum
# 注册或者登录状态枚举
enum User_State {LOGIN, REGISTER}
1. 登录UI管理器(LoginGroup)
登录管理器负责管理其子节点的综合操作,比如子节点窗口是注册还是显示状态,子节点初始是否可见等
以下为演示代码
extends Control
@export var Login_Windows: Window # 子节点,登录注册复用窗口
@export var Confirm_Button: Button # 子节点,确认登录按钮
# 管理器中存储的当前用户选择的注册或者登录状态,以及先前的注册或者登录状态
# 枚举状态从全局枚举类获得
var user_state: GlobalEnum.User_State = GlobalEnum.User_State.LOGIN
var user_per_state: GlobalEnum.User_State = GlobalEnum.User_State.REGISTER
# 初始化,函数在下文
func _ready() -> void:
_initialize()
# 用户操作状态检测
func _user_operator_detected() -> void:
# 仅在用户切换状态的时候触发
if (user_state == user_per_state):
return
# 状态检测
match user_state:
# 用户处于登录状态,窗口为登录窗口
GlobalEnum.User_State.LOGIN:
Login_Windows.title = "登录"
Confirm_Button.text = "登录"
# 用户处于注册状态,窗口为注册窗口
GlobalEnum.User_State.REGISTER:
Login_Windows.title = "注册"
Confirm_Button.text = "注册"
# 更新完后更新用户先前状态与现在一致
user_per_state = user_state
# 执行一次用户状态检测使第一次进入游戏窗口标题为登录
func _initialize() -> void:
# 窗口初始化
_user_operator_detected()
"""
改变用户状态
状态 0 登录
状态 1 注册
"""
# 这里是对两个选择(登录,注册)开放的API接口,用于改变当前用户选择状态
# 0 表示登录状态, 1 表示注册状态
# 这里 0 和 1 同样可以换成常量
func change_user_state(state: int) -> void:
match state:
0: user_state = GlobalEnum.User_State.LOGIN
1: user_state = GlobalEnum.User_State.REGISTER
# 更新一次用户选择状态
_user_operator_detected()
# 返回当前用户选择状态
func get_user_state() -> GlobalEnum.User_State:
return user_state
2. 用户选择管理器(ButtonGroup)
用来改变用户选择(登录或注册)
以下为代码演示
extends VBoxContainer
@export var Login_Window : Window # 登录界面窗口
@export var Login_Group : Control # 登录UI管理器(LoginGroup)
# 初始化
func _ready() -> void:
_initialize()
# 按下登录按键,向登录UI管理器发送状态更改
func _on_login_pressed() -> void:
Login_Window.visible = true # 显示窗口
Login_Group.change_user_state(0) # 发送状态更改值为 0,上文已演示API对应代码
# 按下注册按键,向登录UI管理器发送状态更改
func _on_register_pressed() -> void:
Login_Window.visible = true # 显示窗口
Login_Group.change_user_state(1) # 发送状态更改值为 1,上文已演示API对应代码
# 退出登录系统(这里退出游戏)
func _on_exit_game_pressed() -> void:
get_tree().quit()
# 初始化
func _initialize() -> void:
# 窗口初始化登录窗口为不可见
Login_Window.visible = false
3. 用户输入管理器(InformationInput)
用户输入管理器用于接收并向登录管理器发送用户输入的账户信息,同时得到验证结果后执行弹窗等逻辑
以下为演示代码
extends VBoxContainer
@export var Login_Group: Control # 登录UI管理器(LoginGroup)
@export var Account_Input: LineEdit # 账户单行输入框
@export var Password_Input: LineEdit # 账户密码单行输入框
var login_success: String = "0" # 账号成功状态
var login_info_issue: String = "1" # 账号信息问题状态
var login_info_error: String = "2" # 账号无误,验证错误
# 登录提示框大小
var tip_size: Vector2 = Vector2(400, 200)
# 弹出登录提示
# 输入参数 (Python验证结果, 是否自定义弹窗内容(默认False), 自定义弹窗内容(默认空字符串))
func _general_tip(
python_result: String,
is_custom: bool = false,
custom_tip: String = "") -> void:
# 获取当前玩家选择状态
var current_user_state: GlobalEnum.User_State
current_user_state = Login_Group.get_user_state()
# 创建弹窗,并修改弹窗标题大小
var login_tip = AcceptDialog.new()
login_tip.title = "登录提示"
login_tip.size = tip_size
# 自定义提示
if is_custom:
# 加入自定义信息
login_tip.dialog_text = custom_tip
# 将登录提示加入场景并居中
get_tree().current_scene.add_child(login_tip)
login_tip.popup_centered()
# 自定义提示加入完成后直接完成函数
return
# 匹配弹出类型(登录或注册)
match current_user_state:
# 登录状态
GlobalEnum.User_State.LOGIN:
# 匹配登录状态
match python_result:
login_success:
login_tip.dialog_text = "登录成功"
login_info_issue:
login_tip.dialog_text = "账号信息不存在"
login_info_error:
login_tip.dialog_text = "密码错误"
# 注册状态
GlobalEnum.User_State.REGISTER:
# 匹配注册状态
match python_result:
login_success:
login_tip.dialog_text = "注册成功"
login_info_issue:
login_tip.dialog_text = "注册密码非法或长度小于4(账号长度不能小于3)"
login_info_error:
login_tip.dialog_text = "注册信息已存在"
# 将登录提示加入场景并居中
# 注意 login_tip.popup_centered() 一定要先在弹窗加入节点树后再居中
# 不然就弹窗由于没加入场景树,执行一次居中后不是在当前场景内居中,再加入场景树后就会居中失败
get_tree().current_scene.add_child(login_tip)
login_tip.popup_centered()
# 登录按钮被点击,传输登录数据
func _on_login_pressed() -> void:
# 获取当前状态
var current_user_state: GlobalEnum.User_State
current_user_state = Login_Group.get_user_state()
# 获得ID输入信息
var input_account_name: String = Account_Input.text.strip_edges() # 账户ID
var input_account_password: String = Password_Input.text.strip_edges() # 账户密码
# 先检查账户的输入是否完全
if input_account_name.is_empty() or input_account_password.is_empty():
_general_tip("0", true, "账号或密码不能为空")
Password_Input.clear()
return
var verify_result
# 匹配用户状态
match current_user_state:
# 登录状态使用登录验证
GlobalEnum.User_State.LOGIN:
verify_result = LoginSystem.login_verify(input_account_name, input_account_password)
# 注册状态使用注册验证
GlobalEnum.User_State.REGISTER:
verify_result = LoginSystem.register_verify(input_account_name, input_account_password)
# 输出提示
_general_tip(verify_result)
# 清除密码输入框
Password_Input.clear()
3. 登录界面演示
先给出演示场景结构

可以看到并没有登录场景,这里放在了 UiControl 代码里
extends CanvasLayer
# 这里是登录界面场景
@export var Login_System: PackedScene
func _ready() -> void:
_initialize()
# 初始化主菜单
func _initialize() -> void:
# 初始化登录系统
if not LoginSystem.get_login_state():
# 添加登录界面场景
var login_system = Login_System.instantiate()
add_child(login_system)
启动游戏,看到登录界面显示

登录和注册分别根据选择类型弹出不同窗口

登录已经有账号

显示

其余状态同理,登录失败,用户名或密码不能为空
我们演示注册,同样先给出Excel状态

然后注册

显示

检查Excel可以看到信息被存入

4. 结尾返回
这里展示了Godot内部对接Python后登录管理器的构建与使用,这里返回主要文章
更多推荐
所有评论(0)