CoopGame02-武器1#
导入武器相关资产#
- 下载源文件,密码:zmingu
- 导入CoopGame\Content\Weapons文件夹到项目
Content目录
创建并附加武器到角色身上#
创建武器类#
- 创建武器C++类
SWeapon,继承自Actor,并初始化武器的骨骼网格体。在
SWeapon.h中声明武器的骨骼网格体组件1 2 3//武器的骨骼网格体 UPROPERTY(VisibleAnywhere,BlueprintReadOnly,Category="Weapon") class USkeletalMeshComponent* SKMeshComp;在
SWeapon.cpp中的构造函数中初始化武器的骨骼网格体组件1 2 3 4 5ASWeapon::ASWeapon() { SKMeshComp = CreateDefaultSubobject<USkeletalMeshComponent>(TEXT("SKMeshComp")); RootComponent = SKMeshComp; } - 创建继承
SWeapon武器类的蓝图类BP_SWeapon,并选择设置武器的骨骼网格体SK_Rifle.
为角色生成武器#
在角色骨骼的
hand_r右键添加名为WeaponSocket武器的骨骼插槽。
在
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> StartWeaponClass; //用来放武器的插槽名 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>(StartWeaponClass, FVector::ZeroVector, FRotator::ZeroRotator, SpawnParams); if (CurrentWeapon) { //如果当前武器已经有了,设置武器拥有者为当前玩家,将武器依附到武器插槽。 CurrentWeapon->SetOwner(this); CurrentWeapon->AttachToComponent(GetMesh(), FAttachmentTransformRules::SnapToTargetNotIncludingScale, WeaponAttachSocketName); }在角色蓝图类中设置默认值,将要生成的武器类
StartWeaponClass的类默认值设为BP_SWeapon
调整武器位置#
- 在人物骨骼的武器骨骼插槽
WeaponSocket右键添加预览资产SK_Rifle,在右侧选择预览动画选Idle_Rifle_Hip
- 调整插槽位置使武器位置正确 , 可直接使用如图参数。

让武器能够开火#
在武器类
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); } }在角色蓝图
BP_SCharacter使用鼠标左键事件调用武器的开火函数,此时开火可见从玩家身上摄像机发出的射线。(右键搜索CurrentWeapon变量,从该变量调用Fire()函数)

使开火产生伤害#
- 在武器类中添加伤害类型变量
- 伤害类型介绍:

在
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); }
调一下角色身上的弹簧臂的位置,参数如图,让待会更好观察并测试代码结果.

在角色蓝图类
BP_SCharacter,添加事件点状伤害并绘制调试球体,将网格体碰撞预设设置为BlockAll
拖入一个
BP_SCharacter到场景中, 用于测试, 如果没问题能看到如图射击到人会产生调试球 , 说明我们已经打出伤害了.
添加武器开火特效#
枪口特效和击中特效#
导入之前项目下载的武器特效资产 , 目录为
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()); }BP_SWeapon蓝图类设置特效的默认值如下,并测试。

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

弹道特效#
先修改下弹道特效粒子
P_SmokeTrail的参数名为Target(光束粒子发射器)
为武器类添加弹道特效
在
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); }为武器
BP_SWeapon蓝图类设置上弹道特效默认值.
改变一下射线发射的初始位置.
可以看到我们调用的
GetActorEyesViewPoint()获取的位置是在角色上方加上BaseEyeHeight的值的位置,我们要修改成玩家摄像机位置.
这是父类APawn的函数:


在角色类
SCharacter中重写GetPawnViewLocation()函数在
SCharacter.h中声明要重写的函数1virtual FVector GetPawnViewLocation() const override;在
ASCharacter.cpp重写函数, 让其返回相机组件的位置, 测试完成功后注释掉之前的debug射线1 2 3 4 5 6 7 8FVector ASCharacter::GetPawnViewLocation() const { if (CameraComp) return CameraComp->GetComponentLocation(); return Super::GetPawnViewLocation(); } /*找到Fire()中的debug射线, 使待会观察弹道特效更明显*/ //DrawDebugLine(GetWorld(), EyeLocation, TraceEnd, FColor::Red, false, 1, 0, 1);
添加武器准星#
新建
UI目录,新建控件蓝图,命名WBP_Crosshair.拖入
image图片控件,锚点设置为中心,其他设置如下.
在角色蓝图
BP_SCharacter中的BeginPlayer事件编写逻辑,将控件添加到视口
该篇运行效果#

挑战:制作榴弹发射器#
创建继承
SWeapon的C++类SProjectileWeapon,重写Fire()函数将
SWeapon.h的Fire()函数修改为虚函数1 2UFUNCTION(BlueprintCallable,Category="Weapon") virtual void Fire();在
SProjectileWeapon.h类中声明重写Fire()函数, 声明生成的子弹类1 2 3 4 5protected: virtual void Fire() override; UPROPERTY(EditDefaultsOnly,Category="ProjectileWeapon") TSubclassOf<AActor> ProjectileClass;//生成的子弹类在
SProjectileWeapon.cpp的Fire()中重写生成榴弹的逻辑1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24void 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); } } }创建蓝图类
BP_GrenadeLauncher,继承SProjectileWeapon类,设置该蓝图类的网格体为Launcher并将该骨骼网格体
Launcher的骨骼Launcher_Skeleton的插槽名MuzzleFlashSocket改为MuzzleSocket创建子弹类蓝图
BP_GrenadeProjectile继承Actor类,并添加发射物移动组件ProjectileMovement,设置初始和最大速度为2000,勾选应反弹- 添加组件
球体,并作为根组件,缩放为0.25,勾选模拟物理 - 在
BeginPlay事件中编写特效和伤害代码
- 添加组件

设置
BP_GrenadeLauncher的类默认值Projectile Class发射物类为BP_GrenadeProjectile
在
BP_SCharacter角色类中的类默认值中设置要生成的武器类Starter Weapon Class从之前的步枪改成改为榴弹发射器类BP_GrenadeLauncher
测试结果
