【MMORPG开发日志011】UE4制作minimap小地图
写在前面
最近在考虑做一些相对完整且独立的模块,思来想去最终还是决定写一个minimap这种几乎所有游戏都会用到的功能模块。
关于小地图制作的视频版可以看我的B站个人主页:
【老李RPG】21期,UE4小地图简单实现
视频发布之后我觉得讲的还不够清楚,于是决定写一篇文章来补充一下。
其实关于小地图的实现我之前已经写过一篇文章:
RPG游戏开发日志11:小地图实现
只不过那篇文章更像是提供一种思路。
废话不多说,我们赶紧进入正题吧!
实现思路
小地图的实现思路,其实就是通过一个头顶摄像机将画面实时显示在UI上而已。
在UE4中,将摄像机中的图像渲染到某个纹理中的组件叫SceneCapture2D。这个功能也很大程度上依赖于这个组件。
另一方面就是对于细节设计的补充:通常情况下小地图是会旋转的,而在我们这类视角的游戏中,小地图的旋转不依赖于角色而是摄像机。要实现罗盘效果可以在逻辑层或者材质层实现,这里我们选择后者,所以重点就是关于材质的解析。
原理很简单,就是当摄像机转动时候对应调整材质中UV的转动即可。
而小地图正中心的玩家标记,理论上我们可以使用同样的方式,但这里我们换一种实现思路,改为在逻辑层面实现。实现思路就是借助Sprite组件,这个组件主要用于2D游戏中,在minimap中我们可以设置小地图摄像机捕捉Sprite,而游戏主摄像机不捕捉此Sprite即可。对于游戏中的NPC等特殊标记也可以使用此种方式。
材质M_CircularMinimap
创建材质并命名为M_CircularMinimap,
首先就是对材质本身进行一些设置:
将材质的MaterialDomain设置为UserInterface
(字面意思就是UI用材质),并且将BlendMode设置为Translucent
之后就是纹理,这里我们需要三部分,都做成参数,为我们之后为mod制作开放接口。这三个纹理图分别代表着:底图、罗盘图和实际map渲染图。
由于这里我们使用的是圆形小地图,因此这里将渲染纹理如下图所示处理一下:
然后将其和我们的底图进行组合:
然后如法炮制将罗盘图再进行组合:
当然,要实现罗盘纹理的旋转效果,就需要对其UV进行旋转操作:
这里我们将Rotation(float类型)提取出来作为一个参数,方便我们在蓝图中操作。这样关于材质的设置就全部完成了。
Player角色蓝图设置
在设置角色蓝图前,首先是新建一个SceneCapture2D的子类蓝图
并将其作为子Acto添加到Player蓝图中:
其实这里也可以直接在Player蓝图中添加SceneCapture2D Component
但之后我们需要在这个Actor中添加一些逻辑,因此我们没有选择组件的方式。
简单讲就是组件密闭性高,不利于拓展,Actor相反。
这里我们和游戏主相机一样,给他添加了一个SpringArm组件,理论上游戏中的摄像机都需要配合摇臂一起使用。这里不多谈了。
正如上文中所说,这里我们给Player添加了一个精灵Sprite组件,如下图所示:
这个组件要在minimap中渲染出来代表玩家,并时刻同步玩家转向:
不过这个组件在主相机中可不想被渲染出来,这里我们简单处理,设置精灵组件的OwnerNoSee属性为ture即可。
此外,我们不希望摄像机随着角色转动而变换角度,这会影响minimap中的地图转向,这里我们手动的设置相机摇臂的世界转向值:
SceneCapture2D
在Unreal当中,所有需要将相机中内容渲染在材质上的操作都可以用到这个类。这里我们没有做更多扩展操作,只是简单设置了相机需要渲染的对象类型:
更多扩展内容,我们会在升级版的小地图实现中提到。
W_MiniMapBase界面
为了在之后实现各种不同类型的minimap,这里我们抽象出一个UI基类:W_MiniMapBase
在这里做的操作十分简单,首先就是在构造事件中进行初始化,获取上文中提到的SceneCapture并持有:
同时我们动态的创建一个材质的实例,方便我们之后对其中参数进行调整:
最后我们设置一下材质中用到的渲染纹理:
然后,在tick事件中,我们获取摄像机的旋转,并改变材质罗盘层UV的旋转变化:
此外,我们这里实现一个放大和缩小minimap视野范围的事件,供子类调用:
而关于视野变化的具体方法,也很简单,只需要改变正交相机的OrthoWidth即可,但值得注意的是,由于我们并没有每帧更新游戏画面,所以在变化后需要手动的渲染画面一次:
这样Base基类中需要实现的内容就实现完了。
W_MinimapRotating
这个才是我们游戏中实际的效果,首先我们简单的罗列各种UI控件:
然后我们进行构造事件,这里主要是对父类中使用到的控件变量进行赋值操作:
然后在每一帧中更新材质中的Rotation参数:
最后,就是将按钮的点击事件和父类中的扩大、缩小视野范围的事件绑定上。
至此,一个还算说得过去的minimap就完成了。
写在最后
最后只是唠叨一些和本文内容无关的有的没的。
最近这个项目的开发又进入一个纠结期,大家也能看得出来我在过去几个月的开发过程中,很多时候心态是摇摆不定的。对于游戏的核心玩法究竟如何也没有确定下来,更谈不上信心。也正因如此才频繁开发一些与核心无关的,相对通用的模块。
我清楚这是独立游戏开发过程中时常面对的困境。这种没办法(或者说没勇气确定核心玩法)的心态是我目前很难克服的心理障碍。
希望自己能尽快确定游戏的核心玩法,来让这个项目的开发迈出新的一步吧,我也担心再这样拖下去,可能随时有弃坑的风险!