用Python和Pygame打造经典坦克大战:一次完整的游戏开发实践
用Python复刻童年经典!我写了300行代码,带你把坦克大战搬上电脑屏幕🚀还记得那个暑假,和小伙伴们挤在电视机前,为了一局坦克大战争得面红耳赤的日子吗?今天,我用代码把它“复活”了!
周末心血来潮,翻出那台老旧的笔记本电脑,敲下了300行Python代码。当蓝色小坦克在屏幕上移动,向红色敌人开火的那一刻,童年的记忆瞬间涌上心头。运行效果简直不要太爽:蓝色坦克是你,红色是AI敌人,棕色墙壁可打穿,灰色墙壁绕道走。WASD控制移动,空格键发射炮弹——弹药只有20发,省着点用!游戏界面简洁直观,左上角实时显示你的分数、当前关卡,右上角是弹药和敌人数量。最贴心的是,你的生命值就显示在坦克头顶,三个小红心清清楚楚。说实话,最初只是手痒想写点小游戏。但当我深入思考,发现这个坦克大战项目简直是Python入门到进阶的完美练手项目!最重要的是——它真的好玩!写代码最怕枯燥,而这个项目让你在玩中学习,在成就感中进步。敌人会“随机”决定往哪走
move_choice = random.randint(0, 30)约3%的几率改变方向
还会“看心情”开火
if random.random() < 0.02:“嘿,吃我一炮!”
坦克不能穿墙,子弹打到墙会消失,打到敌人就加分——所有互动都有模有样,玩起来手感流畅。第二步:复制用户提供的完整代码,保存为tank_game.py墙壁系统:棕色墙可破坏,创造新路线;灰色墙永久,必须绕行AI行为:敌人移动有随机性,但射击有冷却时间,不会太过分如果你是新⼿:这是绝佳的Python练手项目,代码结构清晰,注释详细如果你只是怀旧:这就是为你准备的!熟悉的玩法,现代的实现我最开始只是运行了别人写的版本。玩了几局后,手痒开始修改:第一次修改:把弹药从20发改成30发——瞬间感觉自己是土豪第三次修改:给自己加了“无敌模式”(测试用,不推荐长期开)每一次修改,都让我对代码理解更深一层。从“这行代码干嘛的”到“我想让游戏怎样”,这种掌控感,正是编程最大的乐趣。觉得太难?把ENEMY_FIRE_RATE = 0.02改成0.01想更刺激?把PLAYER_SPEED = 4改成5这个坦克大战项目,对我而言不只是300行Python代码。它是:最让我感动的时刻,是看到朋友运行这个游戏时眼里的光——那种“哇,这居然是你写的”的惊喜,是任何赞美都比不上的成就感。复制用户提供的代码,保存为tank_game.py遇到问题?在评论区留言,咱们一起解决。做出酷炫的修改?一定分享出来,让更多人看到你的创意。游戏的世界,因代码而生动;编程的乐趣,在创造中永恒。你的坦克,已经启动!🚀💥
小编提问:你第一次成功运行这个坦克大战时,是什么感觉?在评论区分享你的故事,点赞最高的三位朋友,我将分享更多Python游戏开发的小技巧!⬇️P.S.如果卡关了……偷偷说,可以在代码里给自己“开个小灶”。不过,自己挑战过关的成就感,才是最甜的胜利果实,不是吗?😉#游戏窗口设置WIDTH, HEIGHT =800,600screen = pygame.display.set_mode((WIDTH, HEIGHT))pygame.display.set_caption("Tank Battle")#游戏常量FPS =60PLAYER_SPEED =4BULLET_SPEED =8ENEMY_SPEED =2ENEMY_FIRE_RATE =0.02#每帧敌人开火的概率PLAYER_FIRE_RATE =15#玩家发射子弹的最小帧间隔WALL_SIZE =30GRID_SIZE =20MAX_ENEMIES =5MAX_AMMO =20ENEMY_RESPAWN_TIME =300#敌人重生时间(帧)classTank:def__init__(self,x,y,color,is_player=False):self.x =xself.y =yself.color =colorself.width =40self.height =40self.direction =00:上, 1:右, 2:下, 3:左self.is_player =is_playerself.health =3ifis_playerelse1self.cooldown =0self.alive =Trueself.speed = PLAYER_SPEEDifis_playerelseENEMY_SPEED
defmove(self,dx,dy,walls):#计算新位置new_x=self.x +dxnew_y=self.y +dy#创建坦克的矩形tank_rect= pygame.Rect(new_x,new_y,self.width,self.height)#检查是否与墙壁碰撞can_move=Trueforwallinwalls:iftank_rect.colliderect(wall.rect)andwall.destructible:can_move=Falsebreak#检查边界ifnew_x<0ornew_x+self.width > WIDTHornew_y<0ornew_y+self.height > HEIGHT:can_move=False#如果可移动,则更新位置ifcan_move:self.x =new_xself.y =new_y#更新方向ifdx>0:self.direction =1elifdx<0:self.direction =3elifdy>0:self.direction =2elifdy<0:self.direction =0defupdate(self,walls):returnifself.cooldown >0:self.cooldown -=1#敌人AIif notself.is_playerandself.alive:#简单AI:随机移动和开火move_choice= random.randint(0,30)self.move(-self.speed,0,walls)self.move(self.speed,0,walls)self.move(0, -self.speed,walls)self.move(0,self.speed,walls)#随机开火ifrandom.random() < ENEMY_FIRE_RATEandself.cooldown ==0:return Nonedeffire(self):return Noneself.cooldown = PLAYER_FIRE_RATEifself.is_playerelse30#根据坦克方向计算子弹起始位置ifself.direction ==0:#上bullet_x=self.x +self.width //2bullet_y=self.ydx,dy=0, -1elifself.direction ==1:#右bullet_x=self.x +self.widthbullet_y=self.y +self.height //2dx,dy=1,0elifself.direction ==2:#下bullet_x=self.x +self.width //2bullet_y=self.y +self.heightdx,dy=0,1else:#左bullet_x=self.xbullet_y=self.y +self.height //2dx,dy= -1,0returnBullet(bullet_x,bullet_y,dx,dy,self.is_player)self.health -=1ifself.health <=0:self.alive =Falsereturn Truereturn Falsedefdraw(self,surface):return#绘制坦克主体tank_rect= pygame.Rect(self.x,self.y,self.width,self.height)pygame.draw.rect(surface,self.color,tank_rect)pygame.draw.rect(surface, WHITE,tank_rect,2)#绘制坦克炮管ifself.direction ==0:#上pygame.draw.rect(surface, DARK_GREEN,(self.x +self.width //2-5,self.y -15,10,20))elifself.direction ==1:#右pygame.draw.rect(surface, DARK_GREEN,(self.x +self.width,self.y +self.height //2-5,20,10))elifself.direction ==2:#下pygame.draw.rect(surface, DARK_GREEN,(self.x +self.width //2-5,self.y +self.height,10,20))else:#左pygame.draw.rect(surface, DARK_GREEN,(self.x -20,self.y +self.height //2-5,20,10))#绘制生命值(仅对玩家)ifself.is_player:foriinrange(self.health):pygame.draw.rect(surface, RED,(self.x +i*12,self.y -15,10,10))def__init__(self,x,y,dx,dy,is_player):self.x =xself.y =yself.dx =dxself.dy =dyself.radius =5self.speed = BULLET_SPEEDself.is_player =is_playerself.color = YELLOWifis_playerelseREDself.alive =Truedefupdate(self):self.x +=self.dx *self.speedself.y +=self.dy *self.speed#检查是否出界if(self.x <0orself.x > WIDTHorself.y <0orself.y > HEIGHT):self.alive =Falsedefdraw(self,surface):pygame.draw.circle(surface,self.color, (int(self.x),int(self.y)),self.radius)pygame.draw.circle(surface, WHITE, (int(self.x),int(self.y)),self.radius,1)returnpygame.Rect(self.x -self.radius,self.y -self.radius,self.radius *2,self.radius *2)def__init__(self,x,y,destructible=True):self.x =xself.y =yself.destructible =destructibleself.color = BROWNifdestructibleelseGRAYself.rect = pygame.Rect(x,y, WALL_SIZE, WALL_SIZE)self.alive =Truedefdraw(self,surface):pygame.draw.rect(surface,self.color,self.rect)pygame.draw.rect(surface, WHITE,self.rect,1)#添加纹理ifself.destructible:pygame.draw.line(surface, (139,69,19),(self.x + WALL_SIZE -5,self.y + WALL_SIZE -5),2)pygame.draw.line(surface, (139,69,19),(self.x + WALL_SIZE -5,self.y +5),(self.x +5,self.y + WALL_SIZE -5),2)#不可破坏的墙壁有网格纹理foriinrange(0, WALL_SIZE,5):pygame.draw.line(surface, (100,100,100),(self.x + WALL_SIZE,self.y +i),1)self.player = Tank(WIDTH //2, HEIGHT -100, BLUE,True)self.score =0self.level =1self.ammo = MAX_AMMOself.game_over =Falseself.game_won =Falseself.enemy_respawn_timer =0self.generate_walls()#创建边界墙forxinrange(0, WIDTH, WALL_SIZE):self.walls.append(Wall(x,0,False))self.walls.append(Wall(x, HEIGHT - WALL_SIZE,False))foryinrange(WALL_SIZE, HEIGHT - WALL_SIZE, WALL_SIZE):self.walls.append(Wall(0,y,False))self.walls.append(Wall(WIDTH - WALL_SIZE,y,False))#创建随机可破坏墙壁for_inrange(30):x= random.randint(3, (WIDTH // WALL_SIZE) -4) * WALL_SIZEy= random.randint(3, (HEIGHT // WALL_SIZE) -4) * WALL_SIZE#确保不在玩家起始位置周围生成墙if(abs(x-self.player.x) >100orabs(y-self.player.y) >100):self.walls.append(Wall(x,y,True))#创建一些不可破坏的障碍物for_inrange(10):x= random.randint(2, (WIDTH // WALL_SIZE) -3) * WALL_SIZEy= random.randint(2, (HEIGHT // WALL_SIZE) -3) * WALL_SIZEif(abs(x-self.player.x) >100orabs(y-self.player.y) >100):self.walls.append(Wall(x,y,False))defspawn_enemies(self,count):x= random.randint(50, WIDTH -50)y= random.randint(50, HEIGHT //2)#确保敌人不与玩家太近if(abs(x-self.player.x) >200orabs(y-self.player.y) >200):breakself.enemies.append(Tank(x,y, RED))foreventinpygame.event.get():ifevent.type == pygame.QUIT:return Falseifevent.type == pygame.KEYDOWN:ifevent.key == pygame.K_ESCAPE:return Falseifself.game_overorself.game_won:ifevent.key == pygame.K_r:self.__init__()#重置游戏elifevent.key == pygame.K_SPACEandself.ammo >0:bullet=self.player.fire()self.bullets.append(bullet)self.ammo -=1if notself.game_overand notself.game_won:keys= pygame.key.get_pressed()dx,dy=0,0ifkeys[pygame.K_LEFT]orkeys[pygame.K_a]:ifkeys[pygame.K_RIGHT]orkeys[pygame.K_d]:ifkeys[pygame.K_UP]orkeys[pygame.K_w]:ifkeys[pygame.K_DOWN]orkeys[pygame.K_s]:#对角线移动速度修正ifdx!=0anddy!=0:dx*=0.7071dy*=0.7071self.player.move(dx,dy,self.walls)return Truedefupdate(self):ifself.game_overorself.game_won:return#更新玩家self.player.update(self.walls)#更新敌人forenemyinself.enemies[:]:bullet=enemy.update(self.walls)self.bullets.append(bullet)#更新子弹forbulletinself.bullets[:]:#检查子弹是否击中墙壁forwallinself.walls[:]:ifwall.aliveandbullet.get_rect().colliderect(wall.rect):bullet.alive =Falseifwall.destructible:wall.alive =Falseself.walls.remove(wall)break#检查子弹是否击中玩家if notbullet.is_playerandbullet.alive:player_rect= pygame.Rect(self.player.x,self.player.y,self.player.width,self.player.height)ifbullet.get_rect().colliderect(player_rect)andself.player.alive:bullet.alive =Falseifself.player.take_damage():self.game_over =True#检查子弹是否击中敌人ifbullet.is_playerandbullet.alive:forenemyinself.enemies[:]:enemy_rect= pygame.Rect(enemy.x,enemy.y,enemy.width,enemy.height)ifbullet.get_rect().colliderect(enemy_rect)andenemy.alive:bullet.alive =Falseifenemy.take_damage():self.enemies.remove(enemy)self.score +=100#移除已消失的子弹if notbullet.aliveandbulletinself.bullets:self.bullets.remove(bullet)#敌人重生逻辑iflen(self.enemies) < MAX_ENEMIES:self.enemy_respawn_timer +=1ifself.enemy_respawn_timer >= ENEMY_RESPAWN_TIME:self.enemy_respawn_timer =0#检查关卡完成iflen(self.enemies) ==0andself.level <3:self.level +=1self.spawn_enemies(self.level +2)self.player.health =min(3,self.player.health +1)#检查游戏胜利ifself.level >=3andlen(self.enemies) ==0:self.game_won =Truedefdraw(self,surface):#绘制网格背景forxinrange(0, WIDTH, GRID_SIZE):pygame.draw.line(surface, (20,20,20), (x,0), (x, HEIGHT))foryinrange(0, HEIGHT, GRID_SIZE):pygame.draw.line(surface, (20,20,20), (0,y), (WIDTH,y))#绘制墙壁forwallinself.walls:#绘制子弹forbulletinself.bullets:#绘制敌人forenemyinself.enemies:#绘制玩家self.player.draw(surface)#绘制UIself.draw_ui(surface)#绘制游戏结束/胜利画面ifself.game_over:self.draw_game_over(surface)self.draw_game_won(surface)defdraw_ui(self,surface):#分数font= pygame.font.SysFont(None,36)score_text=font.render(f"score:{self.score}",True, GREEN)surface.blit(score_text, (10,10))#等级level_text=font.render(f"level:{self.level}/3",True, GREEN)surface.blit(level_text, (10,50))#弹药ammo_text=font.render(f"bullet:{self.ammo}/{MAX_AMMO}",True, YELLOW)surface.blit(ammo_text, (WIDTH -150,10))#敌人数量enemies_text=font.render(f"enemy:{len(self.enemies)}",True, RED)surface.blit(enemies_text, (WIDTH -150,50))#控制说明controls_font= pygame.font.SysFont(None,24)"Controls: WASD/Arrow Keys to move, Space to fire","Objective: Eliminate all enemies (3 levels)","Note: Limited ammunition, can destroy brown walls"]fori,textinenumerate(controls):control_text=controls_font.render(text,True, WHITE)surface.blit(control_text, (WIDTH //2-150,10+i*25))defdraw_game_over(self,surface):overlay= pygame.Surface((WIDTH, HEIGHT), pygame.SRCALPHA)overlay.fill((0,0,0,200))surface.blit(overlay, (0,0))font_large= pygame.font.SysFont(None,72)font_medium= pygame.font.SysFont(None,36)game_over_text=font_large.render("game over",True, RED)surface.blit(game_over_text, (WIDTH //2-game_over_text.get_width() //2, HEIGHT //2-100))score_text=font_medium.render(f"finial score:{self.score}",True, WHITE)surface.blit(score_text, (WIDTH //2-score_text.get_width() //2, HEIGHT //2))restart_text=font_medium.render("Press R to restart",True, GREEN)surface.blit(restart_text, (WIDTH //2-restart_text.get_width() //2, HEIGHT //2+100))quit_text=font_medium.render("Press ESC to exit",True, YELLOW)surface.blit(quit_text, (WIDTH //2-quit_text.get_width() //2, HEIGHT //2+150))defdraw_game_won(self,surface):overlay= pygame.Surface((WIDTH, HEIGHT), pygame.SRCALPHA)overlay.fill((0,0,0,200))surface.blit(overlay, (0,0))font_large= pygame.font.SysFont(None,72)font_medium= pygame.font.SysFont(None,36)win_text=font_large.render("Victory!",True, GREEN)surface.blit(win_text, (WIDTH //2-win_text.get_width() //2, HEIGHT //2-100))score_text=font_medium.render(f"Final Score:{self.score}",True, WHITE)surface.blit(score_text, (WIDTH //2-score_text.get_width() //2, HEIGHT //2))restart_text=font_medium.render("Press R to restart",True, GREEN)surface.blit(restart_text, (WIDTH //2-restart_text.get_width() //2, HEIGHT //2+100))quit_text=font_medium.render("Press ESC to exit",True, YELLOW)surface.blit(quit_text, (WIDTH //2-quit_text.get_width() //2, HEIGHT //2+150))clock= pygame.time.Clock()running=Truewhilerunning:running=game.handle_events()