飞机大战系列 Space War 1.0(开源)

经过我为期一周的开发与完善,现在我正式宣布Space War1.0已经完成了

现在网上能找到的那些飞机大战都是飞机只能左右移动,子弹只能向上发射的无聊版本。

而这一款Space War1.0则与之截然不同。飞机选择了歼16的图片(因为我喜欢歼16),而且机头能跟随鼠标以固定角速度旋转,飞机能上下左右全屏移动,且有微微的加速减速使移动操作更为丝滑。子弹呈长条形,按鼠标左键从机头射出,且子弹角度与飞机一致,弹夹打空后过三秒会自动填满。敌机对象是红色正方形,可以用子弹摧毁或用飞机撞毁(但会使自己收到敌机剩余血量的伤害),若摧毁所有敌机之后,则显示胜利,自身血量为零则显示失败。在游戏开始前,按P键开始,期间仍可用P键实现暂停。按C键退出游戏,当然也可直接叉掉界面以关闭游戏。在游戏胜利或失败后可以按O键重新开始。

Space War1.0 2024-07-14

话不多说,先给代码和资源文件,自己体验吧。之后我会慢慢更新这篇文章,分模块对代码进行解析。(资源文件在最下面)

# -*- coding: utf-8 -*-
"""
Created on Tue May  7 15:33:31 2024

@author: yq1215
"""

import pygame
import sys
import math
import time
import random
from pygame.locals import *
import winsound

#定义Bullet(子弹)类
class Bullet(pygame.sprite.Sprite):
    def __init__(self, screen, x, y, angle, damage):
        pygame.sprite.Sprite.__init__(self)
        #self.image = self.image1 = pygame.Surface((10, 1))  # 用一个简单的矩形代替子弹图片  
        #self.image.fill((255, 255, 0))  # 设置为黄色  
        self.image = self.image1 = pygame.image.load("bullet.png")
        self.rect = self.image.get_rect()  
        self.rect.center = (x, y)  
        self.screen = screen
        self.angle = angle
        self.image = pygame.transform.rotate(self.image1,self.angle)
        self.rect = self.image.get_rect(center=self.rect.center)
        self.vel = 15
        self.x =  x
        self.y =  y
        self.damage = damage
    def display(self):  #显示子弹图片的方法
        self.screen.blit(self.image, (self.x, self.y))  #将创建的子弹图片按设定的坐标贴到窗口中
    def move(self): #子弹移动方法
        self.x += self.vel*math.cos(math.radians(self.angle))
        self.y -= self.vel*math.sin(math.radians(self.angle))
    def judge(self):    #判断子弹是否出界
        if self.y<0 or self.y>1200 or self.x<0 or self.x>1800:  
            return True
        else:
            return False
    def judge1(self, enemy_list):   #判断子弹是否击中敌机
        flag = False
        for enemy in enemy_list:
            if   (self.x>enemy.rect.centerx-10 and      
                 self.x<enemy.rect.centerx+10 and
                 self.y>enemy.rect.centery-10 and
                 self.y<enemy.rect.centery+10):  
                winsound.Beep(500,1)
                enemy.health -= self.damage
                flag = True
        return flag

#定义Plane(飞机)类
class Plane(pygame.sprite.Sprite):  
    def __init__(self, screen, image, x, y, max_health, max_bullet):  
        pygame.sprite.Sprite.__init__(self)
        self.image = image
        self.image1 = self.image  #保留原图
        self.rect = self.image.get_rect()  
        self.rect.topleft = (x, y)  
        self.screen = screen
        self.score = 0       #当前得分
        
        self.acceleration = 0.2  
        self.max_speed = 8  
        self.vel_x = 0  
        self.vel_y = 0 
        self.angular_speed = 2  #角速度
        self.current_angle = 0
        
        self.max_health = max_health  
        self.health = max_health  # 初始生命值等于最大生命值
        self.health_bar = HealthBar(screen, 300, 80, 200, 20, max_health)  # 创建血条对象
        
        self.bullet_list = []   #存储发射出去的子弹对象引用
        self.bullet_clip = max_bullet  # 弹夹容量  
        self.bullets_in_clip = self.bullet_clip  # 当前弹夹内子弹数量  
        self.reloading = False  # 是否正在填充子弹  
        self.reload_start_time = 0  # 开始填充子弹的时间点  
        self.reload_time = 3  # 填充子弹所需时间 
        self.bullet_clip_bar = HealthBar(screen, 300, 105, 200, 20, max_bullet)  #创建血条对象显示弹夹条
  
    def move(self, keys):   #移动
        if keys[K_w] and self.rect.top <= 0:
            self.vel_y = 0
        elif keys[K_w] and self.rect.top > 0:  
            if self.vel_y > -self.max_speed:  
                self.vel_y -= self.acceleration  
        elif self.vel_y < 0:  
            self.vel_y += self.acceleration  
            if self.vel_y > -self.acceleration:  
                self.vel_y = 0   

        if keys[K_s] and self.rect.bottom >= 1200:
            self.vel_y = 0
        elif keys[K_s] and self.rect.bottom < 1200:  
            if self.vel_y < self.max_speed:  
                self.vel_y += self.acceleration  
        elif self.vel_y > 0:  
            self.vel_y -= self.acceleration  
            if self.vel_y < self.acceleration:  
                self.vel_y = 0 

        if keys[K_a] and self.rect.left <= 0:
            self.vel_x = 0
        elif keys[K_a] and self.rect.left > 0:  
            if self.vel_x > -self.max_speed:  
                self.vel_x -= self.acceleration  
        elif self.vel_x < 0:  
            self.vel_x += self.acceleration  
            if self.vel_x > -self.acceleration:  
                self.vel_x = 0  

        if keys[K_d] and self.rect.right >= 1800:
            self.vel_x = 0
        elif keys[K_d] and self.rect.right < 1800:  
            if self.vel_x < self.max_speed:  
                self.vel_x += self.acceleration  
        elif self.vel_x > 0:  
            self.vel_x -= self.acceleration  
            if self.vel_x < self.acceleration:  
                self.vel_x = 0
  
        self.rect.x += int(self.vel_x)  
        self.rect.y += int(self.vel_y)  
        
    def zhuandong(self):   #转动
        target_angle = get_angle(self.rect.centerx,self.rect.centery,mouse_x,mouse_y)
        self.current_angle = rotate_towards_mouse(self.current_angle,target_angle,self.angular_speed)
        self.image = rotated_img = pygame.transform.rotate(self.image1,self.current_angle)
        self.rect = new_rect = rotated_img.get_rect(center=self.rect.center)
       
    def fire(self):   #存储发射子弹的方法
        if not self.reloading and self.bullets_in_clip > 0: 
            winsound.Beep(5000,1)
            self.bullet_list.append(Bullet(self.screen, 
                                       self.rect.centerx+24*math.cos(math.radians(self.current_angle)), 
                                       self.rect.centery-24*math.sin(math.radians(self.current_angle)), 
                                       self.current_angle, 1)) #将发射的子弹对象存储到bullet_list中
            self.bullets_in_clip -= 1  
            if self.bullets_in_clip == 0:  
                self.start_reload()
    
    def show_bullet(self, enemy_list, enemy_out):   #显示子弹
        for bullet in self.bullet_list:
            bullet.display()    #显示子弹
            bullet.move()   #移动子弹
            if bullet.judge() or bullet.judge1(enemy_list):#判断子弹是否越界或触碰敌机
                self.bullet_list.remove(bullet) #将子弹从bullet_list中删除 
                # 这里可以添加一些处理子弹消失的操作  
            for enemy in enemy_list:
                if enemy.health <= 0:  
                    enemy_out.append(enemy)
                    enemy_list.remove(enemy)
                    self.score += 1            #用子弹摧毁敌机才会加分,撞毁不加分
            
    def start_reload(self):    #开始装弹的时间
        self.reloading = True  
        self.reload_start_time = time.time()  
  
    def update_reload(self):   #装弹
        if self.reloading:  
            current_time = time.time()  
            if current_time - self.reload_start_time >= self.reload_time:  
                self.bullets_in_clip = self.bullet_clip  
                self.reloading = False 
                
    def take_damage(self, damage):    #受到伤害
        self.health -= damage  
        if self.health < 0:  
            self.health = 0  # 生命值不会小于0  
  
    def heal(self, heal_amount):      #治愈回血
        self.health += heal_amount  
        if self.health > self.max_health:  
            self.health = self.max_health  # 生命值不会超过最大生命值
            
    def collision(self, enemy_list, enemy_out):   #判断是否与敌机发生碰撞
        flag = False
        for enemy in enemy_list:
            if   (self.rect.centerx>enemy.rect.centerx-20 and      
                 self.rect.centerx<enemy.rect.centerx+20 and
                 self.rect.centery>enemy.rect.centery-20 and
                 self.rect.centery<enemy.rect.centery+20):  
                winsound.Beep(300,2)
                self.take_damage(enemy.health)
                enemy.health = 0
                enemy_out.append(enemy)
                enemy_list.remove(enemy)
                flag = True
        return flag

#定义Enemy(敌机)类
class Enemy(pygame.sprite.Sprite):
    def __init__(self, screen, image, x, y, max_health):
        pygame.sprite.Sprite.__init__(self)
        self.screen = screen
        self.image = image 
        self.image1 = self.image  #保留原图
        self.rect = self.image.get_rect()  
        self.rect.topleft = (x, y)  

        self.acceleration = 0.2  
        self.max_speed = 5  
        self.vel_x = 5     
        self.vel_y = 5   
        
        self.max_health = max_health  
        self.health = max_health  # 初始生命值等于最大生命值
        
    def update(self):
        if self.rect.left < 0 or self.rect.right > 1800:
            self.vel_x = -self.vel_x
        if self.rect.top < 0 or self.rect.bottom > 1200:
            self.vel_y = - self.vel_y
        self.rect.left += self.vel_x
        self.rect.top -= self.vel_y
        self.screen.blit(enemy.image, enemy.rect.topleft)
        # 创建并实时更新血条对象位置
        self.health_bar = HealthBar(screen, self.rect.left-5, self.rect.top-10, 30, 3, self.max_health)
        self.health_bar.draw((0,0,0),(0,255,0),self.health)
        
#血条类
class HealthBar(pygame.sprite.Sprite):  
    def __init__(self, screen, x, y, width, height, max_health):  
        pygame.sprite.Sprite.__init__(self)
        self.screen = screen  
        self.x = x  
        self.y = y  
        self.width = width  
        self.height = height  
        self.max_health = max_health  
  
    def draw(self, color_bottom, color_up, current_health):  
        # 计算血条长度  
        bar_length = (current_health / self.max_health) * self.width  
        # 绘制红色底条  
        pygame.draw.rect(self.screen, color_bottom, (self.x, self.y, self.width, self.height))  
        # 绘制绿色血条  
        pygame.draw.rect(self.screen, color_up, (self.x, self.y, bar_length, self.height)) 
    
        
#获取角度
def get_angle(x1,y1,x2,y2):
    dx = x2 - x1
    dy = y2 - y1
    return math.degrees(math.atan2(-dy,dx))

#更新当前角度 current_angle
def rotate_towards_mouse(current_angle,target_angle,angular_speed):
    angle_diff = target_angle - current_angle
    if angle_diff > 180:
        angle_diff -= 360
    elif angle_diff < -180:
        angle_diff += 360
    if angle_diff > 0:
        return (current_angle + min(angular_speed,angle_diff)) % 360
    elif angle_diff < 0:
        return (current_angle - min(angular_speed,-angle_diff)) % 360
    else:
        return current_angle

#显示文字
def show_text(surface_handle, pos, text, color, font_bold=False, font_size=13, font_italic=False):
    cur_font = pygame.font.SysFont("宋体",font_size) #获取系统字体
    #cur_font = pygame.font.Font('simhei.ttf',30)
    cur_font.set_bold(font_bold)                     #设置是否加粗
    cur_font.set_italic(font_italic)                 #设置是否斜体
    text_fmt = cur_font.render(text, 1, color)       #设置文字内容
    surface_handle.blit(text_fmt, pos)               #绘制文字

#初始化
pygame.init()
clock = pygame.time.Clock()
pygame.mouse.set_visible(True)  # 显示鼠标
screen = pygame.display.set_mode((1800, 1200))  #创建屏幕对象
pygame.display.set_caption("Space War 1.0")
paused = True  # 初始化游戏为非暂停状态
#创建Plane对象plane1
plane_image = pygame.image.load("歼16.png")  
plane_image = pygame.transform.scale(plane_image,(72,48))
plane1 = Plane(screen,plane_image,800,600,50,300)
#plane1.image = plane1.image1 = pygame.transform.scale(plane1.image,(72,48))  #可以将角色缩放
# 敌机总数
enemy_number = 20 
# 被击毁和撞毁的敌机列表
enemy_out = [] 
# 当前屏幕最大敌机数量  
max_enemy_count = 8 
# 创建一个空的敌机列表  
Enemy_list = []  
# 控制敌机对象添加的帧数间隔  
frame_count = 0  
add_enemy_interval = 60  # 每隔60帧添加一个敌机对象

screen.fill((0, 204, 255))  # 用纯色填充窗口
# 显示文字
text_pos = u"WELCOME TO SPACE WAR"
show_text(screen, (450,500), text_pos, (255,0,0), True, 100)
text_pos = u"Please press the 'P' to start or stop the game"
show_text(screen, (550,600), text_pos, (0,0,0), True, 50)
text_pos = u"Press the 'C' will close the game"
show_text(screen, (640,650), text_pos, (0,0,0), True, 50)
pygame.display.update() #更新屏幕
#主循环
while True:
    clock.tick(60) # 每秒不超过60帧的速度运行
    for event in pygame.event.get():
        if event.type == QUIT:
            screen.fill((0, 204, 255))  # 用纯色填充窗口
            text_pos = u"GOOD BYE"
            show_text(screen, (700,500), text_pos, (255,0,0), True, 100)
            pygame.display.update() #更新屏幕
            time.sleep(1)
            pygame.quit()
            sys.exit()
        if event.type == pygame.KEYDOWN:  
            if event.key == pygame.K_c: # 按下 "C" 键关闭游戏
                screen.fill((0, 204, 255))  # 用纯色填充窗口
                text_pos = u"GOOD BYE"
                show_text(screen, (700,500), text_pos, (255,0,0), True, 100)
                pygame.display.update() #更新屏幕
                time.sleep(1)
                pygame.quit()
                sys.exit()
            if event.key == pygame.K_p:  
                paused = not paused  # 按下 "P" 键时切换暂停状态 
            if (event.key == pygame.K_o and plane1.health == 0 or
                event.key == pygame.K_o and len(enemy_out)==enemy_number): # 重新开始游戏
                Enemy_list.clear()  # 清空敌机列表  
                frame_count = 0  # 重置帧数计数器  
                enemy_out = [] # 重置剩余敌机数量
                plane1 = Plane(screen,plane1.image1,800,600,50,300)
                paused = False  # 重置游戏结束标志
    
    if not paused:  # 只有在非暂停状态下才更新游戏逻辑和绘制 
        screen.fill((0, 204, 255))  # 用纯色填充窗口
        #重新开始
        if plane1.health == 0:     #如果飞机生命值为 0 
            #显示文字
            text_pos = u"You Out"
            show_text(screen, (750,500), text_pos, (255,0,0), True, 100)
            text_pos = u"You can press the 'O' to restart the game"
            show_text(screen, (550,600), text_pos, (0,0,0), True, 50)   
            pygame.display.update() #更新屏幕
            paused = not paused
        
        if len(enemy_out)==enemy_number and plane1.health > 0:  # 如果敌机全部消灭且生命值仍大于零
            #显示文字
            text_pos = u"Congratulation! You Win!"
            show_text(screen, (450,500), text_pos, (255,0,0), True, 100)
            text_pos = u"Your Score Is: %d"%(plane1.score)
            show_text(screen, (600,600), text_pos, (255,0,0), True, 100) 
            text_pos = u"You can press the 'O' to renew your record"
            show_text(screen, (550,700), text_pos, (0,0,0), True, 50)  
            pygame.display.update() #更新屏幕
            paused = not paused
        # 随机生成敌机
        frame_count += 1 # 帧数递增
        if (len(enemy_out)+len(Enemy_list)<enemy_number 
            and len(Enemy_list)<max_enemy_count 
            and frame_count % add_enemy_interval==0):  
            # 随机生成敌机的初始位置  
            x = random.choice([0, 1780])  # 在屏幕左右边缘随机选择  
            y = random.randint(0, 1180)   # 在屏幕上随机选择纵向位置  
            # 加载敌机图像  
            enemy_image = pygame.image.load("enemy1.png")  
            # 创建敌机对象  
            enemy = Enemy(screen, enemy_image, x, y, 10)  
            # 将敌机对象添加到敌机列表  
            Enemy_list.append(enemy)
            
        # 用键盘控制角色移动
        keys = pygame.key.get_pressed()  # 获取键盘状态
        plane1.move(keys)    # 更新飞机对象  
            
        #用鼠标控制角色转动
        mouse_x,mouse_y = pygame.mouse.get_pos()
        plane1.zhuandong()
        
        #检测鼠标点击,发射子弹
        mouse_left, mouse_middle, mouse_right = pygame.mouse.get_pressed()
        if mouse_left:
            plane1.fire()
        plane1.show_bullet(Enemy_list, enemy_out)        #显示子弹
        plane1.collision(Enemy_list, enemy_out)
        
        #显示文字
        text_pos = u"plane1   score: %d "%(plane1.score)
        show_text(screen, (20,0), text_pos, (255,0,0), True, 40)
        #显示飞机坐标
        text_pos = u"center:(%d,%d)"%(plane1.rect.centerx, plane1.rect.centery)
        show_text(screen, (20,25), text_pos, (0,0,0), True, 40)
        #显示飞机角度
        text_pos = u"current_angle:(%f)"%(plane1.current_angle)
        show_text(screen, (20,50), text_pos, (0,0,0), True, 40)
        # 绘制飞机的血条  
        text_pos = u"current_health:(%d)"%(plane1.health)
        show_text(screen, (20,75), text_pos, (0,255,0), True, 40)
        plane1.health_bar.draw((255,0,0), (0,255,0), plane1.health) 
        #显示弹夹容量
        text_pos = u"bullet_clip:       (%d)"%(plane1.bullets_in_clip)
        show_text(screen, (20,100), text_pos, (255,255,0), True, 40)
        plane1.update_reload()                 # 弹夹填充
        plane1.bullet_clip_bar.draw((0,0,0), (255,255,0), plane1.bullets_in_clip)

        # 显示剩余敌机数量
        text_pos = u"remanent enemy number:(%d)"%(enemy_number-len(enemy_out))
        show_text(screen, (1400,20), text_pos, (255,0,0), True, 40)
        screen.blit(plane1.image, plane1.rect.topleft)  # 绘制图像
        # 更新敌机
        for enemy in Enemy_list:
            enemy.update()
        pygame.display.update() #更新屏幕
    
    

子弹图片(命名为bullet.png): 

 敌机图片(命名为enemy1.png):

我机图片(命名为歼16.png):

  • 5
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值