【原创】pygame学习笔记(5)__打飞机游戏__重写版本1:基础功能版

版权声明:一起学习,一起成长,欢迎关注犀牛先生的博客 https://blog.csdn.net/xuemanqianshan/article/details/84262440

前言:

第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()

第一版可以允许了,接下来准备优化

猜你喜欢

转载自blog.csdn.net/xuemanqianshan/article/details/84262440