本文展示如何在UE4中展示单个3D物体,并实现旋转放大
本文所实现的主要流程如下
- 创建展示的UMG界面
- 动态创建展示的3d模型及旋置到正确的位置
- 锁定输入事件,仅在UI层,防止触发关卡逻辑
- 监听触控事件,实现旋转及放大事件
- 退出,输入事件切换,删除3d模式,关闭界面
分享关键信息小技巧
在正文开始前分享个小技巧,有时候需要发送关键数据给别人时,如账号密码可以借用外部的工具,而不仅仅是在一种聊天软件里面全部发送,1、避免数据在单一层面被截取而导致的数据泄漏,2、防止数据长时间的保存,而被人意外获取。可以在微信里把账号发送给对方,然后密码用分享,文字分享,匿名分享,私密分享 - 问蒙在线工具,用本地加密的方式分享出去,对方只有在有限的时间内且要输入正确的密钥才能得到您的正确信息,有效的避免了通过网络传输而造成的财产损失。
创建UMG的展示界面
在内容浏览器里,右键 =>用户界面 =>控件蓝图 ,在这里创建您需的元素,包括(关闭键)。 在根画布面板里选择锚点如下,以保证全屏铺满界面 最终的界面示意图如下 界面布局完了之后,必须在类设置里面关联您的C++类,这里我命名为UExamineDialogUW,然后在该资源上点右键 =>复制引用 ,得到该umg的地址,会得到 /game/dialog/ExamineOnly.ExamineOnly 的路径,但是在C++使用必须在结尾添加_C 即地址WidgetBlueprint'/game/dialog/ExamineOnly.ExamineOnly_C' , 以下是关键c++实现
UClass* CanvasWidgetClass = LoadClass<UUserWidget>(NULL, TEXT("WidgetBlueprint'/game/dialog/ExamineOnly.ExamineOnly_C'"));
if (auto dialog = CreateWidget<UExamineDialogUW>(GEngine->GameViewport->GetGameInstance(), CanvasWidgetClass)) {
dialog->AddToViewport(-1);
}
动态创建展示的3D模型。
这里我展示的是蓝图类的Actor模型,所以,我们先创建一个Actor模型 右键 =>蓝图类 =>Actor ,这里,我们在视口里拉入两个静态网格组件,一个命名成SpherePivot这个组件是我们操作的组件,我们不会为其绑定Mesh,另一个做为SpherePivot的子节点,命名为ExamineMesh。 这里拆分成两个节点的原因在于通常美术给的Mesh的起始点都是(0,0,0)我们展示的却是需要围绕着Mesh的中心点做旋转变换,所以在这里创建了SpherePivot做为基准点来做旋转变化。示意图如下:
同样的,右键复制引用,得到资源地址如Blueprint'/Game/BluePrint/Examine1001.Examine1001' ,然后我们在C++动态创建这个Actor。是通过SpawnActor 创建出来
UBlueprint* MyWidgetClass = LoadObject<UBlueprint>(NULL, TEXT("Blueprint'/Game/BluePrint/Examine1001.Examine1001'"))
this->ExamineActor = GetWorld()->SpawnActor<AActor>(MyWidgetClass->GeneratedClass, matix, SpawnParams);
以上代码中用到了一个重要属性matix ,这个也是关系到我们能否正常的展示该模型的位置,因为我们无法在umg界面中展示3D模型,实际上该3D模型是添加到关卡中的,所以此时该模型的位置和我们的展示效果相关联。所以此时该值的正确与否和我们展示息息相关。首先我们先获取视口的坐标位置及朝向,然后对该朝向在偏移,最后再把坐标及朝向合成,即得到我们需要的坐标
FVector OutLocation;
FRotator OutRotation;
GetWorld()->GetFirstPlayerController()->GetActorEyesViewPoint(OutLocation, OutRotation);
FVector ExamineOffset(20, 0, 1);
FVector RotatorVector = UKismetMathLibrary::GreaterGreater_VectorRotator(ExamineOffset, OutRotation);
OutLocation += RotatorVector;
auto matix = UKismetMathLibrary::MakeTransform(OutLocation, OutRotation, FVector(1, 1, 1));
此时效果
禁止关卡层触发INPUT事件
此时展示完成,我们设置只有UI层能接受事件
GetWorld()->GetFirstPlayerController()->SetInputMode(FInputModeUIOnly());
监听触控事件,实现旋转及放大事件
我们在当前类UExamineDialogUW 覆写三个函数
virtual FReply NativeOnTouchStarted(const FGeometry& InGeometry, const FPointerEvent& InGestureEvent);
virtual FReply NativeOnTouchMoved(const FGeometry& InGeometry, const FPointerEvent& InGestureEvent);
virtual FReply NativeOnTouchEnded(const FGeometry& InGeometry, const FPointerEvent& InGestureEvent);
TMap<int, FVector2D> touchPoints;
InGestureEvent.GetPointerIndex() 表示当前为第几个手指,也是多点触控的关键,我们对该值与相应坐标存储要匹配, 我们只会对SpherePivot 进行操作,而ExamineMesh 是它的子节点,会根据他的变换而自动变化
FReply UExamineDialogUW::NativeOnTouchStarted(const FGeometry& InGeometry, const FPointerEvent& InGestureEvent)
{
touchPoints.Add(InGestureEvent.GetPointerIndex(), InGestureEvent.GetLastScreenSpacePosition());
return FReply::Handled();
}
FReply UExamineDialogUW::NativeOnTouchMoved(const FGeometry& InGeometry, const FPointerEvent& InGestureEvent)
{
if (!touchPoints.Contains(InGestureEvent.GetPointerIndex()) ) {
return FReply::Handled();
}
if (this->SpherePivot == nullptr) {
return FReply::Handled();
}
FVector2D pre = touchPoints[InGestureEvent.GetPointerIndex()];
FVector2D now = InGestureEvent.GetLastScreenSpacePosition();
if (this->touchPoints.Num() == 1) {
FVector2D offset = now - pre;
auto rotator = this->SpherePivot->GetRelativeRotation();
rotator.Yaw += -offset.X / 10;
rotator.Roll += -offset.Y / 10;
this->SpherePivot->SetRelativeRotation(rotator);
touchPoints.Add(InGestureEvent.GetPointerIndex(), InGestureEvent.GetLastScreenSpacePosition());
}
else {
auto size1 = this->CalcPointSize();
touchPoints.Add(InGestureEvent.GetPointerIndex(), InGestureEvent.GetLastScreenSpacePosition());
auto size2 = this->CalcPointSize();
float ratio = size2 / size1;
FVector scale = this->SpherePivot->GetRelativeScale3D();
scale *= ratio;
this->SpherePivot->SetRelativeScale3D(scale);
}
return FReply::Handled();
}
FReply UExamineDialogUW::NativeOnTouchEnded(const FGeometry& InGeometry, const FPointerEvent& InGestureEvent)
{
touchPoints.Remove(InGestureEvent.GetPointerIndex());
return FReply::Handled();
}
float UExamineDialogUW::CalcPointSize()
{
TArray<FVector2D> array;
for (auto point : this->touchPoints) {
array.Push(point.Value);
}
if (array.Num() <= 1) {
return 1;
}
return FVector2D::Distance(array[0], array[1]);
}
退出,输入事件切换,删除3d模式,关闭界面
在close事件里,记得把我们的AActor模式相应的删除,然后把输入模式设回游戏和UI的双重的触控模式
if (this->ExamineActor) {
this->ExamineActor->Destroy();
GetWorld()->GetFirstPlayerController()->SetInputMode(FInputModeGameAndUI());
}
最终效果图如下: 推荐一个在线工具的网站,问蒙在线工具,希望可以帮到你。
|