我根据这篇文章编写了以下代码:http://kevman3d.blogspot.com/2015/07/basic-games-in-python-1982-would-be.html
在这张图片中的 ZX BASIC 上:
10 LET P=0
20 LET T=P
30 FOR Z=1 T0 10
35 CLS
37 PRINT AT 12,0;T
40 LET R=INT (RND*17)
50 FOR Y=0 TO 10
60 PRINT AT Y,R;"O"
70 LET N=P(INKEY$="4")-(INKEY$="1")
80 IF N<0 OR N>15 THEN LET N=P
100 PRINT AT 11,P;" ";AT 11,N;"┗┛";AT Y,R;" "
110 LET P=N
120 NEXT Y
130 LET T=T+(P=R OR P+1=R)
150 NEXT Z
160 PRINT AT 12,0;"YOU SCORED ";T;"/10"
170 PAUSE 4E4
180 RUN
我还在 Code Review Stack Exchange 上分享了它,并得到了非常有帮助的响应,将其重构为带有类型提示的高质量 Python 代码。
但是,出于我的目的,我希望保持使这项工作稍微不那么先进所需的知识水平,包括避免使用 OOP。我基本上想保持“ZX BASIC 的精神”,但让代码“不糟糕”。函数的使用很好,因为我们在过去被允许 GOSUB
。
我对在主游戏循环中使用嵌套 FOR
循环来使游戏正常运行的方法持怀疑态度,但与此同时我很好奇 BASIC 范例的表现如何映射到 Pygame 的更多事件驱动方法,因此我欢迎任何关于这种方法的优缺点的评论。
更具体地说,
有什么地方可以把退出代码
if event.type == pygame.QUIT
放在游戏回合期间有效的地方,而不必在其他地方重复代码?如果我要避免使用
FOR
循环/嵌套的FOR
循环,这个游戏将如何实现?我是否违反了 pygame/Python 的最佳实践要点?
考虑到我的目的是在保持 ZX81 游戏“精神”的同时编写良好的 Pygame 代码,您能提出哪些改进建议?
非常感谢任何输入。如果有人愿意提供的话,我也很想看到一个完整的列表来实现我最初尝试中产生的一些想法。
import pygame
import random
import sys
# Define colors and other global constants
BLACK = (0, 0, 0)
WHITE = (255, 255, 255)
TEXT_SIZE = 16
SCREEN_SIZE = (16 * TEXT_SIZE, 13 * TEXT_SIZE)
NUM_ROUNDS = 5
def print_at_pos(row_num, col_num, item):
"""Blits text to row, col position."""
screen.blit(item, (col_num * TEXT_SIZE, row_num * TEXT_SIZE))
# Set up stuff
pygame.init()
screen = pygame.display.set_mode(SCREEN_SIZE)
pygame.display.set_caption("Dropout")
game_font = pygame.font.SysFont('consolas', TEXT_SIZE)
# Create clock to manage how fast the screen updates
clock = pygame.time.Clock()
# initialize some game variables
player_pos, new_player_pos, coin_row, score = 0, 0, 0, 0
# -------- Main Program Loop -----------
while True:
score = 0
# Each value of i represents 1 round
for i in range(NUM_ROUNDS):
coin_col = random.randint(0, 15)
# Each value of j represents one step in the coin's fall
for j in range(11):
pygame.event.get()
pressed = pygame.key.get_pressed()
if pressed[pygame.K_RIGHT]:
new_player_pos = player_pos + 1
elif pressed[pygame.K_LEFT]:
new_player_pos = player_pos - 1
if new_player_pos < 0 or new_player_pos > 15:
new_player_pos = player_pos
# --- Game logic
player_pos = new_player_pos
coin_row = j
if player_pos + 1 == coin_col and j == 10:
score += 1
# --- Drawing code
# First clear screen
screen.fill(WHITE)
player_icon = game_font.render("|__|", True, BLACK, WHITE)
print_at_pos(10, new_player_pos, player_icon)
coin_text = game_font.render("O", True, BLACK, WHITE)
print_at_pos(coin_row, coin_col, coin_text)
score_text = game_font.render(f"SCORE: {score}", True, BLACK, WHITE)
print_at_pos(12, 0, score_text)
# --- Update the screen.
pygame.display.flip()
# --- Limit to 6 frames/sec maximum. Adjust to taste.
clock.tick(8)
msg_text = game_font.render("PRESS ANY KEY TO PLAY AGAIN", True, BLACK, WHITE)
print_at_pos(5, 0, msg_text)
pygame.display.flip()
waiting = True
while waiting:
for event in pygame.event.get():
if event.type == pygame.QUIT:
pygame.quit()
sys.exit(0)
if event.type == pygame.KEYDOWN:
waiting = False
最佳答案
这是我对您的代码进行的重组:
import pygame
import random
# Define global constants
TEXT_SIZE = 16
SCREEN_SIZE = (16 * TEXT_SIZE, 13 * TEXT_SIZE)
NUM_ROUNDS = 5
def print_at_pos(row_num, col_num, item):
"""Blits text to row, col position."""
screen.blit(item, (col_num * TEXT_SIZE, row_num * TEXT_SIZE))
# Set up stuff
pygame.init()
screen = pygame.display.set_mode(SCREEN_SIZE)
pygame.display.set_caption("Dropout")
game_font = pygame.font.SysFont("consolas", TEXT_SIZE)
# Create clock to manage how fast the screen updates
clock = pygame.time.Clock()
# draw the images
player_icon = game_font.render("|__|", True, "black", "white")
# if we don't specify a background color, it'll be transparent
coin_text = game_font.render("O", True, "black")
msg_text = game_font.render("PRESS ANY KEY TO PLAY AGAIN", True, "black", "white")
# initialize some game variables
waiting = False # start in game
player_pos = 0
score = 0
game_round = 0
coin_row = 0
coin_col = random.randint(0, 15)
running = True # For program exit
# -------- Main Program Loop -----------
while running:
# event handling
for event in pygame.event.get():
if event.type == pygame.QUIT:
running = False
elif event.type == pygame.KEYDOWN:
if waiting:
waiting = False
score = 0 # reset score
elif event.key == pygame.K_LEFT:
player_pos -= 1
elif event.key == pygame.K_RIGHT:
player_pos += 1
# --- Game logic
if waiting:
# don't update the game state or redraw screen
print_at_pos(5, 0, msg_text)
else:
coin_row += 1 # TODO: decouple from frame rate
if -1 > player_pos:
player_pos = -1 # so we can catch a coin at zero
elif 15 < player_pos:
player_pos = 15
# coin is in scoring position
if coin_row == 10:
if player_pos + 1 == coin_col:
score += 1
elif coin_row > 10: # round is over
coin_col = random.randint(0, 15)
coin_row = 0
game_round+= 1
if game_round >= NUM_ROUNDS:
waiting = True
game_round = 0 # reset round counter
# --- Drawing code
screen.fill("white") # clear screen
print_at_pos(10, player_pos, player_icon)
print_at_pos(coin_row, coin_col, coin_text)
score_text = game_font.render(f"SCORE: {score}", True, "black", "white")
print_at_pos(12, 0, score_text)
# --- Update the screen.
pygame.display.flip()
# --- Limit to 6 frames/sec maximum. Adjust to taste.
clock.tick(6)
pygame.quit()
我使用了一个 bool 值 waiting
来允许仅在游戏过程中移动的常见事件和游戏状态处理。对于更复杂的交互,您需要 state machine .
硬币运动目前与帧速率相关联,这很容易,但理想情况下您会指定一个速率/时间间隔,例如行下降之间有 200 毫秒,然后您可以获得类似于显示器刷新率的刷新率。
关于python - ZX81 BASIC 到 "Dropout"游戏的 Pygame 转换,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/73950834/