UE4组件和碰撞

UE4组件和碰撞

简介

学习利用组件将 Pawn与物理交互、使用粒子效果等效果。官方文档地址

例子实现内容:在场景中创建一个可以交互的球体,可以通过键盘输入控制球体移动,并且使用到粒子系统组件,可以由键盘输入控制开启或者关闭粒子特效。

实践

实现思路(步骤)简述:

  • ACollidingPawn 类继承自 Pawn;
  • 需要有一个可交互的球体组件(SphereComponent),然后给球体组件附上可见的材质;
  • 粒子系统组件,附上材质之后,直接附加在静态网格组件上面;
  • 使用弹簧臂组件,同时需要创建摄像机组件,并将弹簧臂组件附加在摄像机组件上;
  • 将此 Pawn(ACollidingPawn)设置为玩家默认控制;
  • 创建用户自定义移动类(UCollidingPawnMovementComponent),并给 UpdatedComponent赋初值 RootComponent

代码

CollidingPawn.h

// Fill out your copyright notice in the Description page of Project Settings.

#pragma once

#include "CoreMinimal.h"
#include "GameFramework/Pawn.h"
#include "../HowTo_Components/CollidingPawnMovementComponent.h"
// 确保列最后一个头文件是 "generated.h",否则会造成编译错误
#include "CollidingPawn.generated.h"

UCLASS()
class MYFIRSTCPPDEMO_API ACollidingPawn : public APawn
{
	GENERATED_BODY()

public:
	// Sets default values for this pawn's properties
	ACollidingPawn();

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;

	// 定义粒子系统组件
	UPROPERTY()
	class UParticleSystemComponent* MyParticleSystem;

	// 添加自定义的pawn移动组件
	class UCollidingPawnMovementComponent* MyMovementComponent;

	// 提供引擎中其他类访问该Pawn当前所用Pawn移动组件的权限
	virtual UPawnMovementComponent* GetMovementComponent() const override;

	// 处理用户输入的指令移动pawn方法
	void MoveForward(float AxisValue);
	void MoveRight(float AxisValue);
	void Turn(float AxisValue);
	void ParticleToggle();
};

ACollidingPawn.cpp

// Fill out your copyright notice in the Description page of Project Settings.


#include "../HowTo_Components/CollidingPawn.h"
#include "./UObject/ConstructorHelpers.h"
#include "./Particles/ParticleSystemComponent.h"
#include "./Components/SphereComponent.h"
#include "./Camera/CameraComponent.h"
#include "./Gameframework/SpringArmComponent.h"

// Sets default values
ACollidingPawn::ACollidingPawn()
{
 	// Set this pawn to call Tick() every frame.  You can turn this off to improve performance if you don't need it.
	PrimaryActorTick.bCanEverTick = true;

	/** 可交互的球体组件 */ 
	USphereComponent* SphereComponent = CreateDefaultSubobject<USphereComponent>(TEXT("RootComponent"));
	// 根组件成为对物理反应的球体
	RootComponent = SphereComponent;
	SphereComponent->InitSphereRadius(40.0f);
	SphereComponent->SetCollisionProfileName(TEXT("Pawn"));
	/** 静态网格组件 附加半径为50的可见球 */
	UStaticMeshComponent* SphereVisual = CreateDefaultSubobject<UStaticMeshComponent>(TEXT("VisualRepresentation"));
	SphereVisual->SetupAttachment(SphereComponent);
	// 查找材质
	const ConstructorHelpers::FObjectFinder<UStaticMesh> SphereVisualAsset(TEXT("StaticMesh'/Game/StarterContent/Shapes/Shape_Sphere.Shape_Sphere'"));
	if (SphereVisualAsset.Succeeded())
	{
		SphereVisual->SetStaticMesh(SphereVisualAsset.Object);
		SphereVisual->SetRelativeLocation(FVector(0.0f, 0.0f, -40.0f));
		// 设置绝对缩放
		SphereVisual->SetWorldScale3D(FVector(0.8f));
	}


	// 创建可激活或停止的粒子系统
	MyParticleSystem = CreateDefaultSubobject<UParticleSystemComponent>(TEXT("MovementParticles"));
	MyParticleSystem->SetupAttachment(SphereVisual);
	// 粒子系统在场景中是否自动启动粒子特效
	MyParticleSystem->bAutoActivate = false;
	MyParticleSystem->SetRelativeLocation(FVector(-20.0f, 0.0f, 20.0f));
	// 查找粒子材质
	const ConstructorHelpers::FObjectFinder<UParticleSystem> ParticleAsset(TEXT("ParticleSystem'/Game/StarterContent/Particles/P_Fire.P_Fire'"));
	if (ParticleAsset.Succeeded())
	{
		MyParticleSystem->SetTemplate(ParticleAsset.Object);
	}
	// SphereComponent->SetupAttachment(MyParticleSystem);


	/** 弹簧臂组件 控制视角的弹簧臂组件 使用弹簧臂给予摄像机平滑自然的感觉 */
	USpringArmComponent* SpringArm = CreateDefaultSubobject<USpringArmComponent>(TEXT("CameraAttachmentArm"));
	SpringArm->SetupAttachment(RootComponent);
	// 设置相对位置
	SpringArm->SetRelativeLocation(FVector(-45.0f, 0.0f, 0.0f));
	// 设置臂长
	SpringArm->TargetArmLength = 150.0f;
	// 为true,摄像机会有一个滞后效果(摄像机跟随物体移动,有种很漂的感觉)
	SpringArm->bEnableCameraLag = false;
	SpringArm->CameraLagSpeed = 3.0f;
	// 创建摄像机 并附加上弹簧臂
	UCameraComponent* Camera = CreateDefaultSubobject<UCameraComponent>(TEXT("ActualCamera"));
	Camera->SetupAttachment(SpringArm, USpringArmComponent::SocketName);

	/** 将此pawn设置为默认玩家控制 */
	AutoPossessPlayer = EAutoReceiveInput::Player0;


	/** 创建pawn移动组件 并且与pawn关联 */
	MyMovementComponent = CreateDefaultSubobject<UCollidingPawnMovementComponent>(TEXT("CustomMovementComponent"));
	MyMovementComponent->UpdatedComponent = RootComponent;
}

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

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

// Called to bind functionality to input
void ACollidingPawn::SetupPlayerInputComponent(UInputComponent* PlayerInputComponent)
{
	Super::SetupPlayerInputComponent(PlayerInputComponent);

	/** 绑定pawn移动事件 */
	InputComponent->BindAction("ParticleToggle", IE_Pressed, this, &ACollidingPawn::ParticleToggle);
	InputComponent->BindAxis("MoveForward", this, &ACollidingPawn::MoveForward);
	InputComponent->BindAxis("MoveRight", this, &ACollidingPawn::MoveRight);
	InputComponent->BindAxis("Turn", this, &ACollidingPawn::Turn);
}

/** 返回pawn移动组件对象实例 */
UPawnMovementComponent* ACollidingPawn::GetMovementComponent() const
{
	return MyMovementComponent;
}

/** 前后移动 */
void ACollidingPawn::MoveForward(float AxisValue)
{
	if (MyMovementComponent && (MyMovementComponent->UpdatedComponent == RootComponent))
	{
		MyMovementComponent->AddInputVector(GetActorForwardVector() * AxisValue);
	}
}

/** 左右移动 */
void ACollidingPawn::MoveRight(float AxisValue)
{
	if (MyMovementComponent && (MyMovementComponent->UpdatedComponent == RootComponent))
	{
		MyMovementComponent->AddInputVector(GetActorRightVector() * AxisValue);
	}
}

/** 旋转 */
void ACollidingPawn::Turn(float AxisValue)
{
	FRotator NewRotation = GetActorRotation();
	NewRotation.Yaw += AxisValue;
	SetActorRotation(NewRotation);
}

/** 开启或关闭粒子系统 */
void ACollidingPawn::ParticleToggle()
{
	if (MyParticleSystem && MyParticleSystem->Template)
	{
		MyParticleSystem->ToggleActive();
	}
}

CollidingPawnMovementComponent.h

// Fill out your copyright notice in the Description page of Project Settings.

#pragma once

#include "CoreMinimal.h"
#include "GameFramework/PawnMovementComponent.h"
#include "CollidingPawnMovementComponent.generated.h"

/**
 * 
 */
UCLASS()
class MYFIRSTCPPDEMO_API UCollidingPawnMovementComponent : public UPawnMovementComponent
{
	GENERATED_BODY()
	
public:
	// pawn移动组件行为
	virtual void TickComponent(float DeltaTime, enum ELevelTick TickType, FActorComponentTickFunction* ThisTickFunction) override;
};

UCollidingPawnMovementComponent.cpp

// Fill out your copyright notice in the Description page of Project Settings.


#include "../HowTo_Components/CollidingPawnMovementComponent.h"

void UCollidingPawnMovementComponent::TickComponent(float DeltaTime, enum ELevelTick TickType, FActorComponentTickFunction* ThisTickFunction)
{
	Super::TickComponent(DeltaTime, TickType, ThisTickFunction);

	// 确保事物的持续有效,以便进行移动
	if (!PawnOwner || !UpdatedComponent || ShouldSkipUpdate(DeltaTime))
	{
		return;
	}

	// 获取(然后清除)ACollidingPawn::Tick中设置移动向量 最大速度被硬编码为每秒150
	// ConsumeInputVector 报告并清空用于存储移动输入的内置变量值
	FVector DesiredMovenmentThisFrame = ConsumeInputVector().GetClampedToMaxSize(1.0f) * DeltaTime * 150.0f;
	if (!DesiredMovenmentThisFrame.IsNearlyZero())
	{
		FHitResult Hit;
		// SafeMoveUpdatedComponent 利用虚幻引擎物理移动Pawn移动组件,同时考虑固体障碍
		SafeMoveUpdatedComponent(DesiredMovenmentThisFrame, UpdatedComponent->GetComponentRotation(), true, Hit);

		// 如果发生碰撞,尝试滑过去
		if (Hit.IsValidBlockingHit())
		{
			// 移动导致碰撞时, SlideAlongSurface 会处理沿墙壁和斜坡等碰撞表面平滑滑动所涉及的计算和物理,而非直接停留原地,粘在墙壁或斜坡上
			SlideAlongSurface(DesiredMovenmentThisFrame, 1.f - Hit.Time, Hit.Normal, Hit);
		}
	}
}
  • 编译完成之后回到 ue4编辑器,做对应的设置 Edit - Project Settings…
    移动组件的按键绑定设置
  • 完成按键的绑定,将
    CollidingPawn
    拖拽到地图场景合适位置即可运行测试

后记

  • 对于A->SetupAttachment()方法,有点迷惑,按照命名的意思就是A对象给自己设置一个附件;上面的例子,弹簧臂设置一个附件是RootComponent,Camera组件设置的附件是弹簧臂,这些都可以理解,但是粒子系统组件
    MyParticleSystem->SetupAttachment(SphereVisual);,这意思就是粒子系统组件设置一个附件,而这个附件就是附上材质的一个静态网格组件?
  • 按照自己的想法,应该是静态网格组件上附加一个附件SphereComponent->SetupAttachment(MyParticleSystem);,当然,结果是行不通的
    尝试静态网格组件设置附加附件-粒子系统组件
    开启之后,火焰粒子的生成位置是在一个初始化的位置,并不会依附在玩家球体上。
    经过思考和查阅资料后知道,场景中的组件(USceneComponent 以及其子类),可以彼此附加,且父项只有一个
    附加组件的方法有SetupAttachmentAttachToComponent
  • 综上,弹簧臂组件是在 Camera组件下面,粒子系统组件是在静态网格组件下面的,它们父项都是 RootComponent

笔记中如有理解错误的地方,希望看到的大佬不吝指正,十分感谢。继续加油!

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值