diff --git a/0803/saved_networks/bird-dqn-220000.data-00000-of-00001 b/0803/saved_networks/bird-dqn-220000.data-00000-of-00001 new file mode 100644 index 0000000..a7b5f86 Binary files /dev/null and b/0803/saved_networks/bird-dqn-220000.data-00000-of-00001 differ diff --git a/0803/saved_networks/bird-dqn-220000.index b/0803/saved_networks/bird-dqn-220000.index new file mode 100644 index 0000000..8aeb807 Binary files /dev/null and b/0803/saved_networks/bird-dqn-220000.index differ diff --git a/0803/saved_networks/bird-dqn-220000.meta b/0803/saved_networks/bird-dqn-220000.meta new file mode 100644 index 0000000..8d699eb Binary files /dev/null and b/0803/saved_networks/bird-dqn-220000.meta differ diff --git a/0803/saved_networks/bird-dqn-230000.data-00000-of-00001 b/0803/saved_networks/bird-dqn-230000.data-00000-of-00001 new file mode 100644 index 0000000..1906da4 Binary files /dev/null and b/0803/saved_networks/bird-dqn-230000.data-00000-of-00001 differ diff --git a/0803/saved_networks/bird-dqn-230000.index b/0803/saved_networks/bird-dqn-230000.index new file mode 100644 index 0000000..ad44fb1 Binary files /dev/null and b/0803/saved_networks/bird-dqn-230000.index differ diff --git a/0803/saved_networks/bird-dqn-230000.meta b/0803/saved_networks/bird-dqn-230000.meta new file mode 100644 index 0000000..8d699eb Binary files /dev/null and b/0803/saved_networks/bird-dqn-230000.meta differ diff --git a/0803/saved_networks/bird-dqn-240000.data-00000-of-00001 b/0803/saved_networks/bird-dqn-240000.data-00000-of-00001 new file mode 100644 index 0000000..67904d2 Binary files /dev/null and b/0803/saved_networks/bird-dqn-240000.data-00000-of-00001 differ diff --git a/0803/saved_networks/bird-dqn-240000.index b/0803/saved_networks/bird-dqn-240000.index new file mode 100644 index 0000000..d5555e2 Binary files /dev/null and b/0803/saved_networks/bird-dqn-240000.index differ diff --git a/0803/saved_networks/bird-dqn-240000.meta b/0803/saved_networks/bird-dqn-240000.meta new file mode 100644 index 0000000..8d699eb Binary files /dev/null and b/0803/saved_networks/bird-dqn-240000.meta differ diff --git a/0803/saved_networks/bird-dqn-250000.data-00000-of-00001 b/0803/saved_networks/bird-dqn-250000.data-00000-of-00001 new file mode 100644 index 0000000..c385992 Binary files /dev/null and b/0803/saved_networks/bird-dqn-250000.data-00000-of-00001 differ diff --git a/0803/saved_networks/bird-dqn-250000.index b/0803/saved_networks/bird-dqn-250000.index new file mode 100644 index 0000000..ab91986 Binary files /dev/null and b/0803/saved_networks/bird-dqn-250000.index differ diff --git a/0803/saved_networks/bird-dqn-250000.meta b/0803/saved_networks/bird-dqn-250000.meta new file mode 100644 index 0000000..8d699eb Binary files /dev/null and b/0803/saved_networks/bird-dqn-250000.meta differ diff --git a/0803/saved_networks/bird-dqn-260000.data-00000-of-00001 b/0803/saved_networks/bird-dqn-260000.data-00000-of-00001 new file mode 100644 index 0000000..c5f2653 Binary files /dev/null and b/0803/saved_networks/bird-dqn-260000.data-00000-of-00001 differ diff --git a/0803/saved_networks/bird-dqn-260000.index b/0803/saved_networks/bird-dqn-260000.index new file mode 100644 index 0000000..7d6a05d Binary files /dev/null and b/0803/saved_networks/bird-dqn-260000.index differ diff --git a/0803/saved_networks/bird-dqn-260000.meta b/0803/saved_networks/bird-dqn-260000.meta new file mode 100644 index 0000000..8d699eb Binary files /dev/null and b/0803/saved_networks/bird-dqn-260000.meta differ diff --git a/0803/saved_networks/checkpoint b/0803/saved_networks/checkpoint new file mode 100644 index 0000000..3e6d4e8 --- /dev/null +++ b/0803/saved_networks/checkpoint @@ -0,0 +1,6 @@ +model_checkpoint_path: "bird-dqn-260000" +all_model_checkpoint_paths: "bird-dqn-220000" +all_model_checkpoint_paths: "bird-dqn-230000" +all_model_checkpoint_paths: "bird-dqn-240000" +all_model_checkpoint_paths: "bird-dqn-250000" +all_model_checkpoint_paths: "bird-dqn-260000" diff --git a/deep_q_network.py b/deep_q_network.py index 1294f96..d3f9e97 100755 --- a/deep_q_network.py +++ b/deep_q_network.py @@ -1,7 +1,12 @@ #!/usr/bin/env python from __future__ import print_function +import tensorflow as tf +tf = tf.compat.v1 +tf.disable_eager_execution() + import tensorflow as tf +tf = tf.compat.v1 import cv2 import sys sys.path.append("game/") @@ -10,35 +15,90 @@ import numpy as np from collections import deque -GAME = 'bird' # the name of the game being played for log files -ACTIONS = 2 # number of valid actions -GAMMA = 0.99 # decay rate of past observations -OBSERVE = 100000. # timesteps to observe before training -EXPLORE = 2000000. # frames over which to anneal epsilon -FINAL_EPSILON = 0.0001 # final value of epsilon -INITIAL_EPSILON = 0.0001 # starting value of epsilon -REPLAY_MEMORY = 50000 # number of previous transitions to remember -BATCH = 32 # size of minibatch +GAME = 'bird' # 游戏名称,用于日志文件 +ACTIONS = 2 # 有效动作的数量 +GAMMA = 0.99 # 过去观察的衰减率 +OBSERVE = 100000. # 训练之前观察的时间步数 +EXPLORE = 2000000. # epsilon退火的帧数 +FINAL_EPSILON = 0.0001 # epsilon的最终值 +INITIAL_EPSILON = 0.0001 # epsilon的初始值 +REPLAY_MEMORY = 50000 # 记忆的先前转换数量 +BATCH = 32 # minibatch的大小 FRAME_PER_ACTION = 1 +job_prefix = '0803/' def weight_variable(shape): - initial = tf.truncated_normal(shape, stddev = 0.01) + """ + 创建权重变量的函数 + + 参数: + - shape:权重的形状 + + 返回: + - 权重变量 + """ + initial = tf.random.truncated_normal(shape, stddev = 0.01) + # 生成截断的正态分布随机数 + # 以均值为0,标准差为0.01生成随机数 return tf.Variable(initial) def bias_variable(shape): + """ + 创建偏置变量的函数 + + 参数: + - shape:偏置的形状 + + 返回: + - 偏置变量 + """ initial = tf.constant(0.01, shape = shape) + # 生成常数张量 + # 值为0.01,形状为shape return tf.Variable(initial) def conv2d(x, W, stride): + """ + 创建卷积层的函数 + + 参数: + - x:输入张量 + - W:卷积核权重 + - stride:卷积步长 + + 返回: + - 卷积结果张量 + """ return tf.nn.conv2d(x, W, strides = [1, stride, stride, 1], padding = "SAME") def max_pool_2x2(x): + """ + 创建最大池化层的函数 + + 参数: + - x:输入张量 + + 返回: + - 池化结果张量 + """ return tf.nn.max_pool(x, ksize = [1, 2, 2, 1], strides = [1, 2, 2, 1], padding = "SAME") def createNetwork(): - # network weights + """ + 创建深度Q网络的函数 + + 返回: + - 输入张量s + - 输出张量readout + - 隐藏层张量h_fc1 + """ + # 网络权重 W_conv1 = weight_variable([8, 8, 4, 32]) + # conv的全称是convolution,表示卷积层 + # 卷积层的作用是提取图像的特征,通过卷积核与输入图像进行卷积操作,得到特征图 + # 这个卷积层有8x8的卷积核,4个输入通道,32个输出通道 b_conv1 = bias_variable([32]) + # 偏置用来“加在某个输出上”,用于调整神经元的输出值 W_conv2 = weight_variable([4, 4, 32, 64]) b_conv2 = bias_variable([64]) @@ -50,50 +110,59 @@ def createNetwork(): b_fc1 = bias_variable([512]) W_fc2 = weight_variable([512, ACTIONS]) + # fc 的全称是fully connected,表示全连接层 b_fc2 = bias_variable([ACTIONS]) - # input layer + # 输入层 s = tf.placeholder("float", [None, 80, 80, 4]) + # placeholder 是占位符的意思,用于定义过程输入数据的位置 - # hidden layers + # 隐藏层 h_conv1 = tf.nn.relu(conv2d(s, W_conv1, 4) + b_conv1) + # relu是激活函数,用于增加网络的非线性,将负值转换为0 h_pool1 = max_pool_2x2(h_conv1) h_conv2 = tf.nn.relu(conv2d(h_pool1, W_conv2, 2) + b_conv2) - #h_pool2 = max_pool_2x2(h_conv2) h_conv3 = tf.nn.relu(conv2d(h_conv2, W_conv3, 1) + b_conv3) - #h_pool3 = max_pool_2x2(h_conv3) - #h_pool3_flat = tf.reshape(h_pool3, [-1, 256]) h_conv3_flat = tf.reshape(h_conv3, [-1, 1600]) h_fc1 = tf.nn.relu(tf.matmul(h_conv3_flat, W_fc1) + b_fc1) - # readout layer + # 输出层 readout = tf.matmul(h_fc1, W_fc2) + b_fc2 return s, readout, h_fc1 def trainNetwork(s, readout, h_fc1, sess): - # define the cost function + """ + 训练深度Q网络的函数 + + 参数: + - s:输入张量 + - readout:输出张量 + - h_fc1:隐藏层张量 + - sess:TensorFlow会话对象 + """ + # 定义损失函数 a = tf.placeholder("float", [None, ACTIONS]) y = tf.placeholder("float", [None]) - readout_action = tf.reduce_sum(tf.multiply(readout, a), reduction_indices=1) - cost = tf.reduce_mean(tf.square(y - readout_action)) - train_step = tf.train.AdamOptimizer(1e-6).minimize(cost) + readout_action = tf.reduce_sum(tf.multiply(readout, a), reduction_indices=1) # 乘法 + cost = tf.reduce_mean(tf.square(y - readout_action)) # 平方差 + train_step = tf.train.AdamOptimizer(1e-6).minimize(cost) # Adam优化器 AdamW - # open up a game state to communicate with emulator + # 打开游戏状态以与模拟器通信 game_state = game.GameState() - # store the previous observations in replay memory + # 在回放内存中存储先前的观察 D = deque() - # printing + # 打印信息 a_file = open("logs_" + GAME + "/readout.txt", 'w') h_file = open("logs_" + GAME + "/hidden.txt", 'w') - # get the first state by doing nothing and preprocess the image to 80x80x4 + # 通过不做任何操作获取第一个状态,并将图像预处理为80x80x4 do_nothing = np.zeros(ACTIONS) do_nothing[0] = 1 x_t, r_0, terminal = game_state.frame_step(do_nothing) @@ -101,21 +170,23 @@ def trainNetwork(s, readout, h_fc1, sess): ret, x_t = cv2.threshold(x_t,1,255,cv2.THRESH_BINARY) s_t = np.stack((x_t, x_t, x_t, x_t), axis=2) - # saving and loading networks + # 保存和加载网络 saver = tf.train.Saver() sess.run(tf.initialize_all_variables()) - checkpoint = tf.train.get_checkpoint_state("saved_networks") - if checkpoint and checkpoint.model_checkpoint_path: + # checkpoint = tf.train.get_checkpoint_state("saved_networks") + # checkpoint = None + checkpoint = tf.train.get_checkpoint_state(job_prefix + 'saved_networks/') + if checkpoint and checkpoint.model_checkpoint_path: # if checkpoint is not None saver.restore(sess, checkpoint.model_checkpoint_path) print("Successfully loaded:", checkpoint.model_checkpoint_path) else: print("Could not find old network weights") - # start training + # 开始训练 epsilon = INITIAL_EPSILON t = 0 - while "flappy bird" != "angry bird": - # choose an action epsilon greedily + while "flappy bird" != "angry bird": # while True: + # epsilon贪婪地选择一个动作 readout_t = readout.eval(feed_dict={s : [s_t]})[0] a_t = np.zeros([ACTIONS]) action_index = 0 @@ -128,62 +199,63 @@ def trainNetwork(s, readout, h_fc1, sess): action_index = np.argmax(readout_t) a_t[action_index] = 1 else: - a_t[0] = 1 # do nothing + a_t[0] = 1 # 什么都不做 - # scale down epsilon + # 缩小epsilon if epsilon > FINAL_EPSILON and t > OBSERVE: epsilon -= (INITIAL_EPSILON - FINAL_EPSILON) / EXPLORE - # run the selected action and observe next state and reward - x_t1_colored, r_t, terminal = game_state.frame_step(a_t) - x_t1 = cv2.cvtColor(cv2.resize(x_t1_colored, (80, 80)), cv2.COLOR_BGR2GRAY) - ret, x_t1 = cv2.threshold(x_t1, 1, 255, cv2.THRESH_BINARY) - x_t1 = np.reshape(x_t1, (80, 80, 1)) - #s_t1 = np.append(x_t1, s_t[:,:,1:], axis = 2) - s_t1 = np.append(x_t1, s_t[:, :, :3], axis=2) + # 执行选择的动作并观察下一个状态和奖励 + x_t1_colored, r_t, terminal = game_state.frame_step(a_t) # 获取游戏帧 + x_t1 = cv2.cvtColor(cv2.resize(x_t1_colored, (80, 80)), cv2.COLOR_BGR2GRAY) # 转换为灰度 + # 转换为灰度就是将图像转换为黑白图像(只有黑白和不同程度的灰),而二值化时只有黑色和白色,没有其他颜色 + # 灰度:0-255,二值化:0或255 + ret, x_t1 = cv2.threshold(x_t1, 1, 255, cv2.THRESH_BINARY) # 二值化 + x_t1 = np.reshape(x_t1, (80, 80, 1)) # 重塑形状为80x80x1 + s_t1 = np.append(x_t1, s_t[:, :, :3], axis=2) # 将新帧添加到状态中 - # store the transition in D + # 将转换存储在D中 D.append((s_t, a_t, r_t, s_t1, terminal)) if len(D) > REPLAY_MEMORY: D.popleft() - # only train if done observing + # 只有在观察完成后才进行训练 if t > OBSERVE: - # sample a minibatch to train on + # 从回放内存中随机采样一个minibatch进行训练 minibatch = random.sample(D, BATCH) - # get the batch variables - s_j_batch = [d[0] for d in minibatch] - a_batch = [d[1] for d in minibatch] - r_batch = [d[2] for d in minibatch] - s_j1_batch = [d[3] for d in minibatch] + # 获取批量变量 + s_j_batch = [d[0] for d in minibatch] # 当前状态 + a_batch = [d[1] for d in minibatch] # 动作 + r_batch = [d[2] for d in minibatch] # 奖励 + s_j1_batch = [d[3] for d in minibatch] # 下一个状态 y_batch = [] readout_j1_batch = readout.eval(feed_dict = {s : s_j1_batch}) for i in range(0, len(minibatch)): terminal = minibatch[i][4] - # if terminal, only equals reward + # 如果是终止状态,只等于奖励 if terminal: y_batch.append(r_batch[i]) else: y_batch.append(r_batch[i] + GAMMA * np.max(readout_j1_batch[i])) - # perform gradient step + # 执行梯度下降步骤 train_step.run(feed_dict = { y : y_batch, a : a_batch, s : s_j_batch} ) - # update the old values + # 更新旧值 s_t = s_t1 t += 1 - # save progress every 10000 iterations + # 每10000次迭代保存进度 if t % 10000 == 0: - saver.save(sess, 'saved_networks/' + GAME + '-dqn', global_step = t) + saver.save(sess, job_prefix + 'saved_networks/' + GAME + '-dqn', global_step = t) - # print info + # 打印信息 state = "" if t <= OBSERVE: state = "observe" @@ -195,16 +267,12 @@ def trainNetwork(s, readout, h_fc1, sess): print("TIMESTEP", t, "/ STATE", state, \ "/ EPSILON", epsilon, "/ ACTION", action_index, "/ REWARD", r_t, \ "/ Q_MAX %e" % np.max(readout_t)) - # write info to files - ''' - if t % 10000 <= 100: - a_file.write(",".join([str(x) for x in readout_t]) + '\n') - h_file.write(",".join([str(x) for x in h_fc1.eval(feed_dict={s:[s_t]})[0]]) + '\n') - cv2.imwrite("logs_tetris/frame" + str(t) + ".png", x_t1) - ''' def playGame(): + # 配置TensorFlow会话 + # TensorFlow会话是TensorFlow操作的执行环境,它封装了一些操作的执行状态和操作的执行设备 sess = tf.InteractiveSession() + # 创建网络 s, readout, h_fc1 = createNetwork() trainNetwork(s, readout, h_fc1, sess) diff --git a/game/flappy_bird_utils.py b/game/flappy_bird_utils.py index 5220dc2..527546f 100644 --- a/game/flappy_bird_utils.py +++ b/game/flappy_bird_utils.py @@ -1,22 +1,28 @@ import pygame import sys def load(): - # path of player with different states + """ + 加载游戏资源并返回图像、声音和碰撞掩码的字典。 + + Returns: + tuple: 包含图像、声音和碰撞掩码的字典。 + """ + # 玩家不同状态的路径 PLAYER_PATH = ( 'assets/sprites/redbird-upflap.png', 'assets/sprites/redbird-midflap.png', 'assets/sprites/redbird-downflap.png' ) - # path of background + # 背景路径 BACKGROUND_PATH = 'assets/sprites/background-black.png' - # path of pipe + # 管道路径 PIPE_PATH = 'assets/sprites/pipe-green.png' IMAGES, SOUNDS, HITMASKS = {}, {}, {} - # numbers sprites for score display + # 数字图像用于显示分数 IMAGES['numbers'] = ( pygame.image.load('assets/sprites/0.png').convert_alpha(), pygame.image.load('assets/sprites/1.png').convert_alpha(), @@ -30,10 +36,10 @@ def load(): pygame.image.load('assets/sprites/9.png').convert_alpha() ) - # base (ground) sprite + # 地面图像 IMAGES['base'] = pygame.image.load('assets/sprites/base.png').convert_alpha() - # sounds + # 声音 if 'win' in sys.platform: soundExt = '.wav' else: @@ -45,30 +51,30 @@ def load(): SOUNDS['swoosh'] = pygame.mixer.Sound('assets/audio/swoosh' + soundExt) SOUNDS['wing'] = pygame.mixer.Sound('assets/audio/wing' + soundExt) - # select random background sprites + # 随机选择背景图像 IMAGES['background'] = pygame.image.load(BACKGROUND_PATH).convert() - # select random player sprites + # 随机选择玩家图像 IMAGES['player'] = ( pygame.image.load(PLAYER_PATH[0]).convert_alpha(), pygame.image.load(PLAYER_PATH[1]).convert_alpha(), pygame.image.load(PLAYER_PATH[2]).convert_alpha(), ) - # select random pipe sprites + # 随机选择管道图像 IMAGES['pipe'] = ( pygame.transform.rotate( pygame.image.load(PIPE_PATH).convert_alpha(), 180), pygame.image.load(PIPE_PATH).convert_alpha(), ) - # hismask for pipes + # 管道的碰撞掩码 HITMASKS['pipe'] = ( getHitmask(IMAGES['pipe'][0]), getHitmask(IMAGES['pipe'][1]), ) - # hitmask for player + # 玩家的碰撞掩码 HITMASKS['player'] = ( getHitmask(IMAGES['player'][0]), getHitmask(IMAGES['player'][1]), diff --git a/game/wrapped_flappy_bird.py b/game/wrapped_flappy_bird.py index 24a102e..8ad37fc 100644 --- a/game/wrapped_flappy_bird.py +++ b/game/wrapped_flappy_bird.py @@ -17,7 +17,7 @@ pygame.display.set_caption('Flappy Bird') IMAGES, SOUNDS, HITMASKS = flappy_bird_utils.load() -PIPEGAPSIZE = 100 # gap between upper and lower part of pipe +PIPEGAPSIZE = 100 # 上下管道之间的间隙大小 BASEY = SCREENHEIGHT * 0.79 PLAYER_WIDTH = IMAGES['player'][0].get_width() @@ -50,12 +50,12 @@ def __init__(self): # player velocity, max velocity, downward accleration, accleration on flap self.pipeVelX = -4 - self.playerVelY = 0 # player's velocity along Y, default same as playerFlapped - self.playerMaxVelY = 10 # max vel along Y, max descend speed - self.playerMinVelY = -8 # min vel along Y, max ascend speed - self.playerAccY = 1 # players downward accleration - self.playerFlapAcc = -9 # players speed on flapping - self.playerFlapped = False # True when player flaps + self.playerVelY = 0 # 玩家在Y轴上的速度,默认与playerFlapped相同 + self.playerMaxVelY = 10 # Y轴上的最大速度,最大下降速度 + self.playerMinVelY = -8 # Y轴上的最小速度,最大上升速度 + self.playerAccY = 1 # 玩家向下的加速度 + self.playerFlapAcc = -9 # 玩家扇动时的速度 + self.playerFlapped = False # 当玩家扇动时为True def frame_step(self, input_actions): pygame.event.pump() @@ -66,15 +66,15 @@ def frame_step(self, input_actions): if sum(input_actions) != 1: raise ValueError('Multiple input actions!') - # input_actions[0] == 1: do nothing - # input_actions[1] == 1: flap the bird + # input_actions[0] == 1: 什么都不做 + # input_actions[1] == 1: 扇动小鸟 if input_actions[1] == 1: if self.playery > -2 * PLAYER_HEIGHT: self.playerVelY = self.playerFlapAcc self.playerFlapped = True #SOUNDS['wing'].play() - # check for score + # 检查得分 playerMidPos = self.playerx + PLAYER_WIDTH / 2 for pipe in self.upperPipes: pipeMidPos = pipe['x'] + PIPE_WIDTH / 2 @@ -89,7 +89,7 @@ def frame_step(self, input_actions): self.loopIter = (self.loopIter + 1) % 30 self.basex = -((-self.basex + 100) % self.baseShift) - # player's movement + # 玩家的移动 if self.playerVelY < self.playerMaxVelY and not self.playerFlapped: self.playerVelY += self.playerAccY if self.playerFlapped: @@ -98,23 +98,23 @@ def frame_step(self, input_actions): if self.playery < 0: self.playery = 0 - # move pipes to left + # 管道向左移动 for uPipe, lPipe in zip(self.upperPipes, self.lowerPipes): uPipe['x'] += self.pipeVelX lPipe['x'] += self.pipeVelX - # add new pipe when first pipe is about to touch left of screen + # 当第一个管道即将触碰屏幕左侧时添加新的管道 if 0 < self.upperPipes[0]['x'] < 5: newPipe = getRandomPipe() self.upperPipes.append(newPipe[0]) self.lowerPipes.append(newPipe[1]) - # remove first pipe if its out of the screen + # 如果第一个管道已经超出屏幕,则移除它 if self.upperPipes[0]['x'] < -PIPE_WIDTH: self.upperPipes.pop(0) self.lowerPipes.pop(0) - # check if crash here + # 检查是否碰撞 isCrash= checkCrash({'x': self.playerx, 'y': self.playery, 'index': self.playerIndex}, self.upperPipes, self.lowerPipes) @@ -125,7 +125,7 @@ def frame_step(self, input_actions): self.__init__() reward = -1 - # draw sprites + # 绘制精灵 SCREEN.blit(IMAGES['background'], (0,0)) for uPipe, lPipe in zip(self.upperPipes, self.lowerPipes): @@ -133,7 +133,7 @@ def frame_step(self, input_actions): SCREEN.blit(IMAGES['pipe'][1], (lPipe['x'], lPipe['y'])) SCREEN.blit(IMAGES['base'], (self.basex, BASEY)) - # print score so player overlaps the score + # 显示得分 # showScore(self.score) SCREEN.blit(IMAGES['player'][self.playerIndex], (self.playerx, self.playery)) @@ -145,8 +145,8 @@ def frame_step(self, input_actions): return image_data, reward, terminal def getRandomPipe(): - """returns a randomly generated pipe""" - # y of gap between upper and lower pipe + """返回一个随机生成的管道""" + # 上下管道之间的间隙的y坐标 gapYs = [20, 30, 40, 50, 60, 70, 80, 90] index = random.randint(0, len(gapYs)-1) gapY = gapYs[index] @@ -155,15 +155,15 @@ def getRandomPipe(): pipeX = SCREENWIDTH + 10 return [ - {'x': pipeX, 'y': gapY - PIPE_HEIGHT}, # upper pipe - {'x': pipeX, 'y': gapY + PIPEGAPSIZE}, # lower pipe + {'x': pipeX, 'y': gapY - PIPE_HEIGHT}, # 上管道 + {'x': pipeX, 'y': gapY + PIPEGAPSIZE}, # 下管道 ] def showScore(score): - """displays score in center of screen""" + """在屏幕中央显示得分""" scoreDigits = [int(x) for x in list(str(score))] - totalWidth = 0 # total width of all numbers to be printed + totalWidth = 0 # 要打印的所有数字的总宽度 for digit in scoreDigits: totalWidth += IMAGES['numbers'][digit].get_width() @@ -176,12 +176,12 @@ def showScore(score): def checkCrash(player, upperPipes, lowerPipes): - """returns True if player collders with base or pipes.""" + """如果玩家与地面或管道碰撞,则返回True""" pi = player['index'] player['w'] = IMAGES['player'][0].get_width() player['h'] = IMAGES['player'][0].get_height() - # if player crashes into ground + # 如果玩家撞到地面 if player['y'] + player['h'] >= BASEY - 1: return True else: @@ -190,16 +190,16 @@ def checkCrash(player, upperPipes, lowerPipes): player['w'], player['h']) for uPipe, lPipe in zip(upperPipes, lowerPipes): - # upper and lower pipe rects + # 上下管道的矩形 uPipeRect = pygame.Rect(uPipe['x'], uPipe['y'], PIPE_WIDTH, PIPE_HEIGHT) lPipeRect = pygame.Rect(lPipe['x'], lPipe['y'], PIPE_WIDTH, PIPE_HEIGHT) - # player and upper/lower pipe hitmasks + # 玩家和上/下管道的碰撞掩码 pHitMask = HITMASKS['player'][pi] uHitmask = HITMASKS['pipe'][0] lHitmask = HITMASKS['pipe'][1] - # if bird collided with upipe or lpipe + # 如果小鸟与上管道或下管道碰撞 uCollide = pixelCollision(playerRect, uPipeRect, pHitMask, uHitmask) lCollide = pixelCollision(playerRect, lPipeRect, pHitMask, lHitmask) @@ -209,7 +209,7 @@ def checkCrash(player, upperPipes, lowerPipes): return False def pixelCollision(rect1, rect2, hitmask1, hitmask2): - """Checks if two objects collide and not just their rects""" + """检查两个对象是否碰撞,而不仅仅是它们的矩形""" rect = rect1.clip(rect2) if rect.width == 0 or rect.height == 0: