好记性不如烂笔头,前两天刚刚实现了功能,现在把主要过程都记录一下以防忘记,写个博客记录一下,同时也供大家参考。
这个测试工程的目的在于在UE4中利用VRPN实时接入Optitrack的运动追踪数据(操作手柄刚体运动数据)并以此驱动一个方块根据追踪数据在场景中运动。
1.VRPN
VRPN的详细介绍网上有很多,就不赘述了,反正是一种通用的开源设备访问接口,可以把多种VR设备(基本都是交互设备,像是鼠标、ART、optitrack、诺亦腾什么的)的追踪信息转化为统一的格式,并用固定的方式去访问。我用的7.33,大体的配置和使用流程为:
(1)github下载编译源码
Windows下访问https://github.com/vrpn/vrpn网址获取最新的VRPN源码后用CMAKE打包成VS工程。如果仅仅是使用VRPN已经支持的设备就直接生成解决方案即可,然后需要在源码文件中找到下面这几个文件:
a. vrpn_server.exe程序。位于打包后生成的VRPN.sln所在目录下\server_src\Debug(如果编译选的Realease就是Realease)。这个是vrpn的服务器程序,用来直接与外部设备通信以获取信息,我们需要编写自己的client程序来与服务器程序,然后从服务器中获取处理好的程序。
b. vrpnd.lib。我是用Debug模式编译的,估计是因为这个所以才会是vrpnd.lib我看其他都是vrpn.lib估计他们是realease模式编译的,不过都能用。这个文件我是在VRPN.sln所在目录下\Debug文件夹找到的,如果找不到就直接搜索一下,反正我是搜出来的。
c.各种头文件。我也分不清,就在下载的CMAKE源码目录下把所有.h都拷贝出来了。
d.vrpn.cfg文件。在\server_src文件夹里。这是vrpn_server.exe服务器程序的配置文件,用来选定具体需要接入的设备,只用找到对应位置然后把#去掉就行,如果比较熟悉的话自己写一个这样的配置也可以。
现在我们拿到了所有需要的文件,可以先写一个Client的VS测试工程试一下获取一下鼠标的运动信息,网上很多随便搜一下就行,主要是确保找到的文件和开发环境没有问题。写好Client程序后,将vrpn.cfg文件放到vrpn_server.exe目录下,然后运行vrpn_server.exe,没有报错的话运行Client程序即可,如果失败可以检查一下防火墙。
2.Motive软件配置
Motive是Optrack的软件,他其中直接集成了VRPN的服务器程序,这样就简化了操作,我们不需要自己开启vrpn_server.exe并配置vrpn.cfg文件,直接在Motive中做好设置就可以了。
我的Motive版本为2.20,由于官网上的教程比较繁琐很多都用不到,我在此把需要用到的步骤记录一下。
a.首先我们需要添加一个刚体,开启软件后,在view下拉菜单中调出assset pane面板,然后将追踪刚体置于相机视野,等其中出现所有标记点后用鼠标选中所有标记点,右击创建刚体即可,随后就会在左侧assset pane中出现所创建的刚体。注意一点,刚体的名字要记好,后边写程序的时候需要用。
b.随后,点击View-Data Streaming Pane打开实时数据传输面板,这个面板其实就包含了VRPN Server配置,按照下图进行配置,主要是把Broadcast Frame打开、Local Interface选择loopback(我是从本地获取,如果是在网络中其他位置获取需要到官网上看一下配置方式)并且把VRPN打开。至此配置完成。
3.在UE4中写程序
现在是文章的主要内容了。本文使用的UE4版本为4.23,打开UE4创建一个C++工程,并创建一个C++类(我这叫VrpnActor),父类选择Actor(其他父类也不是不行,只不过我这边是打算控制一个actor进行运动,如果是想控制Character也可以用Character作为父类)。完成后我们开始准备写代码。
a.环境配置
UE4中配置环境添加头文件啥的是通过C#进行的,在UE4 Editor下双击创建的类进入VS工程。找到VrpnMotive.Build.cs文件(我的UE工程名称为VrpnMotive,大家请寻找自己工程对应的配置文件),这个文件是用来配置环境用的,头文件,lib等需要在这里添加。具体代码如下,解释直接写到注释了(!!!重点注意,我的注释是后来加的,为了便于理解。注释在使用的时候要删除或者改成英文的,UE4好像对中文支持有问题,注释是中文的都会报错),就不多说了。
// Fill out your copyright notice in the Description page of Project Settings.using UnrealBuildTool;
using System.IO;//需要自己添加,否则无法获取文件路径
public class VrpnMotive : ModuleRules
{public VrpnMotive(ReadOnlyTargetRules Target) : base(Target){PCHUsage = PCHUsageMode.UseExplicitOrSharedPCHs;PublicDependencyModuleNames.AddRange(new string[] { "Core", "CoreUObject", "Engine", "InputCore" });PrivateDependencyModuleNames.AddRange(new string[] { });// Uncomment if you are using Slate UI// PrivateDependencyModuleNames.AddRange(new string[] { "Slate", "SlateCore" });// Uncomment if you are using online features// PrivateDependencyModuleNames.Add("OnlineSubsystem");// To include OnlineSubsystemSteam, add it to the plugins section in your uproject file with the Enabled attribute set to trueAddThirdPartyDependencies(Target);//调用一下就行}
//添加相关配置的函数,里面把文件路径替换即可,其他不用动public void AddThirdPartyDependencies(ReadOnlyTargetRules ROTargetRules){string ThirdPartyPath = Path.GetFullPath(Path.Combine(ModuleDirectory, "../ThirdParty/"));string PathLib = string.Empty;string PathInc = string.Empty;// VRPNPathLib = Path.Combine(ThirdPartyPath, "Vrpn/Lib/x64");PathInc = Path.Combine(ThirdPartyPath, "Vrpn/Include/vrpn");PublicAdditionalLibraries.Add(Path.Combine(PathLib, "vrpn.lib"));PublicAdditionalLibraries.Add(Path.Combine(PathLib, "quat.lib"));PublicIncludePaths.Add(PathInc);}
}
b.程序编写
我们完成了环境的配置,现在开始编写程序。首先是MotiveActor.h文件。中文的注释是针对代码的说明。
// Fill out your copyright notice in the Description page of Project Settings.#pragma once
#include "vrpn_Connection.h"//头文件
#include "vrpn_Tracker.h"//头文件,这两个文件需要添加在上边,如果加在MotiveActor.generated.h后边会报错
#include "EngineUtils.h"
#include "CoreMinimal.h"
#include "GameFramework/Actor.h"
#include "MotiveActor.generated.h"UCLASS()
class VRPNMOTIVE_API AMotiveActor : public AActor
{GENERATED_BODY()
//添加一个可编辑的变量,用来指定刚体名称,需要跟Motive里设定的一致UPROPERTY(EditAnywhere, Category = "Optitrack")FString TargetName = TEXT("Tracker");
//添加一个可编辑的变量,用来设置ip地址UPROPERTY(EditAnywhere,Category = "Optitrack")FString ipAddress = TEXT("localhost:");
//添加一个可编辑的变量,用来设置端口 UPROPERTY(EditAnyWhere, Category = "Optitrack")int32 port = 3883;
//添加一个可见变量,用来显示最终的连接名称UPROPERTY(VisibleAnywhere, Category = "Optitrack")FString connectionName = TEXT("127.0.0.1:3883");public: // Sets default values for this actor's propertiesAMotiveActor();
//初始化连接名称virtual void PostInitProperties() override;
//在连接参数变更时动态更新连接名称virtual void PostEditChangeProperty(FPropertyChangedEvent& PropertyChangedEvent) override;protected:// Called when the game starts or when spawnedvirtual void BeginPlay() override;public: // Called every framevirtual void Tick(float DeltaTime) override;public://For Vrpn
//将获取的位置信息存入position,并设置在蓝图中可见UPROPERTY(EditAnywhere,BlueprintReadOnly,Category = "OptiTrack")FVector position;
//将获取的四元数存入quat,并设置在蓝图中可见UPROPERTY(EditAnywhere,BlueprintReadOnly, Category = "OptiTrack")FVector4 quat;
//连接变量vrpn_Connection* connection;
//追踪变量vrpn_Tracker_Remote* tracker;
//连接VRPN的函数,在蓝图中调用(可以在蓝图中手动设置连接,也可以在代码中直接默认连接,看具体需求)//UFUNCTION(BlueprintCallable,Category = "OptiTrack")void ConnectToMotive();};
然后是cpp文件。
// Fill out your copyright notice in the Description page of Project Settings.#include "MotiveActor.h"
AMotiveActor* MyMotiveActor;//用来存储指向自己的指针
// Sets default values
AMotiveActor::AMotiveActor()
{// Set this actor to call Tick() every frame. You can turn this off to improve performance if you don't need it.PrimaryActorTick.bCanEverTick = true;
}// Called when the game starts or when spawned
void AMotiveActor::BeginPlay()
{Super::BeginPlay();ConnectToMotive();//直接在开始时连接VRPN
}// Called every frame
void AMotiveActor::Tick(float DeltaTime)
{Super::Tick(DeltaTime);
//每次循环,执行我们写的callback函数,更新positon和quat数据tracker->mainloop();connection->mainloop();}//Initialize the parameters
//利用设置的刚体名称,ip地址以及端口生成连接名称
void AMotiveActor::PostInitProperties() {Super::PostInitProperties();connectionName = ipAddress + FString::Printf(TEXT("%d"), port);}
//当参数变化时动态更新连接名称
#if WITH_EDITOR
void AMotiveActor::PostEditChangeProperty(FPropertyChangedEvent& PropertyChangedEvent) {connectionName = ipAddress + FString::Printf(TEXT("%d"), port);Super::PostEditChangeProperty(PropertyChangedEvent);
}#endif//Optitrack function
//回调函数,用来更新追踪数据
void VRPN_CALLBACK handle_pos(void* userdata, const vrpn_TRACKERCB t)
{ MyMotiveActor->position = FVector(t.pos[0], t.pos[1], t.pos[2]);MyMotiveActor->quat = FVector4(t.quat[0], t.quat[1], t.quat[2], t.quat[3]);
}
//连接函数,用来连接到VRPN
void AMotiveActor::ConnectToMotive() {connection = vrpn_get_connection_by_name(TCHAR_TO_ANSI(*connectionName));tracker = new vrpn_Tracker_Remote(TCHAR_TO_ANSI(*TargetName), connection);tracker->register_change_handler(NULL, handle_pos);MyMotiveActor = this;//this指代当前类,为了获取类中存储动捕数据的positon以及quat
}
4.使用测试
完成后进行编译,没问题了以后基于这个自建类创建一个蓝图,在蓝图中绑定一个方块,在蓝图中连接逻辑如下:
随后设置好刚体名称、ip地址等参数后在游戏中创建一个实例即可。
老的工程找不到了,从新用424做了一个,省得以后找不到,备份一份。
链接:https://pan.baidu.com/s/1Jcl983wYz6iQPvMCvgVrpw
提取码:qyyx