当前位置: 首页 > article >正文

【UE5 C++课程系列笔记】15——Assert的基本使用

目录

概念

一、Check

二、Verify

三、Ensure

对比

基本使用

一、check的基本使用 

二、ensure的基本使用

三、verify的基本使用


概念

        assert 可在开发期间帮助检测和诊断不正常或无效的运行时条件。这些条件通常检查是否指针为非空、除数为非零、函数并非递归运行,或代码要求的其他重要假设。但每次检查会使得效率十分低下。某些情况下,assert 会在延迟崩溃发生之前发现导致该崩溃的bug,例如删除未来tick所需的对象,协助开发人员发现引起崩溃的根本原因。assert 的关键特性之一是不存在于发布代码中,这意味着不但不会影响发布产品的性能,也没有任何副作用。对 assert 最简单的理解就是:"断言"必须一律为true,否则程序会停止运行。

        虚幻引擎提供 assert 等同项的三个不同族系:checkverify 和 ensure。若要检查这些功能背后的代码,可在 Engine/Source/Runtime/Core/Public/Misc/AssertionMacros.h 中找到相关的宏。各个功能的行为略有不同,但它们都是开发期间使用的诊断工具,目标大致相同。

一、Check

        Check族系最接近基础 assert,因为当第一个参数得出的值为false时,此族系的成员会停止执行,且默认不会在发布版本中运行。以下Check宏可用:

参数行为
check 或 checkSlowExpression若 Expression 为false,停止执行
checkf 或 checkfSlowExpressionFormattedText...若 Expression 为false,则停止执行并将 FormattedText 输出到日志
checkCodeCode在运行一次的do-while循环结构中执行 Code;主要用于准备另一个Check所需的信息
checkNoEntry(无)若此行被hit,则停止执行,类似于 check(false),但主要用于应不可到达的代码路径
checkNoReentry(无)若此行被hit超过一次,则停止执行
checkNoRecursion(无)若此行被hit超过一次而未离开作用域,则停止执行
unimplemented(无)若此行被hit,则停止执行,类似于 check(false),但主要用于应被覆盖而不会被调用的虚拟函数

        Check宏在调试(Debug)、开发(Development)、测试(Test)和发布编辑器(Shipping Editor)版本中运行(以"Slow"结尾的宏除外,其仅在调试(Debug)版本中运行)。定义 USE_CHECKS_IN_SHIPPING 以保留一个true值(通常为 1),使Check宏可在所有版本中运行。此法在以下情况中十分实用:怀疑Check宏中的代码正在修改值;发现了仅存在于在发布版本中且难以追踪的bug,但认为现有Check宏能找到这些bug。项目发布时应将 USE_CHECKS_IN_SHIPPING 设为默认值 0。 

二、Verify

        在大部分版本中,Verify族系的行为与Check族系相同。但即便在禁用Check宏的版本中,Verify宏也会计算其表达式的值。这意味着仅当该表达式需要独立于诊断检查之外运行时,才应使用Verify宏。举例而言,若某个函数执行操作,然后返回 bool 来说明该操作是否成功,则应使用Verify而非Check来确保该操作成功。因为在发布版本中Verify将忽略返回值,但仍将执行操作。而Check在发布版本中根本不调用该函数,所以行为才会有所不同。

参数行为
verify 或 verifySlowExpression若 Expression 为false,停止执行
verify 或 verifyfSlowExpressionFormattedText...若 Expression 为false,则停止执行并将 FormattedText 输出到日志

        Verify宏在调试(Debug)、开发(Development)、测试(Test)和发布编辑器(Shipping Editor)版本中完整运行(以"Slow"结尾的宏除外,其仅在调试(Debug)版本中运行)。定义 USE_CHECKS_IN_SHIPPING 来保留一个true值(通常为 1),从而覆盖此行为。在所有其他情况下,Verify宏将计算其表达式,但不会停止执行或将文本输出到日志。

三、Ensure

        Ensure族系类似于Verify族系,但可在出现非致命错误时使用。这意味着,若Ensure宏的表达式计算得出的值为false,引擎将通知崩溃报告器,但仍会继续运行。为避免崩溃报告器收到太多通知,Ensure宏在每次引擎或编辑器会话中仅报告一次。若实际情况需要Ensure宏在每次表达式计算得值为false时都报告一次,则使用"Always"版本的宏。

参数行为
ensureExpressionExpression 首次为false时通知崩溃报告器
ensureMsgfExpressionFormattedText...Expression 首次为false时通知崩溃报告器并将 FormattedText 输出到日志
ensureAlwaysExpressionExpression 为false时通知崩溃报告器
ensureAlwaysMsgfExpressionFormattedText...Expression 为false时通知崩溃报告器并将 FormattedText 输出到日志

        Ensure宏在所有版本中计算其表达式的值,但仅在调试(Debug)、开发(Development)、测试(Test)和发布编辑器(Shipping Editor)版本中联系崩溃报告器。

对比

宏名称调试版本行为发行版本行为侧重点及适用场景
check条件为 false 时触发断言失败,程序暂停,输出详细错误信息,方便开发者定位问题通常被移除,不进行条件检查,不影响最终产品性能主要用于开发调试阶段,验证代码逻辑假设条件,查找潜在的代码错误
verify同 check,条件为 false 时触发断言失败,程序暂停,输出错误信息保留条件检查,触发断言失败后处理方式与调试版本不同(通常不会暂停程序),需开发者提前规划错误处理机制侧重于对关键条件在发行版本中也进行持续监控,保障游戏稳定性,适用于如资源加载、关键配置参数验证等重要情况
ensure条件为 false 时触发断言失败,尝试采取相对 “温和” 的措施处理错误,如提示信息、尝试恢复操作等,程序不一定暂停类似调试版本,会尝试采取合适的措施应对错误情况,注重用户体验和游戏的可恢复性从用户体验和程序健壮性角度出发,适用于可能出现异常但希望游戏尽量 “容错” 继续运行的情况,如网络通信、外部设备连接等易受影响的功能模块相关验证

基本使用

一、check的基本使用 

1. 新建一个Actor类,这里命名为“AssertActor”。在“AssertActor.h”中定义一个名为“AttackEnemey”的方法,传入一个Actor类型的引用

再定义一个属性值

在“AssertActor.cpp”中实现“AttackEnemey”函数

2. 创建派生自“AssertActor”的蓝图类“BP_AssertActor”

将“BP_AssertActor”拖入视口

3. 在关卡蓝图中通过按键调用“AttackEnemey”函数

如果“AttackEnemey”函数传入了一个空指针,如下图。此时调用“AttackEnemey”函数,编辑器会崩溃

二、ensure的基本使用

在“AssertActor”中新实现一个“AttackEnemey2”函数,如下图。这里使用“ensure”

在关卡蓝图中通过1键调用“AttackEnemy2”函数

运行后,按下1键,可以看到虽然还是会报错,但是当Continue后编辑器并不会崩溃

三、verify的基本使用

在“AssertActor”中定义修改法力值的函数“ModifyMana”,定义验证法力值的函数“VerifyMana”,设置法力值“Mana”初始大小为50

实现函数“ModifyMana”和“VerifyMana”如下。每次调用“ModifyMana”时将法力值减20。每次调用“VerifyMana”时先调用“ModifyMana”,然后判断“ModifyMana”的返回结果是否大于0

在关卡蓝图中调用“VerifyMana”

调用3次“VerifyMana”后会出现如下错误,但是Continue后还是可以继续执行后续逻辑,不会造成编辑器崩溃。

 

“AssertActor”完整代码:

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

#pragma once

#include "CoreMinimal.h"
#include "GameFramework/Actor.h"
#include "AssertActor.generated.h"

UCLASS()
class STUDY_API AAssertActor : public AActor
{
	GENERATED_BODY()
	
public:	
	// Sets default values for this actor's properties
	AAssertActor();

	UFUNCTION(BlueprintCallable)
	void AttackEnemey(AActor* InActor);

	UFUNCTION(BlueprintCallable)
	void AttackEnemey2(AActor* InActor);

	UFUNCTION(BlueprintCallable)
	void VerifyMana();

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

	int32 ModifyMana();

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

protected:
	int32 Health = 100;

	int32 Mana = 50;
};
// Fill out your copyright notice in the Description page of Project Settings.


#include "Assert/AssertActor.h"

// Sets default values
AAssertActor::AAssertActor()
{
 	// 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;

}

void AAssertActor::AttackEnemey(AActor* InActor)
{
	check(InActor != nullptr);

	AAssertActor* MyAssertActor = CastChecked<AAssertActor>(InActor);
	MyAssertActor->Health -= 10;
}

void AAssertActor::AttackEnemey2(AActor* InActor)
{
	ensure(InActor != nullptr);

	AAssertActor* MyAssertActor = Cast<AAssertActor>(InActor);
	if (MyAssertActor)
	{
		MyAssertActor->Health -= 10;
	}
	else
	{
		UE_LOG(LogTemp, Error, TEXT("转型失败了!!!"))
	}
}

void AAssertActor::VerifyMana()
{
	verify(ModifyMana() > 0);
}

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

int32 AAssertActor::ModifyMana()
{
	Mana -= 20;
	return Mana;
}

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

官方文档地址:

https://dev.epicgames.com/documentation/zh-cn/unreal-engine/asserts-in-unreal-engine?application_version=5.3


http://www.kler.cn/a/460058.html

相关文章:

  • GDPU 数据库原理 期末复习
  • 电脑主机后置音频插孔无声?还得Realtek高清晰音频管理器调教
  • 【刷题日记】455.分发饼干
  • SpringBoot使用TraceId日志链路追踪
  • Java 集合框架之 List、Set 和 Map 的比较与使用
  • 解决tomcat双击startup.bat乱码的几种方法
  • vue3<script setup>中使用Swiper
  • 第八节:GLM-4v-9b模型的大语言模型源码解读(ChatGLMForConditionalGeneration)
  • windows C#-带有命名方法的委托与匿名方法
  • 基于springboot的校园新闻网站系统
  • [创业之路-225]:《华为闭环战略管理》-4-华为的商业智慧:在价值链中探索取舍之道与企业边界
  • WAP短信格式解析及在Linux下用C语言实现
  • 【Spring MVC 核心机制】核心组件和工作流程解析
  • 【OTA】论文学习笔记--《基于RTOS的车载ECU双分区OTA升级技术分析报告》
  • 3.阿里云flinkselectdb-py作业
  • 什么是微服务、微服务如何实现Eureka,网关是什么,nacos是什么
  • PyTorch快速入门教程【小土堆】之Sequential使用和小实战
  • 【RK3588 Linux 5.x 内核编程】-内核IO复用与select
  • 防火墙基础-工作原理
  • 爱思唯尔word模板
  • UE(虚幻)学习(二) 使用UnrealSharp插件让UE支持C#脚本
  • Harbor(2.3.0)的定制页面与安装(x86 arm)
  • 科龙空调:以创新科技,适配多元家居场景
  • 最短路径-Dijkstra 算法
  • 【记录】列表自动滚动轮播功能实现
  • 如何恢复永久删除的PPT文件?查看数据恢复教程!