UEC++ 虚幻5第三人称射击游戏(一)

news2025/4/17 8:52:46

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);

}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1853214.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

正点原子rk3588烧录linux和安卓镜像

1、烧录 Linux buildroot 系统镜像 1.1 进入 Loader 模式&#xff1a; 按住开发板上的 V&#xff08;音量&#xff09;按键不松&#xff0c;给开发板 上电或复位&#xff0c;此时烧录工具会提示&#xff1a;发现一个 LOADER 设备&#xff0c;表示开发板此时已经处于 Loader 模…

什么是深度神经网络?与深度学习、机器学习、人工智能的关系是什么?

什么是深度神经网络&#xff1f;与深度学习、机器学习、人工智能的关系是什么&#xff1f; &#x1f916;什么是深度神经网络&#xff1f;与深度学习、机器学习、人工智能的关系是什么&#xff1f;摘要引言正文内容1. 什么是深度神经网络&#xff1f;&#x1f9e0;1.1 深度神经…

IDEA Plugins中搜索不到插件解决办法

IDEA中搜不到插件有三种解决方案&#xff1a; 设置HTTP选项&#xff0c;可以通过File->Settings->Plugins->⚙->HTTP Proxy Settings进行设置 具体可参考这篇博文&#xff1a;IDEA Plugins中搜索不到插件解决办法本地安装&#xff0c;ile->Settings->Plugin…

Linux下Cmake安装或版本更新

下载Cmake源码 https://cmake.org/download/ 找到对应的版本和类型 放进linux环境解压 编译 安装 tar -vxvf cmake-3.13.0.tar.gz cd cmake-3.13.0 ./bootstrap make make install设置环境变量 vi ~/.bashrc在文件尾加入 export PATH/your_path/cmake-3.13.0/bin:$PAT…

App推广告别邀请码,Xinstall助您一键触达海量用户!

在移动互联网高速发展的今天&#xff0c;App的推广与运营已成为每个开发者都必须面对的问题。然而&#xff0c;随着互联网流量的日益分散和用户需求的不断变化&#xff0c;传统的App推广方式已经难以满足现代市场的需求。尤其是在获取用户时&#xff0c;很多开发者还在采用传统…

面向对象修炼手册(二)(消息与继承)(Java宝典)

&#x1f308; 个人主页&#xff1a;十二月的猫-CSDN博客 &#x1f525; 系列专栏&#xff1a; &#x1f3c0;面向对象修炼手册 &#x1f4aa;&#x1f3fb; 十二月的寒冬阻挡不了春天的脚步&#xff0c;十二点的黑夜遮蔽不住黎明的曙光 目录 前言 消息传递 1 基本概念 1.…

关于read,write,open时出现的文本文件和二进制文件读写的问题(怎么写入怎么读)

1、发现问题 使用read读取文本文件&#xff0c;一般采用字符空间作为缓存&#xff0c;最后输出&#xff1b; 使用read读取二进制文件&#xff0c;这里采用整数读取的展示&#xff1a; 首先创建文本文件&#xff0c;用write写入i的值到文件中&#xff1b; 再通过lseek改变读写一…

1.Triangle

一、你好&#xff0c;三角形 在OpenGL中&#xff0c;任何事物都在3D空间中&#xff0c;而屏幕和窗口却是2D像素数组&#xff0c;这导致OpenGL的大部分工作都是关于把3D坐标转变为适应你屏幕的2D像素。 3D坐标转为2D坐标的处理过程是由OpenGL的图形渲染管线&#xff08;Graphi…

松下课堂 | 什么是EPS?通过马达来辅助转向操作的系统

EPS , 松下 EPS是一种通过马达来减轻和辅助驾驶员在转向操作时所需力量的设备。此外&#xff0c;通过采用EPS&#xff0c;可望提高燃效&#xff0c;降低车辆重量。。。 背景 EPS是一种通过马达来减轻和辅助驾驶员在转向操作时所需力量的设备。此外&#xff0c;通过采用EPS&…

深度学习笔记: 最详尽解释欠拟合(高偏差)和过拟合(高方差)

欢迎收藏Star我的Machine Learning Blog:https://github.com/purepisces/Wenqing-Machine_Learning_Blog。如果收藏star, 有问题可以随时与我交流, 谢谢大家&#xff01; 欠拟合&#xff08;高偏差&#xff09;和过拟合&#xff08;高方差&#xff09; 在机器学习和统计建模中…

卡码网KamaCoder 98. 所有可达路径

题目来源&#xff1a;https://kamacoder.com/problempage.php?pid1170 C题解1&#xff1a;深度优先搜索&#xff0c;邻接矩阵 #include <iostream> #include <vector> using namespace std; vector<vector<int>> result; vector<int> path;voi…

毫秒级响应!清科优能应用 TDengine 建设虚拟电厂运营管理平台

小T导读&#xff1a;在清科优能的虚拟电厂运营管理平台建设中&#xff0c;项目初期预计涉及约一万台设备、总数据采集量达数十万&#xff0c;在数据库选择上&#xff0c;其希望能支持至少两千台设备的并发数据处理。本文介绍了清科优能的数据库选型经验以及最终应用效果&#x…

基于jmeter+perfmon的稳定性测试记录!

最近承接了项目中一些性能测试的任务&#xff0c;因此决定记录一下&#xff0c;将测试的过程和一些心得收录下来。 说起来性能测试算是软件测试行业内&#xff0c;有些特殊的部分。这部分的测试活动&#xff0c;与传统的测试任务差别是比较大的&#xff0c;也比较依赖工具&…

数据结构和算法(1) ---- Queue 的原理和实现

Queue 的定义和结构 队列(Queue) 是只允许在一端进行插入&#xff0c;在另一端进行删除的线性表 队列是一种先进先出(First In First Out)的线性表&#xff0c;简称 FIFO(First IN First OUT), 允许插入的一端称为队尾, 允许删除的一端称为队列头 队列的基本结构如下图所示&a…

百老开通知识星球啦,数据要素、数据治理等资料迅速扩散!

1.写在前面&#xff1a; 做数据相关工作有一些年头了&#xff0c;手里也积攒了几千份案例、解决方案、考试认证资料、数据要素研报等材料&#xff0c;形成自我的架构参考库&#xff0c;按TOGAF开发方法&#xff0c;分别形成标准信息库&#xff08;Standards Information Base&…

【论文通读】SeeClick: Harnessing GUI Grounding for Advanced Visual GUI Agents

SeeClick: Harnessing GUI Grounding for Advanced Visual GUI Agents 前言AbstractMotivationMethodGUI grounding for LVLMsData ConstructionTraining Details ScreenSpotExperimentsGUI Grounding on ScreenSpotVisual GUI Agent TasksMiniWobAITWMind2WebOther experiment…

使用Inno Setup 6制作软件安装包

制作软件安装包的工具非常多&#xff0c;比如SetupFactory、NSIS、Advanced Installer等等&#xff0c;都可以制作不同样式的软件安装包&#xff0c;但是在这些软件里面&#xff0c;Inno Setup最好&#xff0c;原因是他可以定制化个性的安装界面和卸载界面&#xff0c;只是难度…

贪心推公式——AcWing 125. 耍杂技的牛

贪心推公式 定义 贪心算法是一种在每一步选择中都采取在当前状态下最优的选择&#xff0c;希望通过局部的最优选择来得到全局最优解的算法策略。 运用情况 问题具有最优子结构&#xff0c;即一个问题的最优解包含其子问题的最优解。可以通过局部最优决策逐步推导到全局最优…

带百分比的进度条控件(ProgressBar)源码

带百分比的进度条控件&#xff08;ProgressBar&#xff09;&#xff1a; 源码下载地址&#xff1a;https://download.csdn.net/download/wgxds/89472915

48-5 内网渗透 - JuicyPotato、Pipe Potato提权

Juicy Potato Juicy Potato 与 Rotten Potato(烂土豆) 的原理几乎完全相同,只是在后者的基础上做了扩展,以便更灵活地利用 Rotten Potato。Juicy Potato 不再像 Rotten Potato 那样依赖于一个现有的 Meterpreter,并且可以自定义 COM 对象加载的端口,以及根据系统版本更换…