虚拟实验室-Unreal 版本

虚拟实验室的Unreal 版本,第一个版本主要是以《探究通电螺线管外部的磁场分布》颗粒为例,设计和开发一个正式版本。


UE-外壳对接方案

<p>[TOC]</p> <table> <thead> <tr> <th>作者</th> <th>QFord</th> </tr> </thead> <tbody> <tr> <td>创建日期</td> <td>2024-7-23</td> </tr> <tr> <td>更新日期</td> <td>2024-7-25</td> </tr> <tr> <td>版本</td> <td>V1.1.0</td> </tr> </tbody> </table> <h1>需求</h1> <p><em>游浠 2024/7/22 11:58:37 我早上和永弟说了哈 你这边这周先安排做 对接灵峰这边的事情</em></p> <p>经过和灵峰对接掌握的信息,他负责外壳的开发,主要需求如下:</p> <ol> <li>期望采用独立的进程进行通信。</li> <li>颗粒播放器关闭后需通知外壳。 &gt; 永弟反馈外壳对接的是UE-颗粒播放器,不能写UE-物件播放器。</li> </ol> <h1>方案</h1> <p>永弟表示,<strong>优先处理PC端的</strong>。 其他端待后续出包后,提供给外壳开发人员进行正式接入和测试,而此方案实现不依赖我们提供的UE颗粒播放器版本。</p> <p>下面的实现方案由本人创建的 <strong>[AI-Unreal Engine开发专家](<a href="https://ai-hub-sg.sdp.ndaeweb.com/explore/installed/cf2f15d7-b957-4dd0-918e-62b2e3d65e9d">https://ai-hub-sg.sdp.ndaeweb.com/explore/installed/cf2f15d7-b957-4dd0-918e-62b2e3d65e9d</a> &quot;AI-Unreal Engine开发专家&quot;)</strong> 提供,可供参考:</p> <h2>PC-Win、Android和iOS</h2> <h3>提示词</h3> <p>两个使用Unreal开发的程序A和B,如何从A启动B程序并置顶显示,当B程序退出后通知A程序,并将A程序置顶。两个程序之间的通信格式使用JSON。需要同时满足PC端、安卓端和iOS端。</p> <h3>回答</h3> <p><strong>Summary:</strong></p> <ul> <li>为了同时适配PC端、安卓端和iOS端,需要使用Unreal Engine的跨平台API来启动应用程序,并使用平台特定的机制进行进程间通信。</li> </ul> <p><strong>Detailed Explanation:</strong></p> <ol> <li> <p><strong>从A启动B程序并置顶显示:</strong></p> <ul> <li>使用Unreal Engine的<code>FPlatformProcess::CreateProc</code>在PC端启动B程序。</li> <li>在安卓和iOS端,使用平台特定的API启动B程序。</li> </ul> </li> <li> <p><strong>B程序退出后通知A程序:</strong></p> <ul> <li>使用平台特定的机制进行进程间通信,例如使用本地通知或Socket通信。</li> </ul> </li> </ol> <p><strong>Code Example:</strong></p> <ol> <li><strong>从A启动B程序并置顶显示:</strong></li> </ol> <pre><code class="language-cpp">void LaunchBProgram() { #if PLATFORM_WINDOWS || PLATFORM_MAC || PLATFORM_LINUX FString BProgramPath = TEXT(&amp;quot;C:/Path/To/BProgram.exe&amp;quot;); FProcHandle ProcHandle = FPlatformProcess::CreateProc(*BProgramPath, nullptr, true, false, false, nullptr, 0, nullptr, nullptr); if (ProcHandle.IsValid()) { // 获取B程序的窗口句柄并置顶显示(仅适用于Windows) #if PLATFORM_WINDOWS HWND BProgramHWND = FindWindow(nullptr, TEXT(&amp;quot;BProgramWindowTitle&amp;quot;)); if (BProgramHWND) { SetForegroundWindow(BProgramHWND); SetWindowPos(BProgramHWND, HWND_TOPMOST, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE); } #endif } #elif PLATFORM_ANDROID if (JNIEnv* Env = FAndroidApplication::GetJavaEnv()) { jclass Class = FAndroidApplication::FindJavaClass(&amp;quot;com/yourcompany/yourapp/YourActivity&amp;quot;); jmethodID Method = Env-&amp;gt;GetStaticMethodID(Class, &amp;quot;LaunchBProgram&amp;quot;, &amp;quot;()V&amp;quot;); Env-&amp;gt;CallStaticVoidMethod(Class, Method); } #elif PLATFORM_IOS [[UIApplication sharedApplication] openURL:[NSURL URLWithString:@&amp;quot;yourappB://&amp;quot;]]; #endif }</code></pre> <p><strong>②. B程序退出后通知A程序:</strong></p> <pre><code class="language-cpp">void NotifyAProgram() { #if PLATFORM_WINDOWS || PLATFORM_MAC || PLATFORM_LINUX // 使用HTTP请求通知A程序 FString URL = TEXT(&amp;quot;http://localhost:8080/notify&amp;quot;); TSharedRef&amp;lt;IHttpRequest, ESPMode::ThreadSafe&amp;gt; Request = FHttpModule::Get().CreateRequest(); Request-&amp;gt;SetURL(URL); Request-&amp;gt;SetVerb(TEXT(&amp;quot;POST&amp;quot;)); Request-&amp;gt;SetHeader(TEXT(&amp;quot;Content-Type&amp;quot;), TEXT(&amp;quot;application/json&amp;quot;)); FString JsonPayload = TEXT(&amp;quot;{\&amp;quot;message\&amp;quot;: \&amp;quot;BProgramExited\&amp;quot;}&amp;quot;); Request-&amp;gt;SetContentAsString(JsonPayload); Request-&amp;gt;OnProcessRequestComplete().BindLambda([](FHttpRequestPtr Request, FHttpResponsePtr Response, bool bWasSuccessful) { if (bWasSuccessful &amp;amp;&amp;amp; Response-&amp;gt;GetResponseCode() == 200) { UE_LOG(LogTemp, Log, TEXT(&amp;quot;Notification sent to A program successfully.&amp;quot;)); } else { UE_LOG(LogTemp, Error, TEXT(&amp;quot;Failed to send notification to A program.&amp;quot;)); } }); Request-&amp;gt;ProcessRequest(); #elif PLATFORM_ANDROID if (JNIEnv* Env = FAndroidApplication::GetJavaEnv()) { jclass Class = FAndroidApplication::FindJavaClass(&amp;quot;com/yourcompany/yourapp/YourActivity&amp;quot;); jmethodID Method = Env-&amp;gt;GetStaticMethodID(Class, &amp;quot;NotifyAProgram&amp;quot;, &amp;quot;()V&amp;quot;); Env-&amp;gt;CallStaticVoidMethod(Class, Method); } #elif PLATFORM_IOS UILocalNotification* Notification = [[UILocalNotification alloc] init]; Notification.alertBody = @&amp;quot;BProgramExited&amp;quot;; [[UIApplication sharedApplication] presentLocalNotificationNow:Notification]; #endif }</code></pre> <p><strong>③:A程序监听B程序的退出消息并置顶:</strong></p> <pre><code class="language-cpp">void StartHttpServer() { #if PLATFORM_WINDOWS || PLATFORM_MAC || PLATFORM_LINUX FHttpServerModule* HttpServerModule = &amp;amp;FHttpServerModule::Get(); TSharedPtr&amp;lt;IHttpRouter&amp;gt; HttpRouter = HttpServerModule-&amp;gt;GetHttpRouter(8080); HttpRouter-&amp;gt;BindRoute( FHttpPath(TEXT(&amp;quot;/notify&amp;quot;)), EHttpServerRequestVerbs::VERB_POST, [](const FHttpServerRequest&amp;amp; Request, const FHttpResultCallback&amp;amp; OnComplete) { FString RequestBody = FString(UTF8_TO_TCHAR(Request.Body.GetData())); TSharedPtr&amp;lt;FJsonObject&amp;gt; JsonObject; TSharedRef&amp;lt;TJsonReader&amp;lt;&amp;gt;&amp;gt; Reader = TJsonReaderFactory&amp;lt;&amp;gt;::Create(RequestBody); if (FJsonSerializer::Deserialize(Reader, JsonObject) &amp;amp;&amp;amp; JsonObject.IsValid()) { FString Message = JsonObject-&amp;gt;GetStringField(TEXT(&amp;quot;message&amp;quot;)); if (Message == TEXT(&amp;quot;BProgramExited&amp;quot;)) { // 将A程序置顶(仅适用于Windows) #if PLATFORM_WINDOWS HWND AProgramHWND = FindWindow(nullptr, TEXT(&amp;quot;AProgramWindowTitle&amp;quot;)); if (AProgramHWND) { SetForegroundWindow(AProgramHWND); SetWindowPos(AProgramHWND, HWND_TOPMOST, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE); } #endif } } OnComplete(FHttpServerResponse::Create(TEXT(&amp;quot;OK&amp;quot;), EHttpServerResponseCodes::Ok)); return true; } ); HttpServerModule-&amp;gt;StartAllListeners(); #elif PLATFORM_ANDROID // 在Android上,使用JNI监听B程序的退出消息 extern &amp;quot;C&amp;quot; void Java_com_yourcompany_yourapp_YourActivity_onBProgramExited(JNIEnv* Env, jobject Thiz) { // 将A程序置顶 // 在Android上,应用程序通常不需要显式置顶 } #elif PLATFORM_IOS // 在iOS上,使用本地通知监听B程序的退出消息 - (void)application:(UIApplication *)application didReceiveLocalNotification:(UILocalNotification *)notification { if ([notification.alertBody isEqualToString:@&amp;quot;BProgramExited&amp;quot;]) { // 将A程序置顶 // 在iOS上,应用程序通常不需要显式置顶 } } #endif }</code></pre> <p><strong>Additional Tips:</strong></p> <ul> <li>确保B程序的窗口标题是唯一的,以便能够正确找到窗口句柄。</li> <li>在A程序中启动HTTP服务器时,选择一个不常用的端口号以避免冲突,建议走配置。</li> <li>在Android上,可以使用Intent机制启动另一个应用程序。</li> <li>在iOS上,可以使用URL Scheme启动另一个应用程序。</li> </ul> <h3>人工修正</h3> <p>AI提供的代码,大大提高了开发效率。 从提供的代码分析,里面包含较多的API,比起传统的搜索,便捷了很多。 当然,AI提供的代码无法保证100%正确,需要我们开发人员进行验证和调试。 &gt; 在灵峰同学实践过程,发现PC端的方案可以,但是移动端的遇到问题。 所以下面又写了各个平台的方案,经过调研,应该都可以使用url scheme解决。</p> <h3>PC</h3> <p>自行实现,使用url scheme机制,自测使用了微信的例子: <a href="https://blog.csdn.net/weixin_59685936/article/details/138917325">https://blog.csdn.net/weixin_59685936/article/details/138917325</a> 关键蓝图代码: <img src="https://www.showdoc.com.cn/server/api/attachment/visitFile?sign=e7ab05208f2af68823af075b58308f44&amp;amp;file=file.png" alt="" /></p> <p>程序如何注册url scheme? How do I register a custom URL protocol in Windows? - Stack Overflow <a href="https://stackoverflow.com/questions/80650/how-do-i-register-a-custom-url-protocol-in-windows">https://stackoverflow.com/questions/80650/how-do-i-register-a-custom-url-protocol-in-windows</a> &gt; 上述解答详细给出了如何手动通过编辑注册表的方式给PC端程序添加url scheme配置。 还提供了reg注册脚本的实现方式。估计,Unreal没有自行支持,需要我们开发人员自行处理。 在实际正式版本中,应该是制作安装包的时候,通过安装包的脚本来接管上述的url scheme自动化配置过程。</p> <h3>Android</h3> <ol> <li>第三方实现: AI生成的调用安卓的方案有问题,下面的方案比较靠谱: <a href="https://support.singular.net/hc/en-us/articles/4403448781723-Unreal-Engine-SDK-Implementing-Deep-Links">https://support.singular.net/hc/en-us/articles/4403448781723-Unreal-Engine-SDK-Implementing-Deep-Links</a> &gt; 麻烦的是需要注册会员和申请key,免费的用户似乎有限制。</li> </ol> <h3>iOS</h3> <p>使用 url scheme 机制实现唤醒App iOS Deep Linking: URL Schemes vs Universal Links (byby.dev) <a href="https://byby.dev/ios-deep-linking">https://byby.dev/ios-deep-linking</a></p> <h1>协议</h1> <p>采用JSON格式通信,具体协议内容可根据后期需求进行细化和补充。 TODO:</p>

页面列表

ITEM_HTML