虚幻四C++入坑指南11:C++实现FPS游戏实现发射物
虚幻引擎4文档 > 编程指南 > C++ 编程教程 > 第一人称射击游戏教程 > 3 - 实现发射物 > 3.1 - 为游戏添加发射物
虚幻引擎4文档 > 编程指南 > C++ 编程教程 > 第一人称射击游戏教程 > 3 - 实现发射物 > 3.2 - 实现射击
虚幻引擎4文档 > 编程指南 > C++ 编程教程 > 第一人称射击游戏教程 > 3 - 实现发射物 > 3.3 - 设置发射物的碰撞和生命周期
虚幻引擎4文档 > 编程指南 > C++ 编程教程 > 第一人称射击游戏教程 > 3 - 实现发射物 > 3.4 - 使发射物和世界场景形成交互
轴映射
新建Actor类型的C++类“FPSProjectile”
这次动到了4个文件,子弹需要角色的摄像机的属性
01 FPSProjectile.h
// Fill out your copyright notice in the Description page of Project Settings.
#pragma once
#include "CoreMinimal.h"
#include "GameFramework/Actor.h"
#include "Camera/CameraComponent.h"
#include "Components/SkeletalMeshComponent.h"
#include "PhysicsEngine/SphereElem.h"
#include "GameFramework/DefaultPawn.h"//自己加的
#include "Components/SphereComponent.h"//自己加的
#include "GameFramework/ProjectileMovementComponent.h"
#include "FPSProjectile.generated.h"
UCLASS()
class QUICKSTART_API AFPSProjectile : public AActor
{
GENERATED_BODY()
public:
// Sets default values for this actor's properties
AFPSProjectile();
protected:
// Called when the game starts or when spawned
virtual void BeginPlay() override;
public:
// Called every frame
virtual void Tick(float DeltaTime) override;
//**********************************************************************
// 球体碰撞组件。
UPROPERTY(VisibleDefaultsOnly, Category = Projectile)
USphereComponent* CollisionComponent;
// 发射物运动组件。
UPROPERTY(VisibleAnywhere, Category = Movement)
UProjectileMovementComponent* ProjectileMovementComponent;
// 在发射方向上设置发射物初速度的函数。
void FireInDirection(const FVector& ShootDirection);
//******************************************************************
// 发射物命中物体时调用的函数。
void OnHit(UPrimitiveComponent* HitComponent, AActor* OtherActor, UPrimitiveComponent* OtherComponent, FVector NormalImpulse, const FHitResult& Hit);
};
01 FPSProjectile.cpp
// Fill out your copyright notice in the Description page of Project Settings.
#include "FPSProjectile.h"
#include "GameFramework/ProjectileMovementComponent.h"
#include "Components/SphereComponent.h"
// Sets default values
AFPSProjectile::AFPSProjectile()
{
// 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;
//***********************************************************************
// 使用球体代表简单碰撞。
CollisionComponent = CreateDefaultSubobject<USphereComponent>(TEXT("SphereComponent"));
CollisionComponent->BodyInstance.SetCollisionProfileName(TEXT("Projectile"));
CollisionComponent->OnComponentHit.AddDynamic(this, &AFPSProjectile::OnHit);
// 设置球体的碰撞半径。
CollisionComponent->InitSphereRadius(15.0f);
// 将碰撞组件设为根组件。
//模拟将驱动 CollisionComponent,所以将其设为 RootComponent。
RootComponent = CollisionComponent;
// 使用此组件驱动此发射物的运动。
ProjectileMovementComponent = CreateDefaultSubobject<UProjectileMovementComponent>(TEXT("ProjectileMovementComponent"));
//ProjectileMovementComponent,使用此组件驱动/更新发射物的运动,比如位置、碰撞后弹跳、向目标归为
//原始Actor组件会被移动
ProjectileMovementComponent->SetUpdatedComponent(CollisionComponent);
//官网没加
//设置碰撞预设
CollisionComponent->BodyInstance.SetCollisionProfileName(TEXT("Projectile"));
//初速度
ProjectileMovementComponent->InitialSpeed = 3000.0f;
//最大速度
ProjectileMovementComponent->MaxSpeed = 3000.0f;
//旋转每帧更新,匹配速度方向
ProjectileMovementComponent->bRotationFollowsVelocity = true;
//反弹
ProjectileMovementComponent->bShouldBounce = true;
//反弹系数
ProjectileMovementComponent->Bounciness = 0.3f;
//3秒后消亡
InitialLifeSpan = 3.0f;
}
// Called when the game starts or when spawned
void AFPSProjectile::BeginPlay()
{
Super::BeginPlay();
}
// Called every frame
void AFPSProjectile::Tick(float DeltaTime)
{
Super::Tick(DeltaTime);
}
//*******************************************************************************
// 在发射方向上设置发射物初速度的函数。
//只需提供一个发射方向,因为发射物的速度由 ProjectileMovementComponent 定义。
//在发射方向上设置初速度
void AFPSProjectile::FireInDirection(const FVector& ShootDirection)
{
ProjectileMovementComponent->Velocity = ShootDirection * ProjectileMovementComponent->InitialSpeed;
}
// 发射物命中物体时调用的函数。
void AFPSProjectile::OnHit(UPrimitiveComponent* HitComponent, AActor* OtherActor, UPrimitiveComponent* OtherComponent, FVector NormalImpulse, const FHitResult& Hit)
{
if (OtherActor != this && OtherComponent->IsSimulatingPhysics())
{
OtherComponent->AddImpulseAtLocation(ProjectileMovementComponent->Velocity * 100.0f, Hit.ImpactPoint);
}
}
02 FPSCharacter.h
// Fill out your copyright notice in the Description page of Project Settings.
#pragma once
#include "CoreMinimal.h"
#include "GameFramework/Character.h"
#include "Math/Vector.h"
#include "FPSCharacter.generated.h"
//在.h没引入相关的头文件,没报错,是因为下面声明了一下
class UCameraComponent;
class USkeletalMeshComponent;
UCLASS()
class QUICKSTART_API AFPSCharacter : public ACharacter
{
GENERATED_BODY()
public:
// Sets default values for this character's properties
AFPSCharacter();
protected:
// Called when the game starts or when spawned
virtual void BeginPlay() override;
public:
// Called every frame
virtual void Tick(float DeltaTime) override;
// Called to bind functionality to input
virtual void SetupPlayerInputComponent(class UInputComponent* PlayerInputComponent) override;
// 处理前进和后退的输入。
UFUNCTION()
void MoveForward(float Value);
// 处理左右移动的输入。
UFUNCTION()
void MoveRight(float Value);
// 按下按键时设置跳跃标记。
UFUNCTION()
void StartJump();
// 松开按键时清除跳跃标记。
UFUNCTION()
void StopJump();
// FPS 摄像机。
UPROPERTY(VisibleAnywhere)
UCameraComponent* FPSCameraComponent;
// 第一人称模型(手臂),仅对拥有玩家可见。
UPROPERTY(VisibleDefaultsOnly, Category = Mesh)
//骨骼类型的组件
//#include "Components/SkeletalMeshComponent.h"(.cpp)
USkeletalMeshComponent* FPSMesh;
//***************************************************************************
// 处理开火的函数。
UFUNCTION()
void Fire();
// 从摄像机位置的枪口偏移。
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = Gameplay)
FVector MuzzleOffset;
// 生成的发射物类。
UPROPERTY(EditDefaultsOnly, Category = Projectile)
TSubclassOf<class AFPSProjectile> ProjectileClass;
};
02 FPSCharacter.cpp
// Fill out your copyright notice in the Description page of Project Settings.
#include "FPSCharacter.h"
#include "Engine/Engine.h"
#include "Camera/CameraComponent.h"
#include "Components/SkeletalMeshComponent.h"
#include "FPSProjectile.h"
// Sets default values
AFPSCharacter::AFPSCharacter()
{
// 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;
//*************************************************************************
// 创建一个第一人称摄像机组件。
FPSCameraComponent = CreateDefaultSubobject<UCameraComponent>(TEXT("FirstPersonCamera"));
/*
void USceneComponent::SetupAttachment(class USceneComponent* InParent, FName InSocketName)
class UCapsuleComponent* GetCapsuleComponent() const { return CapsuleComponent; }
一个是场景类型,一个是胶囊体类型,类型不匹配,需要强转类型
官网:FPSCameraComponent->SetupAttachment(GetCapsuleComponent());
*/
// 将摄像机组件附加到胶囊体组件。
FPSCameraComponent->SetupAttachment((USceneComponent *)GetCapsuleComponent());
// 将摄像机放置在眼睛上方不远处。
FPSCameraComponent->SetRelativeLocation(FVector(0.0f, 0.0f, 50.0f + BaseEyeHeight));
// 用 pawn 控制摄像机旋转。
FPSCameraComponent->bUsePawnControlRotation = true;
//*************************************************************************
// 为拥有玩家创建一个第一人称模型组件。
FPSMesh = CreateDefaultSubobject<USkeletalMeshComponent>(TEXT("FirstPersonMesh"));
// 该模型仅对拥有玩家可见。
//其他玩家看到的你的胳膊断的,像阴阳师的鬼切
FPSMesh->SetOnlyOwnerSee(true);
// 将 FPS 模型添加到 FPS 摄像机。
FPSMesh->SetupAttachment(FPSCameraComponent);
// 禁用部分环境阴影,保留单一模型存在的假象。
//断臂投影也不行
FPSMesh->bCastDynamicShadow = false;
FPSMesh->CastShadow = false;
// 拥有玩家无法看到普通(第三人称)身体模型。
GetMesh()->SetOwnerNoSee(true);
}
// Called when the game starts or when spawned
void AFPSCharacter::BeginPlay()
{
Super::BeginPlay();
if (GEngine)
{
// 显示调试信息五秒。-1"键"值(首个参数)说明我们无需更新或刷新此消息。
GEngine->AddOnScreenDebugMessage(-1, 5.0f, FColor::Red, TEXT("We are using FPSCharacter."));
}
}
// Called every frame
void AFPSCharacter::Tick(float DeltaTime)
{
Super::Tick(DeltaTime);
}
// Called to bind functionality to input
void AFPSCharacter::SetupPlayerInputComponent(UInputComponent* PlayerInputComponent)
{
Super::SetupPlayerInputComponent(PlayerInputComponent);
// 设置"移动"绑定。
//PlayerInputComponent->BindAxis("通知", this, &AFPSCharacter::调用事件);
/*
//使用结构体内部的函数需要绑定函数,指明是呢一个类来调用的
template<class UserClass>模板结构体
FInputAxisBinding& BindAxis(const FName AxisName, UserClass * Object, typename FInputAxisHandlerSignature::TUObjectMethodDelegate< UserClass >::FMethodPtr Func)
{
FInputAxisBinding AB(AxisName);
AB.AxisDelegate.BindDelegate(Object, Func);
AxisBindings.Emplace(MoveTemp(AB));
return AxisBindings.Last();
}
*/
//前后左右移动
PlayerInputComponent->BindAxis("MoveForward", this, &AFPSCharacter::MoveForward);
PlayerInputComponent->BindAxis("MoveRight", this, &AFPSCharacter::MoveRight);
// 上下左右旋转,具体函数实现官方写好了
//z轴旋转,左右
//y轴旋转,上下
PlayerInputComponent->BindAxis("Turn", this, &AFPSCharacter::AddControllerYawInput);
PlayerInputComponent->BindAxis("LookUp", this, &AFPSCharacter::AddControllerPitchInput);
// 设置"动作"绑定。
//上面都是BingAction
PlayerInputComponent->BindAction("Jump", IE_Pressed, this, &AFPSCharacter::StartJump);
PlayerInputComponent->BindAction("Jump", IE_Released, this, &AFPSCharacter::StopJump);
//*****************************************************************
PlayerInputComponent->BindAction("Fire", IE_Pressed, this, &AFPSCharacter::Fire);
}
//********************************************************************
void AFPSCharacter::MoveForward(float Value)
{
// 明确哪个方向是"前进",并记录玩家试图向此方向移动。
FVector Direction = FRotationMatrix(Controller->GetControlRotation()).GetScaledAxis(EAxis::X);
AddMovementInput(Direction, Value);
}
void AFPSCharacter::MoveRight(float Value)
{
// 明确哪个方向是"向右",并记录玩家试图向此方向移动。
FVector Direction = FRotationMatrix(Controller->GetControlRotation()).GetScaledAxis(EAxis::Y);
AddMovementInput(Direction, Value);
}
void AFPSCharacter::StartJump()
{
/*
源代码
UPROPERTY(BlueprintReadOnly, Category = Character)
uint32 bPressedJump : 1;
*/
bPressedJump = true;
}
void AFPSCharacter::StopJump()
{
bPressedJump = false;
}
//**************************************************************************
void AFPSCharacter::Fire()
{
// 尝试发射物体。
if (ProjectileClass)
{
// 获取摄像机变换。
FVector CameraLocation;
FRotator CameraRotation;
GetActorEyesViewPoint(CameraLocation, CameraRotation);
// 将 MuzzleOffset 从摄像机空间变换到世界空间。
FVector MuzzleLocation = CameraLocation + FTransform(CameraRotation).TransformVector(MuzzleOffset);
FRotator MuzzleRotation = CameraRotation;
// 将准星稍微上抬。
MuzzleRotation.Pitch += 10.0f;
UWorld* World = GetWorld();
if (World)
{
FActorSpawnParameters SpawnParams;
SpawnParams.Owner = this;
SpawnParams.Instigator = Instigator;
//11有评论这么干
//SpawnParams.Instigator = this;
// 在枪口处生成发射物。
AFPSProjectile* Projectile = World->SpawnActor<AFPSProjectile>(ProjectileClass, MuzzleLocation, MuzzleRotation, SpawnParams);
if (Projectile)
{
// 设置发射物的初始轨道。
FVector LaunchDirection = MuzzleRotation.Vector();
Projectile->FireInDirection(LaunchDirection);
}
}
}
}
03 子弹模型
下载、解压、导入
发射物模型
04 子弹蓝图 BP_FPSProjectile11
04 01 基于C++类“FPSProjectile”新建蓝图类,我命名“BP_FPSProjectile11”(11是视频集数,方便索引)
04 02 添加“StaticMesh”类型的组件,命名为“ProjectileMeshComponent”
04 03 蓝图加子弹(建议下图这样加)
04 04 缩放0.09,no collision
05 角色蓝图 给新增的属性赋值
05 01 Gameplay、Projectile
05 02 演示(子弹的位置有点高啊)
06 碰撞通道
07 拷贝Floor,做一个Cube
08 子弹蓝图 碰撞预设
09 演示 Cube被子弹击飞
00 bug 角色蓝图没有写的属性Gameplay、Projectile
主体没跳过,error就把相应位置的头文件补齐。
我是第二天生成,不知道原因,就有如下图在FPSCharacter.h代码中写好的栏目,Gameplay、Projectile。