Godot(4.x): 游戏管理器: Godot 内注入数据处理与总接口实现

前言: 本文是 Godot(4.x): 游戏管理器: Excel 动态依赖注入实现-CSDN博客 中对 Godot 内注入数据处理与总接口实现部分的具体实现。在阅读本文前,建议先阅读以下内容:

Godot(4.x): Python处理转换Excel为注入Json-CSDN博客

Godot(2D)架构细分4:编辑器全局设置解释_godot怎么设置全局变量-CSDN博客

前文(Python处理转换Excel为Json)已经使用Python脚本获得了Json用于Godot内部的注入处理,本文将解释Godot内如何处理接收的Json文件与如何构建Godot内接口自动注入模块,将管理器从外置地址注入进游戏内部,实现管理器与游戏的分离,去除耦合性。

注意: 本文中所有代码演示均为 GDscript代码

这里先重申一下总流程(包含Python部分的流程)

1. Godot启动游戏,切换场景至黑屏,启动注入,将启动参数发送给Python并等待。

2. Python执行Excel对应地址等参数转换,打包成Json固定格式放入Godot指定文件夹。

3. Godot接收到Python处理完成信号,将Json文件格式化成多种字典形式

4. Godot使用格式化完成后得到的字典,检查对应模块和参数,并开始注入至对应AutoLoad的静态数据中完成注入

5. Godot注入模块将游戏切换至主场景,开始游戏。

本文是总流程中的 1, 3, 4 部分流程

其余包括当前使用版本(Godot, Python)等基本信息在主文章中已经解释

1. Godot注入总管理器构建

1.1 流程说明

Godot中注入管理器要对接Python给出的Json格式,先重新给出输出的Json格式:

基于设定好的Json格式(上图),所以本框架的注入流程为:

1. 根据游戏内AutoLoad中已经存在的管理器构建映射表如 {"server": 接口类 .......},以及其他总映射表,用于自动寻找Json中存在的模块

2. 检查得到的映射表中各个模块的路径文件是否存在,是否有额外依赖需要注入等容错检查。

3. 将最终得到的映射表遍历,实例化Json表中对应地址模块并填入模块接口中

本文接口总管理器处理说明:

本文的管理器中识别对应注入模块的流程为:首先将Json文件转换为字典形式使用每个模块规定的标准映射名对应模块的接口类然后将映射好的字典中对应的模块对应取出填入映射的标准接口类实现映射

其余有很多中间映射表,均会在文章各个章节部分详细说明

1.2. Godot接口文件结构展示

在演示代码前,有必要先展示接口的文件接口与项目结构,下图为文件结构

下图为注入总管理器游戏内场景的接口示意图

下图为Godot游戏内全局AutoLoad示意图

2. Godot内总接口管理器实现

本部分内容是Godot内总接口管理器的代码部分,将会根据上图中 注入总管理器游戏内场景的接口示意图 的结构说明代码(不按顺序)

2.1 Godot接口Json处理子管理器

对应上图 Injection_Json_Parsing 子节点。这个子管理器的作用是将得到的Json文件中的内容翻译回Godot中的字典形式,这里翻译得到的形式与Python处理完后的Json文件形式一致,不做特殊格式处理。以下为代码演示

python 复制代码
extends Node

# 接口管理器根节点
@export var Injection_interface_node: Node

#获得注入信息,输入参数为文件路径
func get_Injection_information(filePath: String) -> Dictionary:
	
	#如果文件不存在,就直接返回空字典
	if !FileAccess.file_exists(filePath):
		return {}
		
	#打开文件
	var file = FileAccess.open(filePath, FileAccess.READ)
	#将文件内的数据以字符串的形式读入,注意不是直接读入字典
	var tempText = file.get_as_text()
	file.close()
	
	var json = JSON.new()
	##这里parse()函数负责解析String,解析完后的数据在json中
	##这里解析完后, json.data 就是字典
	json.parse(tempText)
	
	#返回解析后得到的字典
	return json.data as Dictionary

我们单独执行一次Json脚本,可以看到输出的格式:

也就是**{ "模块名" : {"模块属性": 属性值,......} }** 的格式

2.2 Godot接口主程序

对应上图中的 Injection_interface 节点。由于主程序节点中包含了子程序要使用的静态数据类,固定映射表,对应子节点的使用,所以这里提前将主程序解释。接口主程序的作用是执行总接口流程 ,同时存储固定的接口数据提供对外开放的API

以下为代码演示

python 复制代码
extends Node

# 计时型号,定时发送,给接口模块提供信息同步计时
# 发送一次同步一次信息
signal refresh_model_info

# 通用系统状态(父类),用于记录注入模块的状态,预留用于热插拔
# 注意子类脚本会共用一套静态类,使用的时候将静态模式去除,创建对象
class OriginalSystem extends Object:
	static var EXIST_STATE: 	bool	= false			# 模块存在状态
	static var START_STATE: 	bool 	= false			# 模块启用状态
	static var EXTRA_IMPORT: 	bool 	= false			# 模块额外依赖是否存在
	# 状态重置
	static func RESET_STATE() -> void:			# 重置所有状态
		EXIST_STATE 	= false
		START_STATE 	= false
		EXTRA_IMPORT 	= false
		
# Injection_Login_System 系统状态,继承至通用系统状态
class LoginSystemState extends OriginalSystem:
	pass
		
# ServerSystemState 系统状态,继承至通用系统状态
class ServerSystemState extends OriginalSystem:
	pass
	
"""
根管理器启动参数(Python)
	"0": 解析一次Excel到Json

管理器规定映射:
	检查映射管理器:
		登录: 	"login"
		服务器: 	"register"
	对应模块映射:
		登录: 	"login"
		服务器: 	"register"		
"""

#==========================子管理器==========================#
@export var Injection_Json_Parsing		: Node		# Json转换管理器
@export var Injection_Noraml_Operator	: Node		# 注入管理器: 通用操作管理器
@export var INJECTIONCORE				: Node		# 注入管理其: 注入核心,核心启动管理器
#===========================================================#

#=========================================标准执行路径============================================#
# Python转Excel为Json脚本的Python脚本路径
@export var injection_python_path: 	String 	= "res://Python_configuration_table_code/FileProcessingMain.py"
# 获得的Json的文件路径
@export var injection_json_path: 	String 	= "res://Python_configuration_table_code/InjectionConvert.json"
# Python要接收到的Excel表格路径位置
@export var injection_normal_path: 	String	= "res://Python_configuration_table_code/"
#===============================================================================================#

#=========================字典全局数据类============================#
var injection_dict: Dictionary 			= {}		# 完整注入字典(从Json完整转换过来并将绝对路径换为相对路径的字典)
var injection_mapping_dict: Dictionary 	= {}		# 注入字典: "键名" : [对应接口类,Json注入模块路径]
#=================================================================#

#===============================模块常值匹配表===============================#
static var MATCH_LOGIN_MODEL		: String 	= "login"		# 登录模块匹配名字(包含即匹配)
static var MATCH_SERVER_MODEL		: String 	= "server"		# 网络模块匹配名字(包含即匹配)
static var MATCH_AI_MODEL			: String	= "aimodel"		# Ai模块匹配名字(包含即匹配)
static var MODELREGISTERMAP: Dictionary = {						# 默认匹配名字与接口类映射表
	MATCH_LOGIN_MODEL	: InjectionLoginSystem, 		# 登录接口类
	MATCH_SERVER_MODEL	: InjectionServerSystem,		# 网络接口类
	MATCH_AI_MODEL		: InjectionAiModelPackedscene	# Ai接口类
	}
#==========================================================================#

# 注入流程完成后加载进起始场景
func load_to_main_scene() -> void:
	get_tree().change_scene_to_file("res://02_Game/Component/ServerMenu/ServerUi.tscn")
	#get_tree().change_scene_to_file("res://02_Game/Level/MenuScene/menu_scene.tscn")

# 获得注入Json获取路径(res://路径)
func get_injection_json_path() -> String:
	return injection_json_path
	
# 获得注入Json路径(绝对路径)
func get_injection_absolute_json_path() -> String:
	var absolute_result: String = ProjectSettings.globalize_path(get_injection_json_path())
	return absolute_result
	 
# 获得注入Python脚本路径(绝对路径)
func get_injection_python_path() -> String:
	var absolute_result: String = ProjectSettings.globalize_path(injection_python_path) 
	return absolute_result

# 得到Python注入脚本的根目录
func get_normal_path() -> String:
	var absolute_result: String = ProjectSettings.globalize_path(injection_normal_path)
	return absolute_result

# 得到注册模块常值映射
func get_model_register_map() -> Dictionary:
	return MODELREGISTERMAP.duplicate(true)

# 执行一次Excel转换Json,同时完成依赖字典处理,管理器映射表对应
# 格式为 { "模块名": [对应模块静态实例存储, "模块地址"] }
func injection_start(path_head_name: String = "PATH") -> void:
	Injection_Noraml_Operator.injection_start()
	
	# 将Json转化结果换入结果字典中
	injection_dict = Injection_Json_Parsing.get_Injection_information(injection_json_path)
	# 将转化结果中的PATH中的绝对路径替换为对应Godot相对地址
	# convert_injection_dict_local(需要转换的字典, 需要转换地址的值对应的键名(默认"PATH"))
	injection_dict = Injection_Noraml_Operator.convert_injection_dict_local(injection_dict)
	injection_dict = Injection_Noraml_Operator.convert_injection_dict_local(injection_dict, "AFFILIATED")
	
	# 这里是建立  "键名" : [对应接口类,Json注入模块路径] 映射表
	# injection_mapping(完整的表, 路径位置所在的键名)
	injection_mapping_dict = Injection_Noraml_Operator.injection_mapping(injection_dict, "PATH")
		
	# 工具类,用于调试输出	
	DebugTool.debug_log("初始化注入: 初始转换映射字典: %s " % injection_mapping_dict)
	DebugTool.debug_log("完成流程: 注入初始化映射表")
		
# 尝试一次依赖注入
func try_dependency_injection() -> void:
	# 得到验证映射表
	# 表内内容: { "对应模块键名": bool }
	# 即当前对应读取的Excel模块键名 : 是否启用该模块 
	var verify_path_result = Injection_Noraml_Operator.get_path_verify_result(injection_mapping_dict)
	# 执行依赖主流程
	# INJECTIONCORE.INJECTION_CORE_TSCN_START() 为注入核心流程中的注入,后文解释参数与具体实现
	# 用处是执行将识别好的模块注入到对应的接口下
	INJECTIONCORE.INJECTION_CORE_TSCN_START(injection_mapping_dict, verify_path_result, 1, injection_dict)
	DebugTool.debug_log("完成流程: 执行注入")

# 初始化全局静态数据
# 放在 _ready()下,这里初始化是因为静态数据虽然比_ready()提前加载
# 但是不放_ready()下加载仍然会报错空,所以放在_ready()下解决
# 重新再赋值一遍
func _initialize_global_data() -> void:
	MATCH_LOGIN_MODEL 	= "login"
	MATCH_SERVER_MODEL	= "server"
	MODELREGISTERMAP = {
	MATCH_LOGIN_MODEL	: InjectionLoginSystem, 
	MATCH_SERVER_MODEL	: InjectionServerSystem,
	MATCH_AI_MODEL		: InjectionAiModelPackedscene
	}

# 发射状态刷新信号
func _refresh_injection_info() -> void:
	refresh_model_info.emit()

# 注入流程
func _ready() -> void:
	
	# 先将静态数据的值重新赋值一遍
	_initialize_global_data()
	# 使用Python转化一次Excel为Json
	injection_start()
	# 启动依赖注入
	try_dependency_injection()
	# 等待注入完成后进入主场景
	load_to_main_scene()

主程序中流程按照顺序进行,同时预留了用于记录子接口状态的通用状态父类,便于扩展

2.3 Godot接口通用操作管理器

对应上图中的 Injection_Noraml_Operator 节点。接口通用操作类为主程序提供了一系列接口处理的常用操作比如特定格式转换字典内容变更验证对应资源存在 等功能,其目的是将主程序中的逻辑全部抽离出来便于代码的维护。

其中向Python中传值的方式,请参考:

Godot(4.X): 外接Python处理Excel数据: 账号管理系统实现-CSDN博客

以下为代码演示代码

python 复制代码
extends Node

@export var Injection_interface_node: Node

#=========================模块对应统一键值======================#
var login_model_key		: String = ""		# 对应主程序中的 MATCH_LOGIN_MODEL
var server_model_key	: String = ""		# 对应主程序中的 MATCH_SERVER_MODEL
var ai_model_key		: String = ""		# 对应主程序中的 MATCH_AI_MODEL
#============================================================#

"""
标准Godot传入参数:
	[
	Python路径, 
	Python运行参数
	普通Python注入代码绝对路径,
	注入Json文件绝对路径,
	]
"""

# 初始化模块常量
func _ready() -> void:
	# 将主程序中对应的固定模块识别键名取到本地
	login_model_key 	= Injection_interface_node.MATCH_LOGIN_MODEL
	server_model_key 	= Injection_interface_node.MATCH_SERVER_MODEL
	ai_model_key		= Injection_interface_node.MATCH_AI_MODEL
	
# 启动一次注入
func injection_start(start_up_param: int = 0):
	# 存Python程序的返回值
	var return_result: Array = []
	
	# Python路径脚本绝对,从主程序获得
	var python_path = Injection_interface_node.get_injection_python_path()
	# 指定Python转换到的Json绝对路径,从主程序获取
	var json_path	= Injection_interface_node.get_injection_absolute_json_path()
	# Python脚本的绝对路径,从主程序获取
	var normal_path = Injection_interface_node.get_normal_path()
	
	# 启动参数(将对应主程序参数填入)
	# 传参介绍请参考上文给出的文章地址,其中有详细解释
	var start_argv: Array = [
							python_path, 		# Python脚本路径
							start_up_param,		# 传给Python的启动参数,默认为 0 代表执行Excel注入转换
							normal_path,		# 传给Python的Python转换脚本绝对路径
							json_path			# 指定Python转换到的Json绝对路径
							] 

	# 尝试传入参数	
	OS.execute("python", start_argv, return_result, true, true)
	# 遍历表达Python的输出
	for cell in return_result:
		DebugTool.debug_log(cell)

# 将收到的注入字典中的绝对地址转化为相对Godot的地址
func convert_injection_dict_local(injection_dict: Dictionary, 			# 传入需要转换的字典
								  path_head_name: String = "PATH"		# 默认 "PATH" 键
								  ) -> Dictionary: 
		# 结果字典,先复制一遍原字典,在复制的字典中更改
		var result_dict: Dictionary = injection_dict.duplicate(true)
		
		# 遍历转化
		for manager_name in result_dict:
			# 取出单个数据字典
			var module_data = result_dict[manager_name]
			# 不存在键则跳过
			if not module_data.has(path_head_name):
				continue
			
			# 对应路径值为空则跳过
			if (module_data[path_head_name] == null):
				continue
			# 不是字符串类型跳过
			if not (module_data[path_head_name] is String):
				continue
			# 转化为相对Godot路径
			var original_absolute_path = module_data[path_head_name]
			var convert_result: String = ProjectSettings.localize_path(original_absolute_path).strip_escapes()
			
			# 更新结果字典
			result_dict[manager_name][path_head_name] = convert_result
		return result_dict

# 执行注入地址与游戏内管理器映射
# 传入完整注入表
# 返回结果形如 { "模块名": [对应模块静态实例存储, "模块地址"] }
func injection_mapping(injection_dict: Dictionary, path_head_name: String = "PATH") -> Dictionary:
	if injection_dict.is_empty():
		DebugTool.debug_log("地址映射写入: 传入完整映射原字典为空")
		return {}
		
	# 建立传入副本
	var result_dict: Dictionary = {}
	
	# 验证管理器是否已经存入键值对,防止重复存入对应模块
	var matched: Dictionary[String, bool] = {}
	# 匹配键名建立管理器映射,自动寻找和模块有相关的键
	for manager_name: String in injection_dict:
		# 转小写匹配字符建立映射
		var search_name = manager_name.to_lower()
		# 仅记录第一个键名映射管理器
		# 检查是否与登录模块有关
		if (search_name.contains(login_model_key)) and not matched.has(login_model_key):
			result_dict[manager_name] = [InjectionLoginSystem]
			matched[login_model_key] = true
			
		# 检查是否与网络模块有关
		elif (search_name.contains(server_model_key)) and not matched.has(server_model_key):	
			result_dict[manager_name] = [InjectionServerSystem]
			matched[server_model_key] = true
			
		# 检查是否与AI模块有关
		elif (search_name.contains(ai_model_key)) and not matched.has(ai_model_key):	
			result_dict[manager_name] = [InjectionAiModelPackedscene]
			matched[ai_model_key] = true
		
	# 根据映射将地址映入,结果为 { "Json中的键名" : [接口类, "路径"] } 中新增的路径部分
	for manager_name: String in result_dict:
		if injection_dict[manager_name].has(path_head_name):
			# 
			var injection_path = injection_dict[manager_name][path_head_name]
			result_dict[manager_name].append(injection_path)
		else: 
			DebugTool.debug_log("注入警告:模块 %s 不存在路径键 %s" % [manager_name, path_head_name])
			result_dict[manager_name].append(null)
	return result_dict
	
# 检查管理器路径存在,返回验证字典,键名对应传入字典
# 传入字典要求格式形如 { "模块名": [对应模块静态实例存储, "模块地址"] }
# 还需要传入地址相应索引位置(默认1)
# 注意验证的是注入的Godot格式的文件
# 最终返回的是 { "模块名": bool(是否启用) }
func get_path_verify_result(input_dir: Dictionary, default_path_index: int = 1) -> Dictionary[String, bool]:
	var result_dir: Dictionary[String, bool] = {}
	
	# 空字典直接返回空
	if input_dir.is_empty():
		DebugTool.debug_log("全体注入地址校验: 传入字典为空")
		return result_dir
		
	# 遍历字典获得Json中原键名模块,并存入校验字典	
	for manager_name in input_dir:
		var inner_info = input_dir[manager_name] 
		
		# 检查传入格式是否符合要求
		if not (inner_info is Array):
			DebugTool.debug_log("全体注入地址校验: %s 不是数组格式" % manager_name)
			result_dir[manager_name] = false
			continue
		
		# 防止数组读取越界	
		if len(inner_info) <= default_path_index:
			DebugTool.debug_log("全体注入地址校验: %s 数组长度不足" % manager_name)
			result_dir[manager_name] = false
			continue
		
		# 检查路径是否为 null
		var path_value = inner_info[default_path_index]
		if path_value == null:
			# 空路径直接标记为关闭,防止后续操作误读
			DebugTool.debug_log("全体注入地址校验: %s 路径为 null" % manager_name)
			result_dir[manager_name] = false
			continue	
			
		# 验证文件是否存在
		var to_be_verified_path: String = str(path_value)
		var is_resource_exist: bool = verify_injection_resource_exist(to_be_verified_path)
		
		# 添加验证结果
		result_dir[manager_name] = is_resource_exist
		
	return result_dir


#========================以下为单个工具类函数=========================#

# 验证依赖文件资源存在(Godot类型文件)
func verify_injection_resource_exist(resource_path: String) -> bool:
	# 资源不存在则调试输出
	if not ResourceLoader.exists(resource_path):
		DebugTool.debug_log("注入验证: 指定资源不存在 %s" % resource_path)
		return false
	return true

Godot接口通用操作类将主程序的操作分离开,减小了主程序的代码量,方便维护查看

以下为上图中 convert_injection_dict_local等模块的输出

convert_injection_dict_loca输出:

injection_mapping输出:

get_path_verify_result输出:

2.4 Godot接口核心执行管理器

对应上图的INJECTCORE子节点,用于在数据处理完后,将对应的实例注入到对应的接口类里,是依赖注入的核心组件。该节点使用工具类,先给出工具类:

实例化工具,功能解释如下:

python 复制代码
extends Node

class_name InstantiationTool

# instantiationAny 示例化节点
# 输入参数为( 被实例化场景, 作为谁的子节点, 实例化位置(可选) )
static func instantiationAny(
	any_case: PackedScene, 						# 任何场景
	any_node: Variant, 							# 作为任何节点的子节点
	extra_position: Vector2 = Vector2(0, 0)		# 任意位置实例化
) -> Variant:
	
	# 检查节点输入是否存在
	if any_node == null:
		push_error("实例化失败: 传入的实例节点为空")
		return
	
	# 检查父节点是否存在
	if any_case == null:
		push_error("实例化失败: 传入的场景为空")
		return
		
	# 安全范围判断(判断是否是场景中的可实例化节点)
	if not any_node is Node:
		push_error("实例化失败: 传入节点不为有效节点")
		return
	
	# 场景实例化检查并实例化
	var any_instantiation = any_case.instantiate()
	if any_instantiation == null:
		push_error("实例化失败: 节点实例化步骤失败")
		return
		
	# 实例化位置更改
	if any_instantiation is Node2D:
		any_instantiation.global_position = extra_position 
	
	# 作为输入节点的子节点被加入场景
	any_node.add_child(any_instantiation)
	return any_instantiation

INJECTCORE部分注入方式为:依据验证好的,模块的启用状态和模块的存在状态,将真正存在资源的对应模块注入到对应接口中。以下为演示代码:

python 复制代码
extends Node

@export var Injection_interface_node		: Node

#=========================模块对应统一键值======================#
var login_model_key		: String = ""		# 对应主程序中的 MATCH_LOGIN_MODEL
var server_model_key	: String = ""		# 对应主程序中的 MATCH_SERVER_MODEL
var ai_model_key		: String = ""		# 对应主程序中的 MATCH_AI_MODEL
#============================================================#

#=========================已知需要注入的模块===========================#
@export var Injection_Login_Packedscene		: PackedScene 	= null		# 登录注入模块实例
@export var Injection_Server_Packedscene	: PackedScene 	= null		# 网络注入模块实例
@export var Injection_AiModel_Packedscene	: PackedScene	= null		# AI注入模块实例
#===================================================================#

# 初始化模块常量
func _ready() -> void:
	# 将主程序中对应的固定模块识别键名取到本地
	login_model_key 	= Injection_interface_node.MATCH_LOGIN_MODEL
	server_model_key 	= Injection_interface_node.MATCH_SERVER_MODEL
	ai_model_key		= Injection_interface_node.MATCH_AI_MODEL
	
# 启动注入加载(核心正式注入)
# 传入参数(原注入表, 对照注入表)
# 返回注册管理器总表,用于总控注入模块
# 传入前完成数据干净处理
func INJECTION_CORE_TSCN_START(
	injection_mapping: Dictionary, 			# 传入的格式为 {"model_name": [接口类, "路径"]} 的映射字典
	injection_verify: Dictionary, 			# 传入的格式为 { "model_name": bool } 的验证字典
	injection_path_index: int = 1,			# 注入参数: 路径位于映射的位置
	original_injection: Dictionary = {},	# 传入的完整的Json转换进来的字典
	START_STATE_NAME: String = "START",		# 注入参数: 是否启用
	START_EXTRA_NAME: String = "EXTRA"		# 注入参数: 额外启动参数
	) -> Dictionary:
	
	var result_model_dict: Dictionary = {}		# 存入已经被加载的注入式管理器
	# 全部副本复制,避免更改传入字典的值
	var inner_injection_mapping 	= injection_mapping.duplicate(true)
	var inner_injection_verify  	= injection_verify.duplicate(true)
	var inner_original_injection	= original_injection.duplicate(true) 
	
	# original_injection 不能为空传值
	if inner_injection_mapping.is_empty() or inner_injection_verify.is_empty():
		DebugTool.debug_log("注入核心: 传入原映射表或验证映射表为空: 注入终止")
		return {}
	
	# 检验验证映射表与验证表的长度是否一致,因为要对应验证是否可以注入
	if len(inner_injection_mapping) != len(inner_injection_verify):
		DebugTool.debug_log("注入核心: 原映射表与验证映射表长度不一致: 注入终止")
		return {}
	
	# 首先检查对应注入参数的额外参数(启用状态, 额外附加参数)
	# 这里是检查原表中对应模块的启动标记,将未启动的关闭
	if not inner_original_injection.is_empty():
		# 检查未启用直接将地址有效更改为无效
		for manager_name: String in inner_original_injection:
			# 检查是否有额外参数的键,没有说明没有对额外参数的使用状态的标记
			if !inner_original_injection[manager_name].has(START_STATE_NAME):
				DebugTool.debug_log("注入核心: 在 %s 中检测不到额外启动参数 %s" % [manager_name, START_STATE_NAME])
				continue
				
			# 检测启动参数,转化为字符串防止数据错乱
			var model_start = str(inner_original_injection[manager_name][START_STATE_NAME])
			# 未启动则关闭游戏内启动状态
			if model_start == "false":
				if not inner_injection_verify.has(manager_name):
					DebugTool.debug_log("注入核心: 注入验证表中找不到对应映射,你是不是没有用正确的转化函数?")
					continue
				# 更改有效地址状态
				inner_injection_verify[manager_name] = false
				
	# 输出转换结果			
	DebugTool.debug_log("注入核心: 实际验证字典转换结果: %s" % inner_injection_verify)
	#===========预留额外系统参数===========#
	# 目前逻辑未知,预留
	#====================================#
	
	# 启动注入流程(按需匹配注入模块)
	for manager_name: String in inner_injection_mapping:
		# 验证映射对应不上指定映射表中的模块,可能是使用的转换函数不对
		if not inner_injection_verify.has(manager_name):
			DebugTool.debug_log("注入核心: 注入验证表中找不到对应映射,你是不是没有用正确的转化函数?")
			continue
		# 禁用则不注入
		if not inner_injection_verify[manager_name]:
			continue
			
		var lower_search_name: String = manager_name.to_lower() 
		
		#======================启动注入匹配======================#
		
		# 如果在映射表中检测到登录关键字,则启动登录注入
		if lower_search_name.contains(login_model_key):
			# 加载资源,此时由于上文检测排除过了,资源一定存在
			var load_model: PackedScene = load(inner_injection_mapping[manager_name][injection_path_index])
			var load_instantiation: Node = register_injection(load_model, login_model_key)
			result_model_dict[login_model_key] = load_instantiation
			
		# 如果在映射表中检测到服务关键字,则启动服务器网络注入
		if lower_search_name.contains(server_model_key):
			# 加载资源,此时由于上文检测排除过了,资源一定存在
			var load_model: PackedScene = load(inner_injection_mapping[manager_name][injection_path_index])
			var load_instantiation: Node = register_injection(load_model, server_model_key)
			result_model_dict[server_model_key] = load_instantiation
			
		# 如果在映射表中检测到AI关键字,则AI模块注入
		if lower_search_name.contains(ai_model_key):
			# 加载资源,此时由于上文检测排除过了,资源一定存在
			var load_model: PackedScene = load(inner_injection_mapping[manager_name][injection_path_index])
			var load_instantiation: Node = register_injection(load_model, ai_model_key)
			result_model_dict[ai_model_key] = load_instantiation
			
		#==================================================================================#		
	DebugTool.debug_log("注入核心: 注入实例集合: %s" % result_model_dict)		
	# 返回所有被注入的模块的实例集合
	return result_model_dict
	
#======================代码工具类==================#
# 注入工具
# 传入对应场景资源,返回注册完的实例
# 这里是将对应实例单次注入到对应模块
func register_injection(register_pack: PackedScene, register_model: String) -> Node:
	if register_pack == null:
		DebugTool.debug_log("注入模块注册工具: 传入注册实例为空: 注册失败")
		return
	
	# 从主程序中获得 默认匹配名字与接口类映射表,并根据传入模块名字严格对应注入	
	var model_map: Dictionary = Injection_interface_node.get_model_register_map()
	# 找不到模块结束函数
	if not model_map.has(register_model):
		DebugTool.debug_log("注入模块注册工具: 在注入管理器中找不到对应模块: 注册失败")
		return
		
	# 获得注册模块
	# 返回的是一个总类(AutoLoad)
	var to_be_register_model = model_map[register_model]
	DebugTool.debug_log("注入模块注册工具: 读入映射字典: %s" % model_map)
	# 实体注入子节点
	var register_instantiation: Node = InstantiationTool.instantiationAny(register_pack, to_be_register_model)	
	# 实体将对应模块向规定统一含有的 静态SystemInstantiation 注入对应节点
	to_be_register_model.SystemInstantiation = register_instantiation
	# 返回注册的模块实例
	return register_instantiation
				

注入核心由主程序调用,在执行完前面所有格式转换操作后传入注入核心执行注入

3. Godot代码运行演示

Excel格式和状态如下

启用代码注入发现三个模块(LoginServerAi)均被注入

改变Ai模块的START启动参数未FALSE(关闭),再次启动发现Ai模块并未被注入

以上为简要演示,完整使用可以从主文章下载源码

4. 补充

代码中包含了对热重载系统的预留代码,比如通用系统状态类,核心注入子节点返回的注入管理器节点实例集合等,更改可以实现热重载功能

这里返回主文章

Godot(4.x): 游戏管理器: Excel 动态依赖注入实现-CSDN博客

相关推荐
wgc2k3 小时前
Nest.js基础-4:Nest.js,游戏服务器,微服务架构
游戏·typescript·node.js
魔士于安4 小时前
unity volumefog带各种demo第一人称 wsad 穿墙控制
游戏·unity·游戏引擎·贴图·模型
xcLeigh4 小时前
Python小游戏实战:实现2048游戏小游戏附源码
python·游戏·教程·pygame·2048·python3
魔法阵维护师4 小时前
从零开发游戏需要学习的c#模块,第三十二章(Boss 战系统)
学习·游戏·c#
2501_940041744 小时前
A Curated Archive of Tech & Culture / 科技与文化精选档案
游戏
魔法阵维护师5 小时前
从零开发游戏需要学习的c#模块,第三十三章(暂停菜单)
学习·游戏·c#
Kurisu5756 小时前
幻兽帕鲁修改器下载2026最新
游戏
Swift社区6 小时前
鸿蒙游戏中的手势系统详解
游戏·华为·harmonyos
魔士于安6 小时前
红色文化馆技术文档
前端·unity·游戏引擎·贴图·模型