pygame实现的坦克大战

pygame实现的坦克大战


博主不久前做了一道题,要求用pygame实现坦克大战的简易游戏。由于是第一次上手,python也只会基本的操作,class什么的都还不会,所以写的非常复杂,挂在这里算是一个留念,也希望能帮到有需要的朋友,代码很长,放在最后。

库与环境

使用了pygame,math库,在Python 3.7 64-bit下编写

游戏要求

能实现独立控制两个坦克,坦克运动尽量与现实相符。相撞视为平局,不能超出边界。其中一个坦克可以作为NPC自动与玩家战斗,自动向玩家坦克移动并攻击。

思路介绍

以其中一个坦克(不妨叫做tank1)为例,因为要求坦克的行动尽量符合现实,描述坦克状态最少要三个量,分别是 x 坐标,y 坐标,坦克朝向的角度。我的思路是通过中间小量δs1 和δθ来更新角度,这样的话一直按住按键坦克也会有移动,而不是一按一动。按下按键和松开按键会更改△s1 和△θ的值,通过这两个量的累加可以实时计算出 x 坐标和 y 坐标,以及角度,当然在判定撞墙时 x、y 的累加不进行,只进行角度的累加。炮弹也有这三个量,由于做直线运动,x,y 坐标只要确定了初始的变化量就可以一直重复累加,而方向恒等于射出时坦克的方向。对于 NPC模式,用 if 语句屏蔽了通过键盘对于 tank2 的操控,计算 tank2 和 tank1 的距离和角度,让 tank2 一直向 tank1 的方向旋转,同时一直向前移动,并不断调用开炮代码。

代码

import pygame
from pygame.locals import *
import math

#判断是否撞墙,不撞返回True
def wall(a,add,total_a):
    if ((a + add) < total_a-50 and (a + add) > 0):
        return True
    else:
        return False

#判断是否碰撞
def beat(location1,location2,judge=30):
    #judge是碰撞判定边界值,假如两个坐标的差小于这个值,返回True
    if math.fabs(location1-location2)<judge:
        return True
    else:
        return False

def reset():        #一个回合结束,位置重置
    global x1, x2, y1, y2, theta1, theta2, ifshoot1, ifshoot2
    theta1, theta2 = 0, 180
    x1, y1 = 100, 100
    x2, y2 = total_X - 100, total_Y - 100
    ifshoot1, ifshoot2 = False, False


#设置资源文件路径
bg_image_filename="file\\background.png"
tank1_image_filename="file\\tank1.png"
tank2_image_filename="file\\tank2.png"
bullte_image_filename="file\\shot.gif"
bgm_filename="file\\bgm.wav"
sound_shoot_filename="file\\shoot.wav"
sound_boom_fliename="file\\boom.wav"


pygame.init()                                       #初始化设置
pygame.mixer.init()                                 #初始化混响器
my_font=pygame.font.Font(None,30)                   #初始化字体
ifai=False                                          #是否开启NPC模式
total_X,total_Y = 800,600                          #设置场地总大小
width_of_line=20                                    #设置显示的边界线的宽度

#初始化tank1
x1, y1 = 100, 100                       #x1,y1是tank1的实时位置
deltas1 = 0                             #deltas1是检测到的移动距离
deltatheta1 = 0                         #deltatheta1是转动角度
theta1 = 0                              #theta1是实时角度
x1_move, y1_move = 0, 0                 #x1_move和y1_move是通过前两者算出的转化后的坐标变化量
mark1 = 0                               #tank1的得分
#初始化tank2,属性同上
x2, y2 = total_X-100, total_Y-100
deltas2=0
deltatheta2=0
theta2=180
x2_move,y2_move=0,0
mark2 = 0

distance=0                      #NPC模式时计算tank1和tank2的距离,用来做算角度的中间量
angle=0                         #NPC模式时得到的坦克间角度(弧度制)
sina=0                          #NPC模式时得到的sin值
cosa=0                          #NPC模式时得到的cos值



#下面是对于炮弹的初始化
ifshoot1,ifshoot2=False,False                                   #是否开炮
x_shoot1,y_shoot1,x_shoot2,y_shoot2=-100,-100,-100,-100         #炮弹位置
theta_of_shoot1,theta_of_shoot2=0,0                             #炮弹朝向
xshoot1_move,yshoot1_move,xshoot2_move,yshoot2_move=0,0,0,0     #炮弹位置变化量


#导入文件并校准图片方向
background = pygame.image.load(bg_image_filename)                           #导入背景
tank1_image = pygame.image.load(tank1_image_filename)                       #导入tank1图片
tank2_image = pygame.image.load(tank2_image_filename)                       #导入tank2图片
bullet_origin_image = pygame.image.load(bullte_image_filename)              #导入子弹图片,作为子弹1和子弹2的模板
bullet_origin_image = pygame.transform.rotate(bullet_origin_image,180)      #校准子弹方向
tank1_image=pygame.transform.rotate(tank1_image,-90)                        #导入tank1图片
tank2_image=pygame.transform.rotate(tank2_image,-90)                        #导入tank2图片
tank1_image_rotation=pygame.transform.rotate(tank1_image,theta1)            #校准tank1方向
tank2_image_rotation=pygame.transform.rotate(tank2_image,theta2)            #校准tank2方向
pygame.mixer.music.load(bgm_filename)                                       #导入BGM
sound_boom=pygame.mixer.Sound(sound_boom_fliename)                          #导入爆炸声
sound_shoot=pygame.mixer.Sound(sound_shoot_filename)                        #导入射击声
bullet1_image=bullet_origin_image                                           #初始化子弹1图像
bullet2_image=bullet_origin_image                                           #初始化子弹2图像

screen=pygame.display.set_mode((total_X,total_Y),pygame.RESIZABLE)          #创建一个可变的窗口
pygame.display.set_caption("坦克大战")                                       #创建标题
white=(255,255,255)                                                         #定义白色,画线用
pygame.mixer.music.play(-1)                                                 #放BGM
ifbgm=True                                                                  #定义BGM状态

#游戏主体

while True:
    text_image = my_font.render("ai_control:I    bgm_control:O   mark1=%d    mark2=%d "%(mark1,mark2), True, white)         #显示说明和分数
    for event in pygame.event.get():

        if event.type == QUIT:                  #控制程序退出
            exit()

        if event.type == KEYDOWN:               #从键盘获得操作
            if event.key == K_o:                #关闭或开启背景音乐
                ifbgm = not ifbgm
                if ifbgm:
                    pygame.mixer.music.play(-1)
                else:
                    pygame.mixer_music.stop()

            if event.key == K_i:                #关闭或开启NPC模式
                deltas2 = 0
                deltatheta2 = 0
                ifai = not ifai
                if ifai:
                    tank2_image = pygame.transform.rotate(tank2_image,-90)      #图片方向校准
                else:
                    tank2_image = pygame.transform.rotate(tank2_image,+90)      #图片方向校准

            if event.key == K_w:
                deltas1 = 1
            if event.key == K_s:
                deltas1 = -1
            if event.key == K_d:
                deltatheta1 = -1
            if event.key == K_a:
                deltatheta1 = 1
            if event.key == K_g:
                if not ifshoot1:                #限制一次只能打出一颗子弹
                    ifshoot1 = True
                    x_shoot1, y_shoot1 = x1+30, y1+30
                    bullet1_image = pygame.transform.rotate(bullet_origin_image, theta1)
                    xshoot1_move = math.sin(math.radians(theta1))
                    yshoot1_move = math.cos(math.radians(theta1))
                    sound_shoot.play()

            if not ifai:                        #假如没有开启NPC模式,则接收来自键盘对于tank2的控制
                if event.key == K_UP:
                    deltas2=1
                if event.key == K_DOWN:
                    deltas2=-1
                if event.key == K_RIGHT:
                    deltatheta2 = -1
                if event.key == K_LEFT:
                    deltatheta2 = 1
                if event.key == K_SLASH:
                    if not ifshoot2:
                        ifshoot2 =True
                        x_shoot2,y_shoot2=x2+30,y2+30
                        bullet2_image=pygame.transform.rotate(bullet_origin_image,theta2)
                        xshoot2_move=math.sin(math.radians(theta2))
                        yshoot2_move=math.cos(math.radians(theta2))
                        sound_shoot.play()

        elif event.type == KEYUP:
            if event.key == K_w:
                deltas1=0
            if event.key == K_s:
                deltas1=0
            if event.key == K_d:
                deltatheta1 = 0
            if event.key == K_a:
                deltatheta1 = 0
            if not ifai:
                if event.key == K_UP:
                    deltas2=0
                if event.key == K_DOWN:
                    deltas2=0
                if event.key == K_RIGHT:
                    deltatheta2 = 0
                if event.key == K_LEFT:
                    deltatheta2 = 0

    if ifai:
        distance = math.sqrt(pow(x1 - x2, 2) + pow(y1 - y2, 2))     #两点距离公式
        sina = (y1 - y2) / distance
        cosa = (x2 - x1) / distance
        angle = math.atan2(y2 - y1, x2 - x1)                        #两坦克之间线段的弧度值
        theta2 = math.degrees(-angle)                               #弧度转角度
        x2, y2 = (x2 - cosa, y2 + sina)
        #开炮
        if not ifshoot2:
            ifshoot2=True
            x_shoot2, y_shoot2 = x2 + 30, y2 + 30
            bullet2_image = pygame.transform.rotate(bullet_origin_image, theta2-90)
            xshoot2_move = -cosa    #math.sin(math.radians(theta2))
            yshoot2_move = sina         #math.cos(math.radians(theta2))
            sound_shoot.play()

    #tank1的位置和方向的变化
    theta1+=deltatheta1
    x1_move=math.sin(math.radians(theta1))*deltas1
    y1_move=math.cos(math.radians(theta1))*deltas1

    #tank2的位置和方向的变化
    theta2+=deltatheta2
    x2_move=math.sin(math.radians(theta2))*deltas2
    y2_move=math.cos(math.radians(theta2))*deltas2

    #tank撞墙判定,为降低tank1难度,tank1速度更快
    if wall(x1,x1_move,total_X):
        x1 += x1_move*1.5
    if wall(y1,y1_move,total_Y):
        y1 += y1_move*1.5

    if wall(x2,x2_move,total_X):
        x2 += x2_move
    if wall(y2,y2_move,total_Y):
        y2 += y2_move

    #tank1子弹维持飞行
    if ifshoot1==True:
        x_shoot1+=xshoot1_move*2
        y_shoot1+=yshoot1_move*2
        #发射的子弹撞墙判定
        if(not wall(x_shoot1,0,total_X) or not wall(y_shoot1,0,total_Y)):
            ifshoot1=False
    else:
        x_shoot1,y_shoot1=-100,-100

    #tank2子弹维持飞行
    if ifshoot2==True:
        x_shoot2+=xshoot2_move*2
        y_shoot2+=yshoot2_move*2
        #发射的子弹撞墙判定
        if(not wall(x_shoot2,0,total_X) or not wall(y_shoot2,0,total_Y)):
            ifshoot2=False
    else:
        x_shoot2,y_shoot2=-100,-100

    #tank1子弹命中tank2判定
    if beat(x_shoot1,x2) and beat(y_shoot1,y2):
        reset()
        sound_boom.play()
        mark1+=1
        print("player2 die!!")

    #tank2子弹命中tank1判定
    if beat(x_shoot2,x1) and beat(y_shoot2,y1):
        reset()
        sound_boom.play()
        mark2+=1
        print("player1 die!!")

    #相撞判定,平局不计分
    if beat(x1,x2,50) and beat(y1,y2,50):
        print("equal")
        reset()
        sound_boom.play()

    #刷新
    screen.blit(background,(0,0))                                           #显示背景
    tank1_image_rotation=pygame.transform.rotate(tank1_image,theta1)        #tank1旋转
    tank2_image_rotation=pygame.transform.rotate(tank2_image,theta2)        #tank2旋转
    screen.blit(text_image,(100,0))                                       #打印字
    screen.blit(tank1_image_rotation,(x1,y1))                               #显示tank1
    screen.blit(tank2_image_rotation,(x2,y2))                               #显示tank2
    screen.blit(bullet1_image, (x_shoot1, y_shoot1))                        #显示子弹1
    screen.blit(bullet2_image,(x_shoot2,y_shoot2))                          #显示子弹2
    pygame.display.update()

其中的图片分别是
tank1
子弹
tank2
也许是背景
至于音频,相信对程序的实现没有什么太大关系,假如没有的话直接把声音的那些代码删掉便是。假如出现了一些看起来没什么用的变量,那可能是我忘记删了,应该不影响阅读。

  • 2
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值