在UE4中的相机操控方案可能会比U3D中的方式要稍微麻烦一点点,今天分享下UE4中相机的几种操控方式
在本文之前先创建一个继承自GameModeBase和一个继承自APawn类,在这里博主分别命名为CameraGameModeBase、CameraCharacter
在CameraGameModeBase构造函数里将CameraCharacter设置为默认Apawn,代码如下:
DefaultPawnClass = ACameraCharacter::StaticClass();
在CameraCharacter中重写SetupPlayerInputComponent函数,用于绑定本文需要绑定的操控输入事件
// 调用绑定输入事件
void ACameraCharacter::SetupPlayerInputComponent(UInputComponent* PlayerInputComponent)
{
Super::SetupPlayerInputComponent(PlayerInputComponent);
//向上滚轮
UPlayerInput::AddEngineDefinedAxisMapping(FInputAxisKeyMapping("ScrollWheelUp", EKeys::MouseWheelAxis, 10));
PlayerInputComponent->BindAxis("ScrollWheelUp", this, &ACameraCharacter::OnScrollWheelUpPress);
//鼠标右键
UPlayerInput::AddEngineDefinedAxisMapping(FInputAxisKeyMapping("MouseRightDrag", EKeys::RightMouseButton, 10));
PlayerInputComponent->BindAxis("MouseRightDrag", this, &ACameraCharacter::OnMouseRightDrag);
//键盘Shift键
UPlayerInput::AddEngineDefinedActionMapping(FInputActionKeyMapping("KeyLeftShift", EKeys::LeftShift));
PlayerInputComponent->BindAction("KeyLeftShift", IE_Pressed, this, &ACameraCharacter::OnKeyLeftShiftPress);
PlayerInputComponent->BindAction("KeyLeftShift", IE_Released, this, &ACameraCharacter::OnKeyLeftShiftReleased);
//鼠标位置(X,Y方向)
UPlayerInput::AddEngineDefinedAxisMapping(FInputAxisKeyMapping("Turn", EKeys::MouseX, 1.0f));
PlayerInputComponent->BindAxis("Turn", this, &ACameraCharacter::OnTurn);
UPlayerInput::AddEngineDefinedAxisMapping(FInputAxisKeyMapping("LookAtRotate", EKeys::MouseY, 1.0f));
PlayerInputComponent->BindAxis("LookAtRotate", this, &ACameraCharacter::OnLookAtRotate);
}
有了前面的基础,博主要带领大家实现相机的三种操控方案
1.滚动鼠标滑轮,移动相机到鼠标点位置
需要跟随鼠标点移动的话,需要通过当前鼠标点的位置获取到一个世界坐标位置,UE4提供了这个方法,代码如下:
UGameplayStatics::DeprojectScreenToWorld(UGameplayStatics::GetPlayerController(GWorld, 0), CursorPos, WorldPos, MoveDirction);
第一个参数是指指定的的控制器
第二个参数是指当前鼠标位置
第三第四个参数是指引用返回世界位置点与鼠标点指向的世界方向射线(就是我们需要获取到的移动方向)
有了这个移动方向后,我们只需要在世界空间下,移动摄像机在世界空间下的方位就好了,这一功能代码如下:
//滚动滑轮
void ACameraCharacter::OnScrollWheelUpPress(float axisValue)
{
FVector2D CursorPos;
FVector WorldPos;
FVector MoveDirction = FVector::ZeroVector;
if (FMath::Abs(axisValue) > KINDA_SMALL_NUMBER)
{
GetWorld()->GetFirstPlayerController()->GetMousePosition(CursorPos.X, CursorPos.Y);
UGameplayStatics::DeprojectScreenToWorld(UGameplayStatics::GetPlayerController(GWorld, 0), CursorPos, WorldPos, MoveDirction);
}
ViewCamera->AddWorldOffset(MoveDirction*ScrollWheelSpeed*axisValue);
}
2.按住Shift键,拖拽鼠标右键平移视野
通过当前帧鼠标在屏幕下的位置减去前一帧的鼠标位置得到一个位置向量
通过这个比例改变摄像机自身坐标系中的X,Y轴坐标 ,代码如下:
//计算在屏幕中的MoveOffset(前一个鼠标点到当前鼠标点的向量)
void ACameraCharacter::CalcCameraMoveDrogDirction()
{
if (FMath::Abs(FVector2D::Distance(FontCurorPos, CurrentCurcorPos)) < KINDA_SMALL_NUMBER)
{
GetWorld()->GetFirstPlayerController()->GetMousePosition(FontCurorPos.X, FontCurorPos.Y);
}
else
{
GetWorld()->GetFirstPlayerController()->GetMousePosition(CurrentCurcorPos.X, CurrentCurcorPos.Y);
FVector2D MoveOffset = CurrentCurcorPos - FontCurorPos;
FontCurorPos = CurrentCurcorPos;
ViewCamera->AddLocalOffset(FVector(0, -MoveOffset.X, MoveOffset.Y));
}
}
3.按住鼠标右键,移动鼠标绕点旋转
实现这个操作可以让我们的CameraCharacter绑定一个弹簧手臂,然后再弹簧手臂上添加我们的摄像机
这样我们可以通过改变控制器的旋转进而带动摄像机手臂以及摄像机达到旋转的目的
绑定弹簧手臂方法如下
// Sets default values
ACameraCharacter::ACameraCharacter()
{
// 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;
RootComponent = CreateDefaultSubobject(TEXT("SceneComponent"));
/*创建弹簧手臂和摄像机*/
CameraSpringArm = CreateDefaultSubobject(TEXT("CameraSpringArm"));
CameraSpringArm->SetupAttachment(RootComponent);
CameraSpringArm->TargetArmLength = 500.f;
CameraSpringArm->TargetOffset = FVector(100.f, 100.f, 100.f);
CameraSpringArm->bUsePawnControlRotation = true;
CameraSpringArm->SetWorldLocation(FVector::ZeroVector);
//初始化摄像机
ViewCamera = CreateDefaultSubobject(TEXT("ViewCamera"));
ViewCamera->SetupAttachment(CameraSpringArm, USpringArmComponent::SocketName);
ViewCamera->bUsePawnControlRotation = false;
ViewCamera->SetActive(true);
}
然后我们移动鼠标右键代码如下:
void ACameraCharacter::OnTurn(float axisValue)
{
if (!IsRotateCameraState) return;
if (IsRotateCameraState&&FMath::Abs(axisValue) > KINDA_SMALL_NUMBER)
{
AddControllerYawInput(-axisValue);
}
}
void ACameraCharacter::OnLookAtRotate(float axisValue)
{
if (!IsRotateCameraState) return;
if (IsRotateCameraState && FMath::Abs(axisValue) > KINDA_SMALL_NUMBER)
{
AddControllerPitchInput(-axisValue);
}
}
最后给大家贴出这个案例的完整代码:
// Fill out your copyright notice in the Description page of Project Settings.
#include "SCTCameraCharacter.h"
#include "Camera/CameraComponent.h"
#include "GameFramework/SpringArmComponent.h"
#include "GameFramework/PlayerInput.h"
#include "Kismet/GameplayStatics.h"
#include "UnrealMathUtility.h"
#include "Engine/Engine.h"
#include "GameFramework/Controller.h"
#include "GameFramework/Character.h"
#include "Components/CapsuleComponent.h"
// Sets default values
ACameraCharacter::ACameraCharacter()
{
// 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;
RootComponent = CreateDefaultSubobject(TEXT("SceneComponent"));
/*创建弹簧手臂和摄像机*/
CameraSpringArm = CreateDefaultSubobject(TEXT("CameraSpringArm"));
CameraSpringArm->SetupAttachment(RootComponent);
CameraSpringArm->TargetArmLength = 500.f;
CameraSpringArm->TargetOffset = FVector(100.f, 100.f, 100.f);
CameraSpringArm->bUsePawnControlRotation = true;
CameraSpringArm->SetWorldLocation(FVector::ZeroVector);
//初始化摄像机
ViewCamera = CreateDefaultSubobject(TEXT("ViewCamera"));
ViewCamera->SetupAttachment(CameraSpringArm, USpringArmComponent::SocketName);
ViewCamera->bUsePawnControlRotation = false;
ViewCamera->SetActive(true);
}
// Called when the game starts or when spawned
void ACameraCharacter::BeginPlay()
{
Super::BeginPlay();
//初始化变量
ScrollWheelSpeed = 2;
CurrentCurcorPos = FontCurorPos = FVector2D::ZeroVector;
IsBindShiftKey = false;
IsRotateCameraState = false;
IsMoveCameraState = false;
FInputModeGameAndUI InputMode;
InputMode.SetLockMouseToViewportBehavior(EMouseLockMode::DoNotLock);
InputMode.SetHideCursorDuringCapture(false);
UGameplayStatics::GetPlayerController(GWorld, 0)->SetInputMode(InputMode);
UGameplayStatics::GetPlayerController(GWorld, 0)->bShowMouseCursor = 1;
}
void ACameraCharacter::Tick(float DeltaTime)
{
Super::Tick(DeltaTime);
if (IsMoveCameraState)
{
CalcCameraMoveDrogDirction();
}
}
// 调用绑定输入事件
void ACameraCharacter::SetupPlayerInputComponent(UInputComponent* PlayerInputComponent)
{
Super::SetupPlayerInputComponent(PlayerInputComponent);
//向上滚轮
UPlayerInput::AddEngineDefinedAxisMapping(FInputAxisKeyMapping("ScrollWheelUp", EKeys::MouseWheelAxis, 10));
PlayerInputComponent->BindAxis("ScrollWheelUp", this, &ACameraCharacter::OnScrollWheelUpPress);
//鼠标右键
UPlayerInput::AddEngineDefinedAxisMapping(FInputAxisKeyMapping("MouseRightDrag", EKeys::RightMouseButton, 10));
PlayerInputComponent->BindAxis("MouseRightDrag", this, &ACameraCharacter::OnMouseRightDrag);
//键盘Shift键
UPlayerInput::AddEngineDefinedActionMapping(FInputActionKeyMapping("KeyLeftShift", EKeys::LeftShift));
PlayerInputComponent->BindAction("KeyLeftShift", IE_Pressed, this, &ACameraCharacter::OnKeyLeftShiftPress);
PlayerInputComponent->BindAction("KeyLeftShift", IE_Released, this, &ACameraCharacter::OnKeyLeftShiftReleased);
//鼠标位置(X,Y方向)
UPlayerInput::AddEngineDefinedAxisMapping(FInputAxisKeyMapping("Turn", EKeys::MouseX, 1.0f));
PlayerInputComponent->BindAxis("Turn", this, &ACameraCharacter::OnTurn);
UPlayerInput::AddEngineDefinedAxisMapping(FInputAxisKeyMapping("LookAtRotate", EKeys::MouseY, 1.0f));
PlayerInputComponent->BindAxis("LookAtRotate", this, &ACameraCharacter::OnLookAtRotate);
}
//滚动滑轮
void ACameraCharacter::OnScrollWheelUpPress(float axisValue)
{
FVector2D CursorPos;
FVector WorldPos;
FVector MoveDirction = FVector::ZeroVector;
if (FMath::Abs(axisValue) > KINDA_SMALL_NUMBER)
{
GetWorld()->GetFirstPlayerController()->GetMousePosition(CursorPos.X, CursorPos.Y);
UGameplayStatics::DeprojectScreenToWorld(UGameplayStatics::GetPlayerController(GWorld, 0), CursorPos, WorldPos, MoveDirction);
}
ViewCamera->AddWorldOffset(MoveDirction*ScrollWheelSpeed*axisValue);
}
//鼠标右键拖拽
void ACameraCharacter::OnMouseRightDrag(float axisValue)
{
if (FMath::Abs(axisValue) < KINDA_SMALL_NUMBER)
{
CurrentCurcorPos = FontCurorPos = FVector2D::ZeroVector;
IsRotateCameraState = false;
IsMoveCameraState = false;
}
else
{
//按下左shift键,开始拖拽
if (IsBindShiftKey)
{
//拖拽状态
IsMoveCameraState = true;
}
//没有按下左Shift键时候绕鼠标点旋转
else
{
IsRotateCameraState = true;
}
}
}
//计算在屏幕中的MoveOffset(前一个鼠标点到当前鼠标点的向量)
void ACameraCharacter::CalcCameraMoveDrogDirction()
{
if (FMath::Abs(FVector2D::Distance(FontCurorPos, CurrentCurcorPos)) < KINDA_SMALL_NUMBER)
{
GetWorld()->GetFirstPlayerController()->GetMousePosition(FontCurorPos.X, FontCurorPos.Y);
}
else
{
GetWorld()->GetFirstPlayerController()->GetMousePosition(CurrentCurcorPos.X, CurrentCurcorPos.Y);
FVector2D MoveOffset = CurrentCurcorPos - FontCurorPos;
FontCurorPos = CurrentCurcorPos;
ViewCamera->AddLocalOffset(FVector(0, -MoveOffset.X, MoveOffset.Y));
}
}
void ACameraCharacter::OnKeyLeftShiftPress()
{
IsBindShiftKey = true;
}
void ACameraCharacter::OnKeyLeftShiftReleased()
{
IsBindShiftKey = false;
}
void ACameraCharacter::OnTurn(float axisValue)
{
if (!IsRotateCameraState) return;
if (IsRotateCameraState&&FMath::Abs(axisValue) > KINDA_SMALL_NUMBER)
{
AddControllerYawInput(-axisValue);
}
}
void ACameraCharacter::OnLookAtRotate(float axisValue)
{
if (!IsRotateCameraState) return;
if (IsRotateCameraState && FMath::Abs(axisValue) > KINDA_SMALL_NUMBER)
{
AddControllerPitchInput(-axisValue);
}
}
除了以上这种方法还有一种方法,UE4在AActor类中提供给了一个虚方法,继承自AActor的类通过重写ClacCamera虚函数的OutResult引用变量可以改变摄像机位置和旋转,实现代码博主直接贴出来,有兴趣的朋友可以看看:
// Fill out your copyright notice in the Description page of Project Settings.
#include "CameraCharacter.h"
#include "Camera/CameraComponent.h"
#include "GameFramework/SpringArmComponent.h"
#include "GameFramework/PlayerInput.h"
#include "Kismet/GameplayStatics.h"
#include "UnrealMathUtility.h"
#include "Engine/Engine.h"
#include "GameFramework/Controller.h"
#include "GameFramework/Character.h"
#include "Components/CapsuleComponent.h"
// Sets default values
ACameraCharacter::ACameraCharacter()
{
// 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;
GetCapsuleComponent()->SetEnableGravity(false);
/*创建弹簧手臂和摄像机*/
//摄像机手臂
CameraSpringArm = CreateDefaultSubobject(TEXT("CameraSpringArm"));
CameraSpringArm->SetupAttachment(RootComponent);
//设置距离
CameraSpringArm->TargetArmLength = 300.f;
//设置偏移
CameraSpringArm->TargetOffset = FVector(0.f, 0.f, 0.f);
//绑定Controller的旋转
CameraSpringArm->bUsePawnControlRotation = true;
//初始化第三人称摄像机
ViewCamera = CreateDefaultSubobject(TEXT("ViewCamera"));
//将相机绑定到摄像机手臂
ViewCamera->SetupAttachment(CameraSpringArm, USpringArmComponent::SocketName);
//设置ThirdCamera不跟随控制器的旋转
ViewCamera->bUsePawnControlRotation = false;
//处于激活状态
ViewCamera->SetActive(true);
}
// Called when the game starts or when spawned
void ACameraCharacter::BeginPlay()
{
Super::BeginPlay();
//初始化变量
MoveDirction = FVector::ZeroVector;
CameraLocation = ViewCamera->GetSocketLocation(USpringArmComponent::SocketName);
ScrollWheelSpeed = 50;
CurrentCurcorPos = FontCurorPos = FVector2D::ZeroVector;
IsBindShiftKey = false;
IsRotateCameraState = false;
RoundCameraDistTarget = 1000.f;
RoundCameraTargetOffset = FVector::ZeroVector;
RoundCameraDistCur = 1000.f;
CameraOriginLocation = FVector::ZeroVector;
CameraOriginRotate = FRotator::ZeroRotator;
}
void ACameraCharacter::CalcCamera(float DeltaTime, struct FMinimalViewInfo& OutResult)
{
if (ViewCamera->IsVisible())
{
if (bFindCameraComponentWhenViewTarget)
{
ViewCamera->GetCameraView(DeltaTime, OutResult);
}
}
else
{
GetActorEyesViewPoint(OutResult.Location, OutResult.Rotation);
}
if (CameraOriginLocation != FVector::ZeroVector&&CameraOriginRotate != FRotator::ZeroRotator)
{
OutResult.Location = CameraOriginLocation;
OutResult.Rotation = CameraOriginRotate;
}
if (IsRotateCameraState)
{
OutResult.Rotation.Yaw = GetControlRotation().Yaw;
OutResult.Rotation.Pitch = GetControlRotation().Pitch;
OutResult.Rotation.Roll = 0;
OutResult.Location = RoundCameraTargetOffset;
OutResult.Location -= OutResult.Rotation.Vector() * RoundCameraDistCur;
RoundCameraDistCur = FMath::FInterpTo(RoundCameraDistCur, RoundCameraDistTarget, DeltaTime, 20.f);
CameraLocation = OutResult.Location;
}
else
{
//滚动位置变化
CameraLocation += MoveDirction*ScrollWheelSpeed;
//平移位置变化
CameraLocation += FVector(0, MoveOffset.X, -MoveOffset.Y);
if (CameraLocation != FVector::ZeroVector)
{
OutResult.Location = CameraLocation;
}
}
CameraOriginLocation = OutResult.Location;
CameraOriginRotate = OutResult.Rotation;
}
void ACameraCharacter::Tick(float DeltaTime)
{
Super::Tick(DeltaTime);
if (OnStartMoveCamera.IsBound())
{
OnStartMoveCamera.ExecuteIfBound();
}
if (OnStartRotateAroundCamera.IsBound())
{
OnStartRotateAroundCamera.ExecuteIfBound();
}
}
// 调用绑定输入事件
void ACameraCharacter::SetupPlayerInputComponent(UInputComponent* PlayerInputComponent)
{
Super::SetupPlayerInputComponent(PlayerInputComponent);
//向上滚轮
UPlayerInput::AddEngineDefinedAxisMapping(FInputAxisKeyMapping("ScrollWheelUp", EKeys::MouseWheelAxis, 10));
PlayerInputComponent->BindAxis("ScrollWheelUp", this, &ACameraCharacter::OnScrollWheelUpPress);
//鼠标右键
UPlayerInput::AddEngineDefinedAxisMapping(FInputAxisKeyMapping("MouseRightDrag", EKeys::RightMouseButton, 10));
PlayerInputComponent->BindAxis("MouseRightDrag", this, &ACameraCharacter::OnMouseRightDrag);
//键盘Shift键
UPlayerInput::AddEngineDefinedActionMapping(FInputActionKeyMapping("KeyLeftShift", EKeys::LeftShift));
PlayerInputComponent->BindAction("KeyLeftShift", IE_Pressed, this, &ACameraCharacter::OnKeyLeftShiftPress);
PlayerInputComponent->BindAction("KeyLeftShift", IE_Released, this, &ACameraCharacter::OnKeyLeftShiftReleased);
//鼠标位置(X,Y方向)
UPlayerInput::AddEngineDefinedAxisMapping(FInputAxisKeyMapping("Turn", EKeys::MouseX, 10));
PlayerInputComponent->BindAxis("Turn", this, &ACameraCharacter::OnTurn);
UPlayerInput::AddEngineDefinedAxisMapping(FInputAxisKeyMapping("LookAtRotate", EKeys::MouseY, 10));
PlayerInputComponent->BindAxis("LookAtRotate", this, &ACameraCharacter::OnLookAtRotate);
}
//滚动滑轮
void ACameraCharacter::OnScrollWheelUpPress(float axisValue)
{
FVector2D CursorPos;
FVector WorldPos;
MoveDirction = FVector::ZeroVector;
if (axisValue != 0)
{
GetWorld()->GetFirstPlayerController()->GetMousePosition(CursorPos.X, CursorPos.Y);
UGameplayStatics::DeprojectScreenToWorld(GetWorld()->GetFirstPlayerController(), CursorPos, WorldPos, MoveDirction);
if (axisValue < 0)
{
MoveDirction = -MoveDirction;
}
GEngine->AddOnScreenDebugMessage(-1, 0.5f, FColor::White, MoveDirction.ToString());
}
}
//鼠标右键拖拽
void ACameraCharacter::OnMouseRightDrag(float axisValue)
{
if (axisValue == 0)
{
CurrentCurcorPos = FontCurorPos = FVector2D::ZeroVector;
MoveOffset = FVector2D::ZeroVector;
IsRotateCameraState = false;
OnStartMoveCamera.Unbind();
}
else
{
//按下左shift键,开始拖拽
if (IsBindShiftKey)
{
//拖拽状态
OnStartMoveCamera.BindUObject(this, &ACameraCharacter::CalcCameraMoveDrogDirction);
}
//没有按下左Shift键时候绕鼠标点旋转
else
{
IsRotateCameraState = true;
}
}
}
//计算在屏幕中的MoveOffset(前一个鼠标点到当前鼠标点的向量)
void ACameraCharacter::CalcCameraMoveDrogDirction()
{
if (FVector2D::Distance(FontCurorPos, CurrentCurcorPos) == 0)
{
GetWorld()->GetFirstPlayerController()->GetMousePosition(FontCurorPos.X, FontCurorPos.Y);
}
else
{
GetWorld()->GetFirstPlayerController()->GetMousePosition(CurrentCurcorPos.X, CurrentCurcorPos.Y);
MoveOffset = CurrentCurcorPos - FontCurorPos;
FontCurorPos = CurrentCurcorPos;
}
}
void ACameraCharacter::OnKeyLeftShiftPress()
{
IsBindShiftKey = true;
}
void ACameraCharacter::OnKeyLeftShiftReleased()
{
IsBindShiftKey = false;
}
void ACameraCharacter::OnTurn(float axisValue)
{
if (!IsRotateCameraState) return;
//if (axisValue>0)
{
AddControllerYawInput(axisValue);
RoundCameraTargetOffset += FRotator(0, 90, 0).Vector() * axisValue * 160.f * GetWorld()->GetDeltaSeconds() * RoundCameraDistTarget / 500.f;
}
}
void ACameraCharacter::OnLookAtRotate(float axisValue)
{
if (!IsRotateCameraState) return;
//if (axisValue > 0)
{
AddControllerPitchInput(axisValue);
RoundCameraTargetOffset += FRotator(0, 0, 0).Vector() * axisValue * 160.f * GetWorld()->GetDeltaSeconds() * RoundCameraDistTarget / 500.f;
}
}
备注:在创建一个继承自ACharacter的类会自带一个默认的摄像机,当在其添加了一个摄像机组件后替换这个摄像机
结语:博主今后会持续更新UE4、U3D的学习内容,大家多多支持!
博客地址:blog.liujunliang.com.cn