UE5像素流与Vue通信

项目中有将UE程序发布为像素流供前端调用的需求,写篇推文记录一下。

  1. UE准备工作

    按照官方教程中的步骤一步步操作

    https://dev.epicgames.com/documentation/zh-cn/unreal-engine/getting-started-with-pixel-streaming-in-unreal-engine?application_version=5.3

    其中有几个坑点

    1️⃣ 如果我们是测试的话可以不用打包UE程序

    2️⃣ 克隆信令服务器代码和第一次运行信令服务器的时候需要科学上网

    3️⃣ 信令服务器代码不能放到非ntfs格式硬盘中,否则会报错???(别问我怎么知道的🌚)

    4️⃣Windows系统下运行,cmd管理员权限进入

    SignallingWebServer\platform_scripts\cmd 运行run_local.bat即可

  2. Vue准备工作

    复制代码
    npm install @epicgames-ps/lib-pixelstreamingfrontend-ue5.3
    npm install @epicgames-ps/lib-pixelstreamingfrontend-ui-ue5.3

    封装一个拉取UE像素流的组件ue.vue

    javascript 复制代码
    <template>
      <div id="container">
        <!-- 点击进入 -->
        <div id="play">
          <div className="flex flex-col">
            <!-- 加载框 -->
            <span className="loader"></span>
            <span className="mt-4 ml-3">点击进入</span>
          </div>
        </div>
      </div>
    </template>
    <script setup>
    import {
      Config,
      ControlSchemeType,
      Flags,
      Logger,
      PixelStreaming,
      TextParameters,
    } from "@epicgames-ps/lib-pixelstreamingfrontend-ue5.3";
    import {
      Application,
      PixelStreamingApplicationStyle,
      UIElementCreationMode,
    } from "@epicgames-ps/lib-pixelstreamingfrontend-ui-ue5.3";
    import {onMounted} from "vue";
    // 定义emit方法
    const emit = defineEmits(["ready"]);
    // 创建像素流样式
    const style = new PixelStreamingApplicationStyle();
    style.applyStyleSheet();
    // 设置日志等级
    Logger.SetLoggerVerbosity(1);
    // 创建配置对象
    const config = new Config({useUrlParams: false});
    config.setFlagEnabled(Flags.AutoPlayVideo, true);
    config.setFlagEnabled(Flags.AutoConnect, true);
    // 配置像素流地址
    config.setTextSetting(
        TextParameters.SignallingServerUrl,
        "ws://127.0.0.1:8090"
    );
    // 配置鼠标控制方式
    config.setFlagEnabled(Flags.HoveringMouseMode, ControlSchemeType.HoveringMouse);
    // 配置开启鼠标事件
    config.setFlagEnabled(Flags.MouseInput, true);
    // 配置开启键盘事件
    config.setFlagEnabled(Flags.KeyboardInput, true);
    // 配置开启触屏事件
    config.setFlagEnabled(Flags.TouchInput, true);
    // 配置关闭手柄事件
    config.setFlagEnabled(Flags.GamepadInput, false);
    // 配置关闭XR事件
    config.setFlagEnabled(Flags.XRControllerInput, false);
    config.setFlagEnabled(Flags.BrowserSendOffer, false);
    config.setFlagEnabled(Flags.MatchViewportResolution, true);
    // 创建像素流对象
    const stream = new PixelStreaming(config);
    onMounted(() => {
      // 设置浏览器拒绝自动播放时触发的事件
      stream.addEventListener("playStreamRejected", (data) => {
        const play = document.getElementById("play");
        play.className = "visible";
        play.onclick = () => {
          stream.play();
          play.className = "";
          play.onclick = undefined;
          // 通知父组件准备就绪
          emit("ready");
        };
      });
      // 设置UE端消息回调
      stream.addResponseEventListener("handle_responses", (response) => {
        console.log(response);
      });
      // 设置store里的流对象
      window.streamsss = stream;
      // 创建应用程序
      const application = new Application({
        stream,
        // 关闭设置面板
        settingsPanelConfig: {
          isEnabled: false,
          visibilityButtonConfig: {
            creationMode: UIElementCreationMode.Disable,
          },
        },
        // 关闭状态面板
        statsPanelConfig: {
          isEnabled: false,
          visibilityButtonConfig: {
            creationMode: UIElementCreationMode.Disable,
          },
        },
        // 关闭全屏按钮
        fullScreenControlsConfig: {
          creationMode: UIElementCreationMode.Disable,
        },
        // 关闭XR控制按钮
        xrControlsConfig: {
          creationMode: UIElementCreationMode.Disable,
        },
        // 关闭信号指示
        videoQpIndicatorConfig: {
          disableIndicator: true,
        },
      });
      // 挂载到指定元素
      document.getElementById("container").appendChild(application.rootElement);
    });
    </script>
    <style lang="scss" scoped>
    #container {
      width: 100vw;
      height: 100vh;
    }
    
    /* 播放按钮 */
    #play {
      position: absolute;
      top: 0;
      left: 0;
      width: 100%;
      height: 100%;
      display: none;
      font-size: 24px;
      z-index: 999;
      /* 低于30则会展示像素流组件的默认播放按钮 */
    }
    
    #ue-btn {
      position: absolute;
      right: 20px;
      bottom: 20px;
      z-index: 1001;
      padding: 8px 12px;
      background: rgba(0, 0, 0, 0.6);
      color: #fff;
      border: none;
      border-radius: 4px;
      cursor: pointer;
    }
    
    #ue-btn:hover {
      background: rgba(0, 0, 0, 0.8);
    }
    
    #play.visible {
      display: flex;
      align-items: center;
      justify-content: center;
      background-color: black;
      color: white;
      letter-spacing: 8px;
      cursor: pointer;
      font-size: 14px;
    }
    
    /* 加载动画 */
    .loader {
      width: 48px;
      height: 78px;
      position: relative;
      box-sizing: border-box;
      border: 2px solid #fff;
      margin: auto;
      border-radius: 50% 50% 50% 50% / 25% 25% 25% 25%;
    }
    
    .loader::before {
      content: "";
      position: absolute;
      left: 50%;
      top: 20px;
      transform: translateX(-50%);
      width: 4px;
      height: 4px;
      background: #fff;
      border-radius: 10px;
      animation: scrollDown 1.5s linear infinite;
    }
    
    @keyframes scrollDown {
      0% {
        top: 15px;
        height: 4px;
        opacity: 1;
      }
      33% {
        top: 15px;
        height: 40px;
      }
      66% {
        top: 50px;
        height: 10px;
        opacity: 1;
      }
      100% {
        top: 56px;
        height: 4px;
        opacity: 0;
      }
    }
    </style>

    然后是index页面 也就是首页

    javascript 复制代码
    <template>
      <div class="container">
        <!-- UE像素流核心容器 -->
        <div class="ues-container">
          <ues></ues>
        </div>
    
        <!-- 发送UI交互给UE的按钮 -->
        <button class="ueBtn" @click="sendUIInteraction">发送 UI 交互</button>
      </div>
    </template>
    
    <script setup>
    // 仅引入UE组件(确认路径正确,若在src/components下则用@/components/ue.vue)
    import ues from "@/components/ue.vue";
    
    // 发送UI交互给UE的方法(仅保留信令服务器配置,移除MQTT)
    const sendUIInteraction = () => {
      // UE像素流信令服务器配置,
      const payload = {
        server: "http://127.0.0.1:8888"
      };
    
      // 调用UE流对象发送交互
      if (window.streamsss) {
        window.streamsss.emitUIInteraction(payload);
        console.log("已发送UI交互消息给UE(仅信令服务器配置):", payload);
      } else {
        console.error("UE流对象未加载!请检查像素流连接");
      }
    };
    </script>
    
    <style scoped>
    .container {
      width: 100vw;
      height: 100vh;
      position: relative;
      margin: 0;
      padding: 0;
      overflow: hidden;
    }
    
    .ues-container {
      width: 100%;
      height: 100%;
      position: absolute;
      top: 0;
      left: 0;
      z-index: 1;
    }
    
    .ueBtn {
      position: absolute;
      right: 20px;
      bottom: 20px;
      background: #ffffff;
      padding: 10px 20px;
      border: 1px solid #cccccc;
      border-radius: 4px;
      cursor: pointer;
      z-index: 20;
    }
    
    .ueBtn:hover {
      background: #f5f5f5;
    }
    </style>
相关推荐
OpenTiny社区2 小时前
TinyPro v1.4 空降:Spring Boot 集成,后端兄弟也能愉快写前端!
前端·javascript·vue.js
古迪红尘2 小时前
el-tree 采用懒加载方式,怎么初始化就显示根节点和下级节点
前端·javascript·vue.js
糖墨夕2 小时前
当代码照进生活:一个程序员眼中的欲望陷阱
前端
Aotman_2 小时前
Vue el-table 字段自定义排序(进阶)
前端·javascript·vue.js·elementui·前端框架·ecmascript
Charonrise2 小时前
完美解决Microsft Edge浏览器双击无法打开 双击无反应 无响应 不能用
前端·edge
华仔啊2 小时前
这 5 个冷门 HTML 标签,让我直接删了100 行 JS 代码!
前端·html
西维2 小时前
大屏、看板必备的丝滑技巧 — 数字滚动
前端·javascript·动效
前端工作日常2 小时前
我学习到的AG-UI的功能:全面的交互支持
前端