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

《UE5_C++多人TPS完整教程》学习笔记35 ——《P36 武器类(Weapon Class)》


本文为B站系列教学视频 《UE5_C++多人TPS完整教程》 —— 《P36 武器类(Weapon Class)》 的学习笔记,该系列教学视频为计算机工程师、程序员、游戏开发者、作家(Engineer, Programmer, Game Developer, Author) Stephen Ulibarri 发布在 Udemy 上的课程 《Unreal Engine 5 C++ Multiplayer Shooter》 的中文字幕翻译版,UP主(也是译者)为 游戏引擎能吃么。

在这里插入图片描述


文章目录

  • P36 武器类(Weapon Class)
  • 36.1 创建武器 C++ 类
  • 36.2 创建武器蓝图类
  • 36.3 将武器蓝图类添加到关卡中
  • 36.4 Summary


P36 武器类(Weapon Class)

本节课我们将添加武器类应有的基本组件,以及武器状态(Weapon state)的枚举,这样我们就可以根据武器状态来处理武器。
在这里插入图片描述


36.1 创建武器 C++ 类

  1. 在虚幻引擎内容浏览器 “C++ 类”(C++ Classes)目录下新建一个 “Actor” C++ 类,,命名为 “Weapon”,路径为“.../Blaster/Weapon”。
    在这里插入图片描述
    在这里插入图片描述

  2. 在 Visual Studio 中打开头文件 “Weapon.h”,在武器类私有属性中声明所有地方可见(VisibleAnywhere)、归类为武器属性 “Weapon Properties” 的骨骼体组件类(SkeletalMeshComponent) "WeaponMesh 以及球体组件类(SphereComponent) “AreaSphere”,该球体将 “包裹” 武器骨骼体组件,用于判定人物是否碰到球体,若碰到人物将会拾取该武器;在武器类外声明武器状态枚举类型,包括初始状态(武器可以被人物捡起)、已装备状态(武器被人物捡起并装备使用)、已丢弃状态(武器被人物丢弃)以及一个默认最大常量,添加武器状态枚举类型到武器类私有属性中。

    /*** Weapon.h ***/
    // Fill out your copyright notice in the Description page of Project Settings.
    
    #pragma once
    
    #include "CoreMinimal.h"
    #include "GameFramework/Actor.h"
    #include "Weapon.generated.h"
    
    /* P36 武器类(Weapon Class)*/
    // UENUM():参阅《元数据说明符》https://dev.epicgames.com/documentation/zh-cn/unreal-engine/metadata-specifiers-in-unreal-engine?application_version=5.0
    // BlueprintType:将此类公开为可用于蓝图中的变量的类型,参阅《类说明符》https://dev.epicgames.com/documentation/zh-cn/unreal-engine/class-specifiers?application_version=5.0
    UENUM(BlueprintType)									
    enum class EWeaponState : uint8							// 武器状态枚举类型,枚举常量为无符号8位整型						
    {	
    	// UMETA():参阅《元数据说明符》https://dev.epicgames.com/documentation/zh-cn/unreal-engine/metadata-specifiers-in-unreal-engine?application_version=5.0
    	EWS_Initial UMETA(DisplayName = "Initial State"),	// 初始状态,武器可以被人物捡起
    	EWS_Equipped UMETA(DisplayName = "Equipped"),		// 已装备状态,武器被人物捡起并装备使用
    	EWS_Dropped UMETA(DisplayName = "Dropped"),			// 已丢弃状态,武器被人物丢弃
    	EWS_Max UMETA(DisplayName = "DefaultMax")			// 大多数枚举常量都会有一个默认的最大常量,我们通过检查这个最大常量的值,就能知道枚举类型中有多少个常量
    };
    /* P36 武器类(Weapon Class)*/
    
    UCLASS()
    class BLASTER_API AWeapon : public AActor
    {
    	GENERATED_BODY()
    	
    public:	
    	// Sets default values for this actor's properties
    	AWeapon();
    
    protected:
    	// Called when the game starts or when spawned
    	virtual void BeginPlay() override;
    
    public:	
    	// Called every frame
    	virtual void Tick(float DeltaTime) override;
    
    /* P36 武器类(Weapon Class)*/
    private:
    	UPROPERTY(VisibleAnywhere, Category = "Weapon Properties")	// 添加所有地方可见的骨骼网格组件,这样就可以通过蓝图进行编辑武器,归类为 Weapon Properties
    	class USkeletalMeshComponent* WeaponMesh;				
    
    	UPROPERTY(VisibleAnywhere, Category = "Weapon Properties")	// 添加一个重叠体积(Overlap Volume),这里使用球体组件 “包裹” 武器骨骼体组件,用于判定人物是否碰到球体,若碰到人物将会拾取该武器
    	class USphereComponent* AreaSphere;
    
    	UPROPERTY(VisibleAnywhere)									// 添加新声明的武器状态枚举类型
    	EWeaponState WeaponState;									
    /* P36 武器类(Weapon Class)*/
    };
    

    注意:
    定义枚举类型使用了 “UENUM()” 和 “UMETA()”,具体用法参阅虚幻引擎官方文档《元数据说明符》,还使用了类说明符 “BlueprintType” 参阅《类说明符》https://dev.epicgames.com/documentation/zh-cn/unreal-engine/class-specifiers?application_version=5.0

  3. 打开源文件 “Weapon.cpp”,在 “Weapon” 类构造函数中 “AWeapon()”,将 “bReplicate” 变量设置为 “true” 实现武器类的网络同步,设置WeaponMesh 以及 “AreaSphere” 的碰撞响应预设值,接着设置关闭碰撞检测和碰撞响应,物体之间不会发生任何碰撞,然后在函数 “BeginPlay()” 中设置只在服务器再次启用 “AreaSphere” 的碰撞检测和碰撞响应,因为服务器负责掌管所有武器权限,若检测到发生碰撞事件会产生物理效果。最后进行编译。

    // Fill out your copyright notice in the Description page of Project Settings.
    
    /* P36 武器类(Weapon Class)*/
    #include "Weapon.h"	// 原来自动生成的代码是 #include "HUD/OverheadWidget.h",这里需要把 "GameMode/" 去掉,否则找不到文件 "LobbyGameMode.h"
    #include "Components//SphereComponent.h"
    
    // Sets default values
    AWeapon::AWeapon()
    {
    	// Set this actor to call Tick() every frame.  You can turn this off to improve performance if you don't need it.
    	PrimaryActorTick.bCanEverTick = false;												// 设置为 false
    	
    	// 参阅《复制Actor属性》:https://dev.epicgames.com/documentation/zh-cn/unreal-engine/replicate-actor-properties-in-unreal-engine?application_version=5.0
    	// 在多人Gameplay中,当属性被带有 Replicated 或 ReplicatedUsing 元数据说明符标记时,服务器会在复制的属性每次更改其值时向每个连接的客户端发送更新。每个客户端会将更新的值应用到其本地版本的Actor。
    	bReplicates = true;																	// 武器只在服务器拥有权限,需要复制到客户端上
    
    	WeaponMesh = CreateDefaultSubobject<USkeletalMeshComponent>(TEXT("WeaponMesh"));	// 基于骨骼网格体组件类创建 WeaponMesh 对象
    	SetRootComponent(WeaponMesh);														// 设置根组件为 WeaponMesh
    	WeaponMesh->SetupAttachment(RootComponent);											// 将 WeaponMesh 附加到武器类的根组件上
    	// SetRootComponent(WeaponMesh);													// 设置根组件为 WeaponMesh
    	WeaponMesh->SetCollisionResponseToAllChannels(ECollisionResponse::ECR_Block);		// 设置所有通道(Channels)的碰撞响应(Collision response)为阻塞(Block)使得当武器被丢弃(Drop)时能被地面或墙体弹起(Bounce off)
    	WeaponMesh->SetCollisionResponseToChannel(ECollisionChannel::ECC_Pawn, 
    											ECollisionResponse::ECR_Ignore);			// 修改 Pawn 通道:当武器被玩家丢弃时,玩家可以跨过(Step over)跑过(Run through)落下的武器,不与它发生碰撞
    	WeaponMesh->SetCollisionEnabled(ECollisionEnabled::NoCollision);					// 先暂时关闭碰撞检测和碰撞响应,物体之间不会发生任何碰撞,我们将在服务器端启用
    	
    	/* ECollisionResponse
    	// Enum indicating how each type should respond 
    	UENUM(BlueprintType, meta = (ScriptName = "CollisionResponseType"))
    		enum ECollisionResponse
    	{
    		ECR_Ignore UMETA(DisplayName = "Ignore"),	// 忽略(Ignore):无论另一个物理形体的"碰撞响应(Collision Responses)"为何,此物理形体都将忽略交互。
    		ECR_Overlap UMETA(DisplayName = "Overlap"),	// 重叠(Overlap):如果已将另一个物理形体设置为"重叠(Overlap)"或"阻挡(Block)"此物理形体的"对象类型(Object Type)",将发生重叠事件。
    		ECR_Block UMETA(DisplayName = "Block"),		// 阻挡(Block):如果已将另一个物理形体设置为"阻挡(Block)"此物理形体的"对象类型(Object Type)",将发生撞击事件。
    		ECR_MAX,
    	};*/
    
    	/* // ECollisionChannel
    	// Enum indicating different type of objects for rigid - body collision purposes.
    	UENUM(BlueprintType)
    	enum ECollisionChannel
    	{
    		ECC_WorldStatic UMETA(DisplayName = "WorldStatic"),
    		ECC_WorldDynamic UMETA(DisplayName = "WorldDynamic"),
    		ECC_Pawn UMETA(DisplayName = "Pawn"),
    		ECC_Visibility UMETA(DisplayName = "Visibility", TraceQuery = "1"),
    		ECC_Camera UMETA(DisplayName = "Camera", TraceQuery = "1"),
    		ECC_PhysicsBody UMETA(DisplayName = "PhysicsBody"),
    		ECC_Vehicle UMETA(DisplayName = "Vehicle"),
    		ECC_Destructible UMETA(DisplayName = "Destructible"),
    
    		...
    	};*/
    	
    	/*	// ECollisionEnabled
    	// Enum used to describe what type of collision is enabled on a body.
    	UENUM(BlueprintType)
    		namespace ECollisionEnabled
    	{
    		enum Type
    		{
    			// Will not create any representation in the physics engine. Cannot be used for spatial queries (raycasts, sweeps, overlaps) or simulation (rigid body, constraints). Best performance possible (especially for moving objects) 
    			NoCollision UMETA(DisplayName = "No Collision"),	// 在物理引擎中此形体将不具有任何表示。不可用于空间查询(光线投射、Sweep、重叠)或模拟(刚体、约束)。此设置可提供最佳性能,尤其是对于移动对象
    			// Only used for spatial queries (raycasts, sweeps, and overlaps). Cannot be used for simulation (rigid body, constraints). Useful for character movement and things that do not need physical simulation. Performance gains by keeping data out of simulation tree. 
    			QueryOnly UMETA(DisplayName = "Query Only (No Physics Collision)"),	// 此形体仅可用于空间查询(光线投射、Sweep和重叠)。不可用于模拟(刚体、约束)。对于角色运动和不需要物理模拟的对象,此设置非常有用。通过缩小物理模拟树中的数据来实现一些性能提升
    			// Only used only for physics simulation (rigid body, constraints). Cannot be used for spatial queries (raycasts, sweeps, overlaps). Useful for jiggly bits on characters that do not need per bone detection. Performance gains by keeping data out of query tree 
    			PhysicsOnly UMETA(DisplayName = "Physics Only (No Query Collision)"),	// 此形体仅可用于物理模拟(刚体、约束)。不可用于空间查询(光线投射、Sweep、重叠)。对于角色上不需要按骨骼进行检测的模拟次级运动,此设置非常有用。通过缩小查询树中的数据来实现一些性能提升
    			// Can be used for both spatial queries (raycasts, sweeps, overlaps) and simulation (rigid body, constraints). 
    			QueryAndPhysics UMETA(DisplayName = "Collision Enabled (Query and Physics)")	// 此形体可用于空间查询(光线投射、Sweep、重叠)和模拟(刚体、约束)
    		};
    	}
    	*/
    
    	AreaSphere = CreateDefaultSubobject<USphereComponent>(TEXT("AreaSphere"));		// 基于球体组件类创建 AreaSphere 对象
    	AreaSphere->SetupAttachment(RootComponent);										// 将 AreaSphere 附加到武器类的根组件上
    	AreaSphere->SetCollisionResponseToAllChannels(ECollisionResponse::ECR_Ignore);	// 忽略球体组件的碰撞响应,以便于当玩家靠近武器时我们可以检测玩家是否碰到武器(而不是武器被弹开)
    	AreaSphere->SetCollisionEnabled(ECollisionEnabled::NoCollision);				// 先暂时关闭碰撞检测和碰撞响应,物体之间不会发生任何碰撞,我们将在服务器端启用
    }
    
    // Called when the game starts or when spawned
    void AWeapon::BeginPlay()
    {
    	Super::BeginPlay();
    	
    	// 服务器将负责掌管(in charge of)所有武器权限
    	if (HasAuthority()) {	// 判断是否拥有权威角色,等同于 if (GetLocalRole() == ENetRole::ROLE_Authority) 
    		AreaSphere->SetCollisionEnabled(ECollisionEnabled::QueryAndPhysics);		// 既进行碰撞检测,也进行碰撞响应。物体之间会检测是否发生碰撞,同时会产生物理效果
    		AreaSphere->SetCollisionResponseToChannel(ECollisionChannel::ECC_Pawn, 
    												ECollisionResponse::ECR_Overlap);	// 
    	}
    }
    /* P36 武器类(Weapon Class)*/
    
    // Called every frame
    void AWeapon::Tick(float DeltaTime)
    {
    	Super::Tick(DeltaTime);
    }
    

    注意:
    代码中涉及的 “碰撞(Collision)“ 属性类别中的 “碰撞预设值(Collision Presets)”/“碰撞响应(Collision Response)” 可参阅虚幻引擎官方文《碰撞响应参考》


36.2 创建武器蓝图类

  1. 在虚幻引擎内容浏览器 “内容” 目录下的 “Blueprints” 文件夹中新建文件夹 “Weapon”,然后新建蓝图类 “BP_Weapon”,其父类为上文编译好的 C++ 类 “Weapon”。
    在这里插入图片描述

  2. 双击新建好的 “BP_Weapon”,进入蓝图编辑器。在左侧 “组件”(Components)面吧N选择 “Weapon Mesh (WeaponMesh)”,然后在右侧 “细节”(Details)面板中设置 “网格”(Mesh)选项卡下的 “骨骼网格体”(SkeletalMesh)为 “Assaults_Rifle_A”。
    在这里插入图片描述

  3. 在左侧 “组件”(Components)面板选择 “Area Sphere (AreaSphere)”,在视口中调整位置至 “Weapon Mesh (WeaponMesh)” 中心,调整大小直至可以恰好 “包裹” 组件 “Weapon Mesh (WeaponMesh)”。
    在这里插入图片描述
    在这里插入图片描述

  4. 在左侧 “组件”(Components)面板选择 “BP_Weapon (自我)” (BP_Weapon (Self)),可以在右侧 “细节”(Details)面板中 “Weapon” 选项卡下看到武器状态 “Weapon State” 为初始状态 “Initial State”。
    在这里插入图片描述


36.3 将武器蓝图类添加到关卡中

  1. 将蓝图类 “BP_Weapon” 拖拽至游戏关卡 “BlasterMap” 中。
    在这里插入图片描述

  2. 点击工具栏的 “播放”()按钮启动运行,可以看到武器均显示在监听服务器中和两个客户端中。
    在这里插入图片描述


36.4 Summary

本节课详细解析了多人射击游戏的武器类(Weapon Class)的构建。武器 C++ 类 “Weapon” 私有属性包含骨骼体组件类 "WeaponMesh” 、球体组件类 “AreaSphere”(该球体将 “包裹” 武器骨骼体组件,用于判定人物是否碰到球体,若碰到人物将会拾取该武器)以及在武器类外声明武器状态枚举类型(包括初始状态、已装备状态、已丢弃状态以及一个默认最大常量),然后我们利用 “bReplicates” 属性实现武器类服务器与客户端的同步,设置WeaponMesh 以及 “AreaSphere” 的碰撞响应预设值,接着设置关闭碰撞检测和碰撞响应,物体之间不会发生任何碰撞,然后设置只在服务器再次启用 “AreaSphere” 的碰撞检测和碰撞响应,因为服务器负责掌管所有武器权限,若检测到发生碰撞事件会产生物理效果。下一步我们由武器 C++ 类构建蓝图类 “BP_Weapon”,在蓝图编辑器中设置 "WeaponMesh” 的 “骨骼网格体” 为资产 “Assaults_Rifle_A”,然后调整球体组件类 “AreaSphere” 位置及大小。最后我们将 “BP_Weapon” 添加到游戏关卡 “BlasterMap” 中进行测试,在服务器和客户端上均可以看到武器。
在这里插入图片描述
36.1 创建武器 C++ 类 中,我们定义武器枚举类型使用了 “UENUM()” 和 “UMETA()”,它们的具体用法可参阅虚幻引擎官方文档《元数据说明符》,还使用了类说明符 “BlueprintType” 参阅《类说明符》。我们还设置了骨骼体组件类 "WeaponMesh” 、球体组件类 “AreaSphere”的碰撞预设值和碰撞响应,虚幻引擎的 “碰撞” 属性具体可参阅虚幻引擎官方文《碰撞响应参考》。



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

相关文章:

  • open-webui+deepseek api实现deepseek自由
  • 完整版已注册,永久授权!
  • 【基础io】
  • Java面向对象(详细解释)
  • Java网络编程初阶
  • 可变参数与递归
  • 简记_EMC概述
  • C#使用winform实现简单的梯形图指令编译和执行,带编译器和虚拟机代码
  • 性能测试和Jmeter
  • 使用 Prim 算法生成了最小生成树, 使用 Fleury 算法生成了欧拉回路,尝试找到了一个简单的哈密尔顿圈。
  • DTO 命名规范指南
  • Cursor 使用经验,一个需求开发全流程
  • 阿里云CTF2025 ---Web
  • Python条件语句:if-elif vs match 详解
  • 深入理解Vue Router:构建单页应用的导航利器
  • 【FPGA】导航
  • 常用Shell脚本总结
  • SQL Server核心知识总结
  • C# 异步任务队列封装
  • RocketMQ提供了哪些过滤机制?