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

《UE5_C++多人TPS完整教程》学习笔记59 ——《P60 投射物武器(Projectile Weapons)》


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


文章目录

  • P60 投射物武器(Projectile Weapons)
  • 60.1 创建投射物武器类
  • 60.2 Summary


P60 投射物武器(Projectile Weapons)

本节课我们将介绍弹道武器,并创建我们自己的弹道武器类;除此之外,我们还将创建一个投射物(弹体)类,它的实例将由弹道武器类的实例生成。
在这里插入图片描述


60.1 创建投射物武器类

  1. 在枪战游戏中,武器根据命中判定机制(Hit Detection)可分为两种:

    • 投射物武器(Projectile weapons):这类武器发射时会生成实际的弹体,弹体拥有自己的飞行速度,弹体在空中飞行一段时间后,才能到达目标位置从而命中目标。当我们希望弹体能够进行抛射并最终落向地面时,弹体需要具有重力这一属性;当我们希望弹体飞行时间尽可能长,以直线的弹道射向目标时,弹体需要忽略或没有重力这一属性,例如火箭筒(Rocket launcher)的弹体。这类武器通过弹体的弹道判断是否命中物体,若击中物体则会产生命中事件,我们根据命中事件确定弹体产生爆炸粒子和命中物体的位置,由此可以对命中物体施加伤害事件。通常为了展示弹体飞行的效果,我们使用追踪粒子系统(Tracer particles)标记弹道(Leave a trail)。
    • 即时命中武器(Hitscan weapons):这类武器通常不会生成实际的弹体,无需考虑弹体下坠等物理延迟(指哪打哪立刻就到哪,Instant hit),它通过射线追踪(Line trace)即时判定是否命中目标,出于这个原因,我们通常使用光束粒子(Beam particles)系统来显示命中轨迹。

    总的来说,上述两种武器都是实施枪战的合法方式(Legitimate ways to implement gunfire),在游戏中根据实际情况和需求进行运用,例如在近距离(Close ranges)交火时,我们可能需要快速开火(Rapid firing),这时候就需要即时命中武器;而在远距离交火(fire over long ranges)时,可能需要投射物武器,但在一些情况下,即时命中武器中的狙击步枪也可以进行远距离打击。
    在这里插入图片描述

    命中判定机制(Hit Detection)是FPS游戏中最核心的技术之一,直接影响射击手感、公平性和反作弊能力。下面我将详细讲解命中判定机制的原理、分类、延迟补偿、典型实现、代码示例和实际开发建议,适用于C/S强同步架构。


    一、命中判定的基本分类

    1. Hitscan(即时射线判定)
      代表武器:步枪、手枪、狙击枪等
      原理:开火瞬间从枪口/摄像机发出一条射线,检测是否与目标碰撞
      特点:无弹道延迟,命中即反馈
    2. Projectile(弹体/弹道判定)
      代表武器:火箭筒、榴弹、投掷物等
      原理:发射一个有速度和轨迹的物体,实时模拟其运动,碰撞时判定命中
      特点:有飞行时间和抛物线,需考虑重力、风等因素

    二、命中判定的关键环节

    1. 客户端表现
      客户端本地先做一次命中检测,立即给玩家反馈(如击中音效、粒子、动画),提升手感。
      最终命中结果以服务器为准,客户端结果仅作表现。
    2. 服务器权威判定
      服务器收到射击请求后,基于权威状态(包括延迟补偿)重新判定是否命中。
      服务器判定结果才会影响伤害、击杀等核心逻辑。
    3. 双重校验与反作弊
      客户端命中结果仅作表现,服务器独立判定,防止自瞄、穿墙等作弊。
      服务器可采用简化碰撞体(如胶囊体)做二次校验,防止模型漏洞。

    三、延迟补偿(Lag Compensation)

    1. 问题
      网络延迟下,玩家A在本地看到的B的位置,和服务器上B的实际位置可能不同。
      如果直接用服务器当前状态判定,玩家会觉得“明明打中了却没判中”。
    2. 解决方案
      服务器保存最近几秒的所有玩家状态快照(如每帧保存5秒内的状态)。
      当收到射击请求时,根据客户端射击时间戳,回滚所有目标玩家到当时的位置,再做命中判定。
      这样,命中判定基于“射手看到的世界”,保证公平。
      四、典型实现流程
    3. Hitscan命中判定
    • 客户端
      • 开火时本地做一次射线检测,立即表现击中效果。
      • 发送射击请求(含射击时间戳、射线起点、方向等)到服务器。
    • 服务器
      • 根据射击时间戳,回滚目标玩家到当时的位置。
      • 用权威数据做射线检测,判定是否命中。
      • 结果同步给所有客户端。
    1. Projectile命中判定
    • 客户端
      • 本地生成弹体,做弹道模拟,表现飞行和命中。
      • 发送发射请求(含时间戳、初速度、方向等)到服务器。
    • 服务器
      • 生成权威弹体,按物理规则模拟飞行。
      • 每帧检测弹体与目标的碰撞,判定命中。
      • 结果同步给所有客户端。

    —— CSDN 《命中判定机制(Hit Detection)》

  2. 在虚幻引擎中 “创建原生自 Weapon 的 C++ 类”(Create C++ class derived from Weapon)“ProjectileWeapon”(投射物武器 C++ 类) ,创建 Actor C++ 类 “Projectile”(投射物弹体 C++ 类)。接着,在 Visual Studio 中打开 “Projectile.h”,为投射物弹体声明盒体碰撞组件 “CollisionBox”,然后在 “Projectile.cpp” 的构造函数 “AProjectile()” 中基于盒体碰撞组件创建对象,将其设置为根组件,然后设置碰撞。
    在这里插入图片描述在这里插入图片描述

    /*** ProjectileWeapon.h ***/// Fill out your copyright notice in the Description page of Project Settings.#pragma once#include "CoreMinimal.h"/* P60 投射物武器(Projectile Weapons)*/
    #include "Weapon.h"							// 原来自动生成的代码是 #include "Weapon/Weapon.h",这里需要把 "Weapon/" 去掉,否则找不到文件 "Weapon.h"
    /* P60 投射物武器(Projectile Weapons)*/#include "ProjectileWeapon.generated.h"/**
    * 
    */
    UCLASS()
    class BLASTER_API AProjectileWeapon : public AWeapon
    {GENERATED_BODY()};
    
    /*** ProjectileWeapon.cpp ***/// Fill out your copyright notice in the Description page of Project Settings./* P60 投射物武器(Projectile Weapons)*/
    #include "ProjectileWeapon.h"	// 原来自动生成的代码是 #include "Weapon/ProjectileWeapon.h",这里需要把 "Weapon/" 去掉,否则找不到文件 "ProjectileWeapon.h"
    /* P60 投射物武器(Projectile Weapons)*/
    
  3. 创建 Actor C++ 类 “Projectile”(投射物弹体 C++ 类),在 “Projectile.h”,为投射物弹体声明盒体碰撞组件 “CollisionBox”;接着,在 “Projectile.cpp” 的构造函数 “AProjectile()” 中基于盒体碰撞组件创建对象,将其设置为根组件;随后,设置其碰撞类型为动态物体 “WorldDynamic”,物理碰撞启用类型为 “QueryAndPhysics”;在设置与碰撞通道发生碰撞时的响应方式时,先暂时关闭所有通道的碰撞检测和碰撞响应,然后再开启与可视物体以及静态物体发生碰撞的响应为阻挡。

    // Fill out your copyright notice in the Description page of Project Settings.#pragma once#include "CoreMinimal.h"
    #include "GameFramework/Actor.h"
    #include "Projectile.generated.h"UCLASS()
    class BLASTER_API AProjectile : public AActor
    {GENERATED_BODY()public:	// Sets default values for this actor's propertiesAProjectile();protected:// Called when the game starts or when spawnedvirtual void BeginPlay() override;public:	// Called every framevirtual void Tick(float DeltaTime) override;/* P60 投射物武器(Projectile Weapons)*/
    private:UPROPERTY(EditAnywhere)class UBoxComponent* CollisionBox;
    /* P60 投射物武器(Projectile Weapons)*/};
    
    /*** Projectile.cpp ***/
    // Fill out your copyright notice in the Description page of Project Settings./* P60 投射物武器(Projectile Weapons)*/
    #include "Projectile.h"	// 原来自动生成的代码是 #include "Weapon/Projectile.h",这里需要把 "Weapon/" 去掉,否则找不到文件 "Projectile.h"
    #include "Components/BoxComponent.h"
    /* P60 投射物武器(Projectile Weapons)*/// Sets default values
    AProjectile::AProjectile()
    {// 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;/* P60 投射物武器(Projectile Weapons)*/CollisionBox = CreateDefaultSubobject<UBoxComponent>(TEXT("CollisionBox"));			// 基于盒体组件类创建对象SetRootComponent(CollisionBox);														// 设置根组件为 CollisionBox// 指定碰撞检测的对象类型CollisionBox->SetCollisionObjectType(ECollisionChannel::ECC_WorldDynamic);			// 设定自己的碰撞类型为 WorldDynamic,用于动态物体的碰撞,通常是玩家角色、移动的物体等。CollisionBox->SetCollisionEnabled(ECollisionEnabled::QueryAndPhysics);				// 设定自己的物理碰撞启用类型为 QueryAndPhysics,可以用于空间查询(射线投射、扫描、覆盖)和模拟(刚体、约束)。// 设置与碰撞通道发生碰撞时的响应方式CollisionBox->SetCollisionResponseToAllChannels(ECollisionResponse::ECR_Ignore);	// 先暂时关闭所有通道的碰撞检测和碰撞响应,然后再开启需要的通道CollisionBox->SetCollisionResponseToChannel(ECollisionChannel::ECC_Visibility, ECollisionResponse::ECR_Block);	// 与可视物体发生碰撞的响应为阻挡,在碰撞时会停止移动或反弹。CollisionBox->SetCollisionResponseToChannel(ECollisionChannel::ECC_WorldStatic, ECollisionResponse::ECR_Block);	// 与静态物体发生碰撞的响应为阻挡,在碰撞时会停止移动或反弹。/* P60 投射物武器(Projectile Weapons)*/
    }// Called when the game starts or when spawned
    void AProjectile::BeginPlay()
    {Super::BeginPlay();}// Called every frame
    void AProjectile::Tick(float DeltaTime)
    {Super::Tick(DeltaTime);}
    
    1. ECC (Collision Channel)
      ECC 是 Collision Channel 的缩写,表示碰撞通道。它用于定义不同类型的碰撞对象,以便在碰撞检测中进行分类。每个碰撞对象都有一个对应的碰撞通道,在运行时可以根据这个通道来检测和响应碰撞。
      在 Unreal Engine 中,ECC 定义了碰撞的种类,常见的碰撞通道包括:

      • ECC_Visibility: 用于可见物体的碰撞,通常是摄像机视野中的物体。
      • ECC_WorldStatic: 用于静态物体的碰撞,例如建筑、地面等。
      • ECC_WorldDynamic: 用于动态物体的碰撞,通常是玩家角色、移动的物体等。
      • ECC_Pawn: 用于与玩家角色(Pawn)相关的碰撞。
      • ECC_PhysicsBody: 用于物理物体的碰撞。
      • ECC_Vehicle: 用于与车辆相关的碰撞。
      • ECC_Destructible: 用于可破坏物体的碰撞。
      • ECC_GameTraceChannel1ECC_GameTraceChannel8: 自定义的碰撞通道,开发者可以根据需要定义和使用。
    2. ECR (Collision Response)
      ECR 是 Collision Response 的缩写,表示碰撞响应类型。它定义了物体在发生碰撞时应该如何响应。在 Unreal Engine 中,碰撞响应有多个类型,可以设置为以下几种:

      • ECR_Ignore: 忽略碰撞,不做任何响应。
      • ECR_Overlap: 发生重叠但不产生物理反应,即物体不会被推开。通常用于触发事件或碰撞检测。
      • ECR_Block: 发生阻挡,物体在碰撞时会停止移动或反弹。
      • ECR_Custom: 自定义响应,允许开发者实现特定的碰撞处理逻辑。

    —— CSDN 《ECC和ECR》

    物理碰撞启用类型
    NoCollision:不会在物理引擎中创建任何表示。不能用于空间查询(射线投射、扫描、覆盖)或模拟(刚体、约束)。这将提供最佳的性能(尤其是对于移动的对象)。
    QueryOnly:仅用于空间查询(射线投射、扫描、覆盖)。不能用于模拟(刚体、约束)。这对于角色移动和不需要物理模拟的事物非常有用。通过将数据保持在模拟树之外,可以提高性能。
    PhysicsOnly:仅用于物理模拟(刚体、约束)。不能用于空间查询(射线投射、扫描、覆盖)。这对于角色上不需要每个骨骼检测的摇摆部分非常有用。通过将数据保持在查询树之外,可以提高性能。
    QueryAndPhysics:可以用于空间查询(射线投射、扫描、覆盖)和模拟(刚体、约束)。


    —— 知乎《UE物理检测中的类型》


60.2 Summary

本节课我们开始设计投射物武器,为游戏添加更丰富的武器类型。我们了解到,根据命中判定机制,枪战游戏主要分为两种武器,第一种是投射物武器,它发射实际弹体,具有飞行时间和弹道特性,常用追踪粒子标记其飞行轨迹;第二种是即使命中武器,它不发射实际的弹体,通过射线追踪即时判定命中,常用光束粒子显示其命中轨迹。于是,我们在虚幻引擎中创建了继承自现有的 “Weapon” C++ 类的 “ProjectileWeapon”,它将作为投射物武器的基类,后续将实现发射弹体的功能;同时,还创建了投射物武器的弹体类 “Projectile” 类,它继承自 Actor C++ 类,我们为它配置了盒体碰撞组件(CollisionBox)作为根组件,设置它的碰撞类型为WorldDynamic(动态物体),并启用 “QueryAndPhysics” 碰撞检测,在关闭它对所有通道的碰撞检测后,仅对可见物体 “Visibility” 和静态物体 “WorldStatic” 通道启用阻挡响应。
在这里插入图片描述


http://www.dtcms.com/a/403802.html

相关文章:

  • 高新快速建设网站电话wordpress玻璃透主题
  • Splunk DB connect 增量查询数据
  • odoo-068 pdf 批量转 img,及 os、 PyMuPDF
  • Leetcode 394. 字符串解码 栈
  • 安康网站建设公司网站建立初步
  • 建设银行网站 购买外汇国美电器如何进行网站的建设与维护
  • MCU的取指周期与等待周期以及指令预取与缓存机制
  • ESP32 IDF 分区表
  • 房地产网站怎么推广贵阳网站建设方案推广
  • 开源 | 充电桩 运维 管理平台(IoT+运维工单平台)功能清单 - 慧知开源充电桩平台
  • 写给初学网站开发们的一封信宁波微信开发
  • 百度代理公司怎么样seo联盟
  • 在指定的进程中查找特定DLL模块
  • 关于页表过长的一些思考
  • 添加网站备案号链接建设网站应该注意的地方
  • 每日AI学习笔记----Qwen3-Omni 、HuatuoGPT-o1医学复杂推理
  • 专门做儿童的店铺网站长沙网络推广只选智投未来
  • 深圳网站制作厂家电子商务网站建设与管理课程设计
  • 微网站开发框架电子商务平台中搜索词拆解包括
  • 邢台市建设工程质量监督网站wordpress学校主题
  • Pytorch框架笔记
  • OD C卷 - 剩余银饰的重量
  • Linux 用户和组管理
  • phpstudy建设网站教程网站快捷导航ie怎么做
  • 网站颜色字体颜色网站建设宀金手指花总十五
  • 毕赤酵母(K. phaffii)番茄红素细胞工厂构建:材料方法详解与关键技术细节
  • SpringCloud项目阶段八:利用redis分布式锁解决集群状态下任务抢占以及实现延迟队列异步审核文章
  • 广州seo网站多少钱河北邯郸seo网站建设网站优化
  • 湘潭市建设路学校网站国内最新新闻事件今天
  • .NET MVC 框架基础大全