文章目录
- 相机基础
- Project Setting控制输入
- 按键事件控制相机
- 设置追踪目标
- CameraManager实现相机切换
- API接口与多态
- 蓝图之间的通信方式
- GameMode+Manager显示当前相机信息
- 事件调度器
- Sequencer入门
- 遗留问题
相机基础
UE4中的相机主要包括普通相机Camera、电影相机Cine Camera、摇臂相机Camera Rig Crane和滑轨相机Camera Rig Rail。电影相机比普通相机能够调节的参数更多,摇臂相机和滑轨相机需要搭载普通相机或电影相机才可以工作。
Lookat Tracking Setting可以让相机追踪某一目标,Filmback调整画幅,Focus Length调整焦距,Aperture调整光圈。
从ue4的相机类中直接继承蓝图的方式:
- 将相机拖到主视口中,或在在右边的世界大纲World Outliner中点击该相机,在Details栏中点击Blueprint / Add Script,并设置位置并命名即可。
- 点击右键,选择创建“Blueprint Class”,搜索自己需要的类型,并创建新蓝图。注意之后要将该蓝图拖到主视口中才能发挥作用。
为了在同一个蓝图中控制摇臂和摇臂上挂载的相机,我们需要在摇臂相机蓝图内部CraneCameraMount中添加子组件CineCamera,并将CineCamera的相对位置设置为0,这样即可将CineCamera挂载在摇臂上。
滑轨相机如果使用同样的方式挂载,则无法让相机追踪某一物体,因此我们采用child actor的方式,在“蓝图之间的通信方式”中详细说明。
Project Setting控制输入
在主界面的Settings中打开Project Settings,找到Engine–Input–Binding。
Action Mappings和Axis Mappings都能够创建用户输入事件,但Axis Mapping还能为同一事件设置多个不同的按键和返回值。如图,当按下W键时,MoveForward返回1;当按下S键时,MoveForward返回-1,这里的输入事件和输出返回值是持续的。之后在所有蓝图中,都能将创建的Action Mappings和Axis Mappings当作某一带返回值的函数(或事件)使用。
按键事件控制相机
- 自由相机设置MoveForward、MoveRight、MoveUp、TurnRight和TurnUp等功能。前三个调用AddActorLocalOffset节点或AddActorWorldOffset节点,后两个调用AddLocalRotation节点或AddWorldRotation节点。因为按键输入事件是持续发挥作用的,所以只需让返回值与速度常量相乘,即可作为偏移量使用。
- 固定位置的摇臂相机用MoveForward调节摇臂长度,TurnRight和TurnUp调节平角Pitch和仰角Yaw。方法是让原变量加上按键事件返回值和速度常量的乘积,再赋值给该变量。注意根据实际意义使用Clamp节点,将长度、平角和仰角限制在一定范围内,如将平角限制在-90°到90°之间,将仰角限制在0°到90°之间。
- 滑轨相机仅调用MoveForward一个按键事件,控制相机在滑轨上的位置,方法如摇臂相机的控制,只是变量改为Current Position On Rail。
设置追踪目标
设置滑轨相机追踪目标。创建一个Actor类型的变量LookAtTargetActor来存储跟踪目标,设置该变量为是实例可编辑。创建一个函数InitLookAtTarget来设置跟踪目标。函数的逻辑是,取出ChildActor中的组件,将其转为CineCameraActor类型,设置其追踪目标为LookAtTargetActor,并勾选Enable Look At Tracking。
之后在构造函数中调用InitLookAtTarget函数即可。
CameraManager实现相机切换
创建一个空Actor的Blueprint Class,作为CameraManager实现相机控制权的切换。在CameraManager中创建一个Actor类的数组Cameras以调用其它3个蓝图,以及一个String类的数组CameraTypes用来记录相机类型及其顺序。
InitAllCameraTypes函数遍历Cameras数组,将每个Actor的第0个tag取出,加入CameraTypes数组(事先为每个Actor添加描述其类型的tag)。在构造函数Construction Script中调用InitAllCameraTypes。
在Event Graph中开启键盘输入,并为调用不同的相机设置不同的按键及CameraID,对应的键盘操作可触发SwitchCamera函数,并传入一个CameraID。
调用SwitchCamera函数时,先将IsCameraValid设置为true,CurCameraID设置为传入的CameraID值;再取出Cameras中对应index的Actor,启用它的窗口视图(在API中详细说明);接着判断之前是否在使用相机,如果是,禁用掉之前相机的键盘输入;最后授权目前相机的键盘输入。
API接口与多态
我们注意到,启用窗口视图时,FreeCineCamera和CraneCamera的target都是它本身,而RailCamera的target是其ChildActor中的组件,所以无法在CameraManager中用同一函数实现任意Actor的启用窗口视图,因此,我们采用API接口的方式实现多态。
创建Blueprint Interface类型的蓝图CameraFunc,在蓝图中创建函数SetViewTarget。
在各个相机蓝图的上方点击Class Settings栏,右侧Details中找到Interfaces–Implemented Interfaces,并添加CameraFunc。
此时可以看到左侧Interfaces栏中多了一个函数SetViewTarget,右键点击SetViewTarget,并选择Implement Function,可以看到蓝图中多了一个SetViewTarget的节点,我们可以用它编写启用本Actor的相机窗口视图的函数。
利用SetViewTargetWIthBlend节点编写,注意RailCamera应该取两次ChildActor再传入。
最后在SwitchCamera函数中,只要调用SetViewTarget接口,就会自动跳转至对应的函数了。
蓝图之间的通信方式
- Child Actor。以之前提到的滑轨相机挂载问题为例,我们在PreviewMesh_Mount下添加ChildActor组件,并在右侧Details栏中将其类型选为CineCameraActor即可。在主界面世界大纲中可以看到,滑轨相机下挂载了一个电影相机。
- 利用蓝图类中的Tag。在蓝图编辑器的Component栏中选择它本身,在右边Details栏的Actor栏中可以找到Tags选项,它为所有该类的Actor添加一个或一系列Tag。之后可通过GetAllActorByTag节点找到所有含某个Tag的Actor(类似GetAllActorOfClass)。
- 变量赋值。在CameraManager中创建一个Actor类的数组Cameras,打开实例可编辑,在世界大纲中即可为数组添加变量,在CameraManager中也可以调用数组元素。
GameMode+Manager显示当前相机信息
首先,创建一个Widget、一个HUD和一个GameMode,在HUD中将widget加入viewport,在World Settings中设置我们自己创建的GameMode和HUD(常规操作)。
在Widget中拖入一个Text,让它的context和新建函数GetCameraType绑定。
在MainGameMode中创建InitGameMode函数,其内容为用传入值设置蓝图内创建的Actor类变量CameraManager,即将CameraManager变量赋值为蓝图BP_CameraManger。
在关卡蓝图中设置一旦开始播放,就调用MainGameMode中的InitGameMode函数,并将BP_CameraManager传入。
在CameraManager中创建GetCurCameraType函数,如果目前在使用相机,则返回对应的相机类型,否则返回“当前相机不可用”。
在widget中创建BP_MainGameMode类型的变量GameMode,并在构造函数中设置该变量为BP_MainGameMode。
在widget中text绑定的GetCameraType函数中,调用CameraManager中的GetCurCameraType函数,将其返回值转化为Text即可。
这里使用的是变量赋值的方式实现蓝图间的通信,要想在A蓝图中调用B蓝图,只需在A蓝图中创建一个类型为B蓝图的变量,并将该变量赋值为B蓝图,即可在A蓝图中调用该变量。本Demo的层级关系是,关卡蓝图控制GameMode,GameMode控制Manager,Manager控制各个相机蓝图并向UI传入数值信息,HUD创建UI控件并将其加进Viewport,CameraFunc仅作为API使用。
事件调度器
之后学完再补上这部分。
Sequencer入门
之后学完再补上这部分。
遗留问题
- RailCamera需要取两次ChildActor以获得真实的相机target,如果说第一次取得的是BP_RailCamera的ChildActor,第二次取得的是ChildActor中的子组件;那么BP_CraneCamera中真正的相机也是蓝图的组件,也为什么CraneCamera不需要取一次ChildActor呢?
- 蓝图之间的层级关系和执行顺序。是否所有蓝图都会同时被执行(并行)?一个Level中可否设置多个GameMode并在播放过程中切换?
- 利用变量赋值实现蓝图间的通信时,为什么将变量类型设置为需要调用的蓝图后,还要再将变量赋值为对应蓝图?甚至,既然可以通过将蓝图从世界大纲中拖入的方式,用其为变量赋值,那么为什么不直接将它拖入以get该蓝图,然后调用该蓝图的函数?