Unreal Motion Graphics (UMG)是 UE中用于创建用户界面 (UI) 的工具。它可以实现如下复杂功能:Widget Blueprint中创建动画,并在蓝图脚本中控制动画的播放。Widge类,封装复杂的 UI 逻辑,并在其他地方复用。UMG和HUD,在UE4中的HUD主要是指Head-Up Display功能,在UE4中主要是用来DebugHitBox处理,用来检测鼠标等UMG是对Slate进行了扩展方便在游戏中使用的框架,Slate是不依靠某个平台的UI框架,UE4的Editor和游戏中的UI都是使用Slate构建的。UObject使得Blueprint也可以方便使用Editor上确认渲染结果Widget是Blueprint和Slate控件的结合。每个UMG Widget都有一个对应的Slate Widget。UMG 通过将高层次的蓝图控件映射到Slate控件来实现 UI。UUserWidget:UMG 中所有Widget的基类。SUserWidget:与UUserWidget对应的Slate控件。Delegate和Binding系统。UMG 中的事件通常是通过Blueprint或C++中的Delegate绑定来处理的。Property Binding:通过 UMG 的绑定功能,将控件属性绑定到类的属性或函数。Delegate Binding:通过 Delegate 绑定,处理属性变化时的自定义逻辑。UMG的三种控件:
UWidget: 所有 UMG 控件的公共基类,不提供增加子节点功能UPanelWidget: 提供了增加子节点功能,可以有多个子节点UContentWidget: 继承于UPanelWidget,是UPanelWidget的一种特例,只能有一个子节点
(1)不能有子节点的控件:这个类别的控件的公共基类都是UWidget,每个 UMG 控件,都持有一个 Slate控件的智能指针。
(2)可以增加子节点的控件:
UComponentWidget->UPanelWidget,父类 UComponentWidget构造函数中设置了不允许多个孩子标记位:(源码如下)UContentWidget::UContentWidget(const FObjectInitializer& ObjectInitializer) : Super(ObjectInitializer) { bCanHaveMultipleChildren = false; } UPanelWidget定义了AddChild函数,可以增加对应的子节点:(源码如下)UPanelSlot* UPanelWidget::AddChild(UWidget* Content) { if ( Content == nullptr ) { return nullptr; //表示无法添加空控件 } //检查是否可以有多个子控件 if ( !bCanHaveMultipleChildren && GetChildrenCount() > 0 ) { return nullptr; } //将 Content 从其当前父控件中移除。这一步确保控件没有其他父控件,并准备将其添加到新的 UPanelWidget 中。 Content->RemoveFromParent(); //初始化新对象的标志。 //默认情况下,使用RF_Transactional,这意味着该对象的更改是可撤销的。 //如果UPanelWidget有RF_Transient标志,则新对象也设置这个标志,表示它在运行时创建,并且不会保存到磁盘。 EObjectFlags NewObjectFlags = RF_Transactional; if (HasAnyFlags(RF_Transient)) { NewObjectFlags |= RF_Transient; } //创建新的 PanelSlot 对象 UPanelSlot* PanelSlot = NewObject(this, GetSlotClass(), NAME_None, NewObjectFlags); //GetSlotClass()是UPanelSlot的类类型NAME_None表示没有指定名称,新对象的标志是之前设置的NewObjectFlags。 //关联控件和PanelSlot PanelSlot->Content = Content; PanelSlot->Parent = this; //设置控件的 Slot //将Content的Slot属性设置为新创建的PanelSlot。这一步建立了控件与其布局信息之间的双向链接。 Content->Slot = PanelSlot; //将PanelSlot添加到Slots数组 //Slots数组维护所有子控件的布局信息 Slots.Add(PanelSlot); //触发Slot添加事件。OnSlotAdded方法是一个虚函数,可以在子类中重写,以处理Slot添加后的特定逻辑。 OnSlotAdded(PanelSlot); //使布局和易变性失效 //这将强制重建控件树和重新计算布局 InvalidateLayoutAndVolatility(); return PanelSlot; } Slot:Slot是一种用于管理控件布局和对齐的机制,UMG 中的每个Widget都有一个Slot,用于确定其在父控件中的布局规则。① UCanvasPanelSlot:
UCanvasPanel,提供绝对位置和大小。Position, Size, Anchors, Offsets等。②UHorizontalBoxSlot:
UHorizontalBox,提供水平布局。Padding, Size, HorizontalAlignment, VerticalAlignment等。③UVerticalBoxSlot:
UVerticalBox,提供垂直布局。Padding, Size, HorizontalAlignment, VerticalAlignment等。SPanel、SCompoundWidget可以作为父节点,控件之间的父子关系是依赖Slot实现的。父控件引用Slot,Slot引用子控件并且保留子控件相对于父控件的布局信息。Slate控件类:
*Slate中除了SWidget之外有三个基础类,其他控件都是继承者三个基类。
①SPanel: 有多个子节点
②SLeafWidget: 没有子节点
③SCompoundWidget: 可以有一个子节点
UCanvasPanel控件树:
UCanvasPanel是 UMG 中的高层次控件,底层的实现是SConstraintCanvas,它处理实际的布局和渲染逻辑,UCanvasPanel将子控件添加到 SConstraintCanvas中,并通过UCanvasPanelSlot设置布局属性。UCanvasPanelSlot是UCanvasPanel中每个子控件的Slot类。它存储子控件的布局信息,比如位置、大小、锚点等。每个添加到UCanvasPanel的子控件都会有一个对应的UCanvasPanelSlot。SConstraintCanvas使用SConstraintCanvas::FSlot来存储子控件的布局信息。SConstraintCanvas::FSlot继承自FSlot,并添加了特定于SConstraintCanvas的布局属性。UCanvasPanel增加一个UCanvasPanelSlot,其SConstraintCanvas引用也相应的添加一个FSlot(SConstraintCanvas::FSlot),且UCanvasPanelSlot保存FSlot的引用。UCanvasPanelSlot的属性时,通用引用也修改了SConstraintCanvas::FSlot对应的属性。void UCanvasPanelSlot::SetOffsets(FMargin InOffset) { LayoutData.Offsets = InOffset; if ( Slot ) { Slot->Offset(InOffset); } } Health变量,这种方式的弊端显而易见:随着项目的扩展、游戏不断更新迭代新版本,角色的属性可能达到几十上百,若是加上角色的技能、特性、天赋或其他各种角色相关内容,角色类的内容将膨胀到极难维护。在UE中解决这个问题的办法很简单,可以使用UE中的ActorComponent类,让整个类以组件的形式被SCharacter拥有。因此,我们从ActorComponent创建角色的SAttributeComponent类,用于实现角色的各种属性。
SAttributeComponent.h
#pragma once #include "CoreMinimal.h" #include "Components/ActorComponent.h" #include "SurAttributeComponent.generated.h" UCLASS( ClassGroup=(Custom), meta=(BlueprintSpawnableComponent) ) class SURKEAUE_API USurAttributeComponent : public UActorComponent { GENERATED_BODY() protected: UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category="Attributes") float Health; public: USurAttributeComponent(); UFUNCTION(BlueprintCallable, Category="Attributes") bool ApplyHealthChange(float Delta); }; #include "SurAttributeComponent.h" USurAttributeComponent::USurAttributeComponent() { Health = 100; } bool USurAttributeComponent::ApplyHealthChange(float Delta) { Health += Delta; // 用于判断操作是否成功完成,如角色死亡、特殊机制等情况可能失败 // 目前先返回true return true; } SCharacter中声明组件,并在.cpp中创建相应实例:UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Components") USurAttributeComponent* AttributeComp; SMagicProjectile中为球体组件绑定一个OnComponentBeginOverlap事件,旨在当魔法粒子与物体重叠时触发。比起使用阻挡来触发,使用重叠可以通过判定重叠对象来更方便的忽略友军伤害,并且让魔法粒子直接穿过友军继续在场景中计算(记得对应的Projectile碰撞设置也要进行更改)。//SurMagicProjectile.h UFUNCTION() void OnActorOverlap(UPrimitiveComponent* OverlappedComponent, AActor* OtherActor, UPrimitiveComponent* OtherComp, int32 OtherBodyIndex, bool bFromSweep, const FHitResult& SweepResult); //SurMagicProjectile.cpp SphereComp->OnComponentBeginOverlap.AddDynamic(this, &ASurMagicProjectile::OnActorOverlap); void ASurMagicProjectile::OnActorOverlap(...) { if (OtherActor) { //获得AttributeComp USurAttributeComponent* AttributeComp = Cast(OtherActor->GetComponentByClass(USurAttributeComponent::StaticClass())); // 再次判空,可能碰到的是墙壁、箱子等没有血量的物体 if (AttributeComp) { // 魔法粒子造成20血量伤害 AttributeComp->ApplyHealthChange(-20.0f); // 一旦造成伤害就销毁,避免穿过角色继续计算 Destroy(); } } } Player中利用蓝图实现在屏幕上打印血量的字符串,以让我们实时了解角色的血量。这个蓝图实现很简单,唯一注意的是设置显示时长为0并关闭Print to Log,这样屏幕上只会出现一个数字(事件Tick会在每一帧都调用),且不会在Log中输出大量无用信息:
Actor派生一个名为ProjectileCanon的蓝图类,为其添加Utility下的箭头组件并设置为根,并设置颜色(UE中用红绿蓝表示XYZ轴,箭头默认的红色与X轴相同容易混乱),去掉“渲染”属性中的“游戏中隐藏”。OnTimerElapsed的自定义事件,设置该事件为朝箭头方向发射魔法粒子;同时,在Tick节点后实现在每一帧将箭头指向玩家,方向向量由两个Actor的坐标相减得到。Content文件夹下创建UI文件夹,用于存放所有UI相关的资产。在UI文件夹中创建“用户界面” -> “控件蓝图”,命名为PlayerHealth_Widget。双击打开UMG的操作界面Player的蓝图中选择“事件开始运行”,并添加Create Widget节点,选择PlayerHealth_Widget,然后将其添加到视口,这样,在我们运行关卡时UI就会显示在屏幕上。GetHealthText。添加“获取拥有玩家Pawn(Get Owning Player Pawn)” -> 按类获取组件(Get Component By Class)-> 选择class为SAttributeComponet-> GetHealth -> 连接到“返回节点”的return value引脚;然后在“获取拥有玩家Pawn”后添加Is Valid判空。编译保存运行,可以看到数字能够实时显示血量了。
上一篇:cdn行业标准会议_cdn
下一篇:三星t211怎么装系统