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

转载请注明出处: 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倍,为啥月亮没被太阳抢走?

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

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

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

【Android面试八股文】请描述new一个对象的流程

文章目录 请描述new一个对象的流程JVM创建对象的过程检查加载分配内存内存空间初始化设置对象初始化请描述new一个对象的流程 JVM创建对象的过程 当JVM遇到一条new指令时,它需要完成以下几个步骤: 类加载与检查内存分配 并发安全性内存空间初始化设置对象信息对象初始化下图…

upload-labs第十二关教程

upload-labs第十二关教程 一、源代码分析代码审计 二、绕过分析截断绕过magic_quotes_gpc函数介绍关闭magic_quotes_gpc上传eval.png文件使用burpsuite进行抓包修改放包&#xff0c;查看是否上传成功使用中国蚁剑进行连接 一、源代码分析 代码审计 $is_upload false; $msg …

网络协议,OSI,简单通信,IP和mac地址

认识协议 1.讲故事 2004年&#xff0c;小明因为给他爹打电话&#xff08;座机&#xff09;费用太贵&#xff0c;所以约定一种信号&#xff0c;响一次是报平安&#xff0c;响两次是要钱&#xff0c;响三次才需要接通。 2.概念 协议&#xff1a;是一种约定&#xff0c;这种约…

基于CentOS Stream 9平台安装MySQL8.4.0 LTS

1. 安装之前 1.1 查看系统版本 [rootcoisini /]# cat /etc/redhat-release CentOS Stream release 9 1.2 查看cpu架构 [rootcoisini /]# lscpu 架构&#xff1a; x86_64 CPU 运行模式&#xff1a; 32-bit, 64-bit 2. MySQL官方下载https://dev.mysql.com/downloads/mysql/ 或…

Web期末复习指南(2w字总结)

前言&#xff1a;本文旨在梳理Web技术常包含的内容&#xff0c;阅读完整篇文章后会对整体有个系统的认知&#xff0c;从前端的HTML到后端的MySql&#xff0c;对于大概试题中可能会涉及的地方都有所分析&#xff0c;通篇提供了许多代码案例&#xff0c;供读者更好的理解。对于一…

MongoDB~事务了解;可调一致性模型功能与因果一致性模型功能分析

背景 MongoDB 从 3.0版本引入 WiredTiger 存储引擎之后开始支持事务&#xff0c;MongoDB 3.6之前的版本只能支持单文档的事务&#xff0c;从 MongoDB 4.0版本开始支持复制集部署模式下的事务&#xff0c;从 MongoDB 4.2版本开始支持分片集群中的事务。 根据官方文档介绍&…

代码随想录算法训练营第二十四天| (回溯) 77. 组合、 216.组合总和III、17.电话号码的字母组合

77. 组合 题目链接&#xff1a;77. 组合 文档讲解&#xff1a;代码随想录 状态&#xff1a;很多细节忘了 思路&#xff1a;先画图&#xff0c;然后可以发现&#xff0c;从1到n中选择k个数&#xff0c;可以看成是一个递归过程&#xff0c;这个递归的深度就是k。然后遍历当前这层…

企业化运维(2)_nginx

###1.nginx源码安装部署### ###2.平滑升级### &#xff08;1&#xff09;版本升级 当服务器在运行时&#xff0c;需要升级的情况下&#xff0c;平滑升级即就是不断开服务器就可以进行升级&#xff0c;最大限度保证数据的完整性。 下载nginx新版本软件&#xff0c;正常执行./c…

简易五子棋

简介 使用Java实现简易五子棋 规则介绍 游戏使用一个标准的1515方格的棋盘&#xff0c;双方分别用黑白两种颜色的棋子进行对战。黑子先行&#xff0c;双方轮流在空棋盘的交叉点上落子&#xff0c;每人一次只能落一子。游戏的目标是让自己的五颗棋子连成一线&#xff0c;这条…

JavaScript-函数

学习目标&#xff1a; 掌握函数 学习内容&#xff1a; 为什么需要函数函数使用函数传参函数返回值函数细节补充函数作用域匿名函数案例 为什么需要函数&#xff1a; 函数&#xff1a;function 是被设计为执行特定任务的代码块。说明&#xff1a;函数可以把具有相同或相似逻辑…

探索交互的本质:从指令到界面的演进与Linux基础指令的深入剖析

目录 1.指令 vs 界面//选读 1.1交互的需求 满足需求的第一阶段-指令 满足需求的第二阶段-界面 1.2 指令 和 界面交互 区别 2.操作系统介绍 2.1 举例说明 驱动软件层 2.2 为什么要有操作系统&#xff1f; 0x03 为什么要进行指令操作&#xff1f; 3.Linux基本指令 l…

Linux基础命令[29]-chown

文章目录 1. chown 命令说明2. chown 命令语法3. chown 命令示例3.1 修改属主3.2 修改属组3.3 修改属主和属组3.4 修改文件夹所属 4. 总结 1. chown 命令说明 chown&#xff1a;更改文件的用户或用户组&#xff0c;需要 root 用户或 sudo 权限的用户执行该命令。基本信息如下&…

深度学习(PyTorch)批注理解,建议边学可以边看这个笔记

前言 动手学习深度学习&#xff0c;内容丰富&#xff0c;但是对于初学者有很多晦涩难懂的地方&#xff0c;我将日常更新这篇文章以截图的形式&#xff0c;每天高强度学习四五个小时&#xff0c;精力缺乏&#xff0c;我认为&#xff0c;如果想学习这个深度学习&#xff0c;你需…

微信公众号打通与登录的实现

今天实现一下与微信公众号进行对接&#xff0c;通过扫描二维码的方式来进行注册与登录&#xff0c;获取用户的微信唯一标识作为用户的username&#xff0c;下面我们开始编写。 骨架建立&#xff1a; 建包&#xff1a; 第一步还是先将骨架建好&#xff0c;与网关骨架差不多&a…

堆栈溢出的攻击 -fno-stack-protector stack smash 检测

在程序返回的一条语句堆栈项目处&#xff0c;用新函数的起始地址覆盖&#xff0c;将会跳转到执行新函数。 现在系统对这个行为做了判断&#xff0c;已经无法实施这类攻击或技巧。 1&#xff0c;测试代码 #include <stdio.h> void cc() {printf("I am cc( )\n"…

Boom 3D软件下载及安装教程

简介&#xff1a; Boom 3D是适用于Mac和Windows系统的专业音效增强软件&#xff0c;旨在通过播放器&#xff0c;媒体或流媒体服务等介质&#xff0c;在不同类型的耳机上以3D环绕效果播放媒体内容。您无需使用昂贵的耳机或其他附加环绕音效增强器即可感受3D环绕音乐。 安 装 包…

【Python推导式秘籍】:一行代码的艺术,高效数据处理之道

文章目录 &#x1f68b;Python推导式&#x1f680;一、列表推导式&#x1f308;1. 了解推导式❤️2. 实践&#x1f4a5;3. 总结 &#x1f680;二、字典推导式&#x1f308;1. 了解字典推导式❤️2. 实践&#x1f4a5;3. 总结 &#x1f680;三、集合推导式&#x1f308;1. 了解集…

liunx常见指令

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 目录 前言 二、安装环境 1.租借服务器 2.下载安装 XShell 3.使用xshll登录服务器 三、Linux基础命令 一、文件和命令 ​编辑1、cd 命令 2、pwd 命令 3、ls 命令 4、cp 命令 …

人工智能GPU互联技术分析,芯片巨头UALink向英伟达NVLink开战

芯片巨头组团&#xff0c;向英伟达NVLink开战 八大科技巨头——AMD、博通、思科、Google、惠普企业、英特尔、Meta及微软——联合推出UALink&#xff08;Ultra Accelerator Link&#xff09;技术&#xff0c;为人工智能数据中心网络设定全新互联标准。此举旨在打破Nvidia的市场…

响应式德米拉数字内容交易系统素材下载站模板

★模板说明★ 该数字交易系统设计非常完美&#xff0c;两种响应式模式&#xff0c;可打开边栏模式和盒子模式&#xff1b;八种网站颜色&#xff0c;四种风格颜色可供用户自行选择&#xff0c;还可在网站选背景图片&#xff1b;完美的分成系统、充值功能、个人中心等等都以html…