python打飞机小程序

时间:2022-08-12 14:59:43

游戏框架的搭建

安装框架包pygame

pip install pygame

搜索图片资源

教程里是有的,可我觉得太单调了
本来想搜索雷霆战机的,不太好找,便下载的全民飞机大战的素材包,但是发现这个素材包和教程里的不太一样,教程里的都是一个一个的,素材包里有很多一堆图片挤在一个图片里的,估计还需要额外学习。
python打飞机小程序

plist文件相关

本来在网上找到一个可以直接根据plist文件分割大图的py,但是我执行下来一直报错,经过我不断的搜索,最后发现可能是由于我下载的plist是加密的,所以。。。(mmp)

编写相应代码

#coding=utf-8
import pygame


if __name__ == "__main__":
#1. 创建一个窗口,用来显示内容
screen = pygame.display.set_mode((480,890),0,32)

#2. 创建一个和窗口大小打图片,用来充当背景
bgImageFile = "./fjdz/img_bg_logo.jpg"
background = pygame.image.load(bgImageFile).convert()

#3. 把背景图片放到窗口中
while True:
screen.blit(background,(0,0))
pygame.display.update()

很尴尬的是在while True之后发现弹出来的窗口关不掉了,教程中是在vi模式编写,直接Ctrl + c 关掉了,我是在Sublime中写了,Ctrl + c不行,上网搜索杀死进程的方法使用xkill命令关掉了
图太长了好蛋疼,怎么才能显示全呢
大概只显示了三分之二的长度,也不能拉动边框

思路

学习一下如何定义一个背景屏幕,以及如何显示一个图片。

检测键盘

代码

#coding=utf-8
import pygame
from pygame.locals import *

if __name__ == "__main__":
#1. 创建一个窗口,用来显示内容
screen = pygame.display.set_mode((512,768),0,32)

#2. 创建一个和窗口大小打图片,用来充当背景
bgImageFile = "./fjdz/img_bg_logo.jpg"
background = pygame.image.load(bgImageFile).convert()

#3. 把背景图片放到窗口中
while True:
screen.blit(background,(0,0))
for event in pygame.event.get():
if event.type == QUIT:
print "exit"
exit()
elif event.type == KEYDOWN:
if event.key == K_a or event.key == K_LEFT:
print "left"
elif event.key == K_d or event.key == K_RIGHT:
print "right"
elif event.key == K_w or event.key == K_UP:
print "up"
elif event.key == K_s or event.key == K_DOWN:
print "down"
elif event.key == K_SPACE:
print "space"

pygame.display.update()

思路

学习一下如何检测键盘

飞机显示及移动

由于plist的原因,卡了许久,默默去下ps,心塞。
只能先显示个飞机大阵了
(不只是飞机大阵,还有黑背景,只能先用教程里的图了)
只有教程里的gif没有黑背景,尴尬。。。

关于飞机显示的努力

一直不想用教程里的图,便想办法自己从大图里面扣
python打飞机小程序

ps里的.gif

幸好大图里的图都非常好扣,但是移动到程序里的图一直有黑底,很烦,网上教程说应该存gif格式的,这样才能透明,可是在ps里没找到gif格式。然后上网搜索才知道应该选择那个存为web格式的才有gif格式。
python打飞机小程序
存储完成后,到程序中显示,虽然周围确实是透明了,但是为什么图片内一些偏白的地方也透明了???
python打飞机小程序
我还以为我抠图扣错了,又试了几遍,还是不行
(╯‵□′)╯︵┴─┴

png

但是我在抠图的时候发现其实大图里的背景也应该是透明的,因为我在抠图过程中发现这些图并没有背景,所以我觉得应该png格式也是可以保存透明背景的,经过搜索,证实了我的想法,那就用png格式的喽
结果经过ps处理后的png文件根本无法在ubuntu中打开,可能是由于我在存储中没有选择压缩?或者是交错
经过搜索,网上提供的另外一种奇怪的方法解决了我的一部分问题。
那就是 把扩展名删掉。但是不幸的是拿到系统里的图片还是不知道为什么挂了python打飞机小程序
但是,待在共享文件夹里的文件还是好的
python打飞机小程序
但是调用共享文件夹里的文件依然在程序中显示为黑底,由此,我推断:
一定是这个*不够圆!!!
当然我也不想自己去造个*,或者找一个更圆的*,因为我还可以老老实实的用教程里的图片(我屈服了QAQ)
python打飞机小程序
另外,我发现,去掉后缀之后即使再加上后缀也不影响显示了,我本来是想截个无法打开的png图的,但是加上后缀后依然可以打开,而且就是把它删掉再扔进来一个新的依然可以显示,这算什么?开窍了吗?
python打飞机小程序

思路

飞机类定义

  1. 创建一个飞机类
  2. 各种属性

    1. 本身的图片相关,如图片路径,图片尺寸

      #获取图片及相关信息
      self.imageName = "./feiji/hero.gif" #100x124
      self.img = Image.open(self.imageName)
      self.imgWidth = self.img.width
      self.imgHeight = self.img.height
    2. 用于显示图片的屏幕相关,背景屏幕是谁,背景屏幕的尺寸(由于我将屏幕也定义了一个类,所以只传一个参数)

      #获取背景屏幕
      self.bScreen = bgScreen
    3. 以及初始显示位置。x,y坐标,具体数值由背景屏幕尺寸及图片尺寸决定

      #设置飞机默认位置
      self.x = bgScreen.x/2 - self.imgWidth/2
      self.y = bgScreen.y - self.imgHeight
    4. 其它,待添加

  3. 各种方法

    1. 显示飞机,根据如何显示一个图片,显示在默认位置

      #显示飞机
      def display(self):
      self.bScreen.screen.blit(self.heroimage,(self.x,self.y))
    2. 移动飞机,根据如何显示一个图片,根据指令调整图片位置,同时设定边界,禁止移动飞机移出边界,边界数值由背景屏幕尺寸及图片尺寸决定。

      #向左
      def movLeft(self):
      if self.x > (0 - self.imgWidth/2):
      self.x -=10
      else:
      self.x = 0 - self.imgWidth/2
    3. 其它,待添加

飞机类显示

  1. 创建一个实例并应用
    1. 将显示飞机方法放到屏幕显示中
    2. 将移动飞机的方法与按键命令相结合

可以攻击的飞机

即显示我方子弹及我方子弹移动

思路

子弹类定义

  1. 定义一个子弹类
  2. 各种属性
    1. 本身的图片相关,如图片路径,图片尺寸
    2. 用于显示图片的屏幕相关,背景屏幕是谁,背景屏幕的尺寸
    3. 以及初始显示位置。x,y坐标,具体数值由背景屏幕尺寸,图片尺寸及飞机位置决定(所以在定义时需要传入飞机参数或只传入相关参数)
    4. 其它,待添加
  3. 各种方法
    1. 显示子弹,根据如何显示一个图片,显示于默认位置
    2. 移动子弹,根据如何显示一个图片,由于目前子弹只需要竖直移动(修改y值),所以一个函数足以。
    3. 其它,待添加

全部子弹显示及移动

由于子弹一般不会只有一颗,所以需要将全部子弹生成一个列表并显示。列表存放在哪呢?存放在发射子弹的飞机中不错(飞机炸了怎么办…子弹清完再清除飞机?还是将子弹放于飞机类中?)

  1. 在飞机类中定义全部子弹的显示方法,将列表中的子弹一一显示出来
  2. 在显示的时候对每一个子弹修改y值,即可完成移动
  3. 设定边界,在子弹出界后移除(即从列表中删除)

tips:列表删除相关

for bul in self.bullet:
if bul.y > 0:
self.bullet.remove(bul)

这种删除列表中的方法是错误的,当要删除的两个变量相邻时便会出错。
例:
python打飞机小程序
原因嘛~

#方法一
nb = []
for bul in self.bullet:
if bul.y < 0:
nb.append(bul)
for bul in nb:
self.bullet.remove(bul)

#方法一
nb = []
for bul in self.bullet:
if bul.y > 0:
nb.append(bul)
self.bullet = nb

教程中给的是方法一,自己写的方法二,不清楚会有什么后果(数据地址改变?)。

显示敌机及移动

思路

定义敌机类

图片路径,初始位置x,y与普通飞机类不同,其余属性及方法通用

敌机的移动

敌机的移由代码控制,需要编写相关代码
定义一个自动移动的方法。
显然需要随机数,即random模块。
相关代码:


def autoMove(self):
#如果上次的随机移动指令是否结束
if self.amc <= 0:
#若已结束,重新生成移动模式amm,移动次数amc
self.amm = random.randint(1,3)
self.amc = random.randint(5,20)
else:
#若未结束,移动次数减一
self.amc -=1
#是否移动到边缘
if self.x <= 0 :
#若移动到左边缘,剩余次数强制右移
self.amm = 3
elif self.x + self.imgWidth >= self.bScreen.x :
#若移动到右边缘,剩余次数强制左移
self.amm = 2

#移动模式相关
if self.amm == 1:
#向前移,无论移动次数多少强制归0
self.movDown()
self.amc = 0
elif self.amm == 2:
#左移
self.movLeft()
elif self.amm == 3:
#右移
self.movRight()

看上去有些蠢,但是感觉也不太好化简了。

敌机子弹显示及自动攻击

思路

定义敌机子弹类

与我方子弹类基本相同,除了调用的图片路径不同。

全部子弹显示及移动

显示也差不多,移动只要改下y变动的方向即可,删除也是

自动攻击

应该在敌机类中定义一个函数,生成一个随机数,满足一定条件即调用shoot函数。(我把自动攻击和子弹显示写在一起了)

    def autoShoot(self):
nb = []
n = random.randint(1,1000)
if n < 100 :
self.shoot()
for bul in self.bullet:
bul.display()
bul.move(0,3)
if bul.y > 0:
nb.append(bul)
self.bullet = nb

抽取基类

可以看到有许多代码都是重复的,将基类抽取出来,化简代码

一些小心得

类内的变量

在整合基类的过程中,发现在子类的初始化中,其实不用把参数一层层传到父类,父类的__init__也是能调用到对应的变量的(只要定义了)
而self.imageName 这个参数由于在初始化过程中还需要调用,如果在子类没有定义会导致出错(虽然正常来讲不会出现未定义的情况)。便想写一个函数使它在未定义的时候进行定义一个参数。
最初想判断self.imageName == None,报错,说不存在self.imageName 这个东西,想了想,是啊,未定义呢
后来想试试缺省参数。结果由于def xxx后面忘了加冒号导致一直报错,后来发现了以后,测试一下好像也不行
上网搜索方法
1. 调用hasattr方法
2. 使用dir方法
3. 使用locals().has_key(‘var’)方法
4. 以及捕捉异常方法
由于之前并未怎么接触过捕捉异常的写法(只是学习了),导致并不喜欢捕捉异常的方法,但是其它方法都在报错,想了想,应该是在调用self.imageName时就直接报错了,无法进行判断,最后

class BaseImage(object):
"""docstring for BaseImage"""
def __init__(self, bgScreen):
try:
self.imageName = self.imageName
except AttributeError as e:
#error: has not attribute
self.imageName = "./feiji/bomb-2.gif"

呵呵

很奇怪我将一个函数display从父类(Plane)放入更深的基类(BaseImage)时heroPlane.display()便查找不到display函数了
(HeroPlane—>Plane—>BaseImage)
我把display函数粘贴在(BaseImage)中时,多缩进了一行,呵呵呵呵呵呵呵

子弹最后抽抽抽,凑成一个类了

教程到这就结束了。。。有空(猴年马月)再往下写吧

#coding=utf-8
import pygame
from pygame.locals import *
from PIL import Image
import time
import random

class Screen(object):
"""定义一个背景屏幕"""
def __init__(self,x,y):
#背景屏幕的尺寸
self.x = x
self.y = y

#生成背景屏幕
self.screen = pygame.display.set_mode((self.x,self.y),0,32)

#静态方法:获取键盘打值
@staticmethod
def getKey():
for event in pygame.event.get():
if event.type == QUIT:
print "exit"
exit()
elif event.type == KEYDOWN:
if event.key == K_a or event.key == K_LEFT:
print "left"
heroPlane.movLeft()
elif event.key == K_d or event.key == K_RIGHT:
print "right"
heroPlane.movRight()
elif event.key == K_w or event.key == K_UP:
print "up"
heroPlane.movUp()
elif event.key == K_s or event.key == K_DOWN:
print "down"
heroPlane.movDown()
elif event.key == K_SPACE:
print "space"
heroPlane.shoot()

class BaseImage(object):
"""在背景屏幕打印一个图片打基础"""
def __init__(self, bgScreenn):
#判断是否有图片文件打路径传入
try:
self.imageName = self.imageName
except AttributeError as e:
#error: has not attribute
self.imageName = "./feiji/bomb-2.gif"

#打开图像文件,不想检测错误了
self.img = Image.open(self.imageName)

#获取图像的尺寸
self.imgWidth = self.img.width
self.imgHeight = self.img.height

#s设置要显示内容打窗口
self.bScreen = bgScreenn
self.imageScreen = pygame.image.load(self.imageName).convert()

#显示图片
def display(self):
self.bScreen.screen.blit(self.imageScreen,(self.x,self.y))

#向左
def movLeft(self, speed = 10):
if self.x > (speed - 10 - self.imgWidth/2):
self.x -= speed
else:
self.x = speed - 10 - self.imgWidth/2

#向右
def movRight(self, speed = 10):
if self.x < (self.bScreen.x - speed - self.imgWidth/2 ):
self.x += speed
else:
self.x = self.bScreen.x - speed - self.imgWidth/2

#向上
def movUp(self, speed = 10):
if self.y > speed:
self.y -= speed
else:
self.y = 0

#向下
def movDown(self, speed = 10):

if self.y < (self.bScreen.y - self.imgHeight - speed):
self.y += speed
else:
self.y = self.bScreen.y - self.imgHeight





class Plane(BaseImage):
"""一个基础飞机类"""

def __init__(self, bgScreen):
super(Plane,self).__init__(bgScreen)
#根据类型,设置飞机默认位置
if self.planeType == "Hero" :
self.x = bgScreen.x/2 - self.imgWidth/2
self.y = bgScreen.y - self.imgHeight
elif self.planeType == "Enemy" :
self.x = 0
self.y = 0

#全部子弹
self.bullet = []

#射击
def shoot(self):
if self.planeType == "Hero":
newBullet = Bullet(self.x+self.imgWidth/2-10,self.y,self.bScreen,self.planeType)
elif self.planeType == "Enemy":
newBullet = Bullet(self.x+self.imgWidth/2-10,self.y+self.imgHeight,self.bScreen,self.planeType)
self.bullet.append(newBullet)


#展示全部子弹
def bulletDisplay(self,direction = "up", speed = 1):
nb = []
for bul in self.bullet:
bul.display()
bul.move(direction)
if bul.y > 0 or bul.y < self.bgScreen.y:
nb.append(bul)
self.bullet = nb


class HeroPlane(Plane):
"""基础英雄飞机类,可以视为工厂"""
def __init__(self,bgScreen):
#图片路径
self.imageName = "./feiji/hero.gif" #100x124
#self.imageName = "/mnt/share/Hero-1" #100x124
#self.imageName = "./fjdz/Hero-1.gif" #100x124

#飞机类型
self.planeType = "Hero"

super(HeroPlane,self).__init__(bgScreen)


class EnemyPlane(Plane):
"""基础敌方飞机,可以视为工厂"""
def __init__(self,bgScreen):
#图片路径
self.imageName = "./feiji/enemy-1.gif" #100x124

#飞机类型
self.planeType = "Enemy"

super(EnemyPlane,self).__init__(bgScreen)

#用于自动移动的移动次数及移动模式
self.auMoNumofCycles = 0
self.auMoType = 0


#自动射击
def autoShoot(self):
n = random.randint(1,1000)
if n < 100 :
self.shoot()

#自动移动
def autoMove(self):
#如果上次的随机移动指令是否结束
if self.auMoNumofCycles <= 0:
#若已结束,重新生成移动模式,移动次数
self.auMoType = random.randint(1,3)
self.auMoNumofCycles = random.randint(5,20)
else:
#若未结束,移动次数减一
self.auMoNumofCycles -=1

#判断是否移动到边缘
if self.x <= 0 :
#若移动到左边缘,剩余次数强制右移
self.auMoType = 3
elif self.x + self.imgWidth >= self.bScreen.x :
#若移动到右边缘,剩余次数强制左移
self.auMoType = 2

#移动模式相关
if self.auMoType == 1:
#向前移,无论移动次数多少强制归0
self.movDown()
self.auMoNumofCycles = 0
elif self.auMoType == 2:
#左移
self.movLeft()
elif self.auMoType == 3:
#友移
self.movRight()

#显示子弹(敌方)
def bulletDisplay(self):
super(EnemyPlane, self).bulletDisplay("down")


class Bullet(BaseImage):
"""基础子弹类,可视为工厂"""
def __init__(self,x,y,bgScreen, type):
#生成子弹位置
self.x = x
self.y = y

#根据类别选择子弹图片
if type == "Hero":
self.imageName = "./feiji/bullet-3.gif"
elif type == "Enemy":
self.imageName = "./feiji/bullet-1.gif"

super(Bullet,self).__init__(bgScreen)

#子弹的移动
def move(self,direction,speed=1):
if direction == "up":
self.y -=speed
elif direction == "down":
self.y +=speed



if __name__ == "__main__":
#1. 创建一个窗口,用来显示内容
bgScreen = Screen(512,700)

#2. 创建一个和窗口大小打图片,用来充当背景
#bgImageFile = "./fjdz/img_bg_logo.jpg"
bgImageFile = "./fjdz/img_bg_level_1.jpg"
background = pygame.image.load(bgImageFile).convert()

#3.创建一个飞机对象
heroPlane = HeroPlane(bgScreen)

#4.创建一个敌方飞机
enemyPlane = EnemyPlane(bgScreen)

#5. 把全部图片放到窗口中
while True:
#背景图片
bgScreen.screen.blit(background,(0,0))
#我方飞机展示
heroPlane.display()
#我方子弹展示
heroPlane.bulletDisplay();
#敌方飞机展示
enemyPlane.display()
#敌方飞机自动移动
enemyPlane.autoMove()
#敌方飞机自动攻击
enemyPlane.autoShoot()
#敌方飞机子弹展示
enemyPlane.bulletDisplay()
#获取键值及相应操作
Screen.getKey()
#刷新显示
pygame.display.update()
#延迟
time.sleep(0.05)