UE4 不受游戏暂停影响的Timer /Delay C++蓝图都能用

发现了一个比较正宗的写法!!!!!!!!

https://blog.csdn.net/Bob__yuan/article/details/109689234 (3.2的那条)
通过上面的博主的方法,实现一个actor私有的timermanager,对该actor执行SetTickableWhenPaused(true),这就非常完美

我是真他娘的蠢驴

以下都是错误示范 不需要在意 直接看上面超链

UE4 不受游戏暂停影响的Timer /Delay C++蓝图都能用

原因

业务逻辑需要,以及因为序列帧实现的ui动图是通过tick刷的,暂停时ui tick会暂停,只能通过写一个不受暂停影响的timer去刷序列帧,为了填一个坑,而出来的另一个坑,大家都是挖坑人。

基本原理

重点就是 actorSetTickableWhenPaused(true),就是标记actor在游戏
暂停时,是否能够执行Tick(float DeltaTime)

运用了什么知识

通过FName调用蓝图方法,
C++通过委托绑定方法进行回调
蓝图异步节点(UBlueprintAsyncActionBase)

效果图

地图蓝图上写的逻辑,基本的使用方法
在这里插入图片描述

使用时必须创建一个SuperTimer对象
可以看到SetGamePause true后,我的delay没有停止,并执行了我的timer并再刷计时任务

源码 SuperTimer.h demo地址:

>下载链接: https://pan.baidu.com/s/1P4iWV87tiU94VpAlqrnm1A 提取码: j9q9

// Fill out your copyright notice in the Description page of Project Settings.

#pragma once

#include "CoreMinimal.h"
#include "TimerManager.h"
#include "Kismet/BlueprintAsyncActionBase.h"
#include "GameFramework/Actor.h"
#include "SuperTimer.generated.h"

DECLARE_DYNAMIC_MULTICAST_DELEGATE(FSuperDelayDelegate);

USTRUCT(BlueprintType)
struct FSuperTimerHandler
{
	GENERATED_USTRUCT_BODY()

	UPROPERTY(BlueprintReadWrite,EditAnywhere)
	FName FunctionName;
	UPROPERTY(BlueprintReadWrite, EditAnywhere)
	bool Loop;
	UPROPERTY(BlueprintReadWrite, EditAnywhere)
	UObject* object;
	UPROPERTY(BlueprintReadWrite, EditAnywhere)
	float time;

	bool bEnable = true;

	int type = 0; //0 是蓝图 1 是c++

	FTimerUnifiedDelegate* delegateFunc;

	friend inline bool operator==(const FSuperTimerHandler& A, const FSuperTimerHandler& B)
	{
		return A.FunctionName == B.FunctionName && A.Loop == B.Loop && A.object == B.object && A.time == B.time;
	}

	friend inline uint32 GetTypeHash(const FSuperTimerHandler& Key)
	{
		uint32 Hash = 0;

		Hash = HashCombine(Hash, GetTypeHash(Key.FunctionName));
		Hash = HashCombine(Hash, GetTypeHash(Key.Loop));
		Hash = HashCombine(Hash, GetTypeHash(Key.object));
		Hash = HashCombine(Hash, GetTypeHash(Key.time));
		return Hash;
	}
	//TODO :做一个直接通过handler对象实现清理的方法,不需要调用SuperTimer对象的方法
	//就像蓝图里面的 ClearAndInvalidateTimerHandle
	//
	//inline void ClearThisTimer()
	//{
	//	FSuperTimerHandler tmp = this->StaticStruct;
	//	ASuperTimer::GetSuperTimer()->RemoveTimer(tmp);
	//}
};

UCLASS()
class MYP_API ASuperTimer : public AActor
{
	GENERATED_BODY()

public:
	// Sets default values for this actor's properties
	ASuperTimer();
	UFUNCTION(BlueprintPure)
	static ASuperTimer* GetSuperTimer();

	UFUNCTION(BlueprintCallable,meta =(DefaultToSelf = "object"))
	static FSuperTimerHandler SetSuperTimer(FName FuncName, UObject* object, bool loop, float time);
	UFUNCTION(BlueprintCallable)
	void ClearAllSuperTimer()
	{
		// TimerList.Empty();
		for (auto& timer : TimerList)
		{
			timer.Key.bEnable = false;
		}
		UE_LOG(LogTemp, Warning, TEXT("Timerlist clear ,now count:%d"), TimerList.Num())
	}

	UFUNCTION(BlueprintCallable,meta =(DefaultToSelf = "object"))
	void ClearSuperTimer(FName FuncName, UObject* object);


	FSuperTimerHandler InternalSetSuperTimer(FTimerUnifiedDelegate* exefun, bool loop, float time);

	//c++创建Timer,仿照ue4c++timer写的一套,直接把 ue4的 FTimerUnifiedDelegate给搬过来了
	//其实虚幻源码就是通过创建delegate,时间一到直接执行delegate
	template <class UserClass>
	FORCEINLINE FSuperTimerHandler SetSuperTimerDelegate(UserClass* InObj,
	                                                     typename FTimerDelegate::TUObjectMethodDelegate<UserClass
	                                                     >::FMethodPtr InTimerMethod, bool loop, float time)
	{
		FTimerUnifiedDelegate* TimerDelegateFunc = new FTimerUnifiedDelegate(
			FTimerDelegate::CreateUObject(InObj, InTimerMethod));
		FSuperTimerHandler val = InternalSetSuperTimer(TimerDelegateFunc, loop, time);
		return val;
	};
	//c++清理timer
	void RemoveTimer(FSuperTimerHandler timer)
	{
		for (auto& localTimer : TimerList)
		{
			if (localTimer.Key == timer)
			{
				localTimer.Key.bEnable = false;
			}
		}
	}

protected:
	// Called when the game starts or when spawned
	virtual void BeginPlay() override;

public:
	// Called every frame
	virtual void Tick(float DeltaTime) override;

protected:
	TMap<FSuperTimerHandler, float> TimerList;
};

UCLASS()
class MYP_API USuperDelay : public UBlueprintAsyncActionBase
{
	GENERATED_BODY()

	UPROPERTY(BlueprintAssignable)
	FSuperDelayDelegate Completed;

	UFUNCTION(BlueprintCallable, meta = (BlueprintInternalUseOnly = "true", WorldContext = "WorldContextObject", Delay =
		"0.5"))
	static USuperDelay* SuperDelay(UObject* WorldContextObject, float DelayTime);

protected:
	 void  DelayCallBack()
	{
		if (Completed.IsBound())
		{
			Completed.Broadcast();
		}
	}
};

SuperTimer.cpp

// Fill out your copyright notice in the Description page of Project Settings.


#include "SuperTimer.h"

static ASuperTimer* SuperTimerInstance = nullptr;
// Sets default values
ASuperTimer::ASuperTimer()
{
	// 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;
	this->SetTickableWhenPaused(true);
	SuperTimerInstance = this;
}

ASuperTimer* ASuperTimer::GetSuperTimer()
{
	if(!SuperTimerInstance)
	{
		UE_LOG(LogTemp,Warning,TEXT("Supertimer Actor not Vaild"));
	}
	return SuperTimerInstance;
}

FSuperTimerHandler ASuperTimer::SetSuperTimer(FName FuncName, UObject* object, bool loop, float time)
{
	FSuperTimerHandler _handler;
	_handler.FunctionName = FuncName;
	_handler.Loop = loop;
	_handler.object = object;
	_handler.time = time;
	_handler.bEnable = true;
	_handler.type = 0;
	ASuperTimer::GetSuperTimer()->TimerList.Add(_handler, time);
	return _handler;
}

void ASuperTimer::ClearSuperTimer(FName FuncName, UObject* object)
{
	TArray<FSuperTimerHandler> TempAllTimer;
	TimerList.GetKeys(TempAllTimer);
	for (auto timer : TempAllTimer)
	{
		if (timer.FunctionName == FuncName && timer.object == object)
		{
			TimerList.Remove(timer);
			break;
		}
	}
}

FSuperTimerHandler ASuperTimer::InternalSetSuperTimer(FTimerUnifiedDelegate* exefun, bool loop, float time)
{
	FSuperTimerHandler _handler;
	_handler.Loop = loop;
	_handler.time = time;
	_handler.bEnable = true;
	_handler.type = 1;
	_handler.delegateFunc = exefun;
	TimerList.Add(_handler, time);
	return _handler;
}


// Called when the game starts or when spawned
void ASuperTimer::BeginPlay()
{
	Super::BeginPlay();
}

// Called every frame
void ASuperTimer::Tick(float DeltaTime)
{
	Super::Tick(DeltaTime);

	TArray<FSuperTimerHandler> AllTimerArray;
	TimerList.GetKeys(AllTimerArray);
	for (FSuperTimerHandler timer : AllTimerArray)
	{
		if (!timer.bEnable)
		{
			TimerList.Remove(timer);
			continue;
		}
		TimerList[timer] -= DeltaTime;
		if (TimerList[timer] <= 0.0f)
		{
			if (timer.type == 0)
			{
				if (!timer.object)
				{
					TimerList.Remove(timer);
					continue;
				}
				UFunction* fun = timer.object->FindFunction(timer.FunctionName);
				if (!fun)
				{
					TimerList.Remove(timer);
					continue;
				}
				if (timer.Loop)
				{
					TimerList[timer] = timer.time;
				}
				else
				{
					TimerList.Remove(timer);
				}
				timer.object->ProcessEvent(fun, NULL);
				UE_LOG(LogTemp, Warning, TEXT("Super Timer Start Calling BP %s ,Func %s"), *timer.object->GetName(),
				       *timer.FunctionName.ToString());
			}
			else
			{
				if (timer.Loop)
				{
					TimerList[timer] = timer.time;
				}
				else
				{
					TimerList.Remove(timer);
				}
				timer.delegateFunc->Execute();
				// UE_LOG(LogTemp, Warning, TEXT("Super Timer Start Calling c++ Func "));
			}
		}
	}
}

USuperDelay* USuperDelay::SuperDelay(UObject* WorldContextObject, float DelayTime) 
{
	USuperDelay* SuperDelayNode = NewObject<USuperDelay>();
	ASuperTimer::GetSuperTimer()->SetSuperTimerDelegate(SuperDelayNode, &USuperDelay::DelayCallBack, false, DelayTime);
	return SuperDelayNode;
}

使用时得把MYP_API 改成自己项目的 API
C++的使用下次有空在更新吧
demo奉上
下载链接: https://pan.baidu.com/s/1P4iWV87tiU94VpAlqrnm1A 提取码: j9q9

  • 4
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值