开屏小广告:本人待业中,擅长 unity、cocos 开发,如各位大佬有岗位推荐,感激不尽
1. 效果预览
微信小游戏畅玩榜的《小小英雄》中战斗场景采用了开放式地图,玩家可自由移动,地图弯曲的设计区别于传统 2D 场景,仅用平面图片就实现了近大远小、地图弯曲的效果,增加了视觉吸引力
常规
边界
常规
边界
2. 地图拆解
游戏画面中,除了 UI 外,主要分为三个部分:
-
地图:屏幕上半部分的地图呈曲面下沉
-
装饰及角色:和地图表现一致
-
远景:保持不变,当角色移动到边界时,可以呈现更多的远景
地图及其上的元素,规则都是距离摄像机越远下沉越多,且是非线性下沉,有明显的弧度,且具有前后遮挡关系
远景始终保持不变,当角色移动到地图尽头时,会呈现更多远景
地图拆解完毕后,接下来依次实现,先从最简单的远景做起
3. 地图实现
1. 远景
远景始终以固定的位置和角度展示在画面中,不会随着玩家移动而变化,但当玩家移动到地图尽头时,可以看到更多的远景。因此选择正交模式进行渲染,在 Hierarchy 中创建摄像机,将 Projection 选择为 Orthographic
该摄像机只渲染远景,所以将背景及摄像机的 Culling Mask 均设置为远景层:
远景层的展示效果:
ps: 主流设备的分辨率比例也不尽相同,为了在多种分辨率下都可以完整的显示背景,需要根据设备实际分辨率对相机的 orthographicSize 属性进行设置,核心代码如下:
switch (resolutionPolicy)
{
case ResolutionPolicy.FixedWidth:
float aspectRatio = Screen.width * 1.0f / Screen.height;
orthoSize = designResolution.x / 100 / 2 / aspectRatio;
break;
case ResolutionPolicy.FixedHeight:
orthoSize = designResolution.y / 100 / 2;
break;
}
2. 地图及元素
地图及其上的元素距离摄像机越远,下沉弧度越大,即随着 Z 坐标的偏移增值大,元素 X、Z 坐标保持不变,Y 坐标根据 Z 坐标利用曲线公式,计算出下沉后的坐标值
在 Unity 中实现下沉坐标的计算有两种方式
-
Shader Graph
【Unity教程】简单的世界弯曲效果(Shader Graph教程):https://www.bilibili.com/video/BV1Rr4y167ET/
-
Shader
UnityShader 基础(23)-模型弯曲-地铁跑酷与动森地图:https://zhuanlan.zhihu.com/p/407402648
Shader 核心代码:
// 获取模型的空间坐标
float3 WordPos = mul(unity_ObjectToWorld, v.vertex);
// 获取 Y 轴坐标
o.WordData.x = WordPos.y;
// Y 轴扭曲,依据 Z坐标变化
WordPos.y -= pow(WordPos.z, 2) * _BendY;
// 修正模型位置,WordPos 不包含物体自身的空间位移
WordPos -= mul(unity_ObjectToWorld, float4(0, 0, 0, 1));
// 修改世界顶点转回物体自身顶点。
v.vertex = mul(unity_WorldToObject, WordPos);
// 转换为裁切空间
o.vertex = UnityObjectToClipPos(v.vertex);
根据此 Shader 创建材质,并将其应用到地图及元素,如果角色使用 Spine,也需要同步更改 Spine 的 Shader,通过在编辑器面板中调整参数,实现不同程度的弯曲效果
ps: demo 中的地图仅使用了一张纹理,实际项目的地图会丰富多彩,为了优化性能,可以采用多张纹理拼接的形式,根据坐标控制地图切块的显示、隐藏及加载、卸载
4. 角色移动
游戏是欺骗的艺术,游戏中控制角色移动也有两种方式
-
控制角色在地图中的位置,地图位置固定,摄像机跟随角色移动
-
控地图的位置,角色和摄像机位置固定
这里使用控制地图位置的方式,计算出地图边界后,当玩家操作摇杆时,更改地图坐标,并将其约束在边界内,核心代码:
var position = mapController.position;
Vector3 direction = Vector3.forward * joystick.Vertical + Vector3.right * joystick.Horizontal;
if (direction.x < 0)
{
playerTrans.localScale = new Vector3(-0.3f, 0.3f, 0.3f);
}
else if (direction.x > 0)
{
playerTrans.localScale = new Vector3(0.3f, 0.3f, 0.3f);
}
position += -direction * speed * Time.deltaTime;
position.x = Mathf.Clamp(position.x, -11, 11);
position.z = Mathf.Clamp(position.z, -13, 10);
mapController.position = position;
5. 源码说明
源码不包括分辨率适配,地图切块。仔细阅读教程,可独立实现该效果,无须付费购买
源码需费购买:https://weidian.com/item.html?itemID=7260900875