前言:
第1个基础版本:只有最基础的战斗,敌机能被消灭,飞机能动,动画都没做,更高级的功能都没有
一:整理流程图
我理解的按模块分,是基于要做的事情的逻辑拆开,第2版的扩展会以此为基础
(1)基本的对象:我方的plane,敌方的enemys
其中plane下面有很多子弹可以攻击,enemys里面有很多敌机
(2)我方的飞机:需要console进行控制,这也是很多游戏可以独立出来的模块
(3)战斗过程:battle,主要就是碰撞的检测
画图工具:https://www.draw.io/
二 定义游戏的(也是自己的)模块和类
2.1飞机类
(1)类里所有的东西,都应该定义在类的各种函数下面,没有定义在外面的必要。?
(2)类里所有的 变量,方法,都应该以 self.开头
(3) 类里所有的函数,包括构造函数,必须带默认参数 self,虽然在外面调用时不需要传递这个默认参数。
(4)类名,应该 大写开头, plane.Plane 虽然语法并不严格这么要求,但这么写很规范
(5)构造函数__init__(self),我觉得new的时候只是生成了__init__(self)里面的内容
(6)因为update(self) 先用到了 self.move_up ==,作判断。而此时self.move_up必须先声明定义过
一个变量必须先声明,在Python里用赋值声明即可,不用事先声明后再赋值。 self.move_up=0
存在的问题
(1)尝试故意只定义为 class Plane,不用精灵类继承,结果没啥问题,而且后面的碰撞检测用到plane也没问题,为什么?
(2)用""""""注释时,附近的下行经常有缩进问题,暂时原因未知???
# coding: utf-8
import pygame
import bullet
class Plane():
#故意尝试plane()没有声明为精灵,也没出问题,而且碰撞检测时也没问题,为什么?
#class Plane(pygame.sprite.Sprite):
#构造函数解决是什么的问题,new的时候只继承了这个。
def __init__(self,screen):
# pygame.sprite.Sprite.__init__(self)
# super.__init__(self)
self.image=pygame.image.load("C:\\Users\\Administrator\\temp1\\image\\plane001.png")
self.screen=screen #so import!!! 很多地方需要用到self.screen的属性
#为什么要引入screen,因为plane初始化时,需要用到和screen的相对位置
self.rect=self.image.get_rect()
self.screen_rect=screen.get_rect()
self.rect.centerx=self.screen_rect.centerx
self.rect.bottom=self.screen_rect.bottom-30
self.h_speed=5
self.v_speed=3
self.beaten=False
self.move_up=False
self.move_down=False
self.move_left=False
self.move_right=False
self.bullets=pygame.sprite.Group() #子弹一般都定义在外面主函数里,我尝试定义在这里
#其他是类的方法,需要再单独调用
#显示自己
#为什么这个要和构造函数分开,我理解是,new只需要一次,但是显示外观,更新位置等都得反复更新。
#class里面,所有的 变量,方法,都得开头是self.
def blitself(self):
self.screen.blit(self.image,self.rect)
#开火函数,主要就是生成子弹,并且加入子弹组里去
def fire(self,screen,myplane):
newbullet=bullet.Bullet(screen,myplane)
self.bullets.add(newbullet)
#更新函数,和其他语言一样,理论上是一帧执行一次
#因为飞机是需要玩家控制的,所以需要读入 外设的监测结果,决定是否移动,往哪儿移动。
def update(self):
if self.move_up==True:
if self.rect.top<self.screen_rect.top:
self.rect.top=self.screen_rect.top
else:
self.rect.centery -= self.h_speed
if self.move_down==True:
if self.rect.bottom>self.screen_rect.bottom:
self.rect.bottom=self.screen_rect.bottom
else:
self.rect.centery += self.h_speed
if self.move_left==True:
if self.rect.left<self.screen_rect.left:
self.rect.left=self.screen_rect.left
else:
self.rect.centerx -= self.v_speed
if self.move_right==True:
if self.rect.right>self.screen_rect.right:
self.rect.right=self.screen_rect.right
else:
self.rect.centerx += self.v_speed
#用""""""注释时,附近的下行经常有缩进问题,暂时原因未知???
2.2 setting模块的的Setting类
(1) 我感觉用处不大,反而复杂,下一版决定去掉这个,写到main()里去
(2)这个即使要用,为啥要写成 class ,我感觉用 单独模块 def为函数就够了吧?以后试试
# -*- coding:utf-8 -*-
#只能写成class不能def?
class Settings():
def __init__(self):
blue=0,0,255,255 #这么写没问题,但是应该是其他类没法继承到这个类的这个属性。当然下面写到调用blue了
self.width=500
self.height=800
self.backcolor=blue
2.3 子弹类
(1) 我写的这几个,子弹和飞机搞成互相传递 实例对象了,不知道有没问题?但是实测没问题
子弹肯定是要引入myplane等Plane的实例的,位置等就靠这些确定
但是飞机那,只引入了bullet模块,并且直接在开火的时候,现场new了一个newbullet出来
(2)尝试了下,子弹如果不定义为继承父类sprite会BUG
File "C:\Users\Administrator\temp1\plane.py", line 39, in fire
self.bullets.add(newbullet)
File "D:\Python27\lib\site-packages\pygame\sprite.py", line 378, in add
sprite.add_internal(self)
AttributeError: Bullet instance has no attribute 'add_internal'
# coding: utf-8
import pygame
class Bullet(pygame.sprite.Sprite):
def __init__(self,screen,myplane):
#如果不引用sprite,继承精灵类构造函数,就会发现无法被bullets类的add方法加进去
pygame.sprite.Sprite.__init__(self)
self.image=pygame.image.load("C:\\Users\\Administrator\\temp1\\image\\bullet001.png")
self.rect=self.image.get_rect()
self.screen_rect=screen.get_rect()
self.rect.centerx=myplane.rect.centerx #子弹只需要飞机的相对位置即可
self.rect.top=myplane.rect.top
self.speed=6
#bullet的位置等都是基于飞机的相对位置,所以不需要有screen,因为不需要screen的相对位置信息
#但是因为要复制显示子弹,所以还是需要screen
self.screen=screen
def blitself(self):
self.screen.blit(self.image,self.rect)
#子弹位置判断相对简单
def update(self):
self.rect.centery -=self.speed
if self.rect.bottom<=self.screen_rect.top:
self.kill()
2.4敌人类
(1)基本同飞机
# coding: utf-8
import pygame
import random
class Enemy(pygame.sprite.Sprite):
def __init__(self,screen):
pygame.sprite.Sprite.__init__(self)
self.screen=screen
self.image=pygame.image.load("C:\\Users\\Administrator\\temp1\\image\\enemy001.png")
self.rect=self.image.get_rect()
#screen没有rect属性,所以screen.rect这个属性不能这么写,而应该写成self.screen_rect,必须带self.screen
self.screen_rect=screen.get_rect()
self.rect.centerx=random.randint(0+self.rect.width/2,self.screen_rect.width-self.rect.width/2)
self.rect.top=self.screen_rect.top-self.rect.height
self.speed=2
def blitself(self):
#复制屏幕会把之前image的SCREEN复制,且挂到topleft
#但是只要self.rect里已经先把topleft定义为自己想要的了,这里就不用在具体写topleft的坐标了
self.screen.blit(self.image,self.rect)
def update(self):
self.rect.y +=self.speed
if self.rect.top>=self.screen_rect.bottom:
self.kill()
三 控制模块
单独的game_console模块,可以复用
但是问题出了很多,都还没搞明白
(1)or的用法
#错误语句 if event.key==pygame.K_UP or pygame.K_w:
#问题真的出在了or上了!!写法错误,非语法错误,导致逻辑判断错误了
#一旦这么写,就无法移动了?!
(2)没法实现wasd和键盘同时的控制
#if event.key==pygame.K_s:
#这么写会出现很诡异的失控
#if event.key==(pygame.K_DOWN or pygame.K_s):
#( or ) 第2个按键居然不生效,交换更是奇怪了
#感觉像是K_s等键位识别有问题,但是单独用 pygame.K_s 单独用都没问题
#WASD没搞成功,打算试试其他方法
(3)其他写法尝试
#有好多其他的键盘写法,比如写左右键之差为移动变量的,会比这个好吗?打算以后试验下
# 还有这种写法的key_pressed = pygame.key.get_pressed() if key_pressed[K_w] or key_pressed[K_UP]:
# coding: utf-8
import pygame
from pygame.locals import *
import sys
def check_events(myplane):
#带参数太重要了,如果不带myplane,就需要把 myplane基础的类的需要的一切都在这定义
#引用了myplane,就可以省好多麻烦事
#为什么要引入myplane,因为玩家需要控制的就是飞机的对象实例啊!
for event in pygame.event.get():
if event.type==QUIT:
pygame.quit()
sys.exit()
elif event.type==pygame.KEYDOWN:
if event.key==pygame.K_UP:
myplane.move_up=True
#为什么要引入置状态,这是因为键盘只响应一次,也可以写让键盘持续检测的方法
if event.key==pygame.K_DOWN:
myplane.move_down=True
if event.key==pygame.K_LEFT:
myplane.move_left=True
if event.key==pygame.K_RIGHT:
myplane.move_right=True
elif event.type==pygame.KEYUP:
if event.key==pygame.K_UP:
myplane.move_up=False
if event.key==pygame.K_DOWN:
myplane.move_down=False
if event.key==pygame.K_LEFT:
myplane.move_left=False
if event.key==pygame.K_RIGHT:
myplane.move_right=False
#错误语句 if event.key==pygame.K_UP or pygame.K_w:
#问题真的出在了or上了!!写法错误,非语法错误,导致逻辑判断错误了
#一旦这么写,就无法移动了?!
#if event.key==pygame.K_s:
#这么写会出现很诡异的失控
#if event.key==(pygame.K_DOWN or pygame.K_s):
#( or ) 第2个按键居然不生效,交换更是奇怪了
#感觉像是K_s等键位识别有问题,但是单独用 pygame.K_s 单独用都没问题
#WASD没搞成功,打算试试其他方法
#有好多其他的键盘写法,比如写左右键之差为移动变量的,会比这个好吗?打算以后试验下
# 还有这种写法的key_pressed = pygame.key.get_pressed() if key_pressed[K_w] or key_pressed[K_UP]:
四 主模块和主函数
(1)enemy和bullet都没有用到 本身类的 blitself()。而都用的 enemys.draw() 是gruop的方法,但是类本身没定义这个?
# coding: utf-8
#类实例化出过问题,忘了先new直接用
#变量未先赋值声明先判断出过问题
#希望写的这些模块,确实以后可以复用
#刚上来就开始模块化,说实话,这个过程还比较痛苦
#拆为多个模块,好处就是每个都比较短,确实可控,但是坏处之一是每个模块都要独立可用,重复import算是问题吗?
#引入系统模块
import pygame
import sys
#引入自己的模块
import settings
import game_console
import plane
import bullet
import enemy
def main():
pygame.init() #其他东西是不是都应该在init之后?我觉得应该是
ticks=0
#集中new一下,这个setting以后想去掉
mysetting=settings.Settings() #必须先new了才能用,感觉其实这个没有太大的必要
#构建基础显示屏幕内容
screen=pygame.display.set_mode((mysetting.width,mysetting.height))
caption=pygame.display.set_caption("PlaneBattle")
#screen.fill(mysetting.backcolor)
background=pygame.image.load("C:\\Users\\Administrator\\temp1\\image\\map001.png")
#必须在screen之后才能new,NEW的话感觉只是用了构造函数
myplane=plane.Plane(screen)
#创建几个精灵群,其实也是new
enemys=pygame.sprite.Group() #没有特殊声明,需要写全类型不能只写group
enemys_down=pygame.sprite.Group() #没有特殊声明,需要写全类型不能只写group
#enemys_impact没有先声明为group可能是因为只是当变量赋值使用了,而没有用到add方法等,否则肯定需要先声明
#游戏运行标志位
game_run=True
#主函数
while game_run:
ticks += 1
screen.blit(background,(0,0)) #这个之前没有放到循环里,这个放在while外,地图会拉丝
myplane.blitself()#这个如果放在while外,飞机就会隐身
# myenemy.blitself() #这样没用,敌机要在判断内循环产生,放 while内还不够,还得放在每次生成后都得显示
game_console.check_events(myplane) #之前不知道为什么放在模块里,这么调用就是不移动,OR的问题
myplane.update()#代码用模块就可以持续移动了,可能是缩进层级对了,放在while里直接写,反而只能按一下移动一下。。。
#定期产生子弹
if ticks%15==0:
myplane.fire(screen,myplane) #类里还可以传入自己的实例作为参数?好像是可以,看起来只要不是构造函数里用了就行
myplane.bullets.update()
myplane.bullets.draw(screen) #为啥不能用blit,只能用group的draw,而且也没定义过draw啊
#定期产生敌机
if ticks%25==0:
myenemy=enemy.Enemy(screen) #放while外面就只生成一架飞机
enemys.add(myenemy)
enemys.update()
enemys.draw(screen)
#碰撞模块真的简单,简直不敢相信
enemys_down.add(pygame.sprite.groupcollide(enemys,myplane.bullets,True,True))
for enemys_beaten in enemys_down:
enemys_down.remove(enemys_beaten)
enemys_impact=pygame.sprite.spritecollide(myplane,enemys,True)
if len(enemys_impact)>0:
enemys_down.add(enemys_impact) #可以用add加多个对象?
myplane.beaten=True
pygame.display.update()
#调用主函数,运行游戏
main()
第一版可以允许了,接下来准备优化