在正文开始前分享个小技巧,有时候需要发送关键数据给别人时,如账号密码可以借用外部的工具,而不仅仅是在一种聊天软件里面全部发送,1、避免数据在单一层面被截取而导致的数据泄漏,2、防止数据长时间的保存,而被人意外获取。可以在微信里把账号发送给对方,然后密码用分享,文字分享,匿名分享,私密分享 - 问蒙在线工具,用本地加密的方式分享出去,对方只有在有限的时间内且要输入正确的密钥才能得到您的正确信息,有效的避免了通过网络传输而造成的财产损失。
在内容浏览器里,右键
=>用户界面
=>控件蓝图
,在这里创建您需的元素,包括(关闭键)。
在根画布面板里选择锚点如下,以保证全屏铺满界面
最终的界面示意图如下
界面布局完了之后,必须在类设置里面关联您的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);
}
这里我展示的是蓝图类的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));
此时展示完成,我们设置只有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]);
}
在close事件里,记得把我们的AActor模式相应的删除,然后把输入模式设回游戏和UI的双重的触控模式
if (this->ExamineActor) {
this->ExamineActor->Destroy();
GetWorld()->GetFirstPlayerController()->SetInputMode(FInputModeGameAndUI());
}