引擎版本:UE 4.22 + VS 2019; 学习教程:Unreal Engine 4 Mastery - Create Multiplayer Games With Cpp
1、创建一个Actor类(SWeapon)作为武器,并添加USkeletalMeshComponent
UPROPERTY(VisibleAnywhere,BlueprintReadOnly,Category=Components)
class USkeletalMeshComponent* MeshComp;ASWeapon::ASWeapon()
{
// 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;
MeshComp = CreateDefaultSubobject<USkeletalMeshComponent>(TEXT("MeshComp"));
RootComponent = MeshComp;
}
2、基于SWeapon创建蓝图BP_SWeapon,导入Weapon资源包,设置Mesh
3、设置BP_SCharacter在游戏开始时持有武器
(一)、BP_SCharacter蓝图中,Beginplay->Spawn Actor from class(class:BP_Sweapon,AlwaysSpawn,IgnoreCollision)->Attach To Component(snap to surface)
(二)、设置指定socket
(三)、调整Socket,使得武器更好的贴合人物
4、添加Fire()方法,并用蓝图快速测试
(一)、射线检测,起点(位置,旋转)+终点,如果中途与物体发生碰撞,则记录HitResult
#include "DrawDebugHelpers.h"
void ASWeapon::Fire()
{
AActor* MyOwner = GetOwner();
if (MyOwner) {
FVector EyeLocation;
FRotator EyeRotation;
MyOwner->GetActorEyesViewPoint(EyeLocation, EyeRotation);
FVector TraceEnd = EyeLocation + (EyeRotation.Vector() * 10000);
FCollisionQueryParams QueryParams;
QueryParams.AddIgnoredActor(MyOwner);
QueryParams.AddIgnoredActor(this);
QueryParams.bTraceComplex = true;
FHitResult Hit;
if (GetWorld()->LineTraceSingleByChannel(Hit, EyeLocation, TraceEnd, ECC_Visibility, QueryParams))
{
//Do damage
}
DrawDebugLine(GetWorld(),EyeLocation,TraceEnd,FColor::White,false,1.0f,0,1.0f);
}
}
(二)、重写GetActorEyesViewPoint方法,使得射线检测的起点为相机所在位置
FVector ASCharacter::GetPawnViewLocation() const
{
if (CameraComp) {
return CameraComp->GetComponentLocation();
}
return Super::GetPawnViewLocation();
}
(三)、BP_SCharacter调用Fire
5、应用Damage
(一)、完善Do Damage部分代码,其中DamageType的定义为TSubclassOf<UDamageType> DamageType;
if (GetWorld()->LineTraceSingleByChannel(Hit, EyeLocation, TraceEnd, ECC_Visibility, QueryParams))
{
//Do damage
AActor* HitActor = Hit.GetActor();
UGameplayStatics::ApplyPointDamage(HitActor,20.f, ShotDirection,Hit,MyOwner->GetInstigatorController(),this,DamageType);
}
(二)、添加一个Target,设置好collision,添加Event PointDamage->DrawDebugSphere进行测试
6、SWeapon中添加子弹射击特效
(一)、射击时火焰效果,
if (MuzzleEffect) {
UGameplayStatics::SpawnEmitterAttached(MuzzleEffect, MeshComp, MuzzleSocketName);
}
(二)、击中时效果
if (ImpactEffect) {
UGameplayStatics::SpawnEmitterAtLocation(GetWorld(), ImpactEffect, Hit.ImpactPoint, Hit.ImpactNormal.Rotation());
}
(三)、头文件中添加
UPROPERTY(VisibleDefaultsOnly, BlueprintReadOnly, Category = "Muzzle")
FName MuzzleSocketName;UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category = "Muzzle")
UParticleSystem* MuzzleEffect;UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category = "Muzzle")
UParticleSystem* ImpactEffect;
(四)、最终BP_SWeapon蓝图中指定所需要的资源
6、设置简单的准心 ,创建一个WidgetBP,在中心放置一张准心的图片(根据感觉调整UI的位置)
7、创建一个榴弹发射器
(一)、基于SWeapon派生一个C++类(SProjectfileWeapon),重写Fire方法:
protected:
virtual void Fire()override;
UPROPERTY(EditDefaultsOnly,BlueprintReadOnly,Category=Projectile)
TSubclassOf<AActor>ProjectileClass;
Fire方法主要是根据枪口位置发射子弹
void ASProjectfileWeapon::Fire()
{
AActor* MyOwner = GetOwner();
if (MyOwner) {
FVector EyeLocation;
FRotator EyeRotation;
MyOwner->GetActorEyesViewPoint(EyeLocation, EyeRotation);
FVector MuzzleLocation = MeshComp->GetSocketLocation(MuzzleSocketName);FActorSpawnParameters SpawnParams;
SpawnParams.SpawnCollisionHandlingOverride = ESpawnActorCollisionHandlingMethod::AlwaysSpawn;
//在枪口发射子弹
GetWorld()->SpawnActor<AActor>(ProjectileClass, MuzzleLocation,EyeRotation,SpawnParams);
}
}
(二)、派生子类蓝图(BP_GrenaradeLaucher),在其中添加武器的Mesh
(三)、创建一个Actor(BP_GrenaradeProjectile),作为子弹;主要是为该Actor添加一个ProjectileMovementcomponent和一个sphere(或者是子弹的具体模型),注意设置collision和simulate physics
(四)、在BP_GrenaradeProjectile的event graph中应用伤害
主要逻辑是Beginplay->Delay(1)->Explode(customEvent)
explode->spawn Emitter at location(指定爆炸特效,位置等信息)->apply radius damage->draw debugsphere->Destory
最后的效果:
8、添加镜头拉近效果: (主要是通过Camera的FOV实现)
(一)、按键绑定
PlayerInputComponent->BindAction("Zoom", IE_Pressed, this, &ASCharacter::BeginZoom);
PlayerInputComponent->BindAction("Zoom", IE_Released, this, &ASCharacter::EndZoom);void ASCharacter::BeginZoom()
{
bWantsToZoom = true;
}void ASCharacter::EndZoom()
{
bWantsToZoom = false;
}
(二)、 具体的镜头拉近是在tick中进行,FMath::FInterpTo可以使得镜头平缓的拉近
void ASCharacter::Tick(float DeltaTime)
{
Super::Tick(DeltaTime);
float TargetFOV = bWantsToZoom ? ZoomedFOV : DefaultFOV;
float CurrentFOV = FMath::FInterpTo(CameraComp->FieldOfView, TargetFOV, DeltaTime, ZoomInterSpeed);
CameraComp->SetFieldOfView(CurrentFOV);
}
对应的头文件中添加内容如下:
bool bWantsToZoom;
UPROPERTY(EditDefaultsOnly,BlueprintReadOnly,Category=player)
float ZoomedFOV;UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category = player,meta=(ClampMin=0.0,ClampMax=100))
float ZoomInterSpeed;float DefaultFOV;
void BeginZoom();
void EndZoom();
9、重构代码:将之前蓝图中生成武器调用fire方法用C++实现
需要注意的是蓝图与C++的不同,Sweapon中的Fire方法之前是Protected,如果想在SCharater中声明一个CurrentWeapon,要想调用SWeapon的Fire方法,需要把它设置为Public。也就是说蓝图中如果想要使用protected访问权限需要在UPROPERTY中使用BlueprintProtected.
在SCharacter中添加:
ASWeapon* CurrentWeapon;
UPROPERTY(EditDefaultsOnly,Category=player)
TSubclassOf<ASWeapon> StarterWeaponClass;
UPROPERTY(VisibleDefaultsOnly, Category = player)
FName WeaponAttachSocketName;
void Fire();对应的spawn出Weapon作为CurrentWeapon再吸附于指定位置
ASCharacter::ASCharacter()
{。。。。。。。
WeaponAttachSocketName = "WeaponSocket";
}
void ASCharacter::BeginPlay()
{
Super::BeginPlay();
DefaultFOV = CameraComp->FieldOfView;
//spawns a default weapon
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);
}
}
这样暂时就可以通过指定StarterWeaponClass来指定武器了
10、为武器射击时添加镜头抖动效果:
(一)、C++中调用ClientPlayCameraShake
void ASWeapon::PlayFireEffect()
{
if (MuzzleEffect) {
UGameplayStatics::SpawnEmitterAttached(MuzzleEffect, MeshComp, MuzzleSocketName);}
APawn* MyOwner = Cast<APawn>(GetOwner());
if (MyOwner)
{
APlayerController* PC = Cast<APlayerController>(MyOwner->GetController());
if (PC) {
PC->ClientPlayCameraShake(FireCamShake);
}
}
}。。。。。。。。。。。。。。
UPROPERTY(EditDefaultsOnly,Category=Weapon)
TSubclassOf<UCameraShake>FireCamShake;
(二)、创建一个cameraShake蓝图(CamShake_RifileFire),进行对应的水平后座、垂直后座等参数的设置
11、优化与问题:
首先是榴弹发射器的子弹,添加Mesh与TraceEffect
问题:(真实感)
1、关于子弹:Refile的伤害计算是用射线检测做的对应point damage(缺乏真实感),榴弹发射器发射真实的子弹对应不到准心。
2、关于动画:相机向上向下时,持枪的朝向也需要改变