跳过正文

CoopGame02-武器1

··3631 字·8 分钟
目录
coopgame - 这篇文章属于一个选集。
§ 2: 本文

CoopGame02-武器1
#

导入武器相关资产
#

  1. 下载源文件,密码:zmingu
  2. 导入CoopGame\Content\Weapons文件夹到项目Content目录

创建并附加武器到角色身上
#

创建武器类
#

  1. 创建武器C++类SWeapon,继承自Actor,并初始化武器的骨骼网格体。
    SWeapon.h 中声明武器的骨骼网格体组件
    1
    2
    3
    
    //武器的骨骼网格体
    UPROPERTY(VisibleAnywhere,BlueprintReadOnly,Category="Weapon")
    class USkeletalMeshComponent* SKMeshComp;
    
    SWeapon.cpp 中的构造函数中初始化武器的骨骼网格体组件
    1
    2
    3
    4
    5
    
    ASWeapon::ASWeapon()
    {
    	SKMeshComp = CreateDefaultSubobject<USkeletalMeshComponent>(TEXT("SKMeshComp"));
    	RootComponent = SKMeshComp;
    }
    
  2. 创建继承SWeapon武器类的蓝图类BP_SWeapon,并选择设置武器的骨骼网格体SK_Rifle.
    8.webp

为角色生成武器
#

  1. 在角色骨骼的hand_r右键添加名为WeaponSocket武器的骨骼插槽。

    9.webp

  2. SCharacter编写代码将武器生成到玩家手上的名为WeaponSocket的插槽上。

    SCharacter.h 中声明需要添加到角色身上的武器类和插槽名
     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    
    //当前武器类
    UPROPERTY(BlueprintReadOnly)
    class ASWeapon* CurrentWeapon;
    
    //需要生成并添加给角色的武器类
    UPROPERTY(EditDefaultsOnly, Category = "Player")
    TSubclassOf<ASWeapon> StarterWeaponClass;
    
    //用来放武器的插槽名
    UPROPERTY(VisibleDefaultsOnly, Category = "Player")
    FName WeaponAttachSocketName;
    
    SCharacter.cpp 中生成武器并附加到角色身上
     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    
    /*引入武器类*/
    #include "SWeapon.h"
    
    /*在ASCharacter()构造函数中添加*/
    //玩家骨骼上的武器插槽名
    WeaponAttachSocketName = "WeaponSocket";
    
    /*在BeginPlay()函数中添加*/
    /*
    * 生成默认的武器。
    */
    //设置生成参数。
    FActorSpawnParameters SpawnParams;
    SpawnParams.SpawnCollisionHandlingOverride = ESpawnActorCollisionHandlingMethod::AlwaysSpawn;
    //生成当前武器。
    CurrentWeapon = GetWorld()->SpawnActor<ASWeapon>(StarterWeaponClass, FVector::ZeroVector, FRotator::ZeroRotator, SpawnParams);
    if (CurrentWeapon)
    {
    	//如果当前武器已经有了,设置武器拥有者为当前玩家,将武器依附到武器插槽。
    	CurrentWeapon->SetOwner(this);
    	CurrentWeapon->AttachToComponent(GetMesh(), FAttachmentTransformRules::SnapToTargetNotIncludingScale, WeaponAttachSocketName);
    }
    

  3. 在角色蓝图类中设置默认值,将要生成的武器类StarterWeaponClass的类默认值设为BP_SWeapon

调整武器位置
#

  1. 在人物骨骼的武器骨骼插槽WeaponSocket右键添加预览资产SK_Rifle,在右侧选择预览动画选Idle_Rifle_Hip
    10.webp
  2. 调整插槽位置使武器位置正确 , 可直接使用如图参数。
    Untitled.webp

让武器能够开火
#

  1. 在武器类SWeapon中编写开火函数

    SWeapon.h中声明开火函数
    1
    2
    3
    
    //开火函数
    UFUNCTION(BlueprintCallable,Category="Weapon")
    void Fire();
    
    SWeapon.cpp中定义开火函数
     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    
    /*引入绘制调试射线助手类*/
    include "DrawDebugHelpers.h"
    
    /*定义Fire()函数*/
    void ASWeapon::Fire()
    {
    	AActor* WeaponOwner= GetOwner();
    	if (WeaponOwner)
    	{
    		/*
    		 * 弹道的起点和终点
    		 */
    		FVector EyeLocation;
    		FRotator EyeRotator;
    		//弹道起点 = 玩家摄像头位置
    		Cast<APawn>(WeaponOwner)->GetActorEyesViewPoint(EyeLocation,EyeRotator);
    		//弹道终点 = 起点位置 + (方向 * 距离)
    		FVector TraceEnd = EyeLocation + (EyeRotator.Vector() * 1000);
    
    		/*
    		 *打击和碰撞
    		 */
    		//打击结果(里面能存储射线打击到的一些信息)
    		FHitResult Hit;
    		//查询参数
    		FCollisionQueryParams QueryParams;
    		//查询参数设置忽略武器自身和拥有武器的玩家
    		QueryParams.AddIgnoredActor(this);
    		QueryParams.AddIgnoredActor(WeaponOwner);
    		//查询参数启用复合追踪
    		QueryParams.bTraceComplex = true;
    		//判断是否弹道射线打到东西,单射线查询通道(打击结果,射线开始位置,射线结束位置,碰撞通道,查询参数)
    		bool bHit = GetWorld()->LineTraceSingleByChannel(Hit,EyeLocation,TraceEnd,ECC_Visibility,QueryParams);
    		//如果射线打到东西
    		if (bHit)
    		{
    			//处理击中事件,待编写
    		}
    		//把射线检测绘制出来
    		DrawDebugLine(GetWorld(),EyeLocation,TraceEnd,FColor::Red,false,1,0,1);
    	}
    }
    

  2. 在角色蓝图BP_SCharacter使用鼠标左键事件调用武器的开火函数,此时开火可见从玩家身上摄像机发出的射线。(右键搜索CurrentWeapon变量,从该变量调用Fire()函数)

    Untitled 1.webp

    Untitled 2.webp

使开火产生伤害
#

  1. 在武器类中添加伤害类型变量
  • 伤害类型介绍:
    12.webp
    SWeapon.h中声明伤害类型
    1
    2
    3
    
    //伤害类型
    UPROPERTY(EditDefaultsOnly,BlueprintReadOnly,Category="Weapon")
    TSubclassOf<UDamageType> DamageType;
    
    SWeapon.cpp的开火函数中应用伤害
     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    
    //导入库函数头文件
    #include "Kismet/GameplayStatics.h"
    
    
    /*补充射线击中需要处理的事件*/
    
    //如果射线打到东西
    if (bHit)
    {
    	//应用点状伤害(被伤害的Actor,要应用的基础伤害,受击方向,描述命中的碰撞或跟踪结果,照成伤害的控制器(例如射击武器的玩家的控制器),实际造成伤害的Actor,伤害类型)
    	UGameplayStatics::ApplyPointDamage(Hit.GetActor(),20,EyeRotator.Vector(),Hit,WeaponOwner->GetInstigatorController(),this,DamageType);
    }
    
  1. 调一下角色身上的弹簧臂的位置,参数如图,让待会更好观察并测试代码结果.

    13.webp

  2. 在角色蓝图类BP_SCharacter,添加事件点状伤害并绘制调试球体,将网格体碰撞预设设置为BlockAll

    Untitled 3.webp

  3. 拖入一个BP_SCharacter到场景中, 用于测试, 如果没问题能看到如图射击到人会产生调试球 , 说明我们已经打出伤害了.

    Untitled 4.webp

添加武器开火特效
#

枪口特效和击中特效
#

  1. 导入之前项目下载的武器特效资产 , 目录为WeaponEffects,并编写SWeapon武器类。

    SWeapon.h中声明枪口的插槽名,枪口发生的特效,击中物体的特效
     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    
    //枪骨骼上的枪口骨骼名
    UPROPERTY(VisibleDefaultsOnly,BlueprintReadOnly,Category="Weapon")
    FName MuzzleSocketName;
    
    //枪口特效
    UPROPERTY(EditDefaultsOnly,BlueprintReadOnly,Category="Weapon")
    class UParticleSystem* MuzzleEffect;
    
    //击中特效
    UPROPERTY(EditDefaultsOnly,BlueprintReadOnly,Category="Weapon")
    class UParticleSystem* ImpactEffect;
    
    SWeapon.cpp定义枪口插槽名,以及在Fire()中处理生成特效逻辑
     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    14
    15
    16
    17
    
    /*引入特效相关头文件*/
    #include "Kismet/GameplayStatics.h"
    
    /*ASWeapon()构造函数设置枪口骨骼的插槽名*/
    MuzzleSocketName = "MuzzleSocket";
    
    /*Fire()函数的if(WeaponOwner)判断最末尾添加*/
    if (MuzzleEffect)
    {
    	//生成特效并附加到组件上的某个骨骼上,参数为(粒子特效,要依附的组件,生成的命名点)
    	UGameplayStatics::SpawnEmitterAttached(MuzzleEffect,SKMeshComp,MuzzleSocketName);
    }
    if (ImpactEffect)
    {
    	//在生成特效在某个位置,参数为(生成的世界,粒子特效,世界位置,世界旋转)——SpawnEmitterAtLocation有三个重载。
    	UGameplayStatics::SpawnEmitterAtLocation(GetWorld(),ImpactEffect,Hit.ImpactPoint,Hit.ImpactPoint.Rotation());
    }
    

  2. BP_SWeapon蓝图类设置特效的默认值如下,并测试。

Untitled 5.png

  1. 攻击地板,可以看到已经有了枪口特效和击中特效,其中我们会发现奇怪的问题,射线检测是从角色上发射出来的,这明显是不对,需要修改成从摄像机(也就是我们作为玩家看的方向)发射,在后面编写弹道特效的同时会进行修改.

    14.webp

弹道特效
#

  1. 先修改下弹道特效粒子P_SmokeTrail的参数名为Target(光束粒子发射器)

    Untitled 6.webp

  2. 为武器类添加弹道特效

    SWeapon.h中声明弹道特效和弹道特效的追踪目标名
    1
    2
    3
    4
    5
    6
    7
    
    //弹道特效
    UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category = "Weapon")
    UParticleSystem* TracerEffect;
    
    //弹道特效参数名
    UPROPERTY(VisibleDefaultsOnly, BlueprintReadOnly, Category = "Weapon")
    FName TracerTargetName;
    
    SWeapon.cpp中的构造函数中定义弹道特效追踪目标名, 添加生成弹道特效逻辑
     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    14
    15
    16
    
    /*引入粒子组件*/
    #include "Particles/ParticleSystemComponent.h"
    
    /*ASWeapon()构造函数设置枪口骨骼的插槽名*/
    TracerTargetName= "Target";
    
    /*Fire()函数的if(WeaponOwner)判断中添加*/
    //获得枪口插槽位置。
    FVector MuzzleSocketLocation = SKMeshComp->GetSocketLocation(MuzzleSocketName);
    //枪口生成特效并拿到特效组件实例。
    UParticleSystemComponent* TracerComp = UGameplayStatics::SpawnEmitterAtLocation(GetWorld(),TracerEffect,MuzzleSocketLocation);
    if (TracerComp)
    {
    	//在此粒子组件上设置命名矢量实例参数。
    	TracerComp->SetVectorParameter(TracerTargetName,TraceEnd);
    }
    

  3. 为武器BP_SWeapon蓝图类设置上弹道特效默认值.

    15.webp

  4. 改变一下射线发射的初始位置.

    • 可以看到我们调用的GetActorEyesViewPoint()获取的位置是在角色上方加上BaseEyeHeight的值的位置,我们要修改成玩家摄像机位置.

      16.webp

    • 这是父类APawn的函数:

      17.webp

      18.webp

  5. 在角色类SCharacter中重写GetPawnViewLocation()函数

    SCharacter.h中声明要重写的函数
    1
    
        virtual FVector GetPawnViewLocation() const override;	
    
    ASCharacter.cpp重写函数, 让其返回相机组件的位置, 测试完成功后注释掉之前的debug射线
    1
    2
    3
    4
    5
    6
    7
    8
    
    FVector ASCharacter::GetPawnViewLocation() const
    {
    	if (CameraComp) return CameraComp->GetComponentLocation();
    	return Super::GetPawnViewLocation();
    }
    
    /*找到Fire()中的debug射线, 使待会观察弹道特效更明显*/
    //DrawDebugLine(GetWorld(), EyeLocation, TraceEnd, FColor::Red, false, 1, 0, 1);
    

添加武器准星
#

  1. 新建UI目录,新建控件蓝图,命名WBP_Crosshair.

  2. 拖入image图片控件,锚点设置为中心,其他设置如下.

    19.webp

  3. 在角色蓝图BP_SCharacter中的BeginPlayer事件编写逻辑,将控件添加到视口

    20.webp

该篇运行效果
#

gif2.gif

挑战:制作榴弹发射器
#

  1. 创建继承SWeapon的C++类SProjectileWeapon,重写Fire()函数

    SWeapon.h的Fire()函数修改为虚函数
    1
    2
    
    UFUNCTION(BlueprintCallable,Category="Weapon")
    virtual void Fire();
    
    SProjectileWeapon.h类中声明重写Fire()函数, 声明生成的子弹类
    1
    2
    3
    4
    5
    
    protected:
    virtual void Fire() override;
    
    UPROPERTY(EditDefaultsOnly,Category="ProjectileWeapon")
    TSubclassOf<AActor> ProjectileClass;//生成的子弹类
    
    SProjectileWeapon.cppFire()中重写生成榴弹的逻辑
     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    
    void ASProjectileWeapon::Fire()
    {
    	Super::Fire();
    
    	AActor* WeaponOwner = GetOwner();
    	if (WeaponOwner)
    	{
    		FVector EyeLocation;
    		FRotator EyeRotator;
    		WeaponOwner->GetActorEyesViewPoint(EyeLocation, EyeRotator);
    
    		//获取枪口位置
    		FVector MuzzleLocation = SKMeshComp->GetSocketLocation(MuzzleSocketName);
    
    		//生成参数
    		FActorSpawnParameters SpawnParams;
    		SpawnParams.SpawnCollisionHandlingOverride = ESpawnActorCollisionHandlingMethod::AlwaysSpawn;
    
    		if (ProjectileClass)
    		{
    			GetWorld()->SpawnActor<AActor>(ProjectileClass, MuzzleLocation, EyeRotator, SpawnParams);
    		}
    	}
    }
    

  2. 创建蓝图类BP_GrenadeLauncher,继承SProjectileWeapon类,设置该蓝图类的网格体为Launcher

  3. 并将该骨骼网格体Launcher的骨骼Launcher_Skeleton的插槽名MuzzleFlashSocket改为MuzzleSocket

  4. 创建子弹类蓝图BP_GrenadeProjectile继承Actor类,并添加发射物移动组件ProjectileMovement,设置初始和最大速度为2000,勾选应反弹

    • 添加组件球体,并作为根组件,缩放为0.25,勾选模拟物理
    • BeginPlay事件中编写特效和伤害代码
可以直接复制下面的蓝图 , 然后粘贴到你的编辑器哦

21.webp

  1. 设置BP_GrenadeLauncher的类默认值Projectile Class发射物类为BP_GrenadeProjectile

    22.webp

  2. BP_SCharacter角色类中的类默认值中设置要生成的武器类Starter Weapon Class从之前的步枪改成改为榴弹发射器类BP_GrenadeLauncher

    23.webp

  3. 测试结果

    gif3.gif

coopgame - 这篇文章属于一个选集。
§ 2: 本文