UEC++学习笔记(二)运动和物理碰撞(子弹)

改进运动

控制旋转

第三人称游戏中的旋转,控制器的旋转并不等同于人物的旋转。

在角色bp中勾选了Use Contrtoller Rotation Yaw就会将控制器的旋转添加到角色的身上

我们应该把控制器的旋转添加到摄像头上才更加符合第三人称游戏的操作,所以取消勾选的同时在摄像头的机械臂中勾选Use Pawn Control Rotation将控制器的旋转添加到机械臂上

这些操作也可以在c++中完成

SpringArmComp->bUsePawnControlRotation = true;
bUseControllerRotationYaw = false;

但是我们想要rpg风格的角色操作需要角色转向我们要移动的方向之后再去移动

GetCharacterMovement()->bOrientRotationToMovement = true;

也可以在蓝图中勾选

但是需要更改原本的运动的设置

原本的运动设置是朝着角色的方向移动

AddMovementInput(GetActorForwardVector(),value);
AddMovementInput(GetActorFightVector(),value);

但是当bOrientRotationToMovement设置为true后,角色是会转向到所指定的位置之后再向着那个地方往前移动,所以当角色旋转到一开始所指定的位置之后角色的方向改变了,就会继续旋转,无法获得一个rpg的操作手感

所以我们需要把它更改为朝着摄像机的方向移动(前面的设置之后摄像机不会一直跟着角色转向)。比如说我们朝左看再向前移动角色就会朝着那个地方移动,更加符合rpg的操作手感

void APlayerCharacter::MoveRight(float value)
{
   FRotator ControlRot = GetControlRotation();
   //因为这一移动是水平方向的所以不需要pitch和yaw,为了防止向下旋转的诡异现象所以设为0
   ControlRot.Pitch = 0.0f;
   ControlRot.Roll = 0.0f;
   AddMovementInput(UKismetMathLibrary::GetRightVector(ControlRot),value);
}

Tips:右转的时候是将获得的摄像机方向的九十度,需要做数学运算。但是ue其实已经写好了这个函数所以我们不需要自己去再写一遍。

快捷键Shift + Alt + S(如果是拥有Visual Assist或者使用Rider)找到所需要的函数

可以看头文件判别

比如说在头文件Actor内的大概率是关于Actor的右向量。在这里我们需要做到是数学运算,所以找到KismetMathLibrary(Kismet是蓝图)下的GetRightVector函数调用并传入参数

物理碰撞(子弹)

创建子弹

创建c++类MagicProjectile(继承自Actor)

//MagicProjectile.h

#pragma once

#include "CoreMinimal.h"
#include "Components/SphereComponent.h"
#include "GameFramework/Actor.h"
#include "GameFramework/ProjectileMovementComponent.h"
#include "MagicProjectile.generated.h"

UCLASS()
class ACTIONROUGELIKE_API AMagicProjectile : public AActor
{
   GENERATED_BODY()
   
public:    
   // Sets default values for this actor's properties
   AMagicProjectile();

protected:
   //作为最基本的碰撞组件
   UPROPERTY(VisibleAnywhere)
   USphereComponent* SphereComp;
   //这个组件给予物体一个速度,进入直线
   UPROPERTY(VisibleAnywhere)
   UProjectileMovementComponent* MovementComp;
   //粒子系统组件,展示子弹
   UPROPERTY(VisibleAnywhere)
   UParticleSystemComponent* EffectComp;
   
   // Called when the game starts or when spawned
   virtual void BeginPlay() override;
   

public:    
   // Called every frame
   virtual void Tick(float DeltaTime) override;

};

//MagicProjectile.cpp
#include "MagicProjectile.h"

#include "Particles/ParticleSystem.h"
#include "Particles/ParticleSystemComponent.h"

// Sets default values
AMagicProjectile::AMagicProjectile()
{
   // 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;

   SphereComp = CreateDefaultSubobject<USphereComponent>("SphereComp");
   RootComponent = SphereComp;

   EffectComp = CreateDefaultSubobject<UParticleSystemComponent>("EffectComp");
   EffectComp->SetupAttachment(SphereComp);

   MovementComp = CreateDefaultSubobject<UProjectileMovementComponent>("MovementComp");
   MovementComp->InitialSpeed = 1000.0f;
   //为真时,子弹会随着旋转匹配速度的方向
   MovementComp->bRotationFollowsVelocity = true;
   MovementComp->bInitialVelocityInLocalSpace = true;
   
      
}

// Called when the game starts or when spawned
void AMagicProjectile::BeginPlay()
{
   Super::BeginPlay();
   
}

// Called every frame
void AMagicProjectile::Tick(float DeltaTime)
{
   Super::Tick(DeltaTime);

}

然后在ue编辑器中创建对应的蓝图,在EffecComp中的template中选择准备好的例粒子

然后将它放到关卡中运行会发现子弹会往前飞但是不断下坠然后穿过地板掉下去。为了实现子弹的效果和基本功能接下来需要对碰撞需要改进。

生成子弹

生成子弹就是开火,需要按下鼠标左键或者其他操作去触发。所以需要在角色中绑定输入。

因为开火射击是一个动作和轴上的运动关系不大,所以这次用的是BindAction

//第一个参数是绑定的动作的名称,第二个参数是触发的条件(按下或松开或其他)
//第三个参数是使用这个动作的角色,第四个参数是触发的函数
PlayerInputComponent->BindAction("PrimaryAttack",IE_Pressed,this,&APlayerCharacter::PrimaryAttack);
 void APlayerCharacter::PrimaryAttack()
{
   //我们需要获得生成子弹的rotation和location
   //从角色生成子弹发射向我们看向的地方
   FTransform SpawnTM = FTransform(GetControlRotation(), GetActorLocation());

   
   FActorSpawnParameters SpawnParams;
   //指定子弹的生成规则
   //比如默认生成的规则是生成子弹的时候会检查生成的地点是否由物体重叠,
   //如果是就会向其它地方移动一点点去生成
   //但是我们需要在角色中生成子弹发射出去所以需要改变这一个规则
   SpawnParams.SpawnCollisionHandlingOverride = ESpawnActorCollisionHandlingMethod::AlwaysSpawn;
   
   
   //在这个函数中我们需要生成我们的子弹,所以我们需要先获取我们所在的世界(关卡)
   //这里用的是模板编程,<>内指定我们需要生成Actor的类型
   //第一个参数为传递我们想要生成的类
   //第二个参数是一个结构体,按照比例进行位置和方向的转换
   //第三个参数是FActorSpawnParameters,里面包含了很多可选参数,包括生成的发起者是谁等等
   GetWorld()->SpawnActor<AActor>(ProjectileClass, SpawnTM, SpawnParams);
}
//PlayerCharacter.h
protected:
   //指定我们生成的物体,小兵或者子弹等等
   //UPROPERTY将它暴露给蓝图去编辑指定生成的Actor类
   UPROPERTY(EditAnywhere)
   TSubclassOf<AActor> ProjectileClass;

然后绑定输入键

并且在蓝图中绑定生成的Actor

但是我们还需要子弹在角色的手部生成而不是角色的中心

打开角色的骨架并且选定生成地方的骨架,复制名称

 
 
//获取角色的骨架指定具体的生成位置
FVector HandLocation = GetMesh()->GetSocketLocation("Muzzle_01");

FTransform SpawnTM = FTransform(GetControlRotation(), HandLocation);

然后运行就可以成功生成子弹了,但是还有碰撞需要处理

碰撞

首先子弹需要设置的是将它的重力设置为0,不让它下坠

子弹可以在直线上一直飞行,但是还是会穿墙或者穿过地板

打开子弹的SphereComp组件在细节面板中可以看到与碰撞相关的设置

可以看到在默认情况下Collision Presets被设置成OverlapAllDynamic,即穿过所有通道

所以下面的所有channel(通道)都被设置成Overlap

比如穿过的墙和地板就属于WorldStatic的通道

虽然在墙体中我们将它设置成BlockAll,即阻挡所有的物体,但是子弹还是会穿过墙体。因为在碰撞的过程中,永远都会选择反应最小的那个(ignore<overlap<block)。所以子弹会穿过墙体。

首先设置子弹的碰撞类型,子弹是什么类型与其他物体碰撞时就会依据选项产生不同的反应

SphereComp->SetCollisionObjectType(ECC_WorldDynamic);

因为子弹运动时的所以将它设置为动态

也可以通过设置子弹的碰撞预设

第一个设置预设的名称

第二个是碰撞的反应,有四种No Collision,Query Only(No Physics Collision),Physics Only(No Query Collision),Collision Enable(Query and Physics)

Physics是字面上的意思,Query是对角色的运动进行查询,比如是否还能继续前进或者是否被什么东西挡住,不是实际上物理的力而只是查询

这里并没有用到力,所以选择QueryOnly

我们不希望它对摄像机做出反应,所以设置为ignore,但是希望会被墙阻挡,所以block

最后在c++中设置

SphereComp->SetCollisionProfileName("MagicProjectile");

以下是关于碰撞过滤的教程链接

https://www.unrealengine.com/en-US/blog/collision-filtering

猜你喜欢

转载自blog.csdn.net/qq_44020428/article/details/126304674