【UE4 C++】Tick的三种方式、异步蓝图节点
Tick的三种方式
- 包括
- 默认 Tick (Actor、Component、UMG)
- TimerManager 定时器
- FTickableGameObject
- 可以写原生 Object
- 也可以继承UObject 使用
- 下面利用 AActor 直接实现三种 Tick
class FTickableObject :public FTickableGameObject
{
public:
bool bEnableTick = false;
int32 TickCounter = 0;
virtual void Tick(float DeltaTime)override {
UE_LOG(LogTemp, Warning, TEXT(__FUNCTION__"\t [TickCounter]%d"), TickCounter);
}
virtual bool IsTickable()const override { return bEnableTick; }
virtual TStatId GetStatId() const override { RETURN_QUICK_DECLARE_CYCLE_STAT(UTickableObject, STATGROUP_Tickables); }
};
UCLASS()
class DESIGNPATTERNS_API AAsyncTickActor : public AActor
{
GENERATED_BODY()
public:
// Sets default values for this actor's properties
AAsyncTickActor();
// Called every frame
virtual void Tick(float DeltaTime) override;
void TimerTick();
protected:
// Called when the game starts or when spawned
virtual void BeginPlay() override;
private:
int32 TickCounter = 0;
FTimerHandle TimeHandle;
TSharedPtr<FTickableObject> TickableObject;
};
AAsyncTickActor::AAsyncTickActor()
{
// 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 AAsyncTickActor::BeginPlay()
{
Super::BeginPlay();
TickableObject = MakeShareable(new FTickableObject());
TickableObject->bEnableTick = true;
GetWorld()->GetTimerManager().SetTimer(TimeHandle, this, &AAsyncTickActor::TimerTick, 0.1f, true);
}
// Called every frame
void AAsyncTickActor::Tick(float DeltaTime)
{
Super::Tick(DeltaTime);
UE_LOG(LogTemp, Warning, TEXT(__FUNCTION__"\t[TickCounter]%d"), TickCounter);
if (TickableObject.IsValid())
{
TickableObject->TickCounter = TickCounter;
}
}
void AAsyncTickActor::TimerTick()
{
TickCounter++;
UE_LOG(LogTemp, Warning, TEXT(__FUNCTION__"%\t [TickCounter]%d"), TickCounter);
}
异步蓝图节点
使用方法
继承 UBlueprintAsyncActionBase
- 成员函数 Activate 用于内部触发委托绑定的事件
- 成员函数 RegisterWithGameInstance 允许将该对象注册到 GameInstance,防止GC
- 注册后,调用成员方法 SetReadyToDestroy 销毁、释放,从 GameInstance 注销
- 源码
class UGameInstance; UCLASS()
class ENGINE_API UBlueprintAsyncActionBase : public UObject
{
GENERATED_UCLASS_BODY() /** Called to trigger the action once the delegates have been bound */
UFUNCTION(BlueprintCallable, meta=(BlueprintInternalUseOnly="true"))
virtual void Activate(); /**
* Call to globally register this object with a game instance, it will not be destroyed until SetReadyToDestroy is called
* This allows having an action stay alive until SetReadyToDestroy is manually called, allowing it to be used inside loops or if the calling BP goes away
*/
virtual void RegisterWithGameInstance(UObject* WorldContextObject);
virtual void RegisterWithGameInstance(UGameInstance* GameInstance); /** Call when the action is completely done, this makes the action free to delete, and will unregister it with the game instance */
virtual void SetReadyToDestroy(); protected:
TWeakObjectPtr<UGameInstance> RegisteredWithGameInstance;
};
声明静态函数
需要动态多播委托,作为多个输出节点
代码实现
代码
UCLASS()
class DESIGNPATTERNS_API AAsyncTickActor : public AActor
{
public:
UPROPERTY(BlueprintReadWrite)
TArray<int32>CountdownNums;
}; // 定义委托类型
DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FCompleteHandleDelegate, int32, Result); UCLASS()
class DESIGNPATTERNS_API UMyBPAsyncAction : public UBlueprintAsyncActionBase
{
GENERATED_BODY()
public:
UMyBPAsyncAction() { UE_LOG(LogTemp, Warning, TEXT(__FUNCTION__"")); }
~UMyBPAsyncAction() { UE_LOG(LogTemp, Warning, TEXT(__FUNCTION__"")); } // 自定义的异步蓝图节点
UFUNCTION(BlueprintCallable, meta = (WorldContext = "WorldContextObject"), Category = "MyBPAsyncAction")
static UMyBPAsyncAction* AsyncCountdown(UObject* WorldContextObject, AAsyncTickActor* AsyncTickActor, int32 StartNum);
public:
//输出节点
UPROPERTY(BlueprintAssignable)
FCompleteHandleDelegate OnSucceeded; //输出节点
UPROPERTY(BlueprintAssignable)
FCompleteHandleDelegate OnFailed;
private:
FTimerHandle TimerHandle; protected:
virtual void Activate() override;
};
void UMyBPAsyncAction::Activate()
{
// 开新的线程,测试 Activate 调用情况
Async(EAsyncExecution::ThreadPool, [&]()
{
UE_LOG(LogTemp, Warning, TEXT(__FUNCTION__" Start Delay 1s."));
FPlatformProcess::Sleep(1.0f);
UE_LOG(LogTemp, Warning, TEXT(__FUNCTION__" Delay Finished."));
});
} UMyBPAsyncAction* UMyBPAsyncAction::AsyncCountdown(UObject* WorldContextObject, AAsyncTickActor* AsyncTickActor, int32 StartNum)
{
if (WorldContextObject == nullptr)
{
FFrame::KismetExecutionMessage(TEXT("Invalid WorldContextObject"), ELogVerbosity::Error);
UE_LOG(LogTemp, Warning, TEXT(__FUNCTION__" Invalid WorldContextObject."));
return nullptr;
} UMyBPAsyncAction* MyBPAsyncActionNode = NewObject<UMyBPAsyncAction>();
// Lambda 表达式
auto CountdownFunc = [&,MyBPAsyncActionNode, WorldContextObject, AsyncTickActor, StartNum]() {
if (IsValid(AsyncTickActor))
{
if (AsyncTickActor->CountdownNums.Num() == StartNum)
{
// OnSucceeded输出节点
MyBPAsyncActionNode->OnSucceeded.Broadcast(1); // 清空定时器
WorldContextObject->GetWorld()->GetTimerManager().ClearTimer(MyBPAsyncActionNode->TimerHandle); // 如果不使用,则销毁
MyBPAsyncActionNode->SetReadyToDestroy();
UE_LOG(LogTemp, Warning, TEXT(__FUNCTION__" Countdown Completed."));
}
else
{
int32 length = AsyncTickActor->CountdownNums.Num();
AsyncTickActor->CountdownNums.Add(length + 1);
UE_LOG(LogTemp, Warning, TEXT(__FUNCTION__" Counting down... %d"), length + 1);
}
}
else
{
// OnFailed 输出节点
MyBPAsyncActionNode->OnFailed.Broadcast(-1);
WorldContextObject->GetWorld()->GetTimerManager().ClearTimer(MyBPAsyncActionNode->TimerHandle);
UE_LOG(LogTemp, Warning, TEXT(__FUNCTION__" Invalid AsyncTickActor."));
FFrame::KismetExecutionMessage(TEXT("Invalid AsyncTickActor"), ELogVerbosity::Error);
}
}; // 设置定时器并开始
WorldContextObject->GetWorld()->GetTimerManager().SetTimer(MyBPAsyncActionNode->TimerHandle, FTimerDelegate::CreateLambda(CountdownFunc), 1.0f, true);
UE_LOG(LogTemp, Warning, TEXT(__FUNCTION__" Countdown Starting."));
return MyBPAsyncActionNode;
}
【UE4 C++】Tick的三种方式、异步蓝图节点的更多相关文章
- [UE4]C++创建对象的三种方式
#include <iostream> using namespace std; class A { private: int n; public: A(int m):n(m) { } ~ ...
- [UE4]更新UI的三种方式
一.函数绑定 二.属性绑定 只会列出匹配的数据类型. 三.事件驱动更新 啦啦啦啦啦 结论:函数和属性绑定的原理都是每帧都去调用绑定的函数/属性,效率比较低下,一般不推荐使用.事件驱动更新的效率最好,性 ...
- js异步执行 按需加载 三种方式
js异步执行 按需加载 三种方式 第一种:函数引用 将所需加载方法放在匿名函数中传入 //第一种 函数引用 function loadScript(url,callback){ //创建一个js va ...
- 0036 Java学习笔记-多线程-创建线程的三种方式
创建线程 创建线程的三种方式: 继承java.lang.Thread 实现java.lang.Runnable接口 实现java.util.concurrent.Callable接口 所有的线程对象都 ...
- Linux就这个范儿 第15章 七种武器 linux 同步IO: sync、fsync与fdatasync Linux中的内存大页面huge page/large page David Cutler Linux读写内存数据的三种方式
Linux就这个范儿 第15章 七种武器 linux 同步IO: sync.fsync与fdatasync Linux中的内存大页面huge page/large page David Cut ...
- Kafka生产者发送消息的三种方式
Kafka是一种分布式的基于发布/订阅的消息系统,它的高吞吐量.灵活的offset是其它消息系统所没有的. Kafka发送消息主要有三种方式: 1.发送并忘记 2.同步发送 3.异步发送+回调函数 下 ...
- Android录制音频的三种方式
对于录制音频,Android系统就都自带了一个小小的应用,可是使用起来可能不是特别的灵活.所以有提供了另外的俩种. 下边来介绍下这三种录制的方式; 1.通过Intent调用系统的录音器功能,然后在录制 ...
- python 全栈开发,Day94(Promise,箭头函数,Django REST framework,生成json数据三种方式,serializers,Postman使用,外部python脚本调用django)
昨日内容回顾 1. 内容回顾 1. VueX VueX分三部分 1. state 2. mutations 3. actions 存放数据 修改数据的唯一方式 异步操作 修改state中数据的步骤: ...
- 根据服务端生成的WSDL文件创建客户端支持代码的三种方式
第一种:使用wsimport是JDK自带的工具,来生成 生成java客户端代码常使用的命令参数说明: 参数 说明 -p 定义客户端生成类的包名称 -s 指定客户端执行类的源文件存放目录 -d 指定客户 ...
随机推荐
- ELK学习之Logstash篇
Logstash在ELK这一整套解决方案中作为数据采集终端,支持对接Kafka.数据库(MySQL.Oracle).文件等等. 而在Logstash内部的数据流转,主要经过三个环节:input -&g ...
- ubuntu下使用minicom
环境 宿主机平台:Ubuntu 16.04.6 目标机:iMX6ULL 安装及使用 首先时在Ubuntu里安装minicom sudo apt-get install minicom 接下来可以使用 ...
- TCP可靠传输原理
停止等待协议 "停止等待"就是发送方在发送完一个分组后停止发送,等待接收方的确认后再继续发送. 超时重传 发送方在等待一定时间后如果还没有收到接收方的确认,此时发送方将认定分组没有 ...
- Hearthbuddy跳过ConfigurationWindow窗口
Hearthbuddy版本为按照上一条博客修复后的版本. 打开Hearthbuddy后会弹出一个这样的窗口: 这个界面没有什么用,而且也没有人对此进行任何修改. 由于之前折腾版早就已经把这个界面跳过了 ...
- SpringAOP-动态代理,日志注入
SpringAOP 前言: 1.AOP定义? 用来干啥的? 怎么用?(怎么跑通它的思路) 代理模式 为啥要学代理模式? -- 因为是SpringAop的底层 原有的代码不敢动,一动就是Bug,.所以使 ...
- 测试开发【提测平台】分享10-Element UI抽屉和表单校验&增改接口合并实现应用管理
微信搜索[大奇测试开],关注这个坚持分享测试开发干货的家伙. 开篇说个小讨论,一个群里聊天聊到关于更新篇章的长度,是小篇幅多次,还是每次按照一个小完整的功能,我个人的是按照后种来的,主要的思考就是希望 ...
- Docker系列(27)- 容器互联--link
思考 思考一个场景,我们编写了一个微服务,database url=IP:,项目不重启,数据库ip换掉了,我们希望可以处理这个问题,可以使用名字来进行访问容器吗 实践 [root@localhost ...
- [源码解析] PyTorch 流水线并行实现 (3)--切分数据和运行时系统
[源码解析] PyTorch 流水线并行实现 (3)--切分数据和运行时系统 目录 [源码解析] PyTorch 流水线并行实现 (3)--切分数据和运行时系统 0x00 摘要 0x01 分割小批次 ...
- 鸿蒙内核源码分析(用栈方式篇) | 程序运行场地谁提供的 | 百篇博客分析OpenHarmony源码 | v20.04
百篇博客系列篇.本篇为: v20.xx 鸿蒙内核源码分析(用栈方式篇) | 程序运行场地谁提供的 | 51.c.h .o 精读内核源码就绕不过汇编语言,鸿蒙内核有6个汇编文件,读不懂它们就真的很难理解 ...
- Javascript 常见的高阶函数
高阶函数,英文叫 Higher Order function.一个函数可以接收另外一个函数作为参数,这种函数就叫做高阶函数. 示例: function add(x, y, f) { return f( ...