UE5俯视角游戏案例代码查看

2024-04-07 21:12

本文主要是介绍UE5俯视角游戏案例代码查看,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

对于初学者来说,UE的项目案例是我们入手的最佳途径,首先代码量少,思路清晰,还能给你提供一个清晰的结构。所以,我创建了一个俯视角的官方案例,来查看一下官方的代码学习一下。

首先打开引擎,启动引擎
在这里插入图片描述
然后创建一个示例,这是ue自带的案例
在这里插入图片描述
打开源代码,看到案例就几个文件
在这里插入图片描述

TopDownProject

这个项目名称我设置的是TopDownProject,TopDownProject.h和TopDownProject.cpp就是自动生成的项目主文件,它不需要我们自己创建,案例中里面这两个文件主要在里面增加了一个打印日志的宏,宏的第一个参数是定义了名称,可以不一样,但是相同的不需要定义两遍。这样方便后面查看哪里报错。

DECLARE_LOG_CATEGORY_EXTERN(LogTopDownProject, Log, All);
DEFINE_LOG_CATEGORY(LogTopDownProject)

定义完宏以后,使用,需要使用UE_LOG去打印,这里我将输入映射上下文设置为空指针,然后打印

	if(DefaultMappingContext == nullptr){UE_LOG(LogTemplateCharacter, Error, TEXT("当前操作映射上下文未设置"));}

可以看到在输出日志里面显示打印的内容
ELogVerbosity 枚举类型通常包含以下几个级别(具体级别可能因UE版本而异):

  • Verbose:最详细的日志级别,通常用于调试目的,包含大量的信息。
  • Log:常规日志级别,用于记录程序运行时的正常消息。
  • Warning:警告级别,用于记录可能导致问题的情况,但不一定是错误。
  • Error:错误级别,用于记录程序运行时遇到的严重问题或异常。
  • Display:用于显示给用户的信息,通常出现在用户界面上。
  • Fatal:致命错误级别,通常用于记录程序无法继续运行的情况。
    在这里插入图片描述

GameMode

案例重新创建了一个GameMode
在这里插入图片描述
它文件头设置了minimalapi,是为了加快编译,也无法作为蓝图父类,作为直接设置无法修改的类。

UCLASS(minimalapi)

在里面只是在构造函数内做了一些处理

public:ATopDownProjectGameMode();

因为无法在UE里面去修改内容,所以它在构造函数内,重新设置了默认Pawn类和玩家控制器类,可以通过上图看到。这里面也教给我们如何在C++里面去获取对应的蓝图的方法。你也可以看到官方开发人员也写的不标准,下面的NULL虽然也不会出错,但是推荐修改为nullptr(空指针)

ATopDownProjectGameMode::ATopDownProjectGameMode()
{// 使用我们自定义的 PlayerController classPlayerControllerClass = ATopDownProjectPlayerController::StaticClass();// 设置默认的控制Pawn为我们自定义的蓝图创建的Characterstatic ConstructorHelpers::FClassFinder<APawn> PlayerPawnBPClass(TEXT("/Game/TopDown/Blueprints/BP_TopDownCharacter"));if (PlayerPawnBPClass.Class != nullptr){DefaultPawnClass = PlayerPawnBPClass.Class;}// 设置控制器为我们创建蓝图PlayerControllerstatic ConstructorHelpers::FClassFinder<APlayerController> PlayerControllerBPClass(TEXT("/Game/TopDown/Blueprints/BP_TopDownPlayerController"));if(PlayerControllerBPClass.Class != NULL){PlayerControllerClass = PlayerControllerBPClass.Class;}
}

剩下两个文件刚好是一个角色类和一个玩家控制类。

Character

在自定义角色类这里,类继承至ACharacter
在类里面创建了两个私有变量,用于存储相机和弹簧臂

private:/** Top down camera */UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = Camera, meta = (AllowPrivateAccess = "true"))class UCameraComponent* TopDownCameraComponent;/** Camera boom positioning the camera above the character */UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = Camera, meta = (AllowPrivateAccess = "true"))class USpringArmComponent* CameraBoom;

meta 这个参数用于控制属性在引擎和编辑器之间的交互方式。它还有其他设置的内容:

  • meta = (AllowPrivateAccess = “true”) 为变量即使是私有属性,也可以在UE里编辑器和访问
  • meta=(DisplayName=“自定义名称”) 用于在UE中自定义属性的显示名称
  • meta=(ToolTip=“这是一个提示信息”) 为属性提供编辑器中的工具提示,帮助解释属性的用途或如何设置它
  • meta=(EditCondition=“bSomeCondition”) 定义一个布尔表达式,用于控制属性是否可以在UE中编辑
  • meta=(ClampMin=“0.0”, ClampMax=“100.0”) 用于限制编辑器中可编辑属性的最小值和最大值
  • meta=(Category=“MyCustomCategory”) 指定属性在编辑器细节面板中所属的类别
  • meta=(AdvancedDisplay) 用于将属性标记为高级属性,使其在编辑器中默认隐藏,但可以通过点击“显示更多”或类似按钮来显示

然后又增加了两个对属性的获取公共函数,FORCEINLINE 内联函数是在调用点直接插入函数体代码的函数,而不是进行常规的函数调用。这可以减少函数调用的开销,从而可能提高执行速度,但也可能增加生成的代码大小。

	/** Returns TopDownCameraComponent subobject **/FORCEINLINE class UCameraComponent* GetTopDownCameraComponent() const { return TopDownCameraComponent; }/** Returns CameraBoom subobject **/FORCEINLINE class USpringArmComponent* GetCameraBoom() const { return CameraBoom; }

在TopDownProjectCharacter的cpp文件中,只是在构造函数中初始化了一些内容,首先设置角色的胶囊体,然后设置角色移动相关的内容,并创建了相机弹簧臂和相机,最后设置帧回调。

ATopDownProjectCharacter::ATopDownProjectCharacter()
{// 设置角色胶囊体的尺寸GetCapsuleComponent()->InitCapsuleSize(42.f, 96.0f);// 禁止相机随角色旋转bUseControllerRotationPitch = false;bUseControllerRotationYaw = false;bUseControllerRotationRoll = false;// Configure character movementGetCharacterMovement()->bOrientRotationToMovement = true; // 当设置为true时,角色的前方将自动朝向其移动的方向GetCharacterMovement()->RotationRate = FRotator(0.f, 640.f, 0.f); //控制角色旋转的速率GetCharacterMovement()->bConstrainToPlane = true; //当设置为true时,角色的移动将被约束在一个特定的平面上,通常是地面。GetCharacterMovement()->bSnapToPlaneAtStart = true; //游戏开始时,角色被吸附到地面,防止有空中坠落或者卡在地面的问题// 创建相机弹簧臂CameraBoom = CreateDefaultSubobject<USpringArmComponent>(TEXT("CameraBoom")); //创建弹簧臂CameraBoom->SetupAttachment(RootComponent); //附加到根组件上CameraBoom->SetUsingAbsoluteRotation(true); // 不跟随根组件旋转CameraBoom->TargetArmLength = 800.f; //设置弹簧臂长度CameraBoom->SetRelativeRotation(FRotator(-60.f, 0.f, 0.f)); //设置弹簧臂的角度CameraBoom->bDoCollisionTest = false; // 设置为false,弹簧臂将不会与其他碰撞体产生交互// 创建相机TopDownCameraComponent = CreateDefaultSubobject<UCameraComponent>(TEXT("TopDownCamera")); //创建相机组件TopDownCameraComponent->SetupAttachment(CameraBoom, USpringArmComponent::SocketName); //附加到弹簧臂上面TopDownCameraComponent->bUsePawnControlRotation = false; // 设置为false,相机的旋转不受角色控制// 激活帧更新,以便每帧更新光标PrimaryActorTick.bCanEverTick = true; //设置是否帧更新,如果当前值为false,即使设置其它,也不会被更新PrimaryActorTick.bStartWithTickEnabled = true; //游戏开始时,是否立即开始帧回调,如果bCanEverTick为false,也无法帧回调
}

这就是整个Character的内容,其实都是在蓝图里面都可以设置的东西,只不过修改成了使用c++去设置。

PlayerController

在PlayerController里面,首先增加了一个宏用于打印调试,和前面引擎文件里的一样,只是名称不一样,方便区分。

DECLARE_LOG_CATEGORY_EXTERN(LogTemplateCharacter, Log, All);

然后在类内部设置了构造函数,定义了一些可编辑变量,比如点击地面时生成的箭头特效,设置一个时间阈值判断是点击事件还是长按事件,增强输入的上下文,还有点击的action(鼠标和触摸屏的)

public:ATopDownProjectPlayerController();/** 定义一个时间是长按还是点击 */UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = Input)float ShortPressThreshold;/** 点击地面生成的箭头特效 */UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = Input)UNiagaraSystem* FXCursor;/** MappingContext */UPROPERTY(EditAnywhere, BlueprintReadOnly, Category=Input, meta=(AllowPrivateAccess = "true"))UInputMappingContext* DefaultMappingContext;/** 鼠标点击 Input Action */UPROPERTY(EditAnywhere, BlueprintReadOnly, Category=Input, meta=(AllowPrivateAccess = "true"))UInputAction* SetDestinationClickAction;/** 触屏点击 Input Action */UPROPERTY(EditAnywhere, BlueprintReadOnly, Category=Input, meta=(AllowPrivateAccess = "true"))UInputAction* SetDestinationTouchAction;

设置完成,可以在UE面板里面去修改对应的配置
在这里插入图片描述

接着覆盖BeginPlay函数,这个函数在开始运行时触发

virtual void BeginPlay();

在实现这里,获取增强输入的子系统,添加自定义的输入映射上下文

void ATopDownProjectPlayerController::BeginPlay()
{// Call the base class  Super::BeginPlay();//Add Input Mapping Contextif (UEnhancedInputLocalPlayerSubsystem* Subsystem = ULocalPlayer::GetSubsystem<UEnhancedInputLocalPlayerSubsystem>(GetLocalPlayer())){Subsystem->AddMappingContext(DefaultMappingContext, 0);}
}

然后覆盖SetupInputComponent,这个函数允许我们自定义输入

virtual void SetupInputComponent() override;

在函数实现这里,绑定了InputAction的按下,悬停,抬起,取消四个事件(分别绑定了鼠标和触摸,兼容移动端)

void ATopDownProjectPlayerController::SetupInputComponent()
{// set up gameplay key bindingsSuper::SetupInputComponent();// Set up action bindingsif (UEnhancedInputComponent* EnhancedInputComponent = Cast<UEnhancedInputComponent>(InputComponent)){// Setup mouse input eventsEnhancedInputComponent->BindAction(SetDestinationClickAction, ETriggerEvent::Started, this, &ATopDownProjectPlayerController::OnInputStarted);EnhancedInputComponent->BindAction(SetDestinationClickAction, ETriggerEvent::Triggered, this, &ATopDownProjectPlayerController::OnSetDestinationTriggered);EnhancedInputComponent->BindAction(SetDestinationClickAction, ETriggerEvent::Completed, this, &ATopDownProjectPlayerController::OnSetDestinationReleased);EnhancedInputComponent->BindAction(SetDestinationClickAction, ETriggerEvent::Canceled, this, &ATopDownProjectPlayerController::OnSetDestinationReleased);// Setup touch input eventsEnhancedInputComponent->BindAction(SetDestinationTouchAction, ETriggerEvent::Started, this, &ATopDownProjectPlayerController::OnInputStarted);EnhancedInputComponent->BindAction(SetDestinationTouchAction, ETriggerEvent::Triggered, this, &ATopDownProjectPlayerController::OnTouchTriggered);EnhancedInputComponent->BindAction(SetDestinationTouchAction, ETriggerEvent::Completed, this, &ATopDownProjectPlayerController::OnTouchReleased);EnhancedInputComponent->BindAction(SetDestinationTouchAction, ETriggerEvent::Canceled, this, &ATopDownProjectPlayerController::OnTouchReleased);}else{UE_LOG(LogTemplateCharacter, Error, TEXT("'%s' Failed to find an Enhanced Input Component! This template is built to use the Enhanced Input system. If you intend to use the legacy system, then you will need to update this C++ file."), *GetNameSafe(this));}
}

接下来就是定义上面绑定的函数,以及对应所需的变量,就是通过这里实现的角色移动,接下来,我们看一下是如何实现移动的。

/** Input handlers for SetDestination action. */void OnInputStarted();void OnSetDestinationTriggered();void OnSetDestinationReleased();void OnTouchTriggered();void OnTouchReleased();private:FVector CachedDestination; //存储鼠标点击的位置bool bIsTouch; // 是否开启屏幕触摸float FollowTime; // 用于查看按住了多久

在鼠标按下时,会触发OnInputStarted()函数,这个函数内调用StopMovement()函数,停止角色移动。

void ATopDownProjectPlayerController::OnInputStarted()
{StopMovement();
}

鼠标按住,会触发角色跟随鼠标移动事件,在这种模式下,角色会直接朝向鼠标移动,不会自动躲避障碍物,我们看一下悬停回调函数的实现。
函数内先记录一下FollowTime 就是悬停按的时间,这个值会在鼠标抬起是使用。
然后通过鼠标位置发出射线去拾取点击的地面位置,然后通过角色位置和拾取位置计算朝向,根据朝向去移动。

void ATopDownProjectPlayerController::OnSetDestinationTriggered()
{// 长按时将帧的时间存储在变量内,用于鼠标抬起时判断是否为点击事件FollowTime += GetWorld()->GetDeltaSeconds();// 用于存储点击位置的数据信息FHitResult Hit;bool bHitSuccessful = false; //是否成功获取点击位置信息//bIsTouch是由屏幕触摸回调触发,并在触摸回调内设置其开启关闭,因为鼠标点击和触摸点击逻辑一样,只是获取点击地面位置的函数不一样。if (bIsTouch){bHitSuccessful = GetHitResultUnderFinger(ETouchIndex::Touch1, ECollisionChannel::ECC_Visibility, true, Hit);}else{bHitSuccessful = GetHitResultUnderCursor(ECollisionChannel::ECC_Visibility, true, Hit);}// 拾取到为止,从Hit内获取点击位置if (bHitSuccessful){CachedDestination = Hit.Location;}// 通过点击位置和角色位置计算出角色移动方向,并调用AddMovementInput移动。APawn* ControlledPawn = GetPawn();if (ControlledPawn != nullptr){FVector WorldDirection = (CachedDestination - ControlledPawn->GetActorLocation()).GetSafeNormal();ControlledPawn->AddMovementInput(WorldDirection, 1.0, false);}
}

AddMovementInput有三个值:

  • 方向向量(通常是一个FVector):这个参数定义了移动的方向。它可以是代表前后左右移动的二维向量,也可以是包含垂直移动的三维向量。
  • 缩放值(通常是一个浮点数float):这个参数用于调整移动的速度或幅度。通过改变这个值,你可以控制角色移动的快慢。
  • 强制标志(通常是一个布尔值bool):这个参数用于指定是否强制添加移动输入,即使某些条件不满足。它允许开发者在特定情况下覆盖正常的移动逻辑。
ControlledPawn->AddMovementInput(WorldDirection, 1.0, false);

最后我们看一下鼠标抬起事件,鼠标抬起事件首先判断鼠标悬停的时间,如果时间小于我们设置的ShortPressThreshold值,则代表当前属于点击事件,则可以触发自动寻路和播放粒子特效。如果时间大于,则代表属于鼠标悬停,角色跟随鼠标事件,不会触发自动寻路。在最后将FollowTime 设置为零。

void ATopDownProjectPlayerController::OnSetDestinationReleased()
{// 判断是否触发的点击事件,if (FollowTime <= ShortPressThreshold){// 使用函数库,将角色移动到目标位置UAIBlueprintHelperLibrary::SimpleMoveToLocation(this, CachedDestination);//使用Niagara生成生成粒子特效UNiagaraFunctionLibrary::SpawnSystemAtLocation(this, FXCursor, CachedDestination, FRotator::ZeroRotator, FVector(1.f, 1.f, 1.f), true, true, ENCPoolMethod::None, true);}FollowTime = 0.f;
}

还有两个是触摸屏触发的事件,它内部只是设置bIsTouch布尔值,这个值为true代表是触摸屏触发的此事件,所以可以看到在悬停时将其设置为true,然后触发悬停事件,然后在抬起时将其设置为false。这样即使你这次是触摸屏触发的事件,在下一次修改为鼠标也是没问题的。

void ATopDownProjectPlayerController::OnTouchTriggered()
{bIsTouch = true;OnSetDestinationTriggered();
}void ATopDownProjectPlayerController::OnTouchReleased()
{bIsTouch = false;OnSetDestinationReleased();
}

弊端

由于此案例是一个教学案例,所以代码很精简,只实现了简单的功能,其也只适合制作单机游戏,如果将其设置为包含服务器的客户端运行的话,会发现它的自动寻路功能将会失效,那么我们还需要使用其它的方式实现。
在这里插入图片描述
接下来,我将在UE5 RPG的文章中实现可以在服务器上运行的自动寻路功能。

这篇关于UE5俯视角游戏案例代码查看的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/883688

相关文章

Python实例题之pygame开发打飞机游戏实例代码

《Python实例题之pygame开发打飞机游戏实例代码》对于python的学习者,能够写出一个飞机大战的程序代码,是不是感觉到非常的开心,:本文主要介绍Python实例题之pygame开发打飞机... 目录题目pygame-aircraft-game使用 Pygame 开发的打飞机游戏脚本代码解释初始化部

Java中Map.Entry()含义及方法使用代码

《Java中Map.Entry()含义及方法使用代码》:本文主要介绍Java中Map.Entry()含义及方法使用的相关资料,Map.Entry是Java中Map的静态内部接口,用于表示键值对,其... 目录前言 Map.Entry作用核心方法常见使用场景1. 遍历 Map 的所有键值对2. 直接修改 Ma

Windows的CMD窗口如何查看并杀死nginx进程

《Windows的CMD窗口如何查看并杀死nginx进程》:本文主要介绍Windows的CMD窗口如何查看并杀死nginx进程问题,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地... 目录Windows的CMD窗口查看并杀死nginx进程开启nginx查看nginx进程停止nginx服务

六个案例搞懂mysql间隙锁

《六个案例搞懂mysql间隙锁》MySQL中的间隙是指索引中两个索引键之间的空间,间隙锁用于防止范围查询期间的幻读,本文主要介绍了六个案例搞懂mysql间隙锁,具有一定的参考价值,感兴趣的可以了解一下... 目录概念解释间隙锁详解间隙锁触发条件间隙锁加锁规则案例演示案例一:唯一索引等值锁定存在的数据案例二:

深入解析 Java Future 类及代码示例

《深入解析JavaFuture类及代码示例》JavaFuture是java.util.concurrent包中用于表示异步计算结果的核心接口,下面给大家介绍JavaFuture类及实例代码,感兴... 目录一、Future 类概述二、核心工作机制代码示例执行流程2. 状态机模型3. 核心方法解析行为总结:三

python获取cmd环境变量值的实现代码

《python获取cmd环境变量值的实现代码》:本文主要介绍在Python中获取命令行(cmd)环境变量的值,可以使用标准库中的os模块,需要的朋友可以参考下... 前言全局说明在执行py过程中,总要使用到系统环境变量一、说明1.1 环境:Windows 11 家庭版 24H2 26100.4061

pandas实现数据concat拼接的示例代码

《pandas实现数据concat拼接的示例代码》pandas.concat用于合并DataFrame或Series,本文主要介绍了pandas实现数据concat拼接的示例代码,具有一定的参考价值,... 目录语法示例:使用pandas.concat合并数据默认的concat:参数axis=0,join=

MySQL 表的内外连接案例详解

《MySQL表的内外连接案例详解》本文给大家介绍MySQL表的内外连接,结合实例代码给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友参考下吧... 目录表的内外连接(重点)内连接外连接表的内外连接(重点)内连接内连接实际上就是利用where子句对两种表形成的笛卡儿积进行筛选,我

C#代码实现解析WTGPS和BD数据

《C#代码实现解析WTGPS和BD数据》在现代的导航与定位应用中,准确解析GPS和北斗(BD)等卫星定位数据至关重要,本文将使用C#语言实现解析WTGPS和BD数据,需要的可以了解下... 目录一、代码结构概览1. 核心解析方法2. 位置信息解析3. 经纬度转换方法4. 日期和时间戳解析5. 辅助方法二、L

Python使用Code2flow将代码转化为流程图的操作教程

《Python使用Code2flow将代码转化为流程图的操作教程》Code2flow是一款开源工具,能够将代码自动转换为流程图,该工具对于代码审查、调试和理解大型代码库非常有用,在这篇博客中,我们将深... 目录引言1nVflRA、为什么选择 Code2flow?2、安装 Code2flow3、基本功能演示