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

【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 propertiesAAssertActor();UFUNCTION(BlueprintCallable)void AttackEnemey(AActor* InActor);UFUNCTION(BlueprintCallable)void AttackEnemey2(AActor* InActor);UFUNCTION(BlueprintCallable)void VerifyMana();protected:// Called when the game starts or when spawnedvirtual void BeginPlay() override;int32 ModifyMana();public:	// Called every framevirtual 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.lryc.cn/news/512652.html

相关文章:

  • kubernetes Gateway API-1-部署和基础配置
  • likeAdmin架构部署(踩坑后的部署流程
  • 【一款超好用的开源笔记Logseq本地Docker部署与远程使用指南】
  • 浅谈torch.utils.data.TensorDataset和torch.utils.data.DataLoader
  • gesp(C++二级)(16)洛谷:B4037:[GESP202409 二级] 小杨的 N 字矩阵
  • FFmpeg:详细安装教程与环境配置指南
  • 《特征工程:自动化浪潮下的坚守与变革》
  • webrtc 源码阅读 make_ref_counted模板函数用法
  • 【深度学习基础之多尺度特征提取】特征金字塔(Feature Pyramid)是如何在深度学习网络中提取多尺度特征的?附代码
  • 【Docker】离线安装 Docker
  • 三大行业案例:AI大模型+Agent实践全景
  • Dockerfile基础指令
  • 12.30 linux 文件操作,磁盘分区挂载
  • [图形渲染]【Unity Shader】【游戏开发】 Shader数学基础17-法线变换基础与应用
  • YOLOv9-0.1部分代码阅读笔记-train.py
  • 等保测评和密评的相关性和区别
  • 活动预告 |【Part2】 Azure 在线技术公开课:迁移和保护 Windows Server 和 SQL Server 工作负载
  • 大语言模型(LLM)一般训练过程
  • 单片机的基本组成
  • GO性能优化的一些记录:trace工具的使用
  • dede-cms关于shell漏洞
  • NAT 技术如何解决 IP 地址短缺问题?
  • 使用 IDE生成 Java Doc
  • 通过无障碍服务(AccessibilityService)实现Android设备全局水印显示
  • flask后端开发(2):URL与视图
  • 力扣-数据结构-7【算法学习day.78】
  • 【潜意识Java】Java中深入解析抽象类与接口的差异的完整笔记总结【保姆级详细教程】
  • 【Flink运行时架构】系统构架
  • uni-ui样式修改
  • 【linux板卡】lubancat通过vnc远程访问桌面