Python利用3D引擎做一个太阳系行星模拟器

作者:Leleprogrammer 时间:2022-02-03 11:00:58 

这次,我们再来用Ursina引擎来做一个太阳系行星模拟器吧!

想要了解Ursina 3D引擎的基本使用方法的话,查看我的另一篇文章:详解Python 3D引擎Ursina如何绘制立体图形

这一次,我们要实现的效果如下

Python利用3D引擎做一个太阳系行星模拟器

Python利用3D引擎做一个太阳系行星模拟器

首先,送上本次需要用到的资源

Earth.jpg

Python利用3D引擎做一个太阳系行星模拟器

Jupiter.jpg

Python利用3D引擎做一个太阳系行星模拟器

Mars.jpg

Python利用3D引擎做一个太阳系行星模拟器

Mercury.jpg

Python利用3D引擎做一个太阳系行星模拟器

Neptune.jpg

Python利用3D引擎做一个太阳系行星模拟器

Saturn.jpg

Python利用3D引擎做一个太阳系行星模拟器

Sun.jpg

Python利用3D引擎做一个太阳系行星模拟器

Uranus.jpg

Python利用3D引擎做一个太阳系行星模拟器

Venus.jpg

Python利用3D引擎做一个太阳系行星模拟器

现在,就开始写代码吧!

首先,导入我们需要的模块,导入3D引擎ursina,数学库math,ursina中自带的第一人称,sys,random随机库

from ursina import *
from math import *
from ursina.prefabs.first_person_controller import FirstPersonController
import sys
import random as rd

然后,创建app

app=Ursina()

将窗口设置为全屏,并设置背景颜色

window.fullscreen=True
window.color=color.black

定义一个列表,来储存生成的星

planets=[]

引入所有星球的材质

sun_texture=load_texture("texture/Sun.png")
mercury_texture=load_texture("texture/Mercury.png")
venus_texture=load_texture("texture/Venus.png")
earth_texture=load_texture("texture/Earth.png")
mars_texture=load_texture("texture/Mars.png")
jupiter_texture=load_texture("texture/Jupiter.png")
saturn_texture=load_texture("texture/Saturn.png")
uranus_texture=load_texture("texture/Uranus.png")
neptune_texture=load_texture("texture/Neptune.png")

创建一个类Planet,继承自实体Entity,传入_type是星的类型,pos是位置,scale是缩放

angle:每次更新的时候行星围绕太阳转的弧度

fastMode的值为1或0,表示是否让行星围绕太阳公转速度增加到200倍

rotation:星球倾斜度,这里我们随机生成

rotspeed:星球自转的速度

rotMode:表示沿着xyz轴的其中一条进行旋转,自动选择

_type存储星球类型

texture则是材质,通过eval获得该变量

然后进行超类的初始化,model是sphere,也就是球体形状,texture表示贴图,color颜色设置为white,position传入坐标

定义turn方法,传入angle,只要不是太阳,就进行自转公转操作,如果是快速模式,则速度增加到200倍,然后计算得出新的xy坐标,并用exec进行自传操作

最后定义input方法,接受用户输入,注意,这里方法名必须用input,因为它是系统自动调用的,它总会向其传入一个参数,为按下的按键名字,我们就进行判断,如果按下回车,则进行快速模式和普通模式间的切换

class Planet(Entity):
   def __init__(self,_type,pos,scale=2):
       self.angle=rd.uniform(0.0005,0.01)
       self.fastMode=0
       self.rotation=(rd.randint(0,360) for i in range(3))
       self.rotspeed=rd.uniform(0.25,1.5)
       self.rotMode=rd.choice(["x","y","z"])
       self._type=_type
       texture=eval(f"{_type}_texture")
       super().__init__(model="sphere",
                        scale=scale,
                        texture=texture,
                        color=color.white,
                        position=pos)

def turn(self,angle):
       if self._type!="sun":
           if self.fastMode:
               angle*=200
           self.x=self.x*cos(radians(angle))-self.y*sin(radians(angle))
           self.y=self.x*sin(radians(angle))+self.y*cos(radians(angle))
           exec(f"self.rotation_{self.rotMode}+=self.rotspeed")

def input(self,key):
       if key=="enter":
           self.fastMode=1-self.fastMode

接下来,我们定义Player类,继承自FirstPersonController

为什么不直接用FirstPersonController呢?

因为ursina自带的FirstPersonController自带重力,我们这里只是作为第一人称的视角使用,不需要重力,然后还有一些功能我们不需要用到,所以我们就写一个类继承下来,然后重写它的一部分代码即可。首先,引入全局变量planets,超类初始化,视野设置为90,将初始位置设置为地球的位置,重力(gravity)设置为0,表示没有重力,vspeed表示上升下降时的速度,speed表示水平方向移动的速度,mouse_sensitivity是鼠标灵敏度,需要用Vec2的形式,注意,上面除了vspeed变量可以自己命名,其它的都不可以修改。接下来,重写input,只接收esc按键的信息,当我们按下esc时,如果鼠标为锁定,则释放,如果已经释放,则退出程序。然后创建_update方法,这里我们不重写ursina自动调用的update方法,因为系统代码里面,update方法还有很多操作,如果我们要重写的话,可能还要加上把系统代码复制过来,代码过于繁琐,这里我们自己定义一个名字,在接下来会讲到的代码中自己调用它,在该方法中,监听鼠标左键、左shift和空格的事件,空格原本是跳跃,这里我们设置为上升,系统代码是在input中接收空格键的信息的,我们已经重写过了,所以这里不会触发系统代码的跳跃方法。

这里讲一下input和update中进行按键事件监听操作的不同,input每次只接收一个按键,而且,如果我们一个按键一直按下,它不会一直触发,只会触发一次,然后等到该按键释放,才会重新对该按键进行监听;update相当于主循环,在任何于ursina有关的地方(比如继承自Entity、Button这样的类,或者是主程序)写update方法,ursina都会进行自动调用,我们不需要手动调用它,在update方法中监听事件,我们用到了held_keys,不难发现,held_keys有多个元素,只要按下就为True,所以每次运行到这里,只要按键按下,就执行,而input传入的key本身就是一个元素,所以只有一个,我们按下esc的操作不能连续调用,所以用input,其它移动玩家的代码时可以重复执行的,所以写在update(应该说是用held_keys)中。

class Player(FirstPersonController):
   def __init__(self):
       global planets
       super().__init__()
       camera.fov=90
       self.position=planets[3].position
       self.gravity=0
       self.vspeed=2
       self.speed=600
       self.mouse_sensitivity=Vec2(160,160)
       self.on_enable()

def input(self,key):
       if key=="escape":
           if mouse.locked:
               self.on_disable()
           else:
               sys.exit()

def _update(self):
       if held_keys["left mouse"]:
           self.on_enable()
       if held_keys["left shift"]:
           self.y-=self.vspeed
       if held_keys["space"]:
           self.y+=self.vspeed

然后在主程序中写update方法,并在其中调用我们刚刚写的player中的_update方法,再对星球进行自转公转操作

def update():
   global planets,player
   for planet in planets:
       planet.turn(planet.angle)
   player._update()

接下来,我们定义两个列表,分别表示星球名称和星球的大小,其实在实际的大小比例中,和这个相差很多,如果地球是1,太阳则大约为130000,木星和图形分别为1500多和700多,这样相差太大,做在程序里看起来很不寻常,所以我们这里对大多数星球的大小进行放大缩小,把它们大小的相差拉近点。然后遍历并绘制,每颗星球的间隔为前一个的10倍

ps=["sun","mercury","venus","earth","mars","jupiter","saturn","uranus","neptune"]
cp=[200,15,35,42,20,160,145,90,80]
x,y,z=0,0,0
for i,p in enumerate(ps):
   newPlanet=Planet(p,(x,y,z),cp[i])
   planets.append(newPlanet)
   x+=cp[i]*10

最后实例化player,并运行app

player=Player()

if __name__ == '__main__':
   app.run()

然后就能实现文章前面展示的效果啦~

Python利用3D引擎做一个太阳系行星模拟器

Python利用3D引擎做一个太阳系行星模拟器

最后,附上代码

from ursina import *
from math import *
from ursina.prefabs.first_person_controller import FirstPersonController
import sys
import random as rd

app=Ursina()
window.fullscreen=True
window.color=color.black

planets=[]

class Planet(Entity):
   def __init__(self,_type,pos,scale=2):
       self.angle=rd.uniform(0.0005,0.01)
       self.fastMode=0
       self.rotation=(rd.randint(0,360) for i in range(3))
       self.rotspeed=rd.uniform(0.25,1.5)
       self.rotMode=rd.choice(["x","y","z"])
       self._type=_type
       texture=eval(f"{_type}_texture")
       super().__init__(model="sphere",
                        scale=scale,
                        texture=texture,
                        color=color.white,
                        position=pos)

def turn(self,angle):
       if self._type!="sun":
           if self.fastMode:
               angle*=200
           self.x=self.x*cos(radians(angle))-self.y*sin(radians(angle))
           self.y=self.x*sin(radians(angle))+self.y*cos(radians(angle))
           exec(f"self.rotation_{self.rotMode}+=self.rotspeed")

def input(self,key):
       if key=="enter":
           self.fastMode=1-self.fastMode

class Player(FirstPersonController):
   def __init__(self):
       global planets
       super().__init__()
       camera.fov=90
       self.position=planets[3].position
       self.gravity=0
       self.vspeed=2
       self.speed=600
       self.mouse_sensitivity=Vec2(160,160)
       self.on_enable()

def input(self,key):
       if key=="escape":
           if mouse.locked:
               self.on_disable()
           else:
               sys.exit()

def _update(self):
       if held_keys["left mouse"]:
           self.on_enable()
       if held_keys["left shift"]:
           self.y-=self.vspeed
       if held_keys["space"]:
           self.y+=self.vspeed

def update():
   global planets,player
   for planet in planets:
       planet.turn(planet.angle)
   player._update()

sun_texture=load_texture("texture/Sun.png")
mercury_texture=load_texture("texture/Mercury.png")
venus_texture=load_texture("texture/Venus.png")
earth_texture=load_texture("texture/Earth.png")
mars_texture=load_texture("texture/Mars.png")
jupiter_texture=load_texture("texture/Jupiter.png")
saturn_texture=load_texture("texture/Saturn.png")
uranus_texture=load_texture("texture/Uranus.png")
neptune_texture=load_texture("texture/Neptune.png")

ps=["sun","mercury","venus","earth","mars","jupiter","saturn","uranus","neptune"]
cp=[200,15,35,42,20,160,145,90,80]
x,y,z=0,0,0
for i,p in enumerate(ps):
   newPlanet=Planet(p,(x,y,z),cp[i])
   planets.append(newPlanet)
   x+=cp[i]*10

player=Player()

if __name__ == '__main__':
   app.run()

来源:https://blog.csdn.net/leleprogrammer/article/details/125834562

标签:Python,行星,模拟器
0
投稿

猜你喜欢

  • python方向键控制上下左右代码

    2022-01-27 01:44:22
  • Python使用描述符实现属性类型检查的案例解析

    2022-07-30 00:39:15
  • 如何实现让每句话的头一个字母都大写?

    2010-05-24 18:26:00
  • Python实现邮件发送功能的方法详解

    2021-02-02 10:34:58
  • ASP常用函数:IsBlank()

    2008-09-28 13:21:00
  • python 检查是否为中文字符串的方法

    2023-11-22 17:29:57
  • python3新特性函数注释Function Annotations用法分析

    2023-08-09 20:15:29
  • Python类属性与实例属性用法分析

    2022-10-12 03:14:58
  • ORACLE 数据库RMAN备份恢复

    2009-04-24 12:23:00
  • MYSQL教程:my.cnf缓存优化

    2009-07-30 08:58:00
  • Python利用imshow制作自定义渐变填充柱状图(colorbar)

    2023-07-14 00:27:57
  • Python检查 云备份进程是否正常运行代码实例

    2023-07-08 23:59:05
  • js验证表单(form)中多选框(checkbox)值

    2008-03-18 13:39:00
  • python装饰器初探(推荐)

    2023-01-19 14:40:27
  • Python对象的属性访问过程详解

    2023-09-02 13:40:04
  • Python上下文管理器类和上下文管理器装饰器contextmanager用法实例分析

    2022-05-01 15:04:21
  • 最近Python有点火? 给你7个学习它的理由!

    2021-08-03 01:40:05
  • 详尽讲述用Python的Django框架测试驱动开发的教程

    2023-04-21 18:28:19
  • thinkphp5实用入门进阶知识点和各种常用功能代码汇总

    2023-05-25 02:48:34
  • Python Django2.0集成Celery4.1教程

    2023-10-17 21:28:52
  • asp之家 网络编程 m.aspxhome.com