"""UE5 编辑器 Python 脚本:创建 v0.1 角色控制所需的 InputAction 资产并更新 IMC。 由 setup_input.bat 通过 UE5 的 -ExecutePythonScript 参数调用。 在 UE5 编辑器 Python 环境中运行。 功能: 1. 创建 3 个 InputAction 资产:IA_Interact, IA_Cancel, IA_SwitchInteract 2. 更新 IMC_Default:添加 E / ESC / Tab 按键映射 3. 保存所有修改的资产 环境变量(由 setup_input.bat 设置): INPUT_PROJECT_ROOT — 项目根目录绝对路径 """ import os import sys from pathlib import Path # ---- 检查 unreal 模块 ---- try: import unreal except ImportError: print("[InputSetup] FATAL: unreal 模块不可用,必须在 UE5 编辑器环境中运行") sys.exit(1) # ---- 项目根目录 ---- _project_root_str = os.environ.get("INPUT_PROJECT_ROOT", "") if _project_root_str: PROJECT_ROOT = Path(_project_root_str) else: print("[InputSetup] FATAL: INPUT_PROJECT_ROOT 环境变量未设置") sys.exit(1) # ---- 资产路径配置 ---- IA_PACKAGE = "/Game/Input/Actions" IMC_PATH = "/Game/Input/IMC_Default" # 新增 InputAction 定义 NEW_INPUT_ACTIONS = { "IA_Interact": { "description": "E 键交互输入动作", "key": unreal.Key.E, "triggers": [unreal.InputTriggerDown()], }, "IA_Cancel": { "description": "ESC 取消输入动作", "key": unreal.Key.ESCAPE, "triggers": [unreal.InputTriggerDown()], }, "IA_SwitchInteract": { "description": "Tab 切换交互目标输入动作", "key": unreal.Key.TAB, "triggers": [unreal.InputTriggerDown()], }, } def asset_exists(asset_path: str) -> bool: """检查资产是否已存在""" return unreal.EditorAssetLibrary.does_asset_exist(asset_path) def create_input_action(name: str, description: str) -> unreal.InputAction: """创建 InputAction 资产""" asset_path = f"{IA_PACKAGE}/{name}" if asset_exists(asset_path): print(f"[InputSetup] 资产已存在,跳过创建: {asset_path}") return unreal.load_asset(asset_path) # 确保目录存在 unreal.EditorAssetLibrary.make_directory(IA_PACKAGE) # 使用 AssetTools 创建 asset_tools = unreal.get_editor_subsystem(unreal.EditorAssetSubsystem) if asset_tools: # 尝试通过 EditorAssetSubsystem 创建 factory = unreal.InputActionFactory() new_asset = asset_tools.create_asset(name, IA_PACKAGE, unreal.InputAction, factory) if new_asset: print(f"[InputSetup] 创建成功: {asset_path} — {description}") return new_asset # 备用方案:使用 Python BPLib 或直接构造 print(f"[InputSetup] 尝试备用方案创建: {name}") factory = unreal.InputActionFactory() new_asset = unreal.AssetToolsHelpers.get_asset_tools().create_asset( name, IA_PACKAGE[5:], # 去掉 /Game/ 前缀 unreal.InputAction, factory ) if new_asset: print(f"[InputSetup] 备用方案创建成功: {asset_path}") return new_asset print(f"[InputSetup] ERROR: 无法创建 {name}") return None def add_key_mapping_to_imc(imc: unreal.InputMappingContext, ia: unreal.InputAction, key: unreal.Key): """向 IMC 添加按键映射""" mapping = unreal.InputMapping() mapping.set_editor_property("action", ia) key_mapping = unreal.KeyMapping() key_mapping.set_editor_property("key", key) key_mapping.set_editor_property("scale", 1.0) # 设置触发器 # key_mapping 的 triggers 由 IMC 内部管理 # 使用 IMC 的 API 添加映射 try: # UE5.7: 使用 mappings 属性 existing_mappings = imc.get_editor_property("mappings") new_mapping = unreal.InputMapping() new_mapping.set_editor_property("action", ia) km = unreal.KeyMapping() km.set_editor_property("key", key) km.set_editor_property("scale", 1.0) # 设置 triggers triggers = [unreal.InputTriggerDown()] km.set_editor_property("triggers", triggers) new_mapping.set_editor_property("key_mappings", [km]) existing_mappings.append(new_mapping) imc.set_editor_property("mappings", existing_mappings) return True except Exception as e: print(f"[InputSetup] 添加映射失败: {e}") return False def main(): print("=" * 50) print("[InputSetup] v0.1 角色控制 Input 资产创建脚本") print(f"[InputSetup] 项目: {PROJECT_ROOT}") print("=" * 50) # ---- Step 1: 创建 InputAction 资产 ---- print("\n[InputSetup] Step 1: 创建 InputAction 资产") created_ias = {} for name, config in NEW_INPUT_ACTIONS.items(): ia = create_input_action(name, config["description"]) if ia: created_ias[name] = ia else: print(f"[InputSetup] WARNING: {name} 创建失败,后续 IMC 映射将跳过此 IA") if not created_ias: print("[InputSetup] ERROR: 没有成功创建任何 IA,终止") return False # ---- Step 2: 保存新创建的 IA 资产 ---- print("\n[InputSetup] Step 2: 保存新创建的资产") for name, ia in created_ias.items(): asset_path = f"{IA_PACKAGE}/{name}" unreal.EditorAssetLibrary.save_asset(asset_path) print(f"[InputSetup] 已保存: {asset_path}") # ---- Step 3: 更新 IMC_Default ---- print(f"\n[InputSetup] Step 3: 更新 IMC: {IMC_PATH}") if not asset_exists(IMC_PATH): print(f"[InputSetup] ERROR: IMC 不存在: {IMC_PATH}") return False imc = unreal.load_asset(IMC_PATH) if not imc: print(f"[InputSetup] ERROR: 无法加载 IMC: {IMC_PATH}") return False # 读取当前映射数量 try: current_mappings = imc.get_editor_property("mappings") print(f"[InputSetup] 当前 IMC 映射数: {len(current_mappings)}") except Exception as e: print(f"[InputSetup] WARNING: 无法读取映射: {e}") current_mappings = [] # 添加新映射 for name, config in NEW_INPUT_ACTIONS.items(): if name not in created_ias: continue ia = created_ias[name] key = config["key"] success = add_key_mapping_to_imc(imc, ia, key) if success: print(f"[InputSetup] 已添加映射: {name} → {key}") else: print(f"[InputSetup] WARNING: 映射添加失败: {name}") # ---- Step 4: 保存 IMC ---- print("\n[InputSetup] Step 4: 保存 IMC") unreal.EditorAssetLibrary.save_asset(IMC_PATH) print(f"[InputSetup] 已保存: {IMC_PATH}") # ---- Step 5: 验证 ---- print("\n[InputSetup] Step 5: 验证") try: updated_mappings = imc.get_editor_property("mappings") print(f"[InputSetup] 更新后 IMC 映射数: {len(updated_mappings)}") except: pass # 验证 IA 资产 for name in NEW_INPUT_ACTIONS: asset_path = f"{IA_PACKAGE}/{name}" exists = asset_exists(asset_path) print(f"[InputSetup] {name}: {'✅ 存在' if exists else '❌ 不存在'}") print("\n" + "=" * 50) print("[InputSetup] 完成!请在 UE5 编辑器中验证。") print("[InputSetup] 注意:需要在 BP_ChildHoodCharacter 中将新增 IA 属性指向对应资产") print("=" * 50) return True if __name__ == "__main__": success = main() if not success: sys.exit(1)