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>
相关推荐
陈随易15 分钟前
Postman 被曝自动往 Claude Code 和 Codex 装插件
前端·后端·程序员
Csvn3 小时前
性能监控
前端
Csvn4 小时前
构建工具
前端
FRing034 小时前
vue2干货总结3(computed计算属性、watch监听器)
开发语言·前端·vue.js·vue2
Java小卷5 小时前
JuanForm vs FormCreate!Vue3 表单引擎联动配置 PK!
vue.js·低代码
DolitD6 小时前
UE像素流与云渲染技术方案对比分析
3d·云原生·ue5·云计算·图形渲染
kyriewen6 小时前
Skills 榜前 10 我全装了:只有 3 个能直接用
前端·javascript·程序员
IT_陈寒7 小时前
当Java的hashCode遇上Lombok,我的缓存被全清了
前端·人工智能·后端
工控丫丫7 小时前
帆软看板中经常使用的公式函数及经常搭的组合
前端·javascript·数据库
jerrywus7 小时前
我用 GPT-6.1-sol 做了台会讲解的 3D 售货机:60 罐饮料,一口没喝上
前端·agent·claude