年末航天有大事,嫦五揽月取岩石。
这几天,有被嫦娥五号刷屏吗?就在本周二晚11点11分,嫦娥五号的着陆器–上升器组合体成功登月,现已正式开展月面采样任务。嫦娥五号将为我国科学家带回约两千克月球岩石和土壤。时隔44年,人类终于能再次从月球带回“土特产”了!
嫦娥五号(图源:中国探月工程)
作为科技爱好者斜杠程序员爸爸,想不想写一个酷炫的月球着陆游戏,给娃秀一秀专业技能 让娃一起开心开心?
今天就来用Python写一个月球着陆仿真游戏吧!
太长不看提示:本例来源于由“Python之父”吉多·范罗苏姆推荐的《父与子的编程之旅》。请滑至文末了解如何获取完整代码。
游
戏
任
务
我们的飞船正准备登月。它携带定量的燃料,这些燃料会为反推发动机提供推力。
在游戏开始时,飞船离月球表面有一定的距离。月球的重力把它向下拉,我们必须使用反推发动机减缓降落速度,让飞船的纵向速度变为0,从而平缓着陆。
注意,必须小心地操作反推发动机。如果用力不足,飞船就会重重地摔在月面;如果用力过猛,则燃料会很快耗尽,飞船会向上飞入太空!
游
戏
界
面
左下角的小灰条表示反推发动机的操作杆,用鼠标上下拖动即可控制推力。
燃料表(绿色)显示当前剩余的燃料。界面上方的文本给出动态变化的速度、加速度、高度和推力。
逻
辑
分
析
反推发动机的推力取决于消耗了多少燃料,有时推力会大于重力,有时则会小于重力。当发动机关闭时,推力为0,此时只剩下重力。
要得到飞船所受的净作用力,只需把推力和重力相加。由于二者的方向相反,因此可以用一个正数和一个负数来表示。一旦得到飞船所受的净作用力,就可以利用公式得出它的速度和位置。
我们的程序必须跟踪以下几点。
- 飞船相对于月面的高度,以及飞船的速度和加速度。
- 飞船的质量(随着燃料的消耗,质量会变化)。
- 反推发动机的推力。推力越大,燃料消耗得就越快。
- 飞船剩余多少燃料。当反推发动机消耗燃料时,飞船会变轻,但是如果燃料耗尽,就不再有推力了。
- 飞船所受的重力。这取决于月球的大小、飞船的质量、燃料的消耗情况等。
开
始
编
写
提示:本文假定你已经安装了Pygame模块。如果还没有安装,请滑至文末查看安装说明。
我们使用Pygame模块编写该游戏,用单次时钟“嘀嗒”作为时间单位。每“嘀嗒”一次,程序就要检查飞船当前所受的净作用力,并更新高度、速度、加速度和剩余燃料等信息,然后根据这些信息更新图片和文本。
1 首先初始化游戏。创建Pygame窗口,加载图像,并为变量设置一些初始值。
-
import pygame, sys
-
-
pygame.init()
-
screen = pygame.display.set_mode([
400,
600])
-
screen.fill([
0,
0,
0])
-
ship = pygame.image.load(
'lunarlander.png')
-
moon = pygame.image.load(
'moonsurface.png')
-
ground =
540
-
-
start =
90
-
clock = pygame.time.Clock()
-
ship_mass =
5000.0
-
fuel =
5000.0
-
velocity =
-100.0
-
gravity =
10
-
height =
2000
-
thrust =
0
-
delta_v =
0
-
y_pos =
90
-
held_down = False
2 为反推发动机定义Sprite类。
-
class ThrottleClass(pygame.sprite.Sprite):
-
def __init__(self, location = [
0,
0]):
-
pygame.sprite.Sprite.__init__(self)
-
image_surface = pygame.surface.Surface([
30,
10])
-
image_surface.fill([
128,
128,
128])
-
self.image = image_surface.convert()
-
self.rect = self.image.get_rect()
-
self.rect.left, self.rect.centery = location
3 计算飞船的高度、速度、加速度和燃料消耗量。
-
def calculate_velocity():
-
global thrust, fuel, velocity, delta_v, height, y_pos
-
delta_t =
1/fps #对应Pygame循环的一帧
-
thrust = (
500 - myThrottle.rect.centery) *
5.0 #将反推发动机精灵的y坐标转换为推力
-
fuel -= thrust /(
10 * fps) #根据推力减少燃料
-
if fuel <
0: fuel =
0.0
-
if fuel <
0.1: thrust =
0.0
-
delta_v = delta_t * (-gravity +
200 * thrust / (ship_mass + fuel)) #物理公式
-
velocity = velocity + delta_v
-
delta_h = velocity * delta_t
-
height = height + delta_h
-
y_pos = ground - (height * (ground - start) /
2000) -
90 #将高度转换为Pygame的y坐标
4 使用字体对象显示统计信息。
-
def display_stats():
-
v_str =
"velocity: %i m/s" % velocity
-
h_str =
"height: %.1f" % height
-
t_str =
"thrust: %i" % thrust
-
a_str =
"acceleration: %.1f" % (delta_v * fps)
-
f_str =
"fuel: %i" % fuel
-
v_font = pygame.font.Font(None,
26)
-
v_surf = v_font.render(v_str,
1, (
255,
255,
255))
-
screen.blit(v_surf, [
10,
50])
-
a_font = pygame.font.Font(None,
26)
-
a_surf = a_font.render(a_str,
1, (
255,
255,
255))
-
screen.blit(a_surf, [
10,
100])
-
h_font = pygame.font.Font(None,
26)
-
h_surf = h_font.render(h_str,
1, (
255,
255,
255))
-
screen.blit(h_surf, [
10,
150])
-
t_font = pygame.font.Font(None,
26)
-
t_surf = t_font.render(t_str,
1, (
255,
255,
255))
-
screen.blit(t_surf, [
10,
200])
-
f_font = pygame.font.Font(None,
26)
-
f_surf = f_font.render(f_str,
1, (
255,
255,
255))
-
screen.blit(f_surf, [
60,
300])
5 画出尾焰三角形,尾焰大小会随推力变化而变化。
-
def display_flames():
-
flame_size = thrust /
15
-
for i in
range (
2):
-
startx =
252 -
10 + i *
19
-
starty = y_pos +
83
-
pygame.draw.polygon(screen, [
255,
109,
14], [(startx, starty),
-
(startx +
4, starty + flame_size),
-
(startx +
8, starty)],
0)
6 Pygame程序主事件循环,画出所有内容。
-
myThrottle = ThrottleClass([
15,
500]) #创建反推发动机对象
-
running = True
-
while running:
-
clock.tick(
30)
-
fps = clock.get_fps()
-
if fps <
1: fps =
30
-
if height >
0.01:
-
calculate_velocity()
-
screen.fill([
0,
0,
0])
-
display_stats()
-
pygame.draw.rect(screen, [
0,
0,
255], [
80,
350,
24,
100],
2)
-
fuelbar =
96 * fuel /
5000
-
pygame.draw.rect(screen, [
0,
255,
0],
-
[
84,
448-fuelbar,
18, fuelbar],
0) #燃料量
-
pygame.draw.rect(screen, [
255,
0,
0],
-
[
25,
300,
10,
200],
0) #画出反推发动机滑块
-
screen.blit(moon, [
0,
500,
400,
100]) #画出月球
-
pygame.draw.rect(screen, [
60,
60,
60],
-
[
220,
535,
70,
5],
0) #着陆点
-
screen.blit(myThrottle.image, myThrottle.rect) #画出操纵杆
-
display_flames()
-
screen.blit(ship, [
230, y_pos,
50,
90]) #画出飞船
-
instruct1 =
"Land softly without running out of fuel"
-
instruct2 =
"Good landing: < 15m/s Great landing: < 5m/s"
-
inst1_font = pygame.font.Font(None,
24)
-
inst1_surf = inst1_font.render(instruct1,
1, (
255,
255,
255))
-
screen.blit(inst1_surf, [
50,
550])
-
inst2_font = pygame.font.Font(None,
24)
-
inst2_surf = inst1_font.render(instruct2,
1, (
255,
255,
255))
-
screen.blit(inst2_surf, [
20,
575])
-
pygame.display.flip()
7 收尾部分:检查鼠标是否拖动反推发动机,更新反推发动机位置。
-
else:
-
display_final()
-
for event in pygame.event.get():
-
if event.
type == pygame.QUIT:
-
running = False
-
elif event.
type == pygame.MOUSEBUTTONDOWN:
-
held_down = True
-
elif event.
type == pygame.MOUSEBUTTONUP:
-
held_down = False
-
elif event.
type == pygame.MOUSEMOTION:
-
if held_down:
-
myThrottle.rect.centery = event.pos[
1]
-
if myThrottle.rect.centery <
300:
-
myThrottle.rect.centery =
300
-
if myThrottle.rect.centery >
500:
-
myThrottle.rect.centery =
500
-
pygame.quit()
搞定!到底能不能像嫦娥五号一样成功登月呢?今晚就和娃一起试试吧!Happy landing!
推
荐
图
书
沃伦·桑德 卡特·桑德 著
杨文其 苏金国 易郑超 译
老少咸宜的Python编程启蒙书
“Python之父”龟叔推荐,获Jolt生产效率奖
新版全彩印刷,插图生动活泼
“孩子会喜欢上这一本以他们的视角所写的书,也会学到很多。”
——“Python之父”吉多·范罗苏姆
“Python,是00后的BASIC。据我观察,这本书是众多70后和80后教孩子编程的优选图书,也是很多家长自己学Python编程的开始。”
——爱编程的魏校长,知名教育博主
“这本书用Python语言教你如何写程序,是一本老少咸宜的编程书。”
——左耳朵耗子(陈皓)
“近年来少儿编程非常火爆。如果你是一位‘码农’家长,不如发挥自己的专长,做孩子的启蒙老师和学习伙伴,这本书就是很棒的‘亲子编程学习实践手册’。”
——周自恒,公众号“周花卷”主理人
购书传送门
文末提示:随书附赠无毒安装程序,一键式安装Python 3及Pygame模块,并获取本书所有游戏代码。请在桌面端单击“阅读原文”,下载随书资源。
转载:https://blog.csdn.net/turingbooks/article/details/110602251