Home >Backend Development >Python Tutorial >How to use 3D engine to create a solar system planet simulator in Python
This time, the effect we want to achieve is as follows
First of all, send the resources we need this time
Earth.jpg
Jupiter.jpg
Mars.jpg
Mercury.jpg
Neptune.jpg
Saturn.jpg
Sun.jpg
Uranus.jpg
##Venus.jpg Now, start writing code! First, import the modules we need, import the 3D engine ursina, the mathematics library math, the first person, sys, random library that comes with ursinafrom ursina import * from math import * from ursina.prefabs.first_person_controller import FirstPersonController import sys import random as rdThen, create the app
app=Ursina()Set the window to full screen and set the background color
window.fullscreen=True window.color=color.blackDefine a list to store the generated stars
planets=[]Introduce the materials of all 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")Create A class Planet, inherited from Entity. The incoming _type is the type of star, pos is the position, and scale is the scaling. angle: the arc in which the planet rotates around the sun every time it is updated. The value of fastMode is 1 or 0, indicating whether to increase the planet's revolution speed around the sun to 200 times rotation: the tilt of the planet, here we randomly generate rotspeed: the speed of the planet's rotationrotMode: indicates rotation along one of the xyz axes, automatically selecting _type to store the planet type texture is the material, and the variable is obtained through evalThen initialize the super class. The model is sphere, which is the shape of a sphere. The texture represents the texture. The color is set to white. The coordinates are passed in for position. Define the turn method and pass in the angle. As long as it is not the sun, then Carry out rotation and revolution operations. If it is fast mode, the speed will be increased to 200 times. Then calculate the new xy coordinates and use exec to perform the autotransmission operation. Finally define the input method to accept user input. Note that this method The name must be input, because it is automatically called by the system, and it will always pass in a parameter, which is the name of the pressed key. We will judge it. If we press Enter, we will switch between fast mode and normal mode. Switch
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.fastModeNext, we define the Player class, inherited from FirstPersonControllerWhy not use FirstPersonController directly? Because the FirstPersonController that comes with ursina has its own gravity, we only use it here as a first-person perspective and does not require gravity. There are also some functions that we do not need to use, so we will write a class to inherit it. Then just rewrite part of its code. First, introduce the global variable planets, initialize the super class, set the field of view to 90, set the initial position to the position of the earth, set gravity to 0, which means there is no gravity, vspeed means the speed when rising and falling, and speed means moving in the horizontal direction. The speed, mouse_sensitivity is the mouse sensitivity, which needs to be in the form of Vec2. Note that except for the vspeed variable above, which can be named by yourself, the others cannot be modified. Next, rewrite the input to only receive the information of the esc key. When we press esc, if the mouse is locked, it will be released. If it has been released, the program will exit. Then create the _update method. Here we do not override the update method automatically called by ursina, because in the system code, the update method still has many operations. If we want to rewrite it, we may also need to copy the system code. The code is too It’s cumbersome. Here we define a name ourselves and call it ourselves in the code that will be discussed next. In this method, we monitor the events of the left mouse button, left shift and space. Space is originally a jump, here we set it to rise. The system code receives the space key information in the input. We have rewritten it, so the jump method of the system code will not be triggered here.
这里讲一下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()
然后就能实现文章前面展示的效果啦~
最后,附上代码
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()
The above is the detailed content of How to use 3D engine to create a solar system planet simulator in Python. For more information, please follow other related articles on the PHP Chinese website!