前言
在许多射击类游戏中,瞄具的放大变焦效果是提高射击精准度和游戏体验的重要部分。Unity作为一款流行的游戏开发引擎,提供了多种实现瞄准瞄具放大变焦效果的方法。本文将介绍三种常见的实现方式,并分别探讨它们的优缺点。
首先,我们将介绍如何通过调整摄像机的视野来实现放大变焦效果。其次,我们将讨论如何利用Shader来实现瞄具的放大效果,以及如何使用Render Texture来模拟变焦效果。每种方法都有其独特的应用场景和适用性,在本文中,我们将深入探讨这三种方法的具体实现和使用场景,帮助开发者根据自身需求选择合适的实现方式。
无论您是初学者还是有经验的开发者,本文都将为您提供全面的教程和示例代码,帮助您更好地理解和运用Unity中的瞄具放大变焦效果。让我们开始探索这些令人兴奋的技术吧!
第一种办法
调节相机FOV值
public class RifleScopeZoom : MonoBehaviour { [SerializeField] private Camera playerCamera; [SerializeField] private float zoomSpeed = 10f; [SerializeField] private float minFOV = 20f; [SerializeField] private float maxFOV = 60f; private void Update() { // 获取滚轮滑动的值 float scrollValue = Input.GetAxis("Mouse ScrollWheel"); // 根据滚轮滑动的值调整 FOV playerCamera.fieldOfView -= scrollValue * zoomSpeed; // 限制 FOV 的范围在最小值和最大值之间 playerCamera.fieldOfView = Mathf.Clamp(playerCamera.fieldOfView, minFOV, maxFOV); } }
效果
这就是第一个实现方式,如你所见十分简单,它所作的就是调整主摄像机参数,这个方式的优点就是性能好,你不需要什么新Shader或者另一个相机,而只需要调整相机FOV,不过缺点就是不太好看,在理想情况下我们只会希望放大瞄准镜所看到的物体,不过现在整个画面都放大了
还有就是如果你的瞄准镜模型不是空心通透的可能无法使用这种方法,比如狙击枪的瞄准镜,我下面的例子就是,使用我只能去掉瞄准镜
方法二
对于第二个方法我们会需要用到Shader来放大某个物体背后的画面
1. 创建URP环境
如果不懂的可以看我之前的文章,有教具体如何配置URP环境:使用Shader Graph实现动物森友会的世界弯曲效果
2. 配置 Universal Render Pipeline Asset
由于实验中使用了 Scene Depth 和 Scene Color 节点获取深度缓冲区和颜色缓冲区信息,需要在 Universal Render Pipeline Asset 中勾选 Depth Texture 和 Opaque Texture,如下。
3. 这里向我们新建一个无光的ShaderGraph
4. 主图配置
由于镜子是透明的,需要在主图的 Graph Settings 中将 Surface Type 属性设置 Transparent
4. 新建材质,挂载
为了实现这个效果我在瞄准镜中放了个球,然后把它弄得很平,模拟一个镜面效果,绑定带前面ShaderGraph的材质
5. 下面是shaderGraph 的连线图
这里用了Scene Color节点它输出这个物体背后的场景颜色,然后用Tilling and offset节点修改其中的Tilling值来实现放大,然后就是用到物体屏幕坐标这个是最复杂的点ObjectScreenPosition,这个参数需要随着瞄准镜移动而不断修改。
6. 新增脚本控制ObjectScreenPosition随着瞄准镜移动而不断修改
因此我这里有另一个脚本RifleScopeShaderScreenPos,它的作用就是将物体世界坐标转屏幕坐标然后输入到Shader中,所以这个Shader不会放大所有画面,他只会放大瞄准镜里的东西
public class RifleScopeShaderScreenPos : MonoBehaviour { // Shader 材质 [SerializeField] private Material material; private void Update() { // 获取物体在屏幕上的像素坐标 Vector2 screenPixels = Camera.main.WorldToScreenPoint(transform.position); // 将像素坐标转换为 0-1 的范围 screenPixels = new Vector2(screenPixels.x / Screen.width, screenPixels.y / Screen.height); // 将物体的屏幕坐标传递给 Shader material.SetVector("_ObjectScreenPosition", screenPixels); } }
6. 新增脚本控制_ZoomAmount实现滚轮放大缩小效果
public class RifleScopeZoomMaterial : MonoBehaviour { [SerializeField] private float zoomSpeed = 10f; [SerializeField] private float min = 0f; [SerializeField] private float max = 1f; [SerializeField] private Material zoomMaterial; private void Update() { // 获取滚轮滑动的值 float scrollValue = Input.GetAxis("Mouse ScrollWheel"); scrollValue = zoomMaterial.GetFloat("_ZoomAmount") + scrollValue; // 限制的范围在最小值和最大值之间 scrollValue = Mathf.Clamp(scrollValue, min, max); zoomMaterial.SetFloat("_ZoomAmount", scrollValue); } }
和上一个方法相比优点就是它只放大瞄具所看到的东西,所以瞄具外的东西都不会变化,另一个优点就是相比于接下来的方法,这个方法不太吃性能,它只有一个Shader而且相机只渲染一次,当然也有缺点,有一个潜在的问题就是当你放大后画面会变成这样
当你放大倍率不是很高时画面凑合的过去,不过当你放的越大你就越会得到马赛克画面,这和画面的分辨率有关,Shader只是简单的放大了这个图片而不会改变它的分辨率,所以放的越大画面马赛克就会变明显,如果你放大的倍数不会太大这是个不错的方法,还有和前面有相同的问题,就是如果你的瞄准镜模型不是空心通透的可能不适合这种方法
第三种办法
我们得用一下渲染纹理特性,本质上来说就是可以把相机的画面渲染到一个纹理上
1. 新增渲染相机
所以我们需要一个新的相机,然后把它往前面拖一下,现在你可以把它拖到瞄准镜前面或者直接放在枪管上,然后通过拉低FOV来放大画面
2. 创建一个渲染纹理
我们再创建一个渲染纹理,这边主要设置一下尺寸,这主要取决于玩家分辨率以及瞄准镜在屏幕上的大小,这里1024*1024差不多够了
这样子我们就设置好了接下来让相机把渲染画面输出到这个纹理上
这样我门就能看到相机的画面己经渲染到这张纹理之中了
3. 绑定渲染纹理
接下来为了在瞄准镜中显示画面,我们直接在瞄准镜里边放一个Quad,我们只需要把渲染纹理拖进来就能得到瞄具画面了
这样就有效果了,下面就是放大后的相机画面了
4. 解决镜片穿模问题
不过还是有个小小的问题,那就是我门的Quad是方形的而瞄准镜是圆形的,所以你会看到它有些穿模了
一个很简单的解决办法就是只需要做一个透明度裁剪
这里也有个透明度裁剪预制,只需要确保你到设置面板理启出Alpha clip
做完之后点击这边的AlphaClipMask选择一张带透明背景的圆形贴图
完了之后你会看到遮罩外边的贴图被裁减了它只保留了中间的画面
5. 脚本控制实现放大缩小效果
最后一步就是要处理脚本了,我们要做的就是调整渲染纹理相机的FOV,可以复用前面的代码,不够记得把相机修改为我们的渲染相机,而不是主相机
public class RifleScopeZoom : MonoBehaviour { [SerializeField] private Camera playerCamera; [SerializeField] private float zoomSpeed = 10f; [SerializeField] private float minFOV = 20f; [SerializeField] private float maxFOV = 60f; private void Update() { // 获取滚轮滑动的值 float scrollValue = Input.GetAxis("Mouse ScrollWheel"); // 根据滚轮滑动的值调整 FOV playerCamera.fieldOfView -= scrollValue * zoomSpeed; // 限制 FOV 的范围在最小值和最大值之间 playerCamera.fieldOfView = Mathf.Clamp(playerCamera.fieldOfView, minFOV, maxFOV); } }
效果
我可以放大我想要的地方,如你所见和上一个方法相比没有马赛克问题,而且这个方法有一个有趣的特性,那就是即使我枪没有抬起来时瞄具上的画面也在变化
另一个优点就是由于我使用了另一个相机,因此我可以做一些有趣的东西,比如我可以用不同的后处理
效果,我可以在主相机上加上景深效果,由于渲染纹理离相机很近所以它没被模糊化而外边的东西都模糊掉了
就个人而言这个观感效果最好,瞄具外的画面被模糊掉了但内部的画面仍然很清楚,如果你想的话你可以把镜子往前放一放来用其他的后处理效果,你可以实现镜内热成像或者夜视效果而镜外画面保持正常。
当然了这个方法也有一个大缺点那就是性能问题,这个方式使用渲染纹理来显示第二个相机的画面,本质上画面被渲染了2次,如果你做的游戏是PC端这可能不会是个大问题,而如果是手游的话可能就是大问题了,如果你可以承受性能代价,这个方法是最好的一个。
总结
好了你已经学完这三个瞄准镜放大方法了,第一个方法适合性能优先需求或者想找最简单实现方式的人,第二个方法稍优于第一
而最后一个你会得到最好的瞄具效果,去选一个适合的方法用在你的项目里吧!