前言:
笔者目前在校本科大三,目标方向是人工智能、计算机视觉。上一个OpenCV学习笔记专栏已完结,在学习完OpenCV后,我继续学习C++,并用纯C++做UE4项目的方式继续提升自己的水平。
梁迪老师的水平非常高,他的课程本来也无需笔记:课程本身即为最好的笔记。但由于我天赋有限,还是边看边记,以防遗忘——知识点太多,步骤太繁杂了。在学习过程中,我也偶有思考,思索为什么某个方法老师要这样做。所以,一是为了记录,二是为了分享,才有了这个专栏。
内容方面,由于我在开启这个专栏时,此项目已经做完很多了。所以,前期的一些大篇幅叙述的知识,可能在后期应用中一带而过。以及,前期的一些知识,后期会重新剖析,并加上我的个人理解。
另外,若有学术交流/学业交流意愿,可以邮件联系1246210283@qq.com,希望一齐进步。
本篇学习内容:
15.游戏菜单的UI动画
16.UE4退出游戏
17.UE4延时调用函数
15.游戏菜单的UI动画
在UI菜单点击按钮时,菜单会横向卷起再展开,展开到新界面。
如何实现:
(1)创建一个枚举类
namespace EMenuAnim
{
enum Type
{
Stop,//停止动画
Close,//关闭Menu
Open//打开Menu
};
}
(2)准备参数
//动画播放器
FCurveSequence MenuAnimation;
//曲线控制器
FCurveHandle MenuCurve; //在MenuAnimation的控制下进行变化
//用来保存新的高度
float CurrentHeight;
//是否已经显示Menu组件
bool IsMenuShow;
//是否锁住按钮
bool ControlLocked;
//保存当前的动画状态
EMenuAnim::Type AnimState;
//保存当前的菜单
EMenuType::Type CurrentMenu;
(3)写函数
//初始化动画组件
void InitializeAnimation();
//播放关闭动画
void PlayClose(EMenuType::Type NewMenu);
//重写tick函数(Public)
virtual void Tick(const FGeometry& AllottedGeometry, const double InCurrentTime, const float InDeltaTime) override;
InitializeAnimation()函数:
//开始延时
const float StartDelay = 0.3f;
//持续时间
const float AnimDuration = 0.6f;
MenuAnimation = FCurveSequence();
MenuCurve = MenuAnimation.AddCurve(StartDelay, AnimDuration, ECurveEaseFunction::QuadInOut); //从0到1,从1到0
//初始设置Menu大小
ResetWidgetSize(600.f, 510.f);
//初始显示主界面
ChooseWidget(EMenuType::MainMenu);
//允许点击按钮
ControlLocked = false;
//设置动画状态为停止
AnimState = EMenuAnim::Stop;
//设置动画播放器跳到结尾,也就是1
MenuAnimation.JumpToEnd();
注意,用PlayClose替换掉之前鼠标点击函数中的ChooseWidget,因为ChooseWidget会在PlayClose中调用Tick函数,在Tick函数中进行ChooseWidget。
注意:
void SSlAiMenuWidget::PlayClose(EMenuType::Type NewMenu)
{
//设置新的界面
CurrentMenu = NewMenu;
//设置新高度
CurrentHeight = (*MenuMap.Find(NewMenu))->MenuHeight;
//设置播放状态是Close
AnimState = EMenuAnim::Close;
//播放反向动画
MenuAnimation.PlayReverse(this->AsShared());
}
void SSlAiMenuWidget::Tick(const FGeometry & AllottedGeometry, const double InCurrentTime, const float InDeltaTime)
{
switch (AnimState)
{
case EMenuAnim::Stop:
break;
case EMenuAnim::Close:
//如果正在播放
if (MenuAnimation.IsPlaying()) {
//实时修改Menu的大小
ResetWidgetSize(MenuCurve.GetLerp()*600.f, -1.f);
//在关闭了40%的时候设置不显示组件
if (MenuCurve.GetLerp() < 0.6f && IsMenuShow) ChooseWidget(EMenuType::None);
}
else {
//关闭动画播放结束,设置状态为打开
AnimState = EMenuAnim::Open;
//开始播放打开的动画
MenuAnimation.Play(this->AsShared());
}
break;
case EMenuAnim::Open:
//如果正在播放
if (MenuAnimation.IsPlaying()) {
//实时修改Menu的大小
ResetWidgetSize(MenuCurve.GetLerp()*600.f, CurrentHeight);
//打开60%之后显示组件
if (MenuCurve.GetLerp() > 0.6f && !IsMenuShow) ChooseWidget(CurrentMenu);
}
//如果已经播放完毕
if (MenuAnimation.IsAtEnd()) {
//修改状态为Stop
AnimState = EMenuAnim::Stop;
//解锁按钮
ControlLocked = false;
}
break;
}
}
其中,MenuAnimation.PlayReverse(this->AsShared());
的意思是播放反向动画,也就是菜单卷起部分的动画。这一行是不可或缺的:由于之前设置播放状态是Close,所以在Tick函数中的Switch语句调用的是EMenuAnim::Close那一部分。但是,此时如果不进行PlayReverse,虽然状态是Close,但是IsPlaying()是不成立的,要设置PlayReverse才可以顺利执行if下面的语句。
同理,Close动画播放完毕后,会设置状态为Open,并且Play。这样才会播放Open部分的动画。
16.UE4退出游戏
方法一:(本课程方法)
定义QuitGame函数,实现:
void SSlAiMenuWidget::QuitGame()
{
Cast<ASlAiMenuController>(UGameplayStatics::GetPlayerController(GWorld, 0))->ConsoleCommand("quit");
}
方法二:(摘自CSDN)
#include “Runtime/Engine/Classes/Kismet/KismetSystemLibrary.h”
UKismetSystemLibrary::QuitGame(this, nullptr, EQuitPreference::Quit);
*4.25的虚幻引擎,程序退出和之前的有点不一样,多了最后一个参数,true代表立即退出。
17.UE4延时调用函数
定义定时器句柄 FTimerHandle mTimer;
定义调用的委托 FTimerDelegate timeDele;
绑定函数到委托 timeDele.BindRaw(this, &xxxx:ffff);
获取时间控制器并且启动定时器 Gworld->GetTimerManager().SetTimer(mTimer,timeDele,1.f,true);
Gworld->GetTimerManager().PauseTimer(mTimer);
暂停
Gworld->GetTimerManager().UnPauseTimer(mTimer);
唤醒
Gworld->GetTimerManager().ClearTimer(mTimer);
清除计时器
实现案例:
在点击退出游戏按钮时,要先播放退出游戏的音乐,播放完后退出游戏。
所以实现了一个PlayerSoundAndCall函数(在SlAiHelper.h中)
template<class UserClass>
FORCEINLINE FTimerHandle PlayerSoundAndCall(UWorld * World, const FSlateSound Sound, UserClass * InUserObject,
typename FTimerDelegate::TRawMethodDelegate<UserClass>::FMethodPtr InMethod) {
FSlateApplication::Get().PlaySound(Sound);
FTimerHandle Result;
const float SoundDuration = FMath::Max(FSlateApplication::Get().GetSoundDuration(Sound), 0.1f);
FTimerDelegate Callback;
Callback.BindRaw(InUserObject, InMethod);
World->GetTimerManager().SetTimer(Result, Callback, SoundDuration, false);
return Result;
}
按照上面的流程,先创建一个定时器句柄,然后获取到播放的音乐的时长(不足0.1s延长到0.1),
然后创建委托,绑定委托:第一个参数this正好是需要绑定的类,第二个参数&xxxx:ffff是传入的方法。
然后用上面介绍的API,获取时间控制器并且启动定时器。