技术标签: 射线 unity Unity3D github 辐射 地球
嗨,大家伙,我是新发。
好久不见,这是2022年第一篇博客,今天有同学私信我,问我在Unity
中如何实现这种地球辐射线的效果,
这一看,我就想到了GitHub
主页的地球射线,
那么,今天就来讲讲如何实现这个效果吧~
本文最终效果如下:
本文工程源码见文章末尾~
我们先把问题进行拆解,
现在挨个问题进行思考与解答。
一条曲线,它本质上是由N
条直线段组成的,当N
足够大的时候,曲线就会看起来很平滑,我用Blender
给大家演示一下,
我在之前的一些篇文章中有讲过使用LineRenderer
来绘制曲线,这里我们是用LineRenderer
来实现就好啦。
往期相关文章:
《【游戏开发解答】教你在Unity中使用LineRenderer制作行军蚂蚁线(行军 | 虚线 | 路径 | 线段)》
《【游戏开发实战】Unity实现水果忍者切水果的刀痕效果教程(两种实现方式:TrailRenderer、LineRenderer)》
《【游戏开发实战】TapTap物理画线游戏,教你使用Unity实现2D物理画线功能》
上面我们说使用LineRenderer
来绘制曲线,而LineRenderer
需要我们告诉它点的坐标,那么我们如何来构造曲线的点坐标呢?常用的曲线有B
样条、贝塞尔曲线等,关于贝塞尔曲线,我之前也有专门写过文章,
《【游戏开发进阶】玩转贝塞尔曲线,教你在Unity中画Bezier贝塞尔曲线(二阶、三阶),手把手教你推导公式》
这里我们就用贝塞尔曲线即可,这里我打算使用三阶贝塞尔曲线,三阶贝塞尔曲线需要知道四个点坐标,现在问题变成了我们如何确定这四个点的坐标,其中起始点和终止点是在球的表面选取,中间两个点我们通过一些几何运算来获得,现在问题变成了如何在球的表面获取两个点。
这是一个几何问题,我们已知球的球心坐标,想要在球的表面随机选取两个点,我们只需要在球心处随机两个方向向量,然后从球心出发,分别沿着这两个方向走一个半径长度的距离即可到达球的表面,画个图方便大家理解,
写成代码大概是这样子:
// 球心坐标
Vector3 centerPos = Vector3.zero;
// 球半径
float radius = 1;
// 随机一个单位向量作为方向向量
Vector3 randomDir = new Vector3(Random.Range(-1f, 1f),
Random.Range(-1f, 1f),
Random.Range(-1f, 1f)).normalized;
// 球表面的点
Vector pos = centerPos + randomDir * radius;
我们使用上面的方法分别取到球表面的两个点即可。
上面我们说使用三阶贝塞尔曲线,现在还差中间两个控制点的坐标,我们可以用下面这样的方法来计算控制点的坐标:先求出球表面两个点的连线的中点,然后从球心指向这个中点,得到一个方向向量,再分别从点1
和点2
朝这这个方向向量走一段距离,得到控制点1
和控制点2
的坐标,如下
我们可以看到曲线是有一个从起始点飞向目标点的效果的,
这个我们可以在脚本中动态设置LineRenderer
的点来达到这个效果。
讲完了理论,下面我们就来具体实操吧~
工程名就叫UnityEarthRay
好了,工程模板使用3D
,点击创建,
我们先去找6张
宇宙天空盒的图片,导入到工程目录中,如下
创建一个材质球,重命名为Skybox
,如下
设置材质球的Shader
为Skybox/6 Sided
,并设置六个面的贴图,如下,
将Skybox
材质球拖入Scene
窗口中,
我们也可以通过点击菜单Window / Rendering / Lighting
,
然后点击Environment
标签页,设置Skybox Material
来设置天空盒,
如果想改回原来的天空盒,只需把Skybox Material
设置为默认的天空盒材质即可,如下
在Hierarchy
窗口空白处右键鼠标,点击菜单3D Object / Sphere
,创建一个球体,
重命名为Earth
,作为地球的外形,
如下
去找一下地球的贴图,导入工程目录中,我找了云层贴图、地球贴图、灯光贴图,如下
创建一个材质球,重命名为Earth
,
设置材质球的Shader
为Standard
,设置Albedo
贴图,调节Matallic
(金属度)和Smoothness
(光滑度),开启Emission
(自发光),并设置发光贴图,设置发光颜色为橘黄色,如下
然后把材质球赋值给模型,如下
此时效果,
同理,我们在Earth
子节点下再创建一个球体,重命名为Clouds
,如下
调节Clouds
的缩放,让它比Earth
大一点点,
创建一个材质球,重命名为Clouds
,
设置材质球的Shader
为Standard
,设置Rendering Mode
为Transparent
(透明),设置Albedo
贴图,设置颜色值的A
通道为0,调节Matallic
(金属度)和Smoothness
(光滑度),开启Emission
(自发光),并设置发光贴图,设置发光颜色为灰色,如下
把Clouds
材质球赋值给Clouds
节点,
此时效果
在Earth
节点右键鼠标,点击菜单Effects / Line
,创建一个LineRenderer
,
如下
此时可以看到场景中多了一条粗粗的白色短线,它就是LineRenderer
本君了,
我们可以调节一下它的宽度,让它细一点,
如下
我们可以给他创建一个材质球,
如下
创建一个C#
脚本,重命名为Line
,编写曲线的逻辑代码,
首先我们封装一个三阶贝塞尔曲线的函数,如下
注:这里三阶贝塞尔曲线代码看不懂的建议先看我之前的这篇文章,《【游戏开发进阶】玩转贝塞尔曲线,教你在Unity中画Bezier贝塞尔曲线(二阶、三阶),手把手教你推导公式》
// Line.cs
// 三阶贝塞尔曲线
private Vector3 cubicBezier(Vector3 a, Vector3 b, Vector3 c, Vector3 d, float t)
{
Vector3 aa = a + (b - a) * t;
Vector3 bb = b + (c - b) * t;
Vector3 cc = c + (d - c) * t;
Vector3 aaa = aa + (bb - aa) * t;
Vector3 bbb = bb + (cc - bb) * t;
return aaa + (bbb - aaa) * t;
}
接着我们定义曲线的最大点数,
/// <summary>
/// 曲线最大点数
/// </summary>
public int MAX_FRAG_CNT = 100;
声明一个List
用于存储点坐标
private List<Vector3> posList = new List<Vector3>();
我们声明一个lineRenderer
成员,并在Awake
中获取它,
[RequireComponent(typeof(LineRenderer))]
public class RadiationLine : MonoBehaviour
{
private LineRenderer lineRenderer;
...
void Awake()
{
lineRenderer = GetComponent<LineRenderer>();
}
...
}
封装一个绘制曲线的方法,如下
// Line.cs
/// <summary>
/// 绘制曲线
/// </summary>
/// <param name="fromPos">起始坐标</param>
/// <param name="ctrlPoint1">控制点1</param>
/// <param name="ctrlPoint2">控制点2</param>
/// <param name="toPos">目标坐标</param>
public void DrawRay(Vector3 fromPos, Vector3 ctrlPoint1, Vector3 ctrlPoint2, Vector3 toPos)
{
posList.Clear();
for (int i = 0; i <= MAX_FRAG_CNT; ++i)
{
posList.Add(cubicBezier(fromPos, ctrlPoint1, ctrlPoint2, toPos, (float)i / MAX_FRAG_CNT));
lineRenderer.positionCount = posList.Count;
}
lineRenderer.SetPositions(posList.ToArray());
}
把Line
脚本挂到Line
节点上,如下
创建一个C#
脚本,重命名为Earth
,编写地球逻辑代码,
代码很简单,这里就不拆开讲解了,我都有写注释,
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
/// <summary>
/// 地球逻辑
/// </summary>
public class Earth : MonoBehaviour
{
private Transform selfTrans;
// 曲线
public Line line;
void Start()
{
selfTrans = transform;
StartCoroutine(FireLine());
}
void Update()
{
// 地球自转
selfTrans.Rotate(Vector3.up * Time.deltaTime, Space.Self);
}
/// <summary>
/// 发射辐射射线
/// </summary>
/// <returns></returns>
IEnumerator FireLine()
{
this.line.gameObject.SetActive(false);
while (true)
{
// 循环生成曲线,这里只是演示效果,我就不是用对象池了
var line = Instantiate(this.line);
line.gameObject.SetActive(true);
line.transform.SetParent(selfTrans);
// 半径
var radius = selfTrans.localScale.x / 2f;
// 在地球表面随机一个起始点
var from = selfTrans.position + new Vector3(Random.Range(-1f, 1f), Random.Range(-1f, 1f), Random.Range(-1f, 1f)).normalized * radius;
// 在地球表面随机一个终点
var to = selfTrans.position + new Vector3(Random.Range(-1f, 1f), Random.Range(-1f, 1f), Random.Range(-1f, 1f)).normalized * radius;
var center = (from + to) / 2f;
// 控制点1
var ctrlPoint1 = from + (center - selfTrans.position).normalized * (from - to).magnitude * 0.6f;
// 控制点2
var ctrlPoint2 = to + (center - selfTrans.position).normalized * (from - to).magnitude * 0.6f;
line.DrawRay(from, ctrlPoint1, ctrlPoint2, to);
// 随机一个时间后销毁曲线
Destroy(line.gameObject, Random.Range(4, 7));
// 随机等待一个事件
yield return new WaitForSeconds(Random.Range(0.3f, 2f));
}
}
}
把Earth
脚本挂到Earth
节点上,并赋值Line
成员,如下
运行Unity
,基本的效果已经出来了,
我们要让曲线有动态的效果,把原来的DrawRay
改成协程,动态设置点的坐标,如下,
// Line.cs
/// <summary>
/// 绘制曲线
/// </summary>
/// <param name="fromPos">起始坐标</param>
/// <param name="ctrlPoint1">控制点1</param>
/// <param name="ctrlPoint2">控制点2</param>
/// <param name="toPos">目标坐标</param>
public IEnumerator DrawRay(Vector3 fromPos, Vector3 handPos1, Vector3 handPos2, Vector3 toPos)
{
for (int i = 0; i <= MAX_FRAG_CNT; ++i)
{
posList.Clear();
for (int j = 0; j <= i; ++j)
{
posList.Add(cubicBezier(fromPos, handPos1, handPos2, toPos, (float)j / MAX_FRAG_CNT));
}
lineRenderer.positionCount = posList.Count;
lineRenderer.SetPositions(posList.ToArray());
yield return new WaitForSeconds(0.02f);
}
yield return new WaitForSeconds(2);
for (int i = 0; i <= MAX_FRAG_CNT; ++i)
{
posList.Clear();
for (int j = i; j <= MAX_FRAG_CNT; ++j)
{
posList.Add(cubicBezier(fromPos, handPos1, handPos2, toPos, (float)j / MAX_FRAG_CNT));
}
lineRenderer.positionCount = posList.Count;
lineRenderer.SetPositions(posList.ToArray());
yield return new WaitForSeconds(0.001f);
}
Destroy(gameObject);
}
改下Earth
脚本中的调用,如下
// Earth.cs
/// <summary>
/// 发射辐射射线
/// </summary>
/// <returns></returns>
IEnumerator FireLine()
{
this.line.gameObject.SetActive(false);
while (true)
{
...
// 启动协程
StartCoroutine(line.DrawRay(from, ctrlPoint1, ctrlPoint2, to));
// 随机等待一个事件
yield return new WaitForSeconds(Random.Range(0.1f, 0.5f));
}
}
运行效果如下,可以,
制作个粒子效果,
包装成预设,挂到Line
节点下,作为起始点和目标点的特效,如下
在Line
脚本中加上粒子控制的逻辑,
// Line.cs
public Transform startPoint;
public Transform endPoint;
/// <summary>
/// 绘制曲线
/// </summary>
/// <param name="fromPos">起始坐标</param>
/// <param name="ctrlPoint1">控制点1</param>
/// <param name="ctrlPoint2">控制点2</param>
/// <param name="toPos">目标坐标</param>
public IEnumerator DrawRay(Vector3 earthPos, Vector3 fromPos, Vector3 handPos1, Vector3 handPos2, Vector3 toPos)
{
// 起始位置粒子
startPoint.gameObject.SetActive(true);
startPoint.forward = fromPos - earthPos;
startPoint.localPosition = fromPos + startPoint.forward * 0.01f;
for (int i = 0; i <= MAX_FRAG_CNT; ++i)
{
posList.Clear();
for (int j = 0; j <= i; ++j)
{
posList.Add(cubicBezier(fromPos, handPos1, handPos2, toPos, (float)j / MAX_FRAG_CNT));
}
lineRenderer.positionCount = posList.Count;
lineRenderer.SetPositions(posList.ToArray());
yield return new WaitForSeconds(0.02f);
}
// 目标位置粒子
endPoint.gameObject.SetActive(true);
endPoint.forward = toPos - earthPos;
endPoint.localPosition = toPos + endPoint.forward * 0.01f;
yield return new WaitForSeconds(2);
startPoint.gameObject.SetActive(false);
for (int i = 0; i <= MAX_FRAG_CNT; ++i)
{
posList.Clear();
for (int j = i; j <= MAX_FRAG_CNT; ++j)
{
posList.Add(cubicBezier(fromPos, handPos1, handPos2, toPos, (float)j / MAX_FRAG_CNT));
}
lineRenderer.positionCount = posList.Count;
lineRenderer.SetPositions(posList.ToArray());
yield return new WaitForSeconds(0.001f);
}
Destroy(gameObject);
}
最终运行效果如下
本文工程我已上传到GitCode
,感兴趣的同学可自行下载学习,
地址:https://gitcode.net/linxinfa/UnityEarthRay
注:我使用的Unity
版本是2021.1.7.f1c1
好了,就写到这里吧。
我是新发,https://blog.csdn.net/linxinfa
一个在小公司默默奋斗的Unity
开发者,希望可以帮助更多想学Unity
的人,共勉~
文章浏览阅读1.3w次。转载自 http://www.miui.com/thread-2003672-1-1.html 当手机在刷错包或者误修改删除系统文件后会出现无法开机或者是移动定制(联通合约机)版想刷标准版,这时就会用到线刷,首先就是安装线刷驱动。 在XP和win7上线刷是比较方便的,用那个驱动自动安装版,直接就可以安装好,完成线刷。不过现在也有好多机友换成了win8/8.1系统,再使用这个_mt65驱动
文章浏览阅读1k次。SonarQube是一个代码质量管理平台,可以扫描监测代码并给出质量评价及修改建议,通过插件机制支持25+中开发语言,可以很容易与gradle\maven\jenkins等工具进行集成,是非常流行的代码质量管控平台。通CheckStyle、findbugs等工具定位不同,SonarQube定位于平台,有完善的管理机制及强大的管理页面,并通过插件支持checkstyle及findbugs等既有的流..._sonar的客户端区别
文章浏览阅读3.4k次,点赞2次,收藏27次。神经图灵机是LSTM、GRU的改进版本,本质上依然包含一个外部记忆结构、可对记忆进行读写操作,主要针对读写操作进行了改进,或者说提出了一种新的读写操作思路。神经图灵机之所以叫这个名字是因为它通过深度学习模型模拟了图灵机,但是我觉得如果先去介绍图灵机的概念,就会搞得很混乱,所以这里主要从神经图灵机改进了LSTM的哪些方面入手进行讲解,同时,由于模型的结构比较复杂,为了让思路更清晰,这次也会分开几..._神经图灵机方法改进
文章浏览阅读2.8k次。一、模型迭代方法机器学习模型在实际应用的场景,通常要根据新增的数据下进行模型的迭代,常见的模型迭代方法有以下几种:1、全量数据重新训练一个模型,直接合并历史训练数据与新增的数据,模型直接离线学习全量数据,学习得到一个全新的模型。优缺点:这也是实际最为常见的模型迭代方式,通常模型效果也是最好的,但这样模型迭代比较耗时,资源耗费比较多,实时性较差,特别是在大数据场景更为困难;2、模型融合的方法,将旧模..._模型迭代
文章浏览阅读2.3k次。1、前言上传图片一般采用异步上传的方式,但是异步上传带来不好的地方,就如果图片有改变或者删除,图片服务器端就会造成浪费。所以有时候就会和参数同步提交。笔者喜欢base64图片一起上传,但是图片过多时就会出现数据丢失等异常。因为tomcat的post请求默认是2M的长度限制。2、解决办法有两种:① 修改tomcat的servel.xml的配置文件,设置 maxPostSize=..._base64可以装换zip吗
文章浏览阅读1k次,点赞17次,收藏22次。Opencv自然场景文本识别系统(源码&教程)_opencv自然场景实时识别文字
文章浏览阅读1.3k次。拷贝虚拟机文件时间比较长,因为虚拟机 flat 文件很大,所以要等。脚本完成后,以复制虚拟机文件夹。将以下脚本内容写入文件。_exsi6.7快速克隆centos
文章浏览阅读2k次。本文主要实现基于二度好友的推荐。数学公式参考于:http://blog.csdn.net/qq_14950717/article/details/52197565测试数据为自己随手画的关系图把图片整理成文本信息如下:a b c d e f yb c a f gc a b dd c a e h q re f h d af e a b gg h f bh e g i di j m n ..._本关任务:使用 spark core 知识完成 " 好友推荐 " 的程序。
文章浏览阅读367次。南京大学高级程序设计期末复习总结,c++面向对象编程_南京大学高级程序设计
文章浏览阅读3.1k次,点赞2次,收藏12次。实现朴素贝叶斯分类器,并且根据李航《统计机器学习》第四章提供的数据训练与测试,结果与书中一致分别实现了朴素贝叶斯以及带有laplace平滑的朴素贝叶斯%书中例题实现朴素贝叶斯%特征1的取值集合A1=[1;2;3];%特征2的取值集合A2=[4;5;6];%S M LAValues={A1;A2};%Y的取值集合YValue=[-1;1];%数据集和T=[ 1,4,-1;..._朴素贝叶斯 matlab训练和测试输出
文章浏览阅读1.6k次。Markdown 文本换行_markdowntext 换行
文章浏览阅读6.7w次,点赞2次,收藏37次。win10 2016长期服务版激活错误解决方法:打开“注册表编辑器”;(Windows + R然后输入Regedit)修改SkipRearm的值为1:(在HKEY_LOCAL_MACHINE–》SOFTWARE–》Microsoft–》Windows NT–》CurrentVersion–》SoftwareProtectionPlatform里面,将SkipRearm的值修改为1)重..._错误: 0xc0000022 在运行 microsoft windows 非核心版本的计算机上,运行“slui.ex