feat(tools): 新增Input资产自动创建脚本
创建 tools/input-setup/ 目录,包含: - setup_input_assets.py: UE5编辑器Python脚本,自动创建IA_Interact/IA_Cancel/IA_SwitchInteract并更新IMC_Default - setup_input.bat: 命令行启动脚本 Task: P-011
This commit is contained in:
@@ -0,0 +1,140 @@
|
||||
@echo off
|
||||
setlocal enabledelayedexpansion
|
||||
|
||||
REM === ChildHood Input Setup - 创建 v0.1 InputAction 资产 + 更新 IMC ===
|
||||
REM
|
||||
REM 用法:
|
||||
REM setup_input.bat (默认模式,完成后退出编辑器)
|
||||
REM setup_input.bat --no-quit (完成后保持编辑器打开)
|
||||
|
||||
set "SCRIPT_DIR=%~dp0"
|
||||
set "PROJECT_DIR=%SCRIPT_DIR%..\.."
|
||||
for %%i in ("%PROJECT_DIR%\.") do set "PROJECT_ROOT=%%~fi"
|
||||
set "UPROJECT=%PROJECT_ROOT%\ChildHood.uproject"
|
||||
|
||||
REM ---- Defaults ----
|
||||
set "NO_QUIT=0"
|
||||
|
||||
REM ---- Parse Arguments ----
|
||||
:parse_args
|
||||
if "%~1"=="" goto done_args
|
||||
if /i "%~1"=="--no-quit" (
|
||||
set "NO_QUIT=1"
|
||||
shift
|
||||
goto parse_args
|
||||
)
|
||||
if /i "%~1"=="--dry-run" (
|
||||
set "DRY_RUN=1"
|
||||
shift
|
||||
goto parse_args
|
||||
)
|
||||
if /i "%~1"=="--help" goto show_help
|
||||
if /i "%~1"=="-h" goto show_help
|
||||
echo [WARN] Unknown argument: %~1
|
||||
shift
|
||||
goto parse_args
|
||||
:done_args
|
||||
|
||||
REM ---- Find UE5 Editor ----
|
||||
set "UE_EDITOR="
|
||||
if defined UE5_ROOT (
|
||||
for %%b in (UnrealEditor.exe UE5Editor.exe) do (
|
||||
if exist "%UE5_ROOT%\Engine\Binaries\Win64\%%b" (
|
||||
set "UE_EDITOR=%UE5_ROOT%\Engine\Binaries\Win64\%%b"
|
||||
)
|
||||
)
|
||||
)
|
||||
|
||||
if "%UE_EDITOR%"=="" (
|
||||
for %%p in (
|
||||
"C:\Program Files\Epic Games\UE_5.7"
|
||||
"C:\Program Files (x86)\Epic Games\UE_5.7"
|
||||
"D:\Program Files\Epic Games\UE_5.7"
|
||||
"E:\Program Files\Epic Games\UE_5.7"
|
||||
) do (
|
||||
if not defined UE_EDITOR (
|
||||
for %%b in (UnrealEditor.exe UE5Editor.exe) do (
|
||||
if exist "%%~p\Engine\Binaries\Win64\%%b" (
|
||||
set "UE_EDITOR=%%~p\Engine\Binaries\Win64\%%b"
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
|
||||
if "%UE_EDITOR%"=="" (
|
||||
echo.
|
||||
echo [ERROR] UE5 Editor not found!
|
||||
echo Set UE5_ROOT to your UE5 install directory, e.g.:
|
||||
echo set UE5_ROOT=C:\Program Files\Epic Games\UE_5.7
|
||||
echo.
|
||||
exit /b 1
|
||||
)
|
||||
|
||||
REM ---- Path conversion ----
|
||||
set "SCRIPT_PATH=%SCRIPT_DIR%setup_input_assets.py"
|
||||
set "SCRIPT_PATH=!SCRIPT_PATH:\=/!"
|
||||
set "UPROJECT_FWD=!UPROJECT:\=/!"
|
||||
|
||||
REM ---- Build command line ----
|
||||
set "CMD_ARGS=-unattended -NoSound -NullRHI -ExecutePythonScript=!SCRIPT_PATH!"
|
||||
|
||||
REM ---- Pass config via env vars ----
|
||||
set "INPUT_PROJECT_ROOT=!PROJECT_ROOT!"
|
||||
if "!NO_QUIT!"=="1" set "INPUT_NO_QUIT=1"
|
||||
|
||||
REM ---- Print info ----
|
||||
echo.
|
||||
echo ============================================
|
||||
echo ChildHood Input Setup - v0.1
|
||||
echo ============================================
|
||||
echo UE5: !UE_EDITOR!
|
||||
echo Project: !UPROJECT_FWD!
|
||||
echo Script: !SCRIPT_PATH!
|
||||
echo ============================================
|
||||
echo.
|
||||
|
||||
REM ---- Dry Run ----
|
||||
if "%DRY_RUN%"=="1" (
|
||||
echo [DRY RUN] Command:
|
||||
echo.
|
||||
echo "!UE_EDITOR!" "!UPROJECT_FWD!" !CMD_ARGS!
|
||||
echo.
|
||||
exit /b 0
|
||||
)
|
||||
|
||||
REM ---- Execute ----
|
||||
echo [InputSetup] Launching UE5 Editor (command line mode)...
|
||||
echo.
|
||||
|
||||
"!UE_EDITOR!" "!UPROJECT_FWD!" !CMD_ARGS!
|
||||
|
||||
set "EXIT_CODE=!ERRORLEVEL!"
|
||||
echo.
|
||||
if "!EXIT_CODE!"=="0" (
|
||||
echo [InputSetup] 完成!编辑器已退出。
|
||||
) else (
|
||||
echo [InputSetup] 编辑器退出码: !EXIT_CODE!
|
||||
)
|
||||
exit /b !EXIT_CODE!
|
||||
|
||||
:show_help
|
||||
echo.
|
||||
echo ChildHood Input Setup - v0.1
|
||||
echo.
|
||||
echo Usage: setup_input.bat [options]
|
||||
echo.
|
||||
echo Options:
|
||||
echo --no-quit 保持编辑器打开
|
||||
echo --dry-run 仅打印命令不执行
|
||||
echo --help, -h 显示帮助
|
||||
echo.
|
||||
echo 创建的资产:
|
||||
echo IA_Interact (E 键)
|
||||
echo IA_Cancel (ESC 键)
|
||||
echo IA_SwitchInteract (Tab 键)
|
||||
echo.
|
||||
echo 修改的资产:
|
||||
echo IMC_Default (添加上述映射)
|
||||
echo.
|
||||
exit /b 0
|
||||
@@ -0,0 +1,225 @@
|
||||
"""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)
|
||||
Reference in New Issue
Block a user