【UE4】Camera Shake 震屏

2024-03-19 03:59
文章标签 ue4 camera shake 震屏

本文主要是介绍【UE4】Camera Shake 震屏,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

  本文使用的引擎版本是 UE 4.25.1,使用 “Third Person” 模板。

一、创建 Camera Shake

  步骤如下:

  1. 在 ThirdPersonBP 的 Blueprints 文件夹中创建一个 “Blueprint Class”,选择 Parent Class 为 “CameraShake”,命名为 “OnCamShake”,里边的参数设置如下,其中

"Oscillation Duartion" 是震动时间,
PitchYawRoll分别对应摄像机绕着自己的 Y、Z、X 轴的震动,X 轴的震动表现就是吃鸡里边的 左右探头,一般的镜头震动不会使用这个;镜头震动还有X、Y可以设置,这会使得镜头在世界坐标下发生坐标变化,所以没有使用;

"Ampplitude" 是振幅;
"Frequency" 是震动频率,越高震动越快,为了展示效果,设置的很低;
"Initial Offset" 是初始偏移,不用,设为 0;
"Waveform" 是波形,有两个选项:Sine Wave 和 Perlin Noise,对应有规律的正弦震荡以及没有规律的噪声震荡;

  1. "Project Settings" 里添加 Input,叫 “CameraShake”,绑定键盘 C 键;
  2. 打开 ThirdPersonCharacter 蓝图,右键添加 “CameraShake” 这个 Actions Event;
  3. 右键添加 “Get Player Controller”,从 Return Value 拖出 “Client Play Camera Shake”,从 “InputAction CameraShake” 的 “Pressed” 连接到 “Client Play Camera Shake”;“Shake” 中选择刚刚创建的 “OnCamShake”
  1. Compile & Save 之后就可以 Play 了,按 C 看到效果如下:
    在这里插入图片描述

二、根据角色朝向调整 Camera Shake 方向

  从上图可以看到因为选择了 Yaw、Pitch方向的震动,且设的 “Ampplitude” 是正值,所以镜头先向右上方起振,且由于震动频率设的是一样的,所以不会出现震着震着乱了的情况;
  但是有的时候我们需要根据主角的朝向来调整起震方向,比如第三人称游戏,主角挥刀,主角右手持刀,从右上方砍到左下方,这是镜头先向左下方起震,然后在左下-右上之间震荡;这种情况在主角背对摄像机的时候是正确的,但是在主角正对镜头的时候,就不对了,因为这时候主角的右上,对应镜头的左上,主角从右上砍到左下不变,镜头的震动方式应该变为:向右下方起震,然后在右下-左上之间震荡,这样才能对应上主角的挥刀。
  可以看到上边的蓝图中,只用三个参数,一个是 target,一个是震动类,一个是震动大小Scale,代码中调用就是

PlayerController->ClientPlayCameraShake(CameraShake, ShakeScale);

  后两个参数用的是默认值,“Play Space” 是指播放空间,是个枚举类型,有三个值可选:1. “Camera Local”、2. “World”、3. “User Defined”
  默认 “Camera Local”,也就是按照摄像机的局部坐标进行CameraShake,所以我们现在的设置下,无论摄像机怎么转动,主角怎么移动怎么转动,镜头震动效果是不会变的,因为摄像机在自己的局部坐标下没有动;
  “World” 是根据镜头在世界坐标下的转动进行CameraShake,刚才的其他设置不变,选为 “World” 之后的效果如下:
在这里插入图片描述
  可以看到,在主角背对摄像机的时候,是右上–>左下;主角正对摄像机的时候,是右下–>左上;这两种情况都没有问题,但是,在主角侧身对着摄像机的时候,镜头震动就变成了左右摇头;这是因为摄像机在世界坐标下,绕着Z轴旋转了90度,原来配置的 Pitch,就变成了 Roll,这样的效果从逻辑上是没有错的,但是从效果上不太好,如果不想要摇头的效果,只想根据主角朝向调整CameraShake的起震方向,可以用第三个类型;
  “User Defined”,用户定义,这样自由度大了很多,但是理解起来会麻烦一些,首先,这里只有当选成自定义后,最后一个参数 "User Play Space Rot"才可用,从Camera中 GetWorldRotation 然后传给 “User Play Space Rot” 和直接使用 “CameraLocal” 效果是一模一样的,理解了这个后边的就好说了。

  当主角的 Forward 和摄像机的 Forward 的朝向相同时,就用 Camera 的 WorldRotation,当朝向相反时,用Camera 的 WorldRotation,将 Roll 和 Yaw 的转动都加上 180° 之后,才是想要的效果(判断朝向的时候,用的是向量投影到XY平面上之后的点乘):
在这里插入图片描述
  代码中C++实现如下:

const FVector CameraForward = PlayerController->PlayerCameraManager->GetActorForwardVector();
const FVector CharacterForward = Character->GetActorForwardVector();FVector2D CameraForward2D = FVector2D(CameraForward.X, CameraForward.Y);
FVector2D CharacterForward2D = FVector2D(CharacterForward.X, CharacterForward.Y);
CameraForward2D = CameraForward2D.GetSafeNormal();
CharacterForward2D = CharacterForward2D.GetSafeNormal();const float Cosine = FVector2D::DotProduct(CameraForward2D, CharacterForward2D);
FRotator CameraRotation = PlayerController->PlayerCameraManager->GetCameraRotation();
if (Cosine < 0)
{CameraRotation.Roll += 180;CameraRotation.Yaw += 180;
}
PlayerController->ClientPlayCameraShake(CameraShake, 1.0f, ECameraAnimPlaySpace::UserDefined, CameraRotation);

三、Camera Shake 开始、叠加、停止

  值得注意的是,PlayerController->ClientPlayCameraShake 这个函数传入的第一个参数是 TSubclassOf<class UCameraShake> Shake,也就是传入的是类,不是实例,进入函数中:

void APlayerController::ClientPlayCameraShake_Implementation( TSubclassOf<class UCameraShake> Shake, float Scale, ECameraAnimPlaySpace::Type PlaySpace, FRotator UserPlaySpaceRot )
{if (PlayerCameraManager != NULL){PlayerCameraManager->PlayCameraShake(Shake, Scale, PlaySpace, UserPlaySpaceRot);}
}

  再往下:

UCameraShake* APlayerCameraManager::PlayCameraShake(TSubclassOf<UCameraShake> ShakeClass, float Scale, ECameraAnimPlaySpace::Type PlaySpace, FRotator UserPlaySpaceRot)
{if (ShakeClass && CachedCameraShakeMod && (Scale > 0.0f) ){return CachedCameraShakeMod->AddCameraShake(ShakeClass, FAddCameraShakeParams(Scale, PlaySpace, UserPlaySpaceRot));}return nullptr;
}

  AddCameraShake 代码比较长,里边就是对 CameraShake进行实例化,并且用 ActiveShakes.Emplace(ShakeInfo); 进行添加,CameraShake 有一个 array,来记录当前正在进行的震屏,可以通过 GetActiveCameraShakes(TArray<FActiveCameraShakeInfo>& ActiveCameraShakes) 来获取。
  值得注意的是,同一个类,在没有选择单例的情况下,是可以用时实例化很多个实例的,也就是可以叠加,如果震屏发生时间几乎同时,那么看起来的效果就是震动强度变大,如果发生的时间交错,但又在同一时间段内,效果就无法预期了,可能会非常混乱。且如果震动叠加,可能会因为震动过多而导致效果过强,看起来很晕。
  所以在 CameraShake的类中,有一个选项是 “Single Instance”,代码中的注释是:
在这里插入图片描述

/** *  If true to only allow a single instance of this shake class to play at any given time.*  Subsequent attempts to play this shake will simply restart the timer.
*/

  也就是同一时间用一个 CameraShake 类只能有一个实例,勾选上这个就可以解决震动叠加导致的过强,过着混乱的问题了。
  这样的好处是直接,简单,但是问题是有的 CameraShake 类有时候想叠加,有时候不想叠加,那就不能勾选这个,但是可以在代码中,不想震屏的时候,先调用下边函数:

	PlayerController->ClientStopCameraShake(CameraShakeDefine.CameraShake, true);

  这个函数定义如下:

void APlayerController::ClientStopCameraShake_Implementation( TSubclassOf<class UCameraShake> Shake, bool bImmediately )
{if (PlayerCameraManager != NULL){PlayerCameraManager->StopAllInstancesOfCameraShake(Shake, bImmediately);}
}

  这个函数会把当前这个 CameraShake 类的所有实例全部删掉,这样再调用 PlayCameraShake 就不会出现叠加的情况了,需要叠加的时候,不stop已经创建的实例就好了。

这篇关于【UE4】Camera Shake 震屏的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/824719

相关文章

4-4.Andorid Camera 之简化编码模板(获取摄像头 ID、选择最优预览尺寸)

一、Camera 简化思路 在 Camera 的开发中,其实我们通常只关注打开相机、图像预览和关闭相机,其他的步骤我们不应该花费太多的精力 为此,应该提供一个工具类,它有处理相机的一些基本工具方法,包括获取摄像头 ID、选择最优预览尺寸以及打印相机参数信息 二、Camera 工具类 CameraIdResult.java public class CameraIdResult {

【UE4源代码观察】观察Core模块

话题 Core模块是整个引擎中最核心的模块,在之前的博客【UE4源代码观察】可视化所有模块的依赖情况中有统计,它被983个模块引用,恐怕除了第三方的模块外基本所有模块都有引用。我想首先观察其中的内容,然后再做测试:将Core模块拷贝到之前【UE4源代码观察】手动建立一个使用UBT进行编译的空白工程建立的空白工程中,看能否将它成功编译,理论上讲,“核心”不应再依赖太多其他的东西,所以我应该不会再需

【UE4源代码观察】在空白工程中测试跨模块调用函数

目的 在之前的博客【UE4源代码观察】手动建立一个使用UBT进行编译的空白工程中,成功让UBT工作起来了。现在我想要测试编译出的多个模块之间是否能互相调用,我记录下测试的过程。最终工程见 工程GIT链接。 实践 第一部分 首先仿照TestA模块建立TestB模块。 在TestB.h文件中声明了一个函数 int TestBFunc(int x, int y); 并在TestB.cpp中

【UE4源代码观察】观察FEngineLoop中各函数的调用顺序

当启动编辑器时,Launch模块是启动模块,在Windows平台中 LaunchWindows.cpp 的 WinMain函数是入口,而 FEngineLoop 管理了程序的初始化与主循环。他的成员函数如下: public:/*** Pre-Initialize the main loop, and generates the commandline from standard ArgC/Arg

【UE4源代码观察】手动建立一个使用UBT进行编译的空白工程

我想观察UE4是怎么编译的,于是查阅官方文档,了解到UE4有一套自己的编译工具:UnrealBuildTool,简称UBT。关于UBT的官方文档参阅:虚幻编译工具。我想尝试自己手动建立一个使用UBT进行编译的空白工程。不过首先,先了解下UBT的编译流程中一些文件所扮演的角色 UBT的编译流程中一些文件所扮演的角色 模块 每个模块都由一个 .build.cs 文件声明,它存储在 Source

【UE4源代码观察】可视化所有模块的依赖情况

思路 UE4引擎的模块非常多,我想要观察他们的依赖情况。于是我写了程序读取所有模块的.Build.cs文件,记录每个模块依赖的模块(当前是看PublicDependencyModuleNames和PrivateDependencyModuleNames。但后来经评论提醒我发现AddEngineThirdPartyPrivateStaticDependencies也应该视作是“依赖”但没统计,后续

简单梳理UE4的Houdini官方插件代码

前言 Houdini官方插件名字叫 “Houdini Engine”,它搭建了Houdini数据与UE4数据间的桥梁。我接触这个插件已经有段时间了,我想是时候梳理一下插件的结构了。(当前我用的UE4版本是4.24.2,Houdini版本18.0.348) 需要说明的是,这篇博客主要是从代码出发的。我准备先分析插件整体的代码结构,再逐个翻阅每个文件试图搞明白他角色。但如果不准备研究代码结构和实现

【UE4源代码观察】观察TargetPlatform模块

前情提要与本次目标 在之前的博客《【UE4源代码观察】观察 RHI、D3D11RHI、RenderCore 这三个模块的依赖关系》中,我将RHI、D3D11RHI、RenderCore这三个模块加入了我的空白工程中并确保可以成功编译。然而当时RenderCore模块有一个比较大的缺失:没有让shader相关的功能正常编译,因为它需要TargetPlatform模块中的内容。 因此,这次我想观察

【UE4源代码观察】尝试调试UBT

前言 在之前的博客《【UE4源代码观察】手动建立一个使用UBT进行编译的空白工程》中我尝试动手搭建了一个用UBT进行编译的空白的工程。但是对UBT其中的逻辑并不理解。 后来在学习UE4源代码的过程中,又了解了它的一些行为。目前,对我影响较大的是:1.他会有一些逻辑去添加一些宏。2.他会有一些逻辑去给修改ModuleRules(和.build.cs中内容的角色一样)。这时候我发现一些和预期不太一

试用UE4的纹理数组(UTexture2DArray)

UTexture2DArray 我发现在我目前使用的版本(4.25)中,官方已经实现了纹理数组(可能在4.24或更早版本就已经实现了)。 纹理数组,其含义不言而喻。一个重要作用是可以使用更多的纹理而不受制于sampler数目限制。 这一篇里我想对官方的纹理数组进行一下简单的试用。 试用 0. 启用纹理数组 虽然我看到了代码中有UTexture2DArray这个类,不过一开始并没有在编辑器