CoopGame02-武器1

type: Post
status: Published
date: 2022/10/02
slug: CoopGame02
summary: UE4 C++多人游戏入门
category: Unreal

导入资产

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

创建武器

创建武器类

  1. 创建武器C++类SWeapon,继承Actor的。

    /*SWeapon.h*/
    
    //武器的骨骼网格体
    UPROPERTY(VisibleAnywhere,BlueprintReadOnly,Category="Weapon")
    class USkeletalMeshComponent* SKMeshComp;
    
    /*SWeapon.cpp*/
    
    ASWeapon::ASWeapon()
    {
        SKMeshComp = CreateDefaultSubobject<USkeletalMeshComponent>(TEXT("SKMeshComp"));
        RootComponent = SKMeshComp;
    }
    
  2. 创建继承SWeapon武器类的蓝图类BP_SWeapon,并选择设置武器的骨骼网格体SK_Rifle.

    CoopGame02-武器1

为角色生成武器

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

    CoopGame02-武器1

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

    /*SCharacter.h*/
    
    //当前武器
    UPROPERTY(BlueprintReadOnly)
    class ASWeapon* CurrentWeapon;
    //需要生成的武器类
    UPROPERTY(EditDefaultsOnly, Category = "Player")
    TSubclassOf<ASWeapon> StarterWeaponClass;
    //武器插槽名
    UPROPERTY(VisibleDefaultsOnly, Category = "Player")
    FName WeaponAttachSocketName;
    
    /*SCharacter.cpp*/
    
    /*引入武器类*/
    #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

    CoopGame02-武器1

  2. 调整插槽位置使武器位置正确。

    CoopGame02-武器1

武器开火

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

    /*SWeapon.h*/
    
    //开火函数
    UFUNCTION(BlueprintCallable,Category="Weapon")
    void Fire();
    
    /*SWeapon.cpp*/
    
    /*引入绘制调试射线助手类*/
    include "DrawDebugHelpers.h"
    
    /*定义Fire()函数*/
    void ASWeapon::Fire()
    {
        AActor* WeaponOwner= GetOwner();
        if (WeaponOwner)
        {
            /*
             * 弹道的起点和终点
             */
            FVector EyeLocation;
            FRotator EyeRotator;
            //弹道起点 = 玩家摄像头位置
            Cast<APawn>(Owner)->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()函数)

    CoopGame02-武器1

    CoopGame02-武器1

使开火产生伤害

  1. 武器类中添加伤害类型变量

    1. 伤害类型介绍:

      CoopGame02-武器1

/*SWeapon.h*/

//伤害类型
UPROPERTY(EditDefaultsOnly,BlueprintReadOnly,Category="Weapon")
TSubclassOf<UDamageType> DamageType;
/*SWeapon.cpp*/

/*补充射线击中需要处理的事情*/
//如果射线打到东西
if (bHit)
{
    //应用点状伤害(被伤害的Actor,要应用的基础伤害,受击方向,描述命中的碰撞或跟踪结果,照成伤害的控制器(例如射击武器的玩家的控制器),实际造成伤害的Actor,伤害类型)
    UGameplayStatics::ApplyPointDamage(Hit.GetActor(),20,EyeRotator.Vector(),Hit,WeaponOwner->GetInstigatorController(),this,DamageType);
}
  1. 这里调一下角色身上的弹簧臂的位置,让待会更好观察并测试代码结果.

    CoopGame02-武器1

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

    1. 在场景中拖入BP_SCharacter测试射击结果。

    CoopGame02-武器1

    CoopGame02-武器1

开火特效

枪口特效和击中特效

  1. 导入武器特效资产,目录WeaponEffects,并编写SWeapon武器类。

    /*SWeapon.h*/
    
    //枪骨骼上的枪口骨骼名
    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*/
    
    /*引入特效相关头文件*/
    #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蓝图类设置特效的默认值如下,并测试。

    CoopGame02-武器1

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

    CoopGame02-武器1

弹道特效

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

    CoopGame02-武器1

  2. 添加弹道特效

    /*SWeapon.h*/
    
    //弹道特效
    UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category = "Weapon")
    UParticleSystem* TracerEffect;
    
    //弹道特效参数名
    UPROPERTY(VisibleDefaultsOnly, BlueprintReadOnly, Category = "Weapon")
    FName TracerTargetName;
    
    /*SWeapon.cpp*/
    
    /*引入粒子组件*/
    #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蓝图类设置上弹道特效默认值.

    CoopGame02-武器1

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

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

      CoopGame02-武器1

      CoopGame02-武器1

      CoopGame02-武器1

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

      /*SCharacter.h*/
      virtual FVector GetPawnViewLocation() const override;    
      /*SCharacter.cpp*/
      
      FVector ASCharacter::GetPawnViewLocation() const
      {
          if (CameraComp) return CameraComp->GetComponentLocation();
          return Super::GetPawnViewLocation();
      }
  5. 注释掉之前的调试射线

    /*SWeapon.cpp*/
    
    //把射线检测绘制出来
    //DrawDebugLine(GetWorld(), EyeLocation, TraceEnd, FColor::Red, false, 1, 0, 1);

武器准星

  1. 新建UI目录,新建控件蓝图,命名WBP_Crosshair.
  2. 拖入image图片,锚点设置为中心,其他设置如下.

    CoopGame02-武器1

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

    CoopGame02-武器1

运行效果

!CoopGame02-武器1

挑战:榴弹发射器

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

    /*SWeapon.h*/
    UFUNCTION(BlueprintCallable,Category="Weapon")
    virtual void Fire();
  3. SProjectileWeapon类中重写Fire()函数,声明子弹类.

    /*SProjectileWeapon.h*/
    protected:
    virtual void Fire() override;
    
    UPROPERTY(EditDefaultsOnly,Category="ProjectileWeapon")
    TSubclassOf<AActor> ProjectileClass;//生成的子弹类
    /*SProjectileWeapon.cpp*/
    
    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);
            }
        }
    }
    
  4. 创建蓝图类BP_GrenadeLauncher,继承SProjectileWeapon类,设置该蓝图类的网格体为Launcher
  5. 重命名Launcher骨骼网格体的骨骼Launcher_Skeleton中的插槽名MuzzleFlashSocket改为MuzzleSocket
  6. 创建子弹类蓝图BP_GrenadeProjectile继承Actor类,并添加发射物移动组件ProjectileMovement,设置初始和最大速度为2000,勾选应反弹

    1. 添加球体网格,并作为根组件,缩放为0.25,勾选模拟物理
    2. 在BeginPlay事件中编写特效和伤害代码

      CoopGame02-武器1

  7. BP_GrenadeLauncher榴弹发射器类中中设置发射物类为BP_GrenadeProjectile

    CoopGame02-武器1

  8. BP_SCharacter角色类中的类默认值中设置要生成的武器为榴弹发射器类BP_GrenadeLauncher

    CoopGame02-武器1

  9. 测试结果

    !CoopGame02-武器1

文章目录