• 简体中文
  • UE-Connect

    一、概述

    UE-Connect 是一个专为QuickUIDesign 应用设计的 Unreal Engine 5 (UE5) 连接器模块。它提供了一套完整的 hooks 和上下文管理工具,使 UE-Connect 能够与 UE5 进行双向通信,特别适用于基于 QuickUIDesign 的 UE5 交互界面开发。

    二、安装

    • 拷贝 ue-connect 库文件到项目中
    • 配置package.json,添加ue-connect库的引用
    {
      "dependencies": {
        "ue-connect": "./ue-connect",
      }
    }

    三、核心功能

    1. 上下文管理

    UEProvideruseUEContext

    提供全局的 UE5 连接状态管理。

    import { UEProvider, useUEContext } from 'ue-connect'
    
    // 在应用根组件中提供上下文
    function App() {
      return (
        <UEProvider>
          <YourComponent />
        </UEProvider>
      )
    }
    
    // 在子组件中使用上下文
    function YourComponent() {
      const { isConnected, isMobile, useMouse, setUseMouse, lastKeyAction } = useUEContext()
    
      return (
        <div>
          <p>连接状态: {isConnected ? '已连接' : '未连接'}</p>
          <p>设备类型: {isMobile ? '移动设备' : '桌面设备'}</p>
          <p>最近按键: {lastKeyAction || '无'}</p>
          <button onClick={() => setUseMouse(!useMouse)}>{useMouse ? '禁用鼠标' : '启用鼠标'}</button>
        </div>
      )
    }

    返回值说明:

    • isConnected: 表示是否已连接到 UE5 窗口
    • isMobile: 表示是否为移动设备
    • useMouse: 表示是否启用鼠标事件
    • setUseMouse: 用于设置是否启用鼠标事件的函数
    • lastKeyAction: 最近一次按键操作,来自 UE5 的输入事件

    UEProvider 说明:

    UEProvider 内部集成了鼠标事件管理 (useUEMouse) 和输入事件监听 (useInputKeyEventListener),子组件通过 useUEContext 可直接获取连接状态、设备类型、鼠标状态及最近按键操作。

    2. QuickUI自定义事件系统

    useQuickUIEventSenderuseQuickUIEventListener

    实现 Web 与 UE5 之间的自定义事件通信,用户可以设计自己的事件名称和Json格式。

    • useQuickUIEventSender 用于发送事件,需要指定事件名称
      对应 UE5 后端 QuickUIListenEvent
    • useQuickUIEventListener 用于监听事件,需要指定事件名称
      对应 UE5 后端 QuickUIEventCallBack
    import { useQuickUIEvent, useQuickUIEventListener } from 'ue-connect'
    
    function EventSystemComponent() {
      // 发送事件 - 需要指定事件名称(对应后端QuickUIListenEvent)
      const sendEvent = useQuickUIEventSender('playerUpdate')
    
      // 监听事件 - 监听来自 UE 的事件(对应后端QuickUIEventCallBack)
      useQuickUIEventListener('gameStateChange', (event) => {
        console.log('游戏状态改变:', event)
        console.log('事件主题:', event.topic)
        console.log('事件载荷:', event.payload)
        console.log('时间戳:', event.timestamp)
        console.log('事件源:', event.source) // "web" 或 "ue"
      })
    
      const updatePlayer = () => {
        // 发送事件到 UE(fire-and-forget)
        sendEvent({
          health: 100,
          ammo: 30,
          position: { x: 10, y: 20, z: 30 }
        })
      }
    
      return <button onClick={updatePlayer}>更新玩家数据</button>
    }

    useQuickUIEvent 参数说明:

    • fnName: 事件名称(对应 Blueprint / UMG 事件名)
    • 返回值:发送事件的函数,接受 payload?: QuickEventPayload 参数

    useQuickUIEventListener 参数说明:

    • fnName: 事件名称(对应 Blueprint / UMG 事件名)
    • handler: 事件处理函数,接收 QuickEvent 对象
      • event.type: 事件类型(固定为 "event")
      • event.topic: 事件主题/名称
      • event.payload: 事件载荷数据
      • event.timestamp: 事件时间戳
      • event.source: 事件源("web" 或 "ue")

    3. 鼠标事件管理

    useUEMouse

    • 管理鼠标事件与 UE5 的交互,支持自动检测禁用区域。
    • data-nohit 属性:用于标记不触发鼠标事件的元素区域
    import { useUEMouse } from 'ue-connect'
    
    function MouseComponent() {
      const { useMouse, setUseMouse } = useUEMouse(true)
    
      return (
        <div>
          <button onClick={() => setUseMouse(!useMouse)}>{useMouse ? '禁用鼠标事件' : '启用鼠标事件'}</button>
    
          {/* 这个区域不会触发鼠标事件 */}
          <div data-nohit style={{ padding: '20px', background: '#f0f0f0' }}>
            鼠标事件禁用区域
          </div>
        </div>
      )
    }

    特性:

    • 自动检测带有 data-nohit 属性的元素
    • 当鼠标悬停在禁用区域时自动禁用鼠标事件
    • 支持移动设备像素比适配

    4. 游戏控制

    useUEGameControl

    提供游戏控制功能,如退出游戏、执行控制台命令等。

    import { useUEGameControl } from 'ue-connect'
    
    function GameControlComponent() {
      const gameControl = useUEGameControl()
    
      return (
        <div>
          <button onClick={() => gameControl.quitGame()}>退出游戏</button>
          <button onClick={() => gameControl.executeCommand('stat fps')}>显示FPS</button>
          <button onClick={() => gameControl.clientTravel('127.0.0.1')}>连接到服务器</button>
        </div>
      )
    }

    可用方法:

    • quitGame(): 退出游戏
    • executeCommand(command: string): 执行控制台命令
    • clientTravel(address: string): 客户端旅行到指定地址
    • getGraphics(): 获取图形设置
    • applyAndSaveGraphics(...): 应用并保存图形设置

    5. 设备适配

    useDevicePixelRatio

    自动适配设备像素比,确保在不同设备上显示一致。

    import { useDevicePixelRatio } from 'ue-connect'
    
    function ResponsiveComponent() {
      const dpr = useDevicePixelRatio()
    
      return (
        <div
          style={{
            width: `${100 * dpr}px`,
            height: `${50 * dpr}px`
          }}
        >
          自适应元素
        </div>
      )
    }

    6. 输入管理

    useInputBlocker

    管理输入事件阻止,防止意外交互。

    import { useInputBlocker } from 'ue-connect'
    
    function InputComponent() {
      const { blockInput, unblockInput, isBlocked } = useInputBlocker()
    
      return (
        <div>
          <button onClick={blockInput}>阻止输入</button>
          <button onClick={unblockInput}>允许输入</button>
          <p>当前状态: {isBlocked ? '输入已阻止' : '输入正常'}</p>
        </div>
      )
    }

    useInputKeyEventListener(实验性)

    监听 UE5 引擎发送的按键导航事件(上/下/左/右/确认/上一页/下一页),适用于 TV 端或手柄遥控器等非鼠标输入场景。

    注意: 这是一个实验性的 Hook,可能在未来的版本中移除。

    import { useInputKeyEventListener } from 'ue-connect'
    
    function NavigationComponent() {
      const { lastKeyAction } = useInputKeyEventListener({
        onKeyAction: (action) => {
          console.log('按键事件:', action)
          // action 值为 "Up" | "Down" | "Left" | "Right" | "Next" | "Previous" | "Select"
        }
      })
    
      return (
        <div>
          <p>最近按键: {lastKeyAction || '无'}</p>
        </div>
      )
    }

    参数说明:

    • options.onKeyAction (可选): 按键事件回调函数,接收 KeyAction 类型参数

    返回值说明:

    • lastKeyAction: 最近一次按键操作,值为 "Up" | "Down" | "Left" | "Right" | "Next" | "Previous" | "Select" | null

    注册的全局函数:

    该 Hook 会在 window 对象上注册以下全局函数供 UE5 通过 ExecuteJs 调用:

    全局函数名触发的 KeyAction
    window.keyUp"Up"
    window.keyDown"Down"
    window.keyLeft"Left"
    window.keyRight"Right"
    window.keyNext"Next"
    window.keyPrev"Previous"
    window.keyEventChoose"Select"

    组件卸载时自动清理所注册的全局函数。

    7.底层API

    useUEEventJSON

    useUEEventJSON是更底层的API,用于直接发送JSON格式的事件数据到UE5。

    注意: 这是早期使用的 API,推荐使用 useQuickUIEventSender 封装后的 API。

    向 UE5 发送 JSON 格式的事件数据。

    import { useUEEventJSON } from 'ue-connect'
    
    function EventComponent() {
      const triggerEvent = useUEEventJSON({ functionName: 'MyBlueprintEvent' })
    
      const handleClick = () => {
        triggerEvent({
          playerName: '玩家1',
          score: 100,
          level: 5,
          items: ['item1', 'item2']
        })
      }
    
      return <button onClick={handleClick}>发送游戏事件</button>
    }

    参数说明:

    • functionName: UE5 蓝图中注册的事件名称
    • 支持复杂嵌套的 JSON 数据结构

    useUECallback(Old)

    UE5 回调函数,用于在 UE5 引擎发送新数据时,web端触发回调,处理数据。

    注意: 这是早期使用的 API,推荐使用 useQuickUIEventListener 封装后的 API。

    import { useUECallback } from 'ue-connect'
    
    function CallbackComponent() {
      useUECallback('OnPlayerDataUpdated', (onData) => {
        console.log('玩家数据更新:', onData)
        // 处理玩家数据
      })
    
      return <div>回调组件</div>
    }

    参数说明:

    • functionName: 回调函数名称(在UE5中自定义约定的调用函数名)
    • onData: 数据处理回调函数,UE5发送的数据

    特性说明:

    • 优化性能:只在后端真正发送新数据时触发回调
    • 自动管理:组件挂载时注册全局函数,卸载时自动清理
    • 稳定引用:使用ref保持回调引用稳定,避免频繁重新注册

    8.辅助函数

    filterUECallBackJSonData

    这是已给与useUECallback配套的通用筛选函数,用于处理 UE 回调的 Json 序列,支持过滤和筛选属性。

    方法签名:

    function filterUECallBackJSonData<T extends Record<string, any>>(
      data: T | undefined,
      filters: Record<string, any>
    ): Partial<T> | undefined

    参数说明:

    • data: UE 回调的待处理Json数据
    • filters: 筛选条件对象,键为属性名,值为筛选值或筛选函数

    返回值:

    • 筛选后的数据对象,如无匹配则返回 undefined

    使用示例(结合 useUECallback):

    import { useUECallback, filterUECallBackJSonData } from 'ue-connect'
    import { useState } from 'react'
    
    function AuthComponent() {
      const [serverURL, setServerURL] = useState('')
      const [title, setTitle] = useState('')
    
      useUECallback(
        'OnPlayerDataUpdated', // 前端回调函数名称
        (data) => {
          // 注意传回的数据先判断是否为json字符串,普通字符按非json处理
          if (typeof data === 'string') {
            try {
              data = JSON.parse(data)
            } catch {
              console.error('无效的 JSON 字符串按非json处理:', data)
              return
            }
          }
        
          // 过滤数据
          const filter = filterUECallBackJSonData(data, { health: (vs: any) => vs, isAlive: (vs: any) => vs })
          
          // 回调逻辑处理
          console.log('筛选后的数据:', filter)
          /*
          打印结果:
          筛选后的数据: { health: 100, isAlive: true }
          */
        }
      )
    
      return (
        <div>
          <p>健康值: {filter?.health || '未指定'}</p>
          <p>是否存活: {filter?.isAlive ? '是' : '否'}</p>
        </div>
      )
    }

    特性说明:

    • 灵活筛选:支持使用函数或值进行筛选
    • 类型安全:支持 TypeScript 泛型,保持类型一致性
    • 自动清理:无匹配属性时返回 undefined,便于后续处理
    • 与 useUECallback 完美结合:在回调处理中使用,简化数据处理逻辑

    四、完整示例

    import React from 'react'
    import {
      UEProvider,
      useUEContext,
      useUEEventJSON,
      useUEMouse,
      useQuickUIEvent,
      useQuickUIEventListener,
      useUECallback,
      useInputKeyEventListener,
      filterUECallBackJSonData,
    } from 'ue-connect'
    
    function GameUI() {
      const { isConnected, useMouse, lastKeyAction } = useUEContext()
      const { useMouse: mouseEnabled, setUseMouse } = useUEMouse(true)
      const triggerEvent = useUEEventJSON({ functionName: 'UpdateUI' })
    
      // useQuickUIEvent (Old API)
      const sendPlayerEvent = useQuickUIEvent('playerAction')
      // 标准 API
      const sendGameEvent = useQuickUIEventSender('gameState')
    
      // 监听来自 UE5 的事件
      useQuickUIEventListener('enemySpawn', (event) => {
        console.log('敌人生成:', event.payload)
        // 可以在这里更新 UI 状态
      })
    
      // 监听 UE5 按键导航事件(experimental)
      useInputKeyEventListener({
        onKeyAction: (action) => {
          console.log('按键导航:', action)
        }
      })
    
      // 使用优化的回调管理(Old API)
      useUECallback('OnPlayerDataUpdated', (data) => {
        console.log('玩家数据更新:', data)
        // 处理玩家数据更新
      })
    
      // 使用filterUECallBackJSonData进行数据筛选
      useUECallback(
        'OnPlayerDataUpdated', // 前端回调函数名称
        (data) => {
          // 注意传回的数据先判断是否为json字符串,普通字符按非json处理
          if (typeof data === 'string') {
            try {
              data = JSON.parse(data)
            } catch {
              console.error('无效的 JSON 字符串按非json处理:', data)
              return
            }
          }
        
          // 过滤数据
          const filter = filterUECallBackJSonData(data, { health: (vs: any) => vs, isAlive: (vs: any) => vs })
          
          // 回调逻辑处理
          console.log('筛选后的数据:', filter)
          /*
          打印结果:
          筛选后的数据: { health: 100, isAlive: true }
          */
        }
      )
    
      const handlePlayerAction = (action: string) => {
        // 发送 JSON 事件到 UE5
        triggerEvent({
          action,
          timestamp: Date.now(),
          playerId: 'player123'
        })
    
        // 发送 QuickUI 事件
        sendPlayerEvent({
          actionType: action,
          player: 'player123'
        })
      }
    
      const handleGameStateChange = (state: string) => {
        sendGameEvent({
          newState: state,
          previousState: 'playing'
        })
      }
    
      return (
        <div className="game-ui">
          <div className="status-bar">
            <span>状态: {isConnected ? '已连接' : '连接中...'}</span>
            <span>最近按键: {lastKeyAction || '无'}</span>
            <button onClick={() => setUseMouse(!mouseEnabled)}>{mouseEnabled ? '禁用' : '启用'}鼠标</button>
          </div>
    
          <div className="game-controls">
            <button onClick={() => handlePlayerAction('jump')}>跳跃</button>
            <button onClick={() => handlePlayerAction('attack')}>攻击</button>
            <button onClick={() => handleGameStateChange('paused')}>暂停游戏</button>
          </div>
    
          {/* 鼠标事件禁用区域 */}
          <div data-nohit className="no-mouse-zone">
            此区域不响应鼠标事件
          </div>
        </div>
      )
    }
    
    export default function App() {
      return (
        <UEProvider>
          <GameUI />
        </UEProvider>
      )
    }

    五、类型定义

    类型定义

    // 自定义事件接口
    interface QuickEvent<Payload = QuickEventPayload> {
      type: 'event' // 事件类型(固定为 "event")
      topic: string // 事件主题/名称
      payload?: Payload // 事件载荷数据
      timestamp: number // 事件时间戳
      source: 'web' | 'ue' // 事件源
    }
    
    type QuickEventPayload = Record<string, any> | null
    // 按键操作类型
    type KeyAction = "Up" | "Down" | "Left" | "Right" | "Next" | "Previous" | "Select"

    使用 TypeScript 类型

    import { useQuickUIEvent, useQuickUIEventListener, type QuickEvent } from 'ue-connect'
    
    // 定义自定义载荷类型
    interface PlayerEvent {
      playerId: string
      health: number
      position: { x: number; y: number; z: number }
    }
    
    function TypedComponent() {
      // 使用类型化的事件
      const sendPlayerUpdate = useQuickUIEvent('playerUpdate')
    
      // 监听类型化的事件
      useQuickUIEventListener('playerUpdate', (event: QuickEvent<PlayerEvent>) => {
        if (event.payload) {
          console.log('玩家ID:', event.payload.playerId)
          console.log('玩家血量:', event.payload.health)
          console.log('玩家位置:', event.payload.position)
        }
      })
    
      const updatePlayer = () => {
        sendPlayerUpdate({
          playerId: 'player123',
          health: 100,
          position: { x: 10, y: 20, z: 30 }
        })
      }
    
      return <button onClick={updatePlayer}>更新玩家</button>
    }

    六、最佳实践

    1. 性能优化
    • 合理使用 data-nohit 属性避免不必要的鼠标事件处理
    • 在不需要时禁用鼠标事件以减少性能开销
    1. 错误处理
    • 始终检查 isConnected 状态再发送事件
    • 对重要操作添加错误处理和用户反馈
    1. 事件命名
    • 使用清晰的事件命名规范
    • 保持 Web 端和 UE5 端事件名称一致
    1. 数据格式
    • 使用一致的 JSON 数据格式
    • 避免发送过大的数据包
    1. 类型安全
    • 使用 TypeScript 类型定义自定义事件载荷
    • 充分利用类型检查避免运行时错误