【Unity学习笔记】第十八 基于物理引擎的日月地系统简单实现

news2024/11/25 2:27:01

转载请注明出处: https://blog.csdn.net/weixin_44013533/article/details/139701843

作者:CSDN@|Ringleader|

目录

    • 目标
    • 数学理论
    • 资源准备
    • 数据准备
    • 代码实现
    • Unity准备
    • 效果展示
    • 注意事项
    • 后记

目标

目标:利用Unity的物理引擎实现 “日地月三体系统” 。
效果类似下面的示意图:
在这里插入图片描述

数学理论

  1. 万有引力公式
  2. 向心力公式
  3. 天体圆周运动轨道速度公式
    在这里插入图片描述

资源准备

日月地模型及贴图:
https://assetstore.unity.com/packages/3d/environments/planets-of-the-solar-system-3d-90219
在这里插入图片描述

数据准备

名称数值
引力常数 ( G ) G = 6.67430 × 1 0 − 11   m 3   kg − 1   s − 2 G = 6.67430 \times 10^{-11} \, \text{m}^3 \, \text{kg}^{-1} \, \text{s}^{-2} G=6.67430×1011m3kg1s2
太阳半径 R Sun R_{\text{Sun}} RSun R Sun = 6.96 × 1 0 8   m R_{\text{Sun}} = 6.96 \times 10^8 \, \text{m} RSun=6.96×108m
太阳质量 M Sun M_{\text{Sun}} MSun M Sun = 1.989 × 1 0 30   kg M_{\text{Sun}} = 1.989 \times 10^{30} \, \text{kg} MSun=1.989×1030kg
日地距离 r Sun-Earth r_{\text{Sun-Earth}} rSun-Earth r Sun-Earth = 1.496 × 1 0 11   m r_{\text{Sun-Earth}} = 1.496 \times 10^{11} \, \text{m} rSun-Earth=1.496×1011m
地球半径 R Earth R_{\text{Earth}} REarth R Earth = 6.371 × 1 0 6   m R_{\text{Earth}} = 6.371 \times 10^6 \, \text{m} REarth=6.371×106m
地球质量 M Earth M_{\text{Earth}} MEarth M Earth = 5.972 × 1 0 24   kg M_{\text{Earth}} = 5.972 \times 10^{24} \, \text{kg} MEarth=5.972×1024kg
地月距离 r Earth-Moon r_{\text{Earth-Moon}} rEarth-Moon r Earth-Moon = 3.844 × 1 0 8   m r_{\text{Earth-Moon}} = 3.844 \times 10^8 \, \text{m} rEarth-Moon=3.844×108m
月球半径 R Moon R_{\text{Moon}} RMoon R Moon = 1.7371 × 1 0 6   m R_{\text{Moon}} = 1.7371 \times 10^6 \, \text{m} RMoon=1.7371×106m
月球质量 M Moon M_{\text{Moon}} MMoon M Moon = 7.348 × 1 0 22   kg M_{\text{Moon}} = 7.348 \times 10^{22} \, \text{kg} MMoon=7.348×1022kg

当然不能取这么大,缩放下比例尺后的数据如下,同时用这些数据初始化系统。

public class ConstantParamter : MonoBehaviour
    {
        public static float gravitationalConstant = 0.6674f; // 原6.674e-11
        public static float sunMass = 1.989e6f; //缩小10^24倍,原1.989e30
        public static float earthMass = 5.972f; //原5.972e24
        public static float moonMass = 0.07348f; //原7.348e22
        public static float distanceOfSunAndEarth = 1496f; //缩小10^8倍,原1.496e11
        public static float distanceOfEarthAndMoon = 3.844f; //原3.844e8
        public static float sunScale = 6.96f;
        public static float earthScale = 0.06371f;
        public static float moonScale = 0.017371f;
        
        public static float earthTangentialVelocityScale = 1.4f; //1.45是近似标准圆
        public static float moonTangentialVelocityScale = 1f; //1.45是近似标准圆

        public Rigidbody sun;
        public Rigidbody earth;
        public Rigidbody moon;

        private void Start()
        {
            // 初始化太阳
            sun.mass = sunMass;
            sun.position = Vector3.zero;
            
            // 初始化地球
            earth.mass = earthMass;
            earth.position = new Vector3(distanceOfSunAndEarth, 0, 0);
            // var earthScale = ConstantParamter.earthScale;
            // earth.transform.localScale = new Vector3(earthScale, earthScale, earthScale);
            
            //初始化月球 (月球位置在日地之间还是外面不影响)
            moon.mass = moonMass;
            moon.position = new Vector3(distanceOfSunAndEarth - distanceOfEarthAndMoon, 0, 0);
            // var moonScale = ConstantParamter.moonScale;
            // moon.transform.localScale = new Vector3(moonScale, moonScale, moonScale);
        }
    }

scale 和初始位置最好能自己调整,方便后面的collider范围确定。

代码实现

  1. UniversalGravity 万有引力脚本,对进入trigger的物体施加指向自己的万有引力

    public class UniversalGravity : MonoBehaviour
        {
            private float gravitationalConstant = ConstantParamter.gravitationalConstant;
            public Rigidbody center;
            private int moonLayer;
            private int earthLayer;
            private int sunLayer;
            public bool printMsg = false;
    
            private void Start()
            {
                moonLayer = LayerMask.NameToLayer("moon");
                earthLayer = LayerMask.NameToLayer("earth");
                sunLayer = LayerMask.NameToLayer("sun");
            }
    
            private void OnTriggerStay(Collider other)
            {
                try
                {
                    Print(other.name+"进入"+this.name+"引力场触发器");
                    var layer = other.gameObject.layer;
                    if (layer.Equals(moonLayer) || layer.Equals(earthLayer))
                    {
                        var otherAttachedRigidbody = other.attachedRigidbody;
                        var gravityDirection = center.transform.position - other.transform.position ;
    
                        var gravityForce = gravitationalConstant * center.mass * otherAttachedRigidbody.mass /
                                           Mathf.Pow(gravityDirection.magnitude, 2);
                        otherAttachedRigidbody.AddForce(gravityDirection.normalized * gravityForce);
                        var msg = $"{other.name}{center.name}施以{gravityDirection.normalized * gravityForce}的引力。";
                        Print(msg);
                    }
                }
                catch (Exception e)
                {
                    Print(other.name+"异常:"+e);
                    throw;
                }
            }
    
            void Print(string msg)
            {
                if (printMsg)
                {
                    Debug.Log(msg);
                } 
            }
        }
    
  2. EarthTangentialVelocity :计算地球绕日运动的初速度,因为轨道可能是椭圆,所以在标准圆轨道速度基础上乘以个缩放值

    // 计算地球绕日运动的初速度,因为轨道可能是椭圆,所以在标准圆轨道速度基础上乘以个缩放值
    public class EarthTangentialVelocity : MonoBehaviour
    {
        private float gravitationalConstant = ConstantParamter.gravitationalConstant;
        private float earthTangentialVelocityScale = ConstantParamter.earthTangentialVelocityScale; 
        public Rigidbody sun;
        
        Rigidbody rig;
    
        private void Start()
        {
            rig = GetComponent<Rigidbody>();
            rig.velocity = CalculateVelocity(rig, sun, earthTangentialVelocityScale);
            print(name + "的初始速度为:" + rig.velocity);
        }
    
        private Vector3 CalculateVelocity(Rigidbody rigid, Rigidbody center, float tangentialVelocityScale)
        {
            Vector3 startPosition = rigid.position;
    
            var distance = startPosition - center.position;
            var tangentialDirection = Vector3.Cross(distance, Vector3.up).normalized;
            Vector3 tangentialVelocity = tangentialDirection *
                                         Mathf.Sqrt(gravitationalConstant * center.mass / distance.magnitude) *
                                         tangentialVelocityScale;
            return tangentialVelocity;
        }
    }
    
  3. MoonTangentialVelocity :计算月球初始切向速度。

    // 计算月球初始切向速度。
    // 把月亮起源当作地球抛落物,所以月球初始速度=地球绕日标准圆速度+月球绕地标准圆速度
    // 当然由于轨道不一定是标准圆,所以会加一个缩放值
    public class MoonTangentialVelocity : MonoBehaviour
    {
        private float gravitationalConstant = ConstantParamter.gravitationalConstant;
        private float earthTangentialVelocityScale = ConstantParamter.earthTangentialVelocityScale; 
        private float moonTangentialVelocityScale = ConstantParamter.moonTangentialVelocityScale;
        public Rigidbody sun;
        public Rigidbody earth;
        
        Rigidbody rig;
    
        private void Start()
        {
            rig = GetComponent<Rigidbody>();
            var tangentialVelocity1 = CalculateVelocity(rig, earth, moonTangentialVelocityScale);
            var tangentialVelocity2 = CalculateVelocity(earth, sun, earthTangentialVelocityScale);
    
            rig.velocity = tangentialVelocity1 + tangentialVelocity2;
            print(name + "的初始速度为:" + rig.velocity);
        }
        
        private Vector3 CalculateVelocity(Rigidbody rigid, Rigidbody center, float tangentialVelocityScale)
        {
            Vector3 startPosition = rigid.position;
    
            var distance = startPosition - center.position;
            var tangentialDirection = Vector3.Cross(distance, Vector3.up).normalized;
            Vector3 tangentialVelocity = tangentialDirection *
                                         Mathf.Sqrt(gravitationalConstant * center.mass / distance.magnitude) *
                                         tangentialVelocityScale;
            return tangentialVelocity;
        }
    }
    

Unity准备

  1. 本系统使用Unity的built-in physics,将上面store下载的日月地三体模型prefab拖入场景中,各自新增三个万有引力碰撞体,设为isTrigger并调整大小,分别拖入UniversalGravity脚本并添加引力中心。
    在这里插入图片描述 在这里插入图片描述
  2. 为earth和moon分别添加EarthTangentialVelocityMoonTangentialVelocity脚本,这两个脚本是为地球和月球提供初始切向速度, 以保证它们能绕太阳做圆周运动。
  3. 使用trail组件即可展示地月运行轨迹
  4. moon和earth添加对应的layer,注意对应的trigger也要添加layer,过滤不需要施加力的对象。

效果展示

(紫色为月球轨迹,绿色为地球轨迹)

unity物理引擎实现简单日地月三体系统

在这里插入图片描述
地月交会瞬间  

在这里插入图片描述
完整地月绕日轨道  

注意事项

  1. 一定要考虑月球对地球的引力,这是月球不会脱离地球的主要原因
    如果不考虑月球对地球的引力,那么在离心力的作用下月球将会逐渐远离太阳,当然也可能像彗星一样绕超长轨道绕日运动。(就像旋转的雨伞,水滴脱离伞面后将会远远地抛离)

    实验效果:请添加图片描述
    当没有月球对地球引力作用时,月球绕日轨道(类似彗星轨道)  

    我一开始就是因为没考虑月球引力作用,总是得不到正确的轨道,还以为是比例尺导致的,不断调整日地引力常数,缩放月地切线速度,一直没成功,直到考虑到月球引力作用,才瞬间豁然开朗。

  2. 月球初速一定是在地球绕日初速的基础上进行增减的。也就是我代码中提到的 “月球是地球抛落物” 基于这个假说进行的(二体同源)。
    当然也可以采用 “捕获说” ,但实验下来会发现,月球的初速对实验结果影响很大,月球轨道很容易变成或大或小的椭圆。
    在这里插入图片描述
    月球和地球速度不能差异过大,此图为月球速度过大,导致轨道为大椭圆  

    在这里插入图片描述
    月球和地球速度不能差异过大,此图为月球速度过小,导致轨道为小椭圆  

  3. 月球不应作为地球的子对象。第一rigidbody会忽略层级关系,也就是说地球不会带动月球移动;第二也不应该用非物理系统的思想模拟地月系统。

    • 一开始就是因为我得不到正常的月绕地轨道,所以尝试了用transform更新月球和fixJoint 来绑定月球,但觉得这种方式很不物理,所以折腾了几个小时参数,才突然考虑到上面第一条问题。
  4. 本文只是实现了简单的日地月系统,没有精确确定地球月球公转周期,自转也没考虑,如果详细实现的话,就可以做成类似下面链接中所展示的太阳系模型了。

    太阳系模型

后记

经过上面的实验,基本实现了简单的日地月三体系统。还是相当好玩的。至于月球对地球引力是不是实验中所展示的那样重要,可能还需要更多理论学习才能明白。

延申阅读

  • 太阳对月球的引力比地球大两倍多,为什么月球没有被太阳吸过去?
  • 太阳对月球的引力是地球2.2倍,为啥月亮没被太阳抢走?

当然未来有时间的话,还会尝试其他天体系统,比如三体运动。
请添加图片描述
三体问题  

物理系统的话不久将会更新,内容还是比较多的,本节因为比较简短独立且比较好玩,所以独立成章了。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1827794.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

CubeMX Keil Configure

// 使用外部高速晶振 外部高速晶振为8M&#xff08;根据开发板上的晶振频率设置&#xff09;&#xff0c;使用 PLLCLK&#xff0c;HSE 选项 USART1 使用 Asynchronous&#xff0c;PA9&#xff0c;PA10引脚&#xff08;USART1 引脚根据开发板上引脚设置&#xff09; 设置MDK AR…

【2024最新华为OD-C/D卷试题汇总】[支持在线评测] URL拼接(100分) - 三语言AC题解(Python/Java/Cpp)

🍭 大家好这里是清隆学长 ,一枚热爱算法的程序员 ✨ 本系列打算持续跟新华为OD-C/D卷的三语言AC题解 💻 ACM银牌🥈| 多次AK大厂笔试 | 编程一对一辅导 👏 感谢大家的订阅➕ 和 喜欢💗 📎在线评测链接 URL拼接(100分) 🌍 评测功能需要订阅专栏后私信联系清隆解…

Pixel Transformer:用像素代替补丁可以提升图像分类精度

在快速发展的人工智能领域&#xff0c;ViTs已成为各种计算机视觉任务的基础模型。ViTs通过将图像划分为小块并将这些小块作为标记来处理图像。6月刚发布一篇论文&#xff0c;引入了一种新颖的方法&#xff0c;即像素级Transformers&#xff0c;它通过将单个像素视为令牌来挑战这…

大数据实训项目(小麦种子)-03、大数据环境Hadoop、Mapreduce、Hive、Hbase、HDFS搭建服务及调试

文章目录 前言一、Linux系统Centos7安装配置JDK8二、Linxu系统Centos7中搭建Hadoop3.1.0服务下载地址服务1&#xff1a;详细步骤&#xff08;初始化与启动dfs服务&#xff09;详细步骤配置环境变量 服务2&#xff1a;Hadoop(YARN)环境搭建 三、Linux系统搭建Hive3.1.2服务前提条…

桂电人工智能学院大数据实验,使用 Docker 搭建 hadoop 集群

桂电人工智能学院大数据实验&#xff0c;使用 Docker 搭建 hadoop 集群 第一步 安装 Docker, Windows 上可以使用 Docker Desktop 下载地址&#xff1a;https://www.docker.com/products/docker-desktop/ 安装过程自行谷歌 安装好的标志&#xff1a;打开终端 运行docker p…

产品人生(13):从“产品的RFM分析”看如何探索“职业方向”

我们在做产品分析时&#xff0c;经常会用到一种方法“产品的RFM分析”&#xff0c;它是一种客户细分和价值评估的常用方法&#xff0c;广泛应用于电子商务、零售和其他众多行业&#xff0c;它可以帮助企业和产品团队更好地理解用户行为&#xff0c;优化营销策略&#xff0c;提升…

基于协同过滤算法的电影推荐

基于协同过滤算法的电影推荐 电影推荐系统使用了基于**协同过滤&#xff08;Collaborative Filtering&#xff09;的算法来生成推荐。具体来说&#xff0c;使用了基于用户的协同过滤&#xff08;User-Based Collaborative Filtering&#xff09;**算法&#xff0c;步骤如下&am…

国外视频搬运素材去哪里找?可搬运下载国外的素材网站库分享

如果你正在寻找高质量的国外视频素材来提升你的视频制作水平&#xff0c;不用担心&#xff0c;今天我将为大家推荐几个知名的视频素材网站&#xff0c;让你轻松找到理想的素材资源。 蛙学府 首先推荐的是国内非常受欢迎的“蛙学府”。蛙学府是一个综合性网站&#xff0c;专门提…

通过Navicat Premium进行sql文件互转db文件

目录 一、获取原来数据sql文件 二、通过sql文件转换db文件 三、db文件转换成功 一、获取原来数据sql文件 原数据main右键》转储SQL文件》结构和数据》main.sql 二、通过sql文件转换db文件 新建db文件》Navicta》新建SQLite(SQ)》导入db文件》 main右键》运行SQL文件》 mai…

【调试笔记-20240611-Linux-配置 OpenWrt-23.05 支持泛域名 acme 更新】

调试笔记-系列文章目录 调试笔记-20240611-Linux-配置 OpenWrt-23.05 支持泛域名 acme 更新 文章目录 调试笔记-系列文章目录调试笔记-20240611-Linux-配置 OpenWrt-23.05 支持泛域名 acme 更新 前言一、调试环境操作系统&#xff1a;Windows 10 专业版调试环境调试目标 二、调…

计算机专业:黄金时代是否依旧?

计算机专业&#xff1a;黄金时代是否依旧&#xff1f; 随着2024年高考落幕&#xff0c;数百万高三学生将面临人生中的重要抉择&#xff1a;选择大学专业。在这个关键节点&#xff0c;计算机相关专业是否仍是“万金油”的选择&#xff1f;在过去的几十年里&#xff0c;计算机科…

Adobe设计替代软件精选列表

Adobe软件的替代列表&#xff0c;最初由 XdanielArt 收集&#xff0c;并由社区改进。您可以随意打开问题或拉出请求&#xff0c;或从数据中创建图像(以便于共享)。列表总是按照免费和开源选项的顺序排列&#xff0c;但根据您的用例&#xff0c;它可能不是最佳选择 替代因素 &am…

【Qt】QT textBrowser 设置字体颜色和大小

1. 效果 2. 代码 {ui->methodText->append("<font size9 colorgreen> dddddddddd </font>");ui->methodText->append("<font size9 colorred> vvvvvvvvvv </font>"); }

测试 halcon算子 derivate_gauss 高斯一阶导数卷积

参上了 matlab fileexchange 有人上传了高斯 dx,dy一阶导卷积代码 卷积核的计算我修改成了核元素绝对值求做分母 归一化 和halcon的 derivate_gauss算子的计算结果对别如下 还是不知道怎么做到两者结果一致. 测试图像: 我的: halcon的: 获取两份图像的灰度值到数组并做对应位…

利用机器学习重构视频中的人脸

引言 中国与英国的研究团队携手合作&#xff0c;开创了一种创新的视频面孔重塑技术。这项技术能够以极高的一致性对视频中的面部结构进行逼真的放大和缩小&#xff0c;且避免了常见伪影的产生。 从研究人员选取的YouTube视频样例中可见&#xff0c;经过处理后&#xff0c;女演…

nvm 管理多版本node

因有多个前端项目&#xff0c;不同项目使用的node版本不一样&#xff0c;所以在本地使用nvm管理不同的node版本。 一、安装nvm 1&#xff09;nvm理解 nvm全英文也叫node.js version management&#xff0c;是一个nodejs的版本管理工具。nvm和n都是node.js版本管理工具&#xff…

经销商的生意好坏很大程度上跟这群人有关

可以说经销商企业的营收几乎都是靠业务员跑出来的&#xff0c;无论在什么时期、无论在什么区域、无论什么市场情况、无论销售哪些商品&#xff0c;业务员都是企业销售工作的基础&#xff0c;一方面企业需要通过各种手段去提升业务员工作效率&#xff0c;让他们有能力去获得更多…

《C语言》动态内存管理

文章目录 一、动态内存分配二、关于动态内存开辟的函数1、malloc2、free3、calloc4、realloc 三、常见的动态内存的错误1、对NULL指针的解引用操作2、对动态开辟空间的越界访问3、对非动态开辟内存使用free释放4、释放free释放一块动态开辟的内存的一部分5、对同一块动态内存多…

牛客小白月赛96 解题报告 | 珂学家

前言 题解 A. 最少胜利题数 签到 n1 len(set(input())) n2 len(set(input()))if n1 < n2:n1, n2 n2, n1print (-1 if n1 6 else n1 - n2 1)B. 最少操作次数 思路: 分类讨论 只有-1,0,1,2这四种结果 特判 01, 10 n int(input()) s input()# 枚举 from collectio…

Nginx配置文件详解指令示咧Nginx配置文件深入详解与实战

本人详解 作者:王文峰,参加过 CSDN 2020年度博客之星,《Java王大师王天师》 公众号:JAVA开发王大师,专注于天道酬勤的 Java 开发问题中国国学、传统文化和代码爱好者的程序人生,期待你的关注和支持!本人外号:神秘小峯 山峯 转载说明:务必注明来源(注明:作者:王文峰…