【Unity3D】激光灯、碰撞特效
1 需求描述
本文将模拟激光灯(或碰撞)特效,详细需求如下:
- 从鼠标位置发射屏幕射线,检测是否与物体发生碰撞
- 当与物体发生碰撞时,在物体表面覆盖一层激光灯(或碰撞)特效

本文代码见→激光灯、碰撞特效
2 原理
获取屏幕射线与物体的碰撞点,并在 shader 中计算顶点与碰撞点的距离(记为 dist),通过以下衰减函数计算顶点对应的透明度,透明度随碰撞点的距离增大逐渐减小,激光灯(或碰撞)效果逐渐减弱。
alpha = pow(exp(-dist), 4)
为使特效更加逼真,激光灯(或碰撞)特效的红色分量由以下漫反射公式控制。其中,red 为红色分量值,λ 为漫反射因子,值越大,漫反射效果越强,本文 λ = 0.1;lightDir 为顶点光源向量,normalDir 为顶点法线向量。
red = λ * dot(lightDir, normalDir) + (1 - λ)
3 需求实现
SelectController.cs
using UnityEngine;
public class SelectController : MonoBehaviour { // 单击选中控制
private Transform target; // 选中的目标
private RaycastHit hit; // 碰撞信息
private void Update() {
if (Input.GetMouseButtonUp(0)) {
Transform temp = GetHitTrans();
if (temp != target) {
DrawEffect(target, temp);
target = temp;
}
}
}
private void DrawEffect(Transform old, Transform now) { // 绘制特效
DrawEffect(old, false);
DrawEffect(now, true);
}
private void DrawEffect(Transform trans, bool enable) { // 绘制特效
if (trans != null) {
foreach(Transform child in trans.transform.GetComponents<Transform>()) {
if (child.GetComponent<ColliderEffect>() == null) {
if (enable) {
child.gameObject.AddComponent<ColliderEffect>();
}
}
else {
child.GetComponent<ColliderEffect>().enabled = enable;
}
}
}
}
private Transform GetHitTrans() { // 获取屏幕射线碰撞的物体
Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition);
if (Physics.Raycast(ray, out hit)) {
return hit.transform;
}
return null;
}
}
ColliderEffect.cs
using System.Collections.Generic;
using System.Linq;
using UnityEngine;
[DisallowMultipleComponent]
public class ColliderEffect : MonoBehaviour { // 激光灯(或碰撞)特效
private Renderer[] renderers; // 当前对象及其子对象的渲染器
private Material colliderMaterial; // 激光灯(碰撞)材质
private Vector4 hitPos; // 碰撞点
private RaycastHit hit; // 碰撞信息
private void Awake() {
renderers = GetComponentsInChildren<Renderer>();
colliderMaterial = new Material(Shader.Find("MyShader/ColliderEffect"));
hitPos = Vector4.zero;
CombineSubmeshes();
}
private void OnEnable() {
hitPos = GetHitPoint();
colliderMaterial.SetVector("_HitPos", hitPos);
foreach (var renderer in renderers) {
List<Material> materials = renderer.sharedMaterials.ToList();
materials.Add(colliderMaterial);
renderer.sharedMaterials = materials.ToArray();
}
}
private void Update() {
hitPos = GetHitPoint();
if (!hitPos.Equals(Vector4.zero)) {
colliderMaterial.SetInt("_Enable", 1);
colliderMaterial.SetVector("_HitPos", hitPos);
} else {
colliderMaterial.SetInt("_Enable", 0);
}
}
private void OnDisable() {
foreach (var renderer in renderers) {
List<Material> materials = renderer.sharedMaterials.ToList();
materials.Remove(colliderMaterial);
renderer.sharedMaterials = materials.ToArray();
}
}
private Vector4 GetHitPoint() {
Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition);
if (Physics.Raycast(ray, out hit, 1000) && hit.transform == transform) {
return hit.point;
}
return Vector4.zero;
}
private void CombineSubmeshes() { // 绑定子网格
foreach (var meshFilter in GetComponentsInChildren<MeshFilter>()) {
var renderer = meshFilter.GetComponent<Renderer>();
if (renderer != null) {
CombineSubmeshes(meshFilter.sharedMesh, renderer.sharedMaterials.Length);
}
}
foreach (var skinnedMeshRenderer in GetComponentsInChildren<SkinnedMeshRenderer>()) {
CombineSubmeshes(skinnedMeshRenderer.sharedMesh, skinnedMeshRenderer.sharedMaterials.Length);
}
}
private void CombineSubmeshes(Mesh mesh, int materialsLength) { // 绑定子网格
if (mesh.subMeshCount == 1) {
return;
}
if (mesh.subMeshCount > materialsLength) {
return;
}
mesh.subMeshCount++;
mesh.SetTriangles(mesh.triangles, mesh.subMeshCount - 1);
}
}
ColliderEffect.shader
Shader "MyShader/ColliderEffect" {
Properties {
_HitPos ("HitPos", Vector) = (0, 0, 0, 0) // 屏幕射线碰撞位置
_Enable ("Enable", Int) = 0 // 是否开启特效
}
SubShader {
Tags {
// 渲染队列: Background(1000, 后台)、Geometry(2000, 几何体, 默认)、Transparent(3000, 透明)、Overlay(4000, 覆盖)
"Queue" = "Transparent+110"
"RenderType" = "Transparent"
"DisableBatching" = "True"
}
Pass {
Blend SrcAlpha OneMinusSrcAlpha // 混合测试, 与背后的物体颜色混合
CGPROGRAM
#include "UnityCG.cginc"
#pragma vertex vert
#pragma fragment frag
uniform int _Enable;
uniform float4 _HitPos;
struct appdata {
float4 vertex : POSITION;
float3 normal : NORMAL;
};
struct v2f {
float4 pos : SV_POSITION;
float4 worldPos : TEXCOORD0;
float3 worldNormal : Normal;
};
v2f vert(appdata v) {
v2f o;
o.pos = UnityObjectToClipPos(v.vertex); // 裁剪坐标系下顶点坐标
o.worldPos = mul(unity_ObjectToWorld, v.vertex); // 世界坐标系下顶点坐标
o.worldNormal = UnityObjectToWorldNormal(v.normal); // 世界坐标系下顶点法线向量
return o;
}
fixed4 frag(v2f i) : SV_Target {
if(_Enable == 1) {
float3 worldLightDir = normalize(UnityWorldSpaceLightDir(i.worldPos)); // 世界坐标系下由顶点指向光源的方向向量
float diffuse = 0.1 * dot(worldLightDir, i.worldNormal) + 0.9; // 漫反射颜色强度
float dist = distance(i.worldPos, _HitPos);
float alpha = pow(exp(-dist), 4); // 透明度(随距离衰减)
return float4(diffuse , 0, 0, alpha);
} else {
return float4(0, 0, 0, 0);
}
}
ENDCG
}
}
}
4 运行效果

5 推荐阅读
声明:本文转自【Unity3D】激光灯、碰撞特效
【Unity3D】激光灯、碰撞特效的更多相关文章
- Unity3D使用碰撞体做触发器实现简单的自己主动开门
在游戏制作中触发器的使用很的方便也很有用. 这一张我们简介一下怎样使用一个简单的触发器来实现自己主动开门关门的效果. 首先确保你已经对门进行了动画的设置. 详细流程例如以下. 选择Window- ...
- Unity3D --对撞机/碰撞器 介绍
碰撞器一般都用作触发器而用,刚体一般用作真实碰撞. 静态对撞机:一个对象有对撞机组件,没有刚体组件. 这种情况在场景中的静态物体应用较多,比如墙体,房屋等静止不动的物体. 物理引擎假设静态对撞机是不会 ...
- Unity3d -- Collider(碰撞器与触发器)
(2d与3d的Collider可以相互存在,但是无法相互协作,如2d是无法检测3d的,反之,一样) 在目前掌握的情况分析,在Unity中参与碰撞的物体分2大块:1.发起碰撞的物体.2.接收碰撞的物体. ...
- unity3d关于碰撞问题
这个是我做忍者游戏出现的问题,做个记录也为以后有人遇到也可以借鉴.因为刚接触unity,所以对其所知甚少,说错的地方请指教. 问题:角色碰撞墙为什么会先触发碰撞地面,然后再触发碰撞墙 想要的效果:是角 ...
- Unity3D之碰撞体,刚体
一 概念介绍 刚体 Rigidbody(刚体)组件可使游戏对象在物理系统的控制下来运动,刚体可接受外力与扭矩力用来保证游戏对象像在真实世界中那样进行运动.任何游戏对象只有添加了刚体组件才能受到重力的影 ...
- 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 来搜索你要的东西,如果有广告,不用理会,关掉就可以了,如 ...
- 《Unity3D》通过对象池模式,管理场景中的元素
池管理类有啥用? 在游戏场景中,我们有时候会需要复用一些游戏物体,比如常见的子弹.子弹碰撞类,某些情况下,怪物也可以使用池管理,UI部分比如:血条.文字等等 这些元素共同的特性是:存在固定生命周期,使 ...
随机推荐
- DataGrip连接MySql数据库失败:dataGrip java.net.ConnectException: Connection refused: connect.
1.问题 报错:dataGrip java.net.ConnectException: Connection refused: connect. 详细错误:[08S01] Communications ...
- MySQL-多表查询练习
首先创建练习所需要的数据表 储备:建表操作: CREATE TABLE `t_dept` ( `id` INT(11) NOT NULL AUTO_INCREMENT, `deptName` VARC ...
- [转帖]会长期锁表吗?Oracle add column default 在各版本的优化
最近大家讨论到各类数据库在新增字段带默认值时是否会锁表的问题,发现Oracle在不同的版本其实是有对应优化的,更新了下之前的知识点. 主要考虑以下三种情形,例如: 不加default:alter ta ...
- [转帖]Oracle Exadata X10M 数据库一体机发布,树立 OLTP 数据库性能新标杆
https://www.modb.pro/db/653936 2023年6月25日消息,Oracle 公司继续以其专用的 Exadata 平台重新定义 OLTP 数据库性能.上周,该公司延续了这一传统 ...
- [转帖]10.2 Data Collector与MDW
10.2 Data Collector与MDW Data Collection功能是SQL SERVER 2005版本提供的数据库监控报表的功能,通过定时地对数据库的语句运行情况,服务器各种资源的监控 ...
- [转帖]华为FusionSphere虚拟化解决方案介绍
https://huaweicloud.csdn.net/63566589d3efff3090b5d243.html?spm=1001.2101.3001.6650.2&utm_medium= ...
- [转帖]《Linux性能优化实战》笔记(24)—— 动态追踪 DTrace
使用 perf 对系统内核线程进行分析时,内核线程依然还在正常运行中,所以这种方法也被称为动态追踪技术.动态追踪技术通过探针机制来采集内核或者应用程序的运行信息,从而可以不用修改内核和应用程序的代码就 ...
- [转帖]Java连接 MySQL详细教程,分享复习经验和后台开发面经
(由于安装了汉化包,英文版的用户可以对应图标来操作) 选中菜单栏文件,之后选择项目结构 选择Libraries 点击+ ![在这里插入图片描述](https://img-blog.csdnimg.cn ...
- Oracle表数量对数据泵备份恢复速度的影响情况
Oracle表数量对数据泵备份恢复速度的影响情况 背景 随着公司产品交付后的时间越来越久. 数据库的备份恢复速度会越来越慢. 最开始一直认为是因为数据量导致的. 但是最近发现, 如果只是将数据库表的量 ...
- Python控制微信,实现聊天机器人
自从微信禁止网页版登陆之后,itchat 库实现的功能也就都不能用了,那现在 Python 还能操作微信吗?答案是:可以! 在Github上有一个项目叫<WeChatPYAPI>可以使用 ...