UEC++ 虚幻5第三人称射击游戏(一)
- 创建一个空白的C++工程
人物角色基本移动
- 创建一个Character类
- 添加一些虚幻商城中的基础动画
- 给角色类添加Camera与SPringArm组件
UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "SpringArm")
class USpringArmComponent* SpringArm;
UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Camera")
class UCameraComponent* Camera;
// Sets default values
AMyCharacter::AMyCharacter()
{
// Set this character to call Tick() every frame. You can turn this off to improve performance if you don't need it.
PrimaryActorTick.bCanEverTick = true;
SpringArm = CreateDefaultSubobject<USpringArmComponent>(TEXT("SpringArm"));
SpringArm->SetupAttachment(GetRootComponent());
SpringArm->TargetArmLength = 400.f;
SpringArm->bUsePawnControlRotation = true;
Camera = CreateDefaultSubobject<UCameraComponent>(TEXT("Camera"));
Camera->SetupAttachment(SpringArm);
Camera->bUsePawnControlRotation = false;
}
角色基本移动增强输入系统MyCharacter.h
- 角色基本移动增强输入系统
// Fill out your copyright notice in the Description page of Project Settings.
#pragma once
#include "CoreMinimal.h"
#include "GameFramework/Character.h"
#include "InputActionValue.h"
#include "MyCharacter.generated.h"
UCLASS()
class SHOOTGAME_API AMyCharacter : public ACharacter
{
GENERATED_BODY()
public:
// Sets default values for this character's properties
AMyCharacter();
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Input")
class UInputMappingContext* DefaultMappingContext;
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Input")
class UInputAction* MoveAction;
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Input")
class UInputAction* LookAction;
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Input")
class UInputAction* CrouchAction;
UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "SpringArm")
class USpringArmComponent* SpringArm;
UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Camera")
class UCameraComponent* Camera;
protected:
// Called when the game starts or when spawned
virtual void BeginPlay() override;
void CharacterMove(const FInputActionValue& Value);
void CharacterLook(const FInputActionValue& Value);
void BeginCrouch();
void EndCtouch();
public:
// Called every frame
virtual void Tick(float DeltaTime) override;
// Called to bind functionality to input
virtual void SetupPlayerInputComponent(class UInputComponent* PlayerInputComponent) override;
};
角色基本移动增强输入系统MyCharacter.cpp
GetMovementComponent()->GetNavAgentPropertiesRef().bCanCrouch = true;
:允许角色进行蹲伏(crouch)动作,并且能够影响导航代理(Navigation Agent)的行为- GetMovementComponent():获取角色的移动组件
- GetNavAgentPropertiesRef():获取导航代理属性的引用。这些属性用于定义角色如何与导航系统交互,例如高度、半径、最大爬坡角度等。
- .bCanCrouch = true;:设置导航代理的一个布尔属性,表示该角色可以进行蹲伏,并且在寻路过程中应当考虑其能通过更低矮的空间。这意味着在自动寻路时,引擎会考虑到角色在蹲伏状态下可以通过的高度限制区域。
- Crouch与OnCrouch:虚幻自带的蹲伏函数
// Fill out your copyright notice in the Description page of Project Settings.
#include "MyCharacter.h"
#include "Camera/CameraComponent.h"
#include "GameFramework/SpringArmComponent.h"
#include "EnhancedInputComponent.h"
#include "EnhancedInputSubsystems.h"
#include "GameFramework/PawnMovementComponent.h"
#include "GameFramework/CharacterMovementComponent.h"
// Sets default values
AMyCharacter::AMyCharacter()
{
// Set this character to call Tick() every frame. You can turn this off to improve performance if you don't need it.
PrimaryActorTick.bCanEverTick = true;
//允许角色进行蹲伏(crouch)动作,并且能够影响导航代理(Navigation Agent)的行为
GetMovementComponent()->GetNavAgentPropertiesRef().bCanCrouch = true;
//自动转向
GetCharacterMovement()->bOrientRotationToMovement = true;
//对Character的Pawn的朝向进行硬编码
bUseControllerRotationPitch = false;
bUseControllerRotationYaw = false;
bUseControllerRotationRoll = false;
SpringArm = CreateDefaultSubobject<USpringArmComponent>(TEXT("SpringArm"));
SpringArm->SetupAttachment(GetRootComponent());
SpringArm->TargetArmLength = 400.f;
SpringArm->bUsePawnControlRotation = true;
Camera = CreateDefaultSubobject<UCameraComponent>(TEXT("Camera"));
Camera->SetupAttachment(SpringArm);
Camera->bUsePawnControlRotation = false;
}
// Called when the game starts or when spawned
void AMyCharacter::BeginPlay()
{
Super::BeginPlay();
APlayerController* PlayerController = Cast<APlayerController>(Controller);
if (PlayerController)
{
UEnhancedInputLocalPlayerSubsystem* Subsystem =
ULocalPlayer::GetSubsystem<UEnhancedInputLocalPlayerSubsystem>(PlayerController->GetLocalPlayer());
if (Subsystem)
{
Subsystem->AddMappingContext(DefaultMappingContext, 0);
}
}
}
void AMyCharacter::CharacterMove(const FInputActionValue& Value)
{
FVector2D MovementVector = Value.Get<FVector2D>();
if (Controller)
{
FRotator Rotation = Controller->GetControlRotation();
FRotator YawRotation = FRotator(0., Rotation.Yaw, 0.);
FVector ForwardDirection = FRotationMatrix(YawRotation).GetUnitAxis(EAxis::X);
FVector RightDirection = FRotationMatrix(YawRotation).GetUnitAxis(EAxis::Y);
AddMovementInput(ForwardDirection, MovementVector.Y);
AddMovementInput(RightDirection, MovementVector.X);
}
}
void AMyCharacter::CharacterLook(const FInputActionValue& Value)
{
FVector2D LookVector = Value.Get<FVector2D>();
if (Controller)
{
AddControllerPitchInput(LookVector.Y);
AddControllerYawInput(LookVector.X);
}
}
void AMyCharacter::BeginCrouch()
{
Crouch();
}
void AMyCharacter::EndCtouch()
{
UnCrouch();
}
// Called every frame
void AMyCharacter::Tick(float DeltaTime)
{
Super::Tick(DeltaTime);
}
// Called to bind functionality to input
void AMyCharacter::SetupPlayerInputComponent(UInputComponent* PlayerInputComponent)
{
Super::SetupPlayerInputComponent(PlayerInputComponent);
UEnhancedInputComponent* EnhancedInputComponent = Cast<UEnhancedInputComponent>(PlayerInputComponent);
if (EnhancedInputComponent)
{
EnhancedInputComponent->BindAction(MoveAction, ETriggerEvent::Triggered, this, &AMyCharacter::CharacterMove);
EnhancedInputComponent->BindAction(LookAction, ETriggerEvent::Triggered, this, &AMyCharacter::CharacterLook);
EnhancedInputComponent->BindAction(CrouchAction, ETriggerEvent::Triggered, this, &AMyCharacter::BeginCrouch);
EnhancedInputComponent->BindAction(CrouchAction, ETriggerEvent::Completed, this, &AMyCharacter::EndCtouch);
}
}
创建动画蓝图与混合空间
- 创建一个混合空间1D
- 创建一个动画蓝图,将这个混合空间链接上去
引入第三人称射击模型
- 我们新建一个Character蓝图作为第三人称射击测试角色
- 在虚幻商城里面添加我们需要的射击动画
- 打开这个内容包的动画蓝图
- 我们只需要移动所以跳跃那些都可以删除
- 然后使用这个包自己的动画蓝图与骨骼资产,它的移动全是在动画蓝图中实现的,所以角色蓝图就不用实现移动,用我们创建的角色去使用这个动画蓝图与骨骼资产,然后设置蹲伏的逻辑
人物动画IK绑定
- 首先在虚幻商城下载一个自己喜欢的模型添加到项目资产里面
- 添加IK绑定
- 选择我们第三人称射击动作那个骨骼,Pelvis设置为根组件
- 然后添加骨骼链条
人物动画重定向
- 设置自己角色的IK绑定
- 创建重定向器,源是小白人
- 导出重定向的动画
- 将动画蓝图给到角色蓝图
创建武器类
- 导入武器模型
- 创建武器类
- 创建武器类的蓝图添加模型上去
- 在角色蓝图中添加一个手持武器的插槽
- 在角色蓝图中,将武器附加到角色手上
- 调整好武器的位置参数
- 运行结果
创建武器追踪线
-
在
Weapon
类中创建一个开火的函数,描绘一些射击的追踪线
-
LineTraceSingleByChannel:使用特定的通道追踪光线并返回第一个阻塞命中TRUE(如果发现了阻塞命中)
-
逻辑源码
void AWeapon::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;
//使用特定的通道追踪光线并返回第一个阻塞命中TRUE(如果发现了阻塞命中)
if (GetWorld()->LineTraceSingleByChannel(Hit, EyeLocation, TraceEnd, ECC_Visibility, QueryParams))
{
//设置受阻,造成伤害结果
}
DrawDebugLine(GetWorld(), EyeLocation, TraceEnd,FColor::Red,false,1.0f,0,1.0f);
}
}
调整追踪线位置
- 我们需要将追踪线变为我们摄像机眼睛看见的位置,我们需要重写
APawn
类中的GetPawnViewLocation
函数
- 在角色蓝图中实例化对象
Weapon
类,进行鼠标左键点击测试绘画的线是否是从摄像机眼睛处发出
- 运行结果
创建伤害效果
- 补全
Fire
函数中的伤害处理逻辑 - 首先添加一个
UDamageType
模版变量,来存储造成伤害的类
- 补全逻辑
Fire
函数
void AWeapon::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;
FVector ShotDirection = EyeRotation.Vector();
//使用特定的通道追踪光线并返回第一个阻塞命中TRUE(如果发现了阻塞命中)
if (GetWorld()->LineTraceSingleByChannel(Hit, EyeLocation, TraceEnd, ECC_Visibility, QueryParams))
{
//设置受阻,造成伤害结果
AActor* HitActor = Hit.GetActor();
UGameplayStatics::ApplyPointDamage(HitActor, 10.f, ShotDirection, Hit, MyOwner->GetInstigatorController(),
this, DamageType);
}
DrawDebugLine(GetWorld(), EyeLocation, TraceEnd,FColor::Red,false,1.0f,0,1.0f);
}
}
- 运行结果
创建射击特效
- 导入粒子资源
- 添加两个粒子系统,与一个附加到骨骼的变量
- 默认骨骼名
- 在
Fire
函数中添加特效与位置逻辑,一个是开火的特效粒子,一个是子弹打击到目标身上的掉血粒子
- 将特效添加到武器蓝图中
- 将武器骨骼插槽的名字改为我们设置的名字
- 微调一下摄像机方便测试
- 运行结果
Weapon.h
// Fill out your copyright notice in the Description page of Project Settings.
#pragma once
#include "CoreMinimal.h"
#include "GameFramework/Actor.h"
#include "Weapon.generated.h"
UCLASS()
class SHOOTGAME_API AWeapon : public AActor
{
GENERATED_BODY()
public:
// Sets default values for this actor's properties
AWeapon();
protected:
// Called when the game starts or when spawned
virtual void BeginPlay() override;
//武器骨骼
UPROPERTY(VisibleAnywhere,BlueprintReadOnly,Category = "Components")
class USkeletalMeshComponent* SkeletalComponent;
//开火
UFUNCTION(BlueprintCallable,Category = "WeaponFire")
void Fire();
//描述所造成的伤害的类
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Weapon")
TSubclassOf<class UDamageType> DamageType;
//炮口粒子系统
UPROPERTY(EditAnywhere,BlueprintReadOnly,Category="WeaponParticle")
class UParticleSystem* MuzzleEffect;
//粒子附加到骨骼的名字
UPROPERTY(EditAnywhere,BlueprintReadOnly,Category="WeaponParticle")
FName MuzzleSocketName;
//撞击到敌人身上的粒子系统
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "WeaponParticle")
UParticleSystem* ImpactEffect;
public:
// Called every frame
virtual void Tick(float DeltaTime) override;
};
Weapon.cpp
// Fill out your copyright notice in the Description page of Project Settings.
#include "Weapon.h"
#include <Kismet/GameplayStatics.h>
#include "Particles/ParticleSystem.h"
// Sets default values
AWeapon::AWeapon()
{
// 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;
SkeletalComponent = CreateAbstractDefaultSubobject<USkeletalMeshComponent>(TEXT("SkeletalComponent"));
SkeletalComponent->SetupAttachment(GetRootComponent());
MuzzleSocketName = "MuzzleSocket";
}
// Called when the game starts or when spawned
void AWeapon::BeginPlay()
{
Super::BeginPlay();
}
void AWeapon::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;
FVector ShotDirection = EyeRotation.Vector();
//使用特定的通道追踪光线并返回第一个阻塞命中TRUE(如果发现了阻塞命中)
if (GetWorld()->LineTraceSingleByChannel(Hit, EyeLocation, TraceEnd, ECC_Visibility, QueryParams))
{
//设置受阻,造成伤害结果
AActor* HitActor = Hit.GetActor();
UGameplayStatics::ApplyPointDamage(HitActor, 10.f, ShotDirection, Hit, MyOwner->GetInstigatorController(),
this, DamageType);
//粒子生成的位置
UGameplayStatics::SpawnEmitterAtLocation(GetWorld(), ImpactEffect, Hit.ImpactPoint,
Hit.ImpactNormal.Rotation());
}
DrawDebugLine(GetWorld(), EyeLocation, TraceEnd,FColor::Red,false,1.0f,0,1.0f);
if (MuzzleEffect)
{
//附加粒子效果
UGameplayStatics::SpawnEmitterAttached(MuzzleEffect,SkeletalComponent,MuzzleSocketName);
}
}
}
// Called every frame
void AWeapon::Tick(float DeltaTime)
{
Super::Tick(DeltaTime);
}
创建十字准心
- 创建一个控件蓝图作为十字准心的窗口
- 在角色蓝图的
BeginPlay
中添加这个视口到窗口中
- 运行结果
MyCharacter.h
// Fill out your copyright notice in the Description page of Project Settings.
#pragma once
#include "CoreMinimal.h"
#include "GameFramework/Character.h"
#include "InputActionValue.h"
#include "MyCharacter.generated.h"
UCLASS()
class SHOOTGAME_API AMyCharacter : public ACharacter
{
GENERATED_BODY()
public:
// Sets default values for this character's properties
AMyCharacter();
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Input")
class UInputMappingContext* DefaultMappingContext;
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Input")
class UInputAction* MoveAction;
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Input")
class UInputAction* LookAction;
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Input")
class UInputAction* CrouchAction;
UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "SpringArm")
class USpringArmComponent* SpringArm;
UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Camera")
class UCameraComponent* Camera;
//UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Animation", meta = (AllowPrivateAccess = "true"))
//class UAnimInstance* MyAnimInstance; // 或者使用 TSubclassOf<UAnimInstance> 作为类型指向动画蓝图类
protected:
// Called when the game starts or when spawned
virtual void BeginPlay() override;
void CharacterMove(const FInputActionValue& Value);
void CharacterLook(const FInputActionValue& Value);
void BeginCrouch();
void EndCtouch();
public:
// Called every frame
virtual void Tick(float DeltaTime) override;
// Called to bind functionality to input
virtual void SetupPlayerInputComponent(class UInputComponent* PlayerInputComponent) override;
//重写GetPawnViewLocation函数将其返回摄像机的眼睛看见的位置
virtual FVector GetPawnViewLocation() const override;
};
MyCharacter.cpp
// Fill out your copyright notice in the Description page of Project Settings.
#include "MyCharacter.h"
#include "Camera/CameraComponent.h"
#include "GameFramework/SpringArmComponent.h"
#include "EnhancedInputComponent.h"
#include "EnhancedInputSubsystems.h"
#include "GameFramework/PawnMovementComponent.h"
#include "GameFramework/CharacterMovementComponent.h"
#include "Engine/Engine.h"
// Sets default values
AMyCharacter::AMyCharacter()
{
// Set this character to call Tick() every frame. You can turn this off to improve performance if you don't need it.
PrimaryActorTick.bCanEverTick = true;
//允许角色进行蹲伏(crouch)动作,并且能够影响导航代理(Navigation Agent)的行为
GetMovementComponent()->GetNavAgentPropertiesRef().bCanCrouch = true;
//自动转向
GetCharacterMovement()->bOrientRotationToMovement = true;
//对Character的Pawn的朝向进行硬编码
bUseControllerRotationPitch = false;
bUseControllerRotationYaw = false;
bUseControllerRotationRoll = false;
SpringArm = CreateDefaultSubobject<USpringArmComponent>(TEXT("SpringArm"));
SpringArm->SetupAttachment(GetRootComponent());
SpringArm->TargetArmLength = 400.f;
SpringArm->bUsePawnControlRotation = true;
Camera = CreateDefaultSubobject<UCameraComponent>(TEXT("Camera"));
Camera->SetupAttachment(SpringArm);
Camera->bUsePawnControlRotation = false;
}
// Called when the game starts or when spawned
void AMyCharacter::BeginPlay()
{
Super::BeginPlay();
APlayerController* PlayerController = Cast<APlayerController>(Controller);
if (PlayerController)
{
UEnhancedInputLocalPlayerSubsystem* Subsystem =
ULocalPlayer::GetSubsystem<UEnhancedInputLocalPlayerSubsystem>(PlayerController->GetLocalPlayer());
if (Subsystem)
{
Subsystem->AddMappingContext(DefaultMappingContext, 0);
}
}
}
void AMyCharacter::CharacterMove(const FInputActionValue& Value)
{
FVector2D MovementVector = Value.Get<FVector2D>();
if (Controller)
{
FRotator Rotation = Controller->GetControlRotation();
FRotator YawRotation = FRotator(0., Rotation.Yaw, 0.);
FVector ForwardDirection = FRotationMatrix(YawRotation).GetUnitAxis(EAxis::X);
FVector RightDirection = FRotationMatrix(YawRotation).GetUnitAxis(EAxis::Y);
AddMovementInput(ForwardDirection, MovementVector.Y);
AddMovementInput(RightDirection, MovementVector.X);
}
}
void AMyCharacter::CharacterLook(const FInputActionValue& Value)
{
FVector2D LookVector = Value.Get<FVector2D>();
if (Controller)
{
AddControllerPitchInput(LookVector.Y);
AddControllerYawInput(LookVector.X);
}
}
void AMyCharacter::BeginCrouch()
{
//bIsCrouched = true;
//Crouch();
//FString MessageString;
//MessageString.AppendInt((TEXT("bIsCrouched is111, %s"), bIsCrouched));
//GEngine->AddOnScreenDebugMessage(0, 10.f, FColor::Red, MessageString);
GEngine->AddOnScreenDebugMessage(0, 10.f, FColor::Red, FString::Printf(bIsCrouched));
}
void AMyCharacter::EndCtouch()
{
//bIsCrouched = false;
//UnCrouch();
//FString MessageString;
//MessageString.AppendInt((TEXT("bIsCrouched is222, %s"), bIsCrouched));
//GEngine->AddOnScreenDebugMessage(0, 10.f, FColor::Red, MessageString);
}
// Called every frame
void AMyCharacter::Tick(float DeltaTime)
{
Super::Tick(DeltaTime);
}
// Called to bind functionality to input
void AMyCharacter::SetupPlayerInputComponent(UInputComponent* PlayerInputComponent)
{
Super::SetupPlayerInputComponent(PlayerInputComponent);
UEnhancedInputComponent* EnhancedInputComponent = Cast<UEnhancedInputComponent>(PlayerInputComponent);
if (EnhancedInputComponent)
{
EnhancedInputComponent->BindAction(MoveAction, ETriggerEvent::Triggered, this, &AMyCharacter::CharacterMove);
EnhancedInputComponent->BindAction(LookAction, ETriggerEvent::Triggered, this, &AMyCharacter::CharacterLook);
EnhancedInputComponent->BindAction(CrouchAction, ETriggerEvent::Triggered, this, &AMyCharacter::BeginCrouch);
EnhancedInputComponent->BindAction(CrouchAction, ETriggerEvent::Completed, this, &AMyCharacter::EndCtouch);
}
}
FVector AMyCharacter::GetPawnViewLocation() const
{
if (Camera)
{
//返回摄像机眼睛的位置
return Camera->GetComponentLocation();
}
return Super::GetPawnViewLocation();
}
Weapon.h
// Fill out your copyright notice in the Description page of Project Settings.
#pragma once
#include "CoreMinimal.h"
#include "GameFramework/Actor.h"
#include "Weapon.generated.h"
UCLASS()
class SHOOTGAME_API AWeapon : public AActor
{
GENERATED_BODY()
public:
// Sets default values for this actor's properties
AWeapon();
protected:
// Called when the game starts or when spawned
virtual void BeginPlay() override;
//武器骨骼
UPROPERTY(VisibleAnywhere,BlueprintReadOnly,Category = "Components")
class USkeletalMeshComponent* SkeletalComponent;
//开火
UFUNCTION(BlueprintCallable,Category = "WeaponFire")
void Fire();
//描述所造成的伤害的类
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Weapon")
TSubclassOf<class UDamageType> DamageType;
//炮口粒子系统
UPROPERTY(EditAnywhere,BlueprintReadOnly,Category="WeaponParticle")
class UParticleSystem* MuzzleEffect;
//粒子附加到骨骼的名字
UPROPERTY(EditAnywhere,BlueprintReadOnly,Category="WeaponParticle")
FName MuzzleSocketName;
//撞击到敌人身上的粒子系统
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "WeaponParticle")
UParticleSystem* ImpactEffect;
public:
// Called every frame
virtual void Tick(float DeltaTime) override;
};
Weapon.cpp
// Fill out your copyright notice in the Description page of Project Settings.
#include "Weapon.h"
#include <Kismet/GameplayStatics.h>
#include "Particles/ParticleSystem.h"
// Sets default values
AWeapon::AWeapon()
{
// 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;
SkeletalComponent = CreateAbstractDefaultSubobject<USkeletalMeshComponent>(TEXT("SkeletalComponent"));
SkeletalComponent->SetupAttachment(GetRootComponent());
MuzzleSocketName = "MuzzleSocket";
}
// Called when the game starts or when spawned
void AWeapon::BeginPlay()
{
Super::BeginPlay();
}
void AWeapon::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;
FVector ShotDirection = EyeRotation.Vector();
//使用特定的通道追踪光线并返回第一个阻塞命中TRUE(如果发现了阻塞命中)
if (GetWorld()->LineTraceSingleByChannel(Hit, EyeLocation, TraceEnd, ECC_Visibility, QueryParams))
{
//设置受阻,造成伤害结果
AActor* HitActor = Hit.GetActor();
UGameplayStatics::ApplyPointDamage(HitActor, 10.f, ShotDirection, Hit, MyOwner->GetInstigatorController(),
this, DamageType);
//粒子生成的位置
UGameplayStatics::SpawnEmitterAtLocation(GetWorld(), ImpactEffect, Hit.ImpactPoint,
Hit.ImpactNormal.Rotation());
}
DrawDebugLine(GetWorld(), EyeLocation, TraceEnd,FColor::White,false,1.0f,0,1.0f);
if (MuzzleEffect)
{
//附加粒子效果
UGameplayStatics::SpawnEmitterAttached(MuzzleEffect,SkeletalComponent,MuzzleSocketName);
}
}
}
// Called every frame
void AWeapon::Tick(float DeltaTime)
{
Super::Tick(DeltaTime);
}