教程的第5和6章我不打算写文档,毕竟不是什么很难的东西,跟着做就行了,debug用的代码段我会在布置的几个作业里面很经常性的用到,各位可以做一个参考。
这篇文章先解决第一个作业:魔法子弹的落点问题,各位看教程估计也看到了,游戏内人物发射的子弹朝向虽然是和我们的视角朝向相同,但是它的落点并不是我们视角最终落在的地方。所以需要我们来修复子弹落点的问题。
首先重新审视我们之前敲的代码,先把我们所写的代码做个结构的划分。我们会发现,发射子弹的实质是生成子弹这个对象,并给子弹这个对象一个初速度,初速度方向,以及生成位置罢了。而初速度方向,这个是我们实现这个功能需要关注的点。视频里面教程给了一个示例,就是一直瞄准玩家的那个方向键,对此我进行了一个思考,我希望这个检测只在我每次摁下鼠标左键的时候才发生,而不是时时刻刻都在检测,所以我做了一个linetrace,就是最开始我们打开宝箱所用的那个函数,我们使用linetrace找到离我们最近的一面墙,让我们的子弹的最终落点落在我们的准心所指向的地方。换句话说就是,将我们的子弹的方向从与我们视角方向一直更变为朝向准心所指的方向。
如图所示,红色是从摄像机出发,沿着我们的视觉中心的一个碰撞检测,而绿色的线是我们子弹的移动路线,每次摁下鼠标左键的时候,进行一次碰撞检测,如果检测到了碰撞,那么就将碰撞点设为我们子弹的落点,而我们子弹的起点照旧不需要改变,这样一来,子弹的rotation就确定了。
代码如下:
SCharacter.cpp
// Fill out your copyright notice in the Description page of Project Settings.
#include "SCharacter.h"
#include "GameFramework/SpringArmComponent.h"
#include "Camera/CameraComponent.h"
#include "GameFramework/Controller.h"
#include "EnhancedInputComponent.h"
#include "EnhancedInputSubsystems.h"
#include "GameFramework/CharacterMovementComponent.h"
#include "SInteractionComponent.h"
#include "TimerManager.h"
//#include "../../../../../UE_5.1/Engine/Plugins/EnhancedInput/Source/EnhancedInput/Public/EnhancedInputSubsystems.h"
//#include "../../../../../UE_5.1/Engine/Plugins/EnhancedInput/Source/EnhancedInput/Public/EnhancedInputComponent.h"
// Sets default values
ASCharacter::ASCharacter()
{
// 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;
bUseControllerRotationPitch = false;
bUseControllerRotationRoll = false;
bUseControllerRotationYaw = false;
GetCharacterMovement()->bOrientRotationToMovement = true;
SpringArmComp = CreateDefaultSubobject<USpringArmComponent>(TEXT("CameraBoom"));
SpringArmComp->SetupAttachment(RootComponent);
SpringArmComp->bUsePawnControlRotation = true;
CameraComp = CreateDefaultSubobject<UCameraComponent>(TEXT("Camera"));
CameraComp->SetupAttachment(SpringArmComp);
CameraComp->bUsePawnControlRotation = false;
InteractionComp = CreateDefaultSubobject<USInteractionComponent>(TEXT("InteractionComp"));
}
// Called when the game starts or when spawned
void ASCharacter::BeginPlay()
{
Super::BeginPlay();
if (APlayerController* PlayerController = Cast<APlayerController>(Controller))
{
if (UEnhancedInputLocalPlayerSubsystem* Subsystem = ULocalPlayer::GetSubsystem<UEnhancedInputLocalPlayerSubsystem>(PlayerController->GetLocalPlayer()))
{
Subsystem->AddMappingContext(DefaultMappingContext, 0);
}
}
}
void ASCharacter::Move(const FInputActionValue& Value)
{
FVector2D MovementVector = Value.Get<FVector2D>();
if (Controller!=nullptr)
{
const FRotator Rotation = Controller->GetControlRotation();
//拿到控制器的Yaw面的旋转角度,而不是Character的旋转角度
const FRotator YawRotation(0, Rotation.Yaw, 0);
//把角度用向量来表示,同时获取到不同方向的向量值,而这个向量的角度是按照我们控制器的角度来说的
const FVector ForwardDirection = FRotationMatrix(YawRotation).GetUnitAxis(EAxis::X);
const FVector RightDirection = FRotationMatrix(YawRotation).GetUnitAxis(EAxis::Y);
//MovementVector的X和Y是相对于我们的视角来说的,也就是我们视角的正前方是Y,而我们视角的正右方是X
AddMovementInput(ForwardDirection, MovementVector.Y);
AddMovementInput(RightDirection, MovementVector.X);
}
}
void ASCharacter::Look(const FInputActionValue& Value)
{
FVector2D LookAxisVector = Value.Get<FVector2D>();
if (Controller!=nullptr)
{
AddControllerYawInput(LookAxisVector.X);
AddControllerPitchInput(LookAxisVector.Y);
}
}
void ASCharacter::PrimaryAttack()
{
GetWorldTimerManager().SetTimer(TimerHandle_PrimaryAttack,this,&ASCharacter::PrimaryAttack_TimeElasped,0.2f);
}
void ASCharacter::PrimaryInteract()
{
if (InteractionComp)
{
InteractionComp->PrimaryInteract();
}
}
//此次更改代码位置
void ASCharacter::PrimaryAttack_TimeElasped()
{
//人物起始位置
FVector StartPoint;
//视角起始位置
FVector StartPoint_Camera = CameraComp->GetComponentLocation();
//增加的旋转
FRotator EyeRotation;
GetActorEyesViewPoint(StartPoint, EyeRotation);
StartPoint = GetActorLocation();
//终点位置
FVector EndPoint = StartPoint_Camera+(EyeRotation.Vector()*10000.0f);
//碰撞结果
FHitResult HitResult;
//配置属性
FCollisionObjectQueryParams ObjectQueryParams;
ObjectQueryParams.AddObjectTypesToQuery(ECC_WorldStatic);
//判断是否有碰撞
bool bBlockingHit = GetWorld()->LineTraceSingleByObjectType(HitResult,StartPoint_Camera,EndPoint,ObjectQueryParams);
//旋转
FRotator StartRotation = EyeRotation;
//没有碰撞的话那就该怎么样就怎么样
if (bBlockingHit)
{
DrawDebugLine(GetWorld(), StartPoint_Camera, EndPoint, FColor::Red, false, 2.0f, 0U, 1.0f);
//终点减去起点
StartRotation = (HitResult.ImpactPoint - StartPoint).Rotation();
FString HitPoint = FString::Printf(TEXT("EndPoint:%s, Rotation:%s"), *HitResult.ImpactPoint.ToString(), *StartRotation.ToString());
DrawDebugString(GetWorld(), HitResult.ImpactPoint, HitPoint , nullptr, FColor::Red, 2.0f, true, 1.0f);
DrawDebugLine(GetWorld(), StartPoint, HitResult.ImpactPoint, FColor::Green, false, 2.0f, 0U, 1.0f);
};
//记得更改SpawnTM中的Rotation属性
FTransform SpawnTM = FTransform(StartRotation, StartPoint);
FActorSpawnParameters SpawnParams;
//生成参数的碰撞覆盖处理,是尽可能不和自己有重叠呢,还是说不论三七二十一就是生成对象
SpawnParams.SpawnCollisionHandlingOverride = ESpawnActorCollisionHandlingMethod::AlwaysSpawn;
SpawnParams.Instigator = this;
GetWorld()->SpawnActor < AActor >(ProjectileClass, SpawnTM, SpawnParams);
}
// Called every frame
void ASCharacter::Tick(float DeltaTime)
{
Super::Tick(DeltaTime);
}
// Called to bind functionality to input
void ASCharacter::SetupPlayerInputComponent(UInputComponent* PlayerInputComponent)
{
Super::SetupPlayerInputComponent(PlayerInputComponent);
if (UEnhancedInputComponent* EnhancedInputComponent = CastChecked<UEnhancedInputComponent>(PlayerInputComponent))
{
//MoveAction
EnhancedInputComponent->BindAction(MoveAction, ETriggerEvent::Triggered, this, &ASCharacter::Move);
//JumpAction
EnhancedInputComponent->BindAction(JumpAction, ETriggerEvent::Triggered, this, &ACharacter::Jump);
EnhancedInputComponent->BindAction(JumpAction, ETriggerEvent::Completed, this, &ACharacter::StopJumping);
//LookAction
EnhancedInputComponent->BindAction(LookAction, ETriggerEvent::Triggered, this, &ASCharacter::Look);
//PrimaryAttack
EnhancedInputComponent->BindAction(PrimaryAttackAction, ETriggerEvent::Triggered, this, &ASCharacter::PrimaryAttack);
//Interaction
EnhancedInputComponent->BindAction(InteractionAction, ETriggerEvent::Triggered, this, &ASCharacter::PrimaryInteract);
}
}