【Unity3D】地面网格特效
1 前言
本文实现了地面网格特效,包含以下两种模式:
- 实时模式:网格线宽度和间距随相机的高度实时变化;
- 分段模式:将相机高度分段,网格线宽度和间距在每段中对应一个值。

本文完整资源见→Unity3D地面网格特效。
2 地面网格实现
SceneController.cs
using System;
using UnityEngine;
public class SceneController : MonoBehaviour {
private static SceneController instance; // 单例
private Action cameraChangedHandler; // 相机状态改变处理器
private Transform cam; // 相机
public static SceneController Instance() { // 获取实例
return instance;
}
public void AddHandler(Action handler) { // 添加处理器
cameraChangedHandler += handler;
}
private void Awake() {
instance = this;
cam = Camera.main.transform;
}
private void Update() { // 更新场景(Scroll: 缩放场景, Ctrl+Drag: 平移场景, Alt+Drag: 旋转场景)
float scroll = Input.GetAxis("Mouse ScrollWheel");
ScaleScene(scroll);
if ((Input.GetMouseButton(0))) {
if (Input.GetKey(KeyCode.LeftControl) || Input.GetKey(KeyCode.RightControl)) {
float hor = Input.GetAxis("Mouse X");
float ver = Input.GetAxis("Mouse Y");
MoveScene(hor, ver);
}
if (Input.GetKey(KeyCode.LeftAlt) || Input.GetKey(KeyCode.RightAlt)) {
float hor = Input.GetAxis("Mouse X");
float ver = Input.GetAxis("Mouse Y");
RotateScene(hor, ver);
}
}
}
private void ScaleScene(float scroll) { // 缩放场景
if (Mathf.Abs(scroll) > Mathf.Epsilon) {
cam.position += cam.forward * scroll * 50;
cameraChangedHandler?.Invoke();
}
}
private void MoveScene(float hor, float ver) { // 平移场景
if (Mathf.Abs(hor) > Mathf.Epsilon || Mathf.Abs(ver) > Mathf.Epsilon) {
cam.position -= (cam.right * hor * 3 + cam.up * ver * 3);
cameraChangedHandler?.Invoke();
}
}
private void RotateScene(float hor, float ver) { // 旋转场景
if (Mathf.Abs(hor) > Mathf.Epsilon || Mathf.Abs(ver) > Mathf.Epsilon) {
cam.RotateAround(Vector3.zero, Vector3.up, hor * 3);
cam.RotateAround(Vector3.zero, -cam.right, ver * 3);
cameraChangedHandler?.Invoke();
}
}
}
说明:SceneController 脚本组件挂在相机对象上,这里旋转中心是固定的,如果想设置为随相机焦点自动变化,可以参考 缩放、平移、旋转场景。
GridPlane.cs
using UnityEngine;
public class GridPlane : MonoBehaviour {
public GridType gridType = GridType.REALTIME; // 网格类型
private const float lineGapFactor = 0.2f; // 线段间距因子(相机距离单位长度变化时线段间距的变化量)
private const float lineWidthFactor = 0.01f; // 线段宽度因子(相机距离单位长度变化时线段宽度的变化量)
private const int segmentFactor = 100; // 分段因子(每隔多远分一段)
private Transform cam; // 相机
private float camDist; // 相机距离
private float lineGap = 1; // 线段间距
private float lineWidth = 0.05f; // 线段宽度
private Vector4 planeCenter = Vector4.zero; // 地面中心
private Material material; // 网格材质
private void Start() {
SceneController.Instance().AddHandler(UpdateGrid);
cam = Camera.main.transform;
material = Resources.Load<Material>("GridPlaneMat");
material.SetVector("_PlaneCenter", planeCenter);
UpdateGrid();
}
private void UpdateGrid() { // 更新网格
camDist = Mathf.Abs(cam.position.y - planeCenter.y);
if (gridType == GridType.REALTIME) {
RealtimeUpdateGrid();
} else if (gridType == GridType.SEGMENTED) {
SegmentedUpdateGrid();
}
}
private void RealtimeUpdateGrid() { // 实时更新网格
lineGap = camDist * lineGapFactor;
lineWidth = camDist * lineWidthFactor;
UpdateMatProperties();
}
private void SegmentedUpdateGrid() { // 分段更新网格
int dist = (((int) camDist) / segmentFactor + 1) * segmentFactor;
lineGap = dist * lineGapFactor;
lineWidth = dist * lineWidthFactor;
UpdateMatProperties();
}
private void UpdateMatProperties() { // 更新材质属性
lineGap = Mathf.Max(lineGap, lineGapFactor);
lineWidth = Mathf.Max(lineWidth, lineWidthFactor);
material.SetFloat("_LineGap", lineGap);
material.SetFloat("_LineWidth", lineWidth);
}
}
public enum GridType { // 网格类型
REALTIME, // 实时模式(网格随相机高度实时变化)
SEGMENTED // 分段模式(网格随相机高度分段变化)
}
说明:GridPlane 脚本组件挂在地面对象上。
GridPlane.shader
Shader "MyShader/GridPlane" { // 路径上的节点移动特效
Properties {
_PlaneColor("Plane Color", Color) = (1, 1, 1, 1) // 地面颜色
_LineColor("Line Color", Color) = (1, 1, 1, 1) // 线条颜色
_LineGap("Line Gap", Int) = 1 // 线段间距
_LineWidth("Line Width", Range(0, 1)) = 0.1 // 线段宽度
_PlaneCenter("Plane Center", Vector) = (0, 0, 0, 0) // 地面中心
}
SubShader {
Pass {
cull off
CGPROGRAM
#include "UnityCG.cginc"
#pragma vertex vert
#pragma fragment frag
float4 _PlaneColor; // 地面颜色
float4 _LineColor; // 线条颜色
int _LineGap; // 线段间距
float _LineWidth; // 线段宽度
float4 _PlaneCenter; // 地面中心
struct v2f {
float4 pos : SV_POSITION; // 裁剪空间顶点坐标
float2 worldPos : TEXCOORD0; // 世界空间顶点坐标(只包含xz)
};
v2f vert(float4 vertex: POSITION) {
v2f o;
o.pos = UnityObjectToClipPos(vertex); // 模型空间顶点坐标变换到裁剪空间, 等价于: mul(UNITY_MATRIX_MVP, vertex)
o.worldPos = mul(unity_ObjectToWorld, vertex).xz; // 将模型空间顶点坐标变换到世界空间
return o;
}
fixed4 frag(v2f i) : SV_Target {
float2 vec = abs(i.worldPos - _PlaneCenter.xz);
float2 mod = fmod(vec, _LineGap);
float2 xz = min(mod, _LineGap - mod);
float dist = min(xz.x, xz.y);
float factor = 1 - smoothstep(0, _LineWidth, dist);
fixed4 color = lerp(_PlaneColor, _LineColor, factor);
return fixed4(color.xyz, 1);
}
ENDCG
}
}
}
说明:在 Assets 窗口新建 Resources 目录,接着在 Resources 目录下面创建材质,重命名为 GridPlaneMat,将 GridPlane.shader 与 GridPlaneMat 材质绑定。
3 运行效果
1)实时模式

2)分段模式

声明:本文转自【Unity3D】地面网格特效。
【Unity3D】地面网格特效的更多相关文章
- Unity3D 导航网格自动寻路(Navigation Mesh)
NavMesh(导航网格)是3D游戏世界中用于实现动态物体自动寻路的一种技术,将游戏中复杂的结构组织关系简化为带有一定信息的网格,在这些网格的基础上通过一系列的计算来实现自动寻路..导航时,只需要给导 ...
- unity3d 摄像机抖动特效
摄像机抖动特效 在须要的地方调用CameraShake.Shake()方法就能够
- Unity3D图像后处理特效——Depth of Field 3.4
Depth of Field 3.4 is a common postprocessing effect that simulates the properties of a camera lens. ...
- Unity3D特效-场景淡入淡出
最近公司开始搞Unity3D..整个游戏..特效需求还是比较多的.关于UI部分的特效淡入淡出.看网上用的方法都是用个黑东东遮挡然后设置alpha这么搞....本大神感觉非常的low.而且很渣.故奋笔疾 ...
- [Unity3D]Unity资料大全免费分享
都是网上找的连七八糟的资料了,整理好分享的,有学习资料,视频,源码,插件……等等 东西比较多,不是所有的都是你需要的,可以按 ctrl+F 来搜索你要的东西,如果有广告,不用理会,关掉就可以了,如 ...
- cocos2d-x 网格动画深入分析
转自:http://www.2cto.com/kf/201212/179828.html 在TestCpp中的EffectsTest示例中展示了一些屏幕特效,它是将屏幕划分为多个格子,并对这些格子进行 ...
- 24、Cocos2dx 3.0游戏开发找小三之网格动作:高炫酷的3D动作
重开发人员的劳动成果,转载的时候请务必注明出处:http://blog.csdn.net/haomengzhu/article/details/37596763 网格动作类似于动作特效,能够实现翻转. ...
- WebGL场景的两种地面构造方法
总述:大部分3D编程都涉及到地面元素,在场景中我们使用地面作为其他物体的承载基础,同时也用地面限制场景使用者的移动范围,还可以在通过设置地块的属性为场景的不同位置设置对应的计算规则.本文在WebGL平 ...
- Unity塔防游戏开发
Unity3D塔防开发流程 配置环境及场景搭建编程语言:C#,略懂些许设计模式,如果不了解设计模式,BUG More开发工具:Unity3D编辑器.Visual Studio编译器开发建议:了解Uni ...
- 手搓一个“七夕限定”,用3D Engine 5分钟实现烟花绽放效果
七夕来咯!又到了给重要的人送惊喜的时刻. 今年,除了将心意融入花和礼物,作为程序员,用自己的代码本事手搓一个技术感十足"七夕限定"惊喜,我觉得,这是不亚于车马慢时代手写信的古典主义 ...
随机推荐
- 问题--flask无法发邮件,无法登录
1.问题 早上测试项目的时候,一直无法正确发送邮件,无法接收,但是查不出原因是什么 2.解决 改变了一下思路,去登录了不需要邮件验证码的用户,发现错误 这个错误提示是一个数据库连接错误,表明应用程序无 ...
- 【FreeRTOS】堆内存管理
动态内存分配及其与FreeRTOS的相关性 为了使FreeRTOS更易用,内核对象(如任务.队列.信号量.事件组)不在编译期静态分配,而是在运行时动态分配,FreeRTOS在内核对象创建时分配RAM, ...
- 英语学习 : Get + 形容词 ( 表示由无到有的变化过程 )
I am happy . 表示事实 I got happy . 强调变化过程 get + 名词 : 得到 ...物或人 get + 形容词 : 变得 ...样
- Linux-磁盘-di-目录查询-du-tree
- [转帖]彻底搞明白 GB2312、GBK 和 GB18030
https://zhuanlan.zhihu.com/p/453675608 日常工作的过程中,关于字符编码的问题经常让人头疼不已,这篇文章就来捋一捋关于 GB2312.GBK.GB18030 相关的 ...
- [转帖]资料整理——Oracle版本历史(很全面)(Releases and versions of Oracle Database)
资料来源: https://en.wikipedia.org/wiki/Oracle_Database Oracle Database Version Initial Release Version ...
- [转帖]centos7 firewall-cmd主机之间端口转发
目录 1. firewalld 1.1 firewalld守护进程 1.2 控制端口/服务 1.3 伪装IP 1.4 端口转发 2. 案例 2.1 配置ServerA 2.2 安装nginx测试 (可 ...
- Linux 界面能够出现ip地址提示的方法
cat <<EOF >/etc/profile.d/ip.sh if [[ `tty | grep "pts"` ]]; then export PS1='['& ...
- 【笔记】VictoriaMetrics中,对大量的pull模式的targets进行分片
作者:张富春(ahfuzhang),转载时请注明作者和引用链接,谢谢! cnblogs博客 zhihu Github 公众号:一本正经的瞎扯 配置的方法请看这里:https://docs.victor ...
- 【K哥爬虫普法】房产数据刑吗?爬虫多年没踩过缝纫机,劝你找找自己原因!
我国目前并未出台专门针对网络爬虫技术的法律规范,但在司法实践中,相关判决已屡见不鲜,K哥特设了"K哥爬虫普法"专栏,本栏目通过对真实案例的分析,旨在提高广大爬虫工程师的法律意识,知 ...