Stanford UE4 + C++ Course Learning Record 12: Getting Started with Debug

Table of contents

1. Update the explosive barrel

2. Output debugging information

3. Using breakpoints

4. Using assertions

5. Complete code


        An important point was first mentioned in the course, that is, try not to use UE's hot reload, that is, to use its compilation function directly without closing UE after modifying and saving the C++ code. Because this function is not stable, it is more prone to bugs, even if the UE prompts that the compilation is complete, there may be errors (I have encountered it myself, and I have repeatedly checked my own code because of this bug...). Therefore, try to close UE after modifying the C++ code, and then use compilation in VS to open UE, so as to ensure that the code takes effect as much as possible.

        This section will show some debugging skills in UE based on the explosive keg implemented in the previous section 6.

1. Update the explosive barrel

        In the previous section 6, we have implemented the effect of the impact force of the explosive barrel after being hit. But in the previous section, we changed the blueprint of the magic particle and modified the configuration of the custom collision preset Projectile, so that the current explosive barrel can no longer be triggered by the magic particle. Therefore, before starting to debug related content, we need to make some changes to the explosive keg.

        The reason why the explosive barrel does not explode is because the OnActorHit function in SurExplosiveBarrel.cpp is not executed. In the content of Section 6, I mentioned that "I haven't understood the calling method of OnActorHit() in the code for the time being, and I will add it later", so I just learned at this place.

The description of this function         in the official documentation is slightly less, so start with the project source code provided by the course. After carefully reading the source code provided by the course, I learned that the OnActorHit function is automatically called by the UE in the form of the "Component Hit (OnComponentHit)" event (the event node OnComponentHit already exists in the UE blueprint, and the code passed in A bunch of inputs are actually pins of this node as well). Therefore, the functions we write in C++ need to be bound to this event to take effect. In the implementation of the previous section 6, I copied the complete course source code and ran it successfully, but for the purpose of streamlining knowledge points, I deleted the code while hot reloading, and the project did run normally at that time, no Know if this is a bug of UE hot reload...

        After supplementing the code of the binding event, the explosive barrel can be successfully triggered by the magic particle, see the end of the complete code. It should be noted here that the "simulated physics" of the explosive barrel is turned on, otherwise the explosive barrel will remain motionless in place; the "simulated hit event" of the explosive barrel is turned off, otherwise the explosive barrel will explode automatically as soon as it hits the ground, as shown in Figure 12-1:

Figure 12-1 Turn on "Simulation Generate Hit Events"

2. Output debugging information

        This part will introduce several methods of outputting debugging information in UE development, which will facilitate our debugging work during development in the future.

        First of all, the use of logs is an essential part of all kinds of computer development, and UE_LOG can be used to output logs in UE. The course provides an English wiki , which gives a more detailed introduction to the log. Example usage is as follows:

UE_LOG(LogTemp, Log, TEXT("OtherActor is %s, at game time %f"), *GetNameSafe(OtherActor), GetWorld()->TimeSeconds);

        The three inputs are:
        1. The category name displayed in the log, we can add different category names for the logs of different modules, so that we can use filters to quickly filter the required log messages; 2. The level of
        the log display , the higher the level is displayed The more important the content, the brighter the displayed color, and the fewer corresponding output messages;         3. For the output content, TEXT() is used to convert the string to the format required by UE, and use UNICODE encoding to support more symbols. GetNameSafe returns empty when the object is empty, so there is no need for additional empty judgment. In addition, it will return the FString type. Note that an asterisk must be added before the string type.

        For UE with a graphical interface, it can not only print debugging information in the log output, but also print various debugging information in real time directly when running the level, such as the ray detection and magic attack we used when opening the treasure chest. the circle displayed when In addition to directly displaying graphics, UE also supports displaying character strings. This code is implemented, displaying a string of position information where the magic particle hits:

FString CombStr = FString::Printf(TEXT("Hit at %s"), *Hit.ImpactPoint.ToString());
// 获取世界,位置,打印的内容,需要attach的actor,颜色,持续时间,是否有影子
DrawDebugString(GetWorld(), Hit.ImpactPoint, CombStr, nullptr, FColor::Green, 2.0f, true);

        In this way, after saving and compiling and entering the level, you can see our debugging information, and its window can be selected to open in Window->Developer Tools->Output Log. After left-clicking to control the Gideon attack, you can see that the LogTemp we set is successfully printed out.

Figure 12-2 UE output logs

        If the UE is opened with debugging in VS, the same log message will also be output to the output window of VS:

Figure 12-3 VS output log

         In the game, the string printed by DrawDebugString is also displayed normally:

Figure 12-4 DrawDebugString effect

3. Using breakpoints

        Friends who have experience in program development should be familiar with breakpoints, and breakpoint debugging is a great tool for Debug. By using breakpoints to stop the program at a certain place, and then execute it step by step under the monitoring of the developer, this is very helpful for finding program bugs and sorting out the program running process. There are two kinds of breakpoints developed by UE, one is to use VS breakpoints in general, and the other is to break points directly in the blueprint system.

        The method of breaking the breakpoint in VS is very simple. Click before any line of code that needs to be interrupted, and a red circle will be displayed. Here I choose to set a breakpoint at the code that controls the explosion of the explosive barrel.

Figure 12-5 VS sets breakpoints

        Then click Debug in VS (or directly press F5), run the level, and launch magic particles to the explosive barrel. At this time, it will directly jump back to the VS interface, and a small yellow arrow will be displayed at the breakpoint to indicate the location of code execution. At this time, various debugging operations can be performed. If you return to the UE interface at this time, you will find that the entire UE is stuck, and it will not continue to run until we click Continue in VS. In addition, in order to debug the code that steps into UE more conveniently, we can choose to download "Input symbols for debugging" in the "Options" of the corresponding version of the engine in Epic.

        There is another noteworthy point about VS debugging. If a breakpoint is hit but not triggered, it may be because C++ automatically optimizes the code. At this time, you can try to set the "Solution Configuration" of VS (in the Debug button Next) switch from "Development Editor" to "DebugGame Editor", this will prevent code auto-optimization, output more detailed test information, but will also increase compilation time. The techniques and methods of VS debugging do not belong to the content of UE development, and you can understand it by yourself.

Figure 12-6 VS runs to a breakpoint

        The way to set a breakpoint in the blueprint is also simple. After opening the blueprint, right click on any node and select "Add Breakpoint". After running, the game will also stay at the breakpoint, and a series of control buttons for debugging will appear above the blueprint editor. Unlike the VS break point, we can see the flow of the program in real time in the blueprint, which has been introduced in the previous article on optimizing the treasure chest animation in Section 10.


4. Using assertions

        Assertion is also widely used in the Debug stage. It is similar to if judgment to verify the truth of an expression, but the difference is that assertion does not need to write a large number of repeated judgments and print log codes like if. Once the assertion check does not meet expectations , the program will directly interrupt or even throw an exception, which is helpful for developers to locate the problem; and the assertion is only valid in the debug phase, and will not take effect for the packaged program.

        Taking the role's PrimaryAttack as an example, first add an empty logic to the PrimaryAttack_TimeElapsed function, which is entirely out of the correct consideration of improving the robustness of the program:

void ASurCharacter::PrimaryAttack_TimeElapsed() {
	if (ProjectileClass) {
		// 获取模型右手位置
		FVector RightHandLoc = GetMesh()->GetSocketLocation("Muzzle_01");

		// 朝向角色方向,在角色的右手位置生成
		FTransform SpawnTM = FTransform(GetActorRotation(), RightHandLoc);

		// 此处设置碰撞检测规则为:即使碰撞也总是生成
		FActorSpawnParameters SpawnParams;
		SpawnParams.SpawnCollisionHandlingOverride = ESpawnActorCollisionHandlingMethod::AlwaysSpawn;
		SpawnParams.Instigator = this;

		GetWorld()->SpawnActor<AActor>(ProjectileClass, SpawnTM, SpawnParams);
	}
}

        But this way of writing may cause problems like: long after this code has been developed, you have forgotten the specific details, or this was developed by another colleague of yours. Occasionally, the ProjectileClass is empty when this function is called, and this code will not run at this time, but the program will not give any prompts, which will undoubtedly increase the workload of debugging.

        To solve this problem, one way is to write wrong output information in else, and the other is to use UE's assertion. Just change the if to if (ensure(ProjectileClass)), ensure is the assertion function in UE. There is also check, but the difference between the two is that ensure prompts an error but does not interrupt the program, and check will directly end your level test, so ensure is usually used.

        In order to test the effect of ensure, we manually specify that the ProjectileClass of the Player blueprint class is empty in UE, as shown in Figure 12-7:

Figure 12-7 Player settings

        After running the level, once the left button is attacked, the UE will get stuck and output a red Error log message. If the UE is debugged and opened in VS, it will directly jump back to VS to display errors. In addition, ensure will only trigger the prompt once after the compilation is completed. If you want to be prompted every time an error occurs, you can select ensureAlways.


5. Complete code

AboutExplosiveBarrel.h

#pragma once
#include "CoreMinimal.h"
#include "GameFramework/Actor.h"
#include "SurExplosiveBarrel.generated.h"

class UStaticMeshComponent;
class URadialForceComponent;

UCLASS()
class SURKEAUE_API ASurExplosiveBarrel : public AActor
{
	GENERATED_BODY()
	
public:	
	
	ASurExplosiveBarrel();

protected:

	UPROPERTY(VisibleAnywhere)
	UStaticMeshComponent* MeshComp;

	UPROPERTY(VisibleAnywhere)
	URadialForceComponent* ForceComp;

	virtual void PostInitializeComponents() override;

	// 必须使用UFUNCTION宏才能绑定事件
	UFUNCTION()
	void OnActorHit(UPrimitiveComponent* HitComp, AActor* OtherActor, UPrimitiveComponent* OtherComp, FVector NormalImpulse, const FHitResult& Hit);

};

AboutExplosiveBarrel.cpp

#include "SurExplosiveBarrel.h"
#include "PhysicsEngine/RadialForceComponent.h"
#include "Components/StaticMeshComponent.h"
#include "DrawDebugHelpers.h"

// Sets default values
ASurExplosiveBarrel::ASurExplosiveBarrel()
{
 
	MeshComp = CreateDefaultSubobject<UStaticMeshComponent>("MeshComp");
	// UE中的“模拟物理”选项
	MeshComp->SetSimulatePhysics(true);
	// 等同于在UE中将“碰撞预设”设置为“PhysicsActor”
	MeshComp->SetCollisionProfileName(UCollisionProfile::PhysicsActor_ProfileName);
	RootComponent = MeshComp;

	ForceComp = CreateDefaultSubobject<URadialForceComponent>("ForceComp");
	ForceComp->SetupAttachment(MeshComp);

	ForceComp->Radius = 750.0f;			 // 爆炸范围
	ForceComp->ImpulseStrength = 700.0f; // 冲击力
	ForceComp->bImpulseVelChange = true; // 忽略质量大小;见UE中ForceComp的“冲量速度变更”
}

// PostInitializeComponents在Actor初始化完毕后再调用
void ASurExplosiveBarrel::PostInitializeComponents()
{
	// 执行该函数原本的功能
	Super::PostInitializeComponents();
	// 绑定到OnComponentHit事件上
	MeshComp->OnComponentHit.AddDynamic(this, &ASurExplosiveBarrel::OnActorHit);
}

void ASurExplosiveBarrel::OnActorHit(UPrimitiveComponent* HitComp, AActor* OtherActor, UPrimitiveComponent* OtherComp, FVector NormalImpulse, const FHitResult& Hit)
{
	ForceComp->FireImpulse();

	// log信息的category,log/warning/error等表示日志的详细程度,打印的文字内容

	UE_LOG(LogTemp, Log, TEXT("OtherActor is %s, at game time %f"), *GetNameSafe(OtherActor), GetWorld()->TimeSeconds);
	UE_LOG(LogTemp, Warning, TEXT("HHHHHHHHHHHHH"));

	FString CombStr = FString::Printf(TEXT("Hit at %s"), *Hit.ImpactPoint.ToString());
	// 获取世界,位置,打印的内容,需要attach的actor,颜色,持续时间,是否有影子
	DrawDebugString(GetWorld(), Hit.ImpactPoint, CombStr, nullptr, FColor::Green, 2.0f, true);
}

Guess you like

Origin blog.csdn.net/surkea/article/details/127178815