Crossin的编程教室

标题: 【Pygame 第6课】 面向对象的游戏设计 [打印本页]

作者: crossin先生    时间: 2013-8-28 20:28
标题: 【Pygame 第6课】 面向对象的游戏设计
上节课中,我们的飞机已经可以发射子弹了,尽管只有一颗。为什么我只加了一颗?试着多加几颗你就会发现,你得用好几个变量去分别记录它们的xy坐标,在主循环中判断每一颗子弹的状态。你可以用list把程序写得稍稍不那么复杂,但这还没完。别忘了你打飞机的对手--敌机还没有加入到游戏。到时候你又需要更多的变量去记录它们的坐标,去判断它们的状态,去处理敌机、子弹、玩家飞机之间的关系。想想都觉得头大。

于是乎,我之前煞费苦心讲解的面向对象就该派上用场了。我要把子弹相关的东西都封装在一起。

先看看目前子弹相关的有哪些东西:x、y坐标,一张图片,好像就这么多。然后,还有一段处理子弹运动状态的代码。来建一个Bullet类,把x、y、image作为成员变量,再提供一个叫做move的成员函数,处理子弹的运动。

#定义一个Bullet类,封装子弹相关的数据和方法
  1. class Bullet:
  2.     def __init__(self):
  3.         #初始化成员变量,x,y,image
  4.         self.x = 0
  5.         self.y = -1
  6.         self.image = pygame.image.load('bullet.png').convert_alpha()

  7.     def move(self):
  8.         #处理子弹的运动
  9.         if self.y < 0:
  10.             mouseX, mouseY = pygame.mouse.get_pos()
  11.             self.x = mouseX - self.image.get_width() / 2
  12.             self.y = mouseY - self.image.get_height() / 2
  13.         else:
  14.             self.y -= 5
复制代码
代码的内容基本和之前一样,只是改为了面向对象的写法。如果你对__init__,self这些字眼感到陌生的话,请发送数字47到50,回顾一下关于python面向对象的课程。

接下来,程序主体就可以瘦身了。在原本加载子弹图片、初始化位置的地方,直接创建一个Bullet的实例。
  1. bullet = Bullet()
复制代码
在主循环中处理子弹运动的地方,调用Bullet的move方法。
  1. bullet.move()
复制代码
绘制子弹的时候,从bullet实例中取数据。
  1. screen.blit(bullet.image, (bullet.x, bullet.y))
复制代码
就这么简单。

运行程序看看效果是否正常。相比昨天,游戏的功能没有任何进展,但在结构上清晰了许多。之后,可以放心地添加更多子弹和敌机,而不会导致代码变成一坨。


6.png

#==== Crossin的编程教室 ====#
微信ID:crossincode
论坛:http://crossin.me
QQ群:312723402

面向零基础初学者的编程课
每天5分钟,轻松学编程


作者: Miracle_Wong    时间: 2013-8-28 20:46
coding中,赶上了crossin先生的更新。
作者: xiaotong125    时间: 2013-8-28 22:41
一步一步学习
作者: leo    时间: 2013-8-29 07:55
def __init__(self)   
还有看别人的代码
最下面经常有if __name__ == '__main__'这样的代码
到底是什么意思
网上查了一下  没有搞明白
谢谢老师
作者: crossin先生    时间: 2013-8-29 16:54
leo 发表于 2013-8-29 07:55
def __init__(self)   
还有看别人的代码
最下面经常有if __name__ == '__main__'这样的代码
  1. def __init__(self)  
复制代码
是一个类被实例化的时候调用的,也就是你创建一个实例时,会先运行这段代码,一般用来初始化类的成员变量。
  1. if __name__ == '__main__'
复制代码
是为了让这里面的代码在这个文件被直接执行时调用。而当这个文件被作为模块被其他程序import的时候,就不会调用这里面的代码
作者: Victor    时间: 2013-8-29 20:17
看完留个名,差一个就赶上进度啦
作者: Victor    时间: 2013-8-29 20:20
不对,我好像已经跟上了==!
作者: crossin先生    时间: 2013-8-29 23:14
Victor 发表于 2013-8-29 20:20
不对,我好像已经跟上了==!


作者: viking    时间: 2013-8-30 16:17
同样追上进度了
作者: test777    时间: 2013-9-11 00:55
感觉可以把self.image.get_width()放到__init__里面去执行, 就不用每次move都计算一次了
作者: crossin先生    时间: 2013-9-11 11:59
test777 发表于 2013-9-11 00:55
感觉可以把self.image.get_width()放到__init__里面去执行, 就不用每次move都计算一次了 ...

对的
作者: shallecker    时间: 2013-10-20 00:14
两弹齐发。。
  1. # -*- coding: utf-8 -*-
  2. import pygame
  3. from sys import exit

  4. pygame.init()
  5. screen = pygame.display.set_mode((450, 600), 0, 32)
  6. background = pygame.image.load("back.jpg").convert()
  7. plane = pygame.image.load("plane.png").convert_alpha()
  8. pygame.display.set_icon(plane)
  9. pygame.display.set_caption("打飞机")

  10. class Bullet:
  11.     def __init__(self):
  12.         #初始化成员变量,x,y,image
  13.         self.x = 0
  14.         self.y = -1
  15.         self.image = pygame.image.load('bullet.png').convert_alpha()

  16.     def move(self):
  17.         #处理子弹的运动
  18.         if self.y < 0:
  19.             mouseX, mouseY = pygame.mouse.get_pos()
  20.             self.x = mouseX - self.image.get_width() / 2
  21.             self.y = mouseY - self.image.get_height() / 2
  22.         else:
  23.             self.y -= 5
  24. bullet = Bullet()

  25. while True:
  26.     for event in pygame.event.get():
  27.         if event.type == pygame.QUIT:
  28.             pygame.quit()
  29.             exit()
  30.     bullet.move()
  31.     x, y = pygame.mouse.get_pos()
  32.     x -= plane.get_width() / 2
  33.     y -= plane.get_height() / 2
  34.     screen.blit(background, (0,0))
  35.     screen.blit(bullet.image, (bullet.x-23, bullet.y))
  36.     screen.blit(bullet.image, (bullet.x+25, bullet.y))
  37.     screen.blit(plane, (x,y))
  38.     pygame.display.update()
复制代码

作者: crossin先生    时间: 2013-10-20 21:47
shallecker 发表于 2013-10-20 00:14
两弹齐发。。

不错的
作者: wcjking331    时间: 2015-10-18 20:39
请问一个问题,我习惯写c,刚开始写python,用的是自带的idle,总是感觉里面的语法提示不出来,经常会因为一些单词打错了,导致程序出错。有什么好办法,或者插件能够让我减少这种错误吗?
作者: crossin先生    时间: 2015-10-18 22:12
wcjking331 发表于 2015-10-18 20:39
请问一个问题,我习惯写c,刚开始写python,用的是自带的idle,总是感觉里面的语法提示不出来,经常会因为 ...

装个pycharm之类的

作者: 周末晒被子    时间: 2015-12-27 15:50
  1. # -*- coding: utf-8 -*-
  2. import pygame
  3. from sys import exit

  4. class Bullet:
  5.     def __init__(self):  #初始化成员函数,可是为什么python第49课例子的直接上‘speed = 0’而不用__init__函数初始化呢?
  6.         self.x = 0
  7.         self.y = -1
  8.         self.image = pygame.image.load('f:\plane\sullet.png').convert_alpha()
  9.     def move(self):
  10.         if self.y < 0:
  11.             mouseX,mouseY = pygame.mouse.get_pos()
  12.             self.x = mouseX - self.image.get_width() / 2
  13.             self.y = mouseY - self.image.get_height() / 2
  14.         else:
  15.             self.y -= 5
  16.             
  17. class Bullet_l(Bullet):  #继承了父类Bullet;这个类是为了确定左边子弹的坐标
  18.     def move(self):
  19.         if self.y < 0:
  20.             mouseX,mouseY = pygame.mouse.get_pos()
  21.             self.x = mouseX - self.image.get_width() / 2
  22.             self.y = mouseY - self.image.get_height() / 2
  23.             self.x = self.x - 3.5 * self.image.get_width()
  24.         else:
  25.             self.y -= 5
  26.             
  27. class Bullet_r(Bullet):  #同样继承父类Bullet;为了确定右边子弹的坐标
  28.     def move(self):
  29.         if self.y < 0:
  30.             mouseX,mouseY = pygame.mouse.get_pos()
  31.             self.x = mouseX - self.image.get_width() / 2
  32.             self.y = mouseY - self.image.get_height() / 2
  33.             self.x = self.x + 3.5 * self.image.get_width()
  34.         else:
  35.             self.y -= 5
  36. #启动pygame
  37. pygame.init()

  38. #创建pygame的窗口,设置了窗口尺寸和窗口标题
  39. screen = pygame.display.set_mode((450,800),0,32)
  40. pygame.display.set_caption('Hello World')
  41. background = pygame.image.load('f:\plane\some.jpg')
  42. plane = pygame.image.load('f:\plane\plane.png').convert_alpha()

  43. #创建三个类的对象
  44. bullet = Bullet()
  45. bullet_l = Bullet_l()
  46. bullet_r = Bullet_r()

  47. while True:
  48.     for event in pygame.event.get():
  49.         if event.type == pygame.QUIT:
  50.             pygame.quit()
  51.             exit()
  52.     screen.blit(background,(0,0))

  53.     #确定三个图片(子弹)的坐标
  54.     bullet.move()
  55.     bullet_l.move()
  56.     bullet_r.move()

  57.     #因为这3张图片互不干扰,所以可以不分前后顺序
  58.     screen.blit(bullet.image,(bullet.x,bullet.y))
  59.     screen.blit(bullet.image,(bullet_l.x,bullet_l.y))
  60.     screen.blit(bullet.image,(bullet_r.x,bullet_r.y))

  61.     #感觉飞机的图片并不是完全对称的,我把子弹图片的中心点设置为鼠标坐标,然而子弹竟然不是从飞机头正中射出去的,而是稍微偏左了。
  62.     #左右两边的子弹看起来偏离地更离谱。
  63.     x,y = pygame.mouse.get_pos()
  64.     planeX = x - plane.get_width() / 2
  65.     planeY = y - plane.get_height() / 2
  66.     screen.blit(plane,(planeX,planeY))
  67.     pygame.display.update()
复制代码

作者: catherinemic    时间: 2016-2-2 12:15
get it~~
作者: mike90326    时间: 2016-3-14 12:32
第5课里自己编的情况是 只有按下鼠标的时候才会发射子弹,所以不用面向对象的方式的话可以很轻松的实现
但是这一课里,如果用 crossin给的 class的话,似乎没办法实现按鼠标才发射子弹?
应该怎么办呢?
作者: crossin先生    时间: 2016-3-14 12:44
mike90326 发表于 2016-3-14 12:32
第5课里自己编的情况是 只有按下鼠标的时候才会发射子弹,所以不用面向对象的方式的话可以很轻松的实现
但 ...

不是没办法,只是你要改代码。不是让子弹自己循环move
你判断鼠标按着的时候,就创建新的子弹,让它们move,子弹类内部判断,如果高度超出屏幕了,就自己销毁。
作者: mike90326    时间: 2016-3-14 12:55
crossin先生 发表于 2016-3-14 12:44
不是没办法,只是你要改代码。不是让子弹自己循环move
你判断鼠标按着的时候,就创建新的子弹,让它们mov ...

还是有几点疑问:
1. MOUSEBUTTONDOWN 是一个状态还是一个即时动作呢?从切换图片的那课程来看,应该是一个即时动作。因为如果一直按着鼠标,图片没有不停的切换。
2. 如果在while循环内部套嵌if event.type == pygame.MOUSEBUTTONDOWN 的话,并且把bullet.move()放到if中,应该是只有在鼠标按下的一瞬间才会创建新子弹并移动吧?结果是时间太短显示不出来。
所以还是不大明白应该怎么写哈。。。麻烦crossin了
作者: crossin先生    时间: 2016-3-14 21:44
mike90326 发表于 2016-3-14 12:55
还是有几点疑问:
1. MOUSEBUTTONDOWN 是一个状态还是一个即时动作呢?从切换图片的那课程来看,应该是一 ...

你加个变量比如叫 pressed,buttondown之后设为true,buttonup再设为false

循环中判断是 pressed 的状态,就定时发射子弹,怎么定时,就看你自己控制了

主循环里只负责创建子弹,至于子弹的飞行、碰撞、销毁,都放子弹类里实现。
作者: mike90326    时间: 2016-3-15 09:10
crossin先生 发表于 2016-3-14 21:44
你加个变量比如叫 pressed,buttondown之后设为true,buttonup再设为false

循环中判断是 pressed 的状态 ...

感谢!!
作者: 肖明    时间: 2017-2-10 14:47
少了一行代码吧,在创建bullet实例之后,要调用_init_()方法,要不就报错,调用move的时候就没y属性
作者: crossin先生    时间: 2017-2-10 21:46
肖明 发表于 2017-2-10 14:47
少了一行代码吧,在创建bullet实例之后,要调用_init_()方法,要不就报错,调用move的时候就没y属性 ...

你这里错了,__init__ 前后是两个下划线,会自动调用。你写错了,但是你手动调用了,歪打正着
作者: brahmagupta    时间: 2017-3-5 18:33
本帖最后由 brahmagupta 于 2017-3-5 18:34 编辑

上节课刚想着用 class来构造子弹。。这节课就讲了!
还有很多要学习
作者: mihchaelli    时间: 2017-7-31 09:39
本帖最后由 mihchaelli 于 2017-7-31 09:43 编辑

crossin
今天在写这个代码 一个问题:
为什么要 有两个获取位置的方法啊:

class Bullet:
    def __init__(self):
        self.x = 0
        self.y = -1
        self.image = pygame.image.load('bullet.png').convert_alpha()
        
    def move(self):
        if self.y > 0:
            self.y -= 30
        else:   
            self.x = x- self.image.get_width() /2
            self.y = y- self.image.get_height() /2

x,y = pygame.mouse.get_pos()
bullet.move

这样为什么不行啊 因为子弹的发射位置和飞机的位置是一个啊 为什么要单独在类中定义这个方法啊
作者: mihchaelli    时间: 2017-7-31 09:44
mihchaelli 发表于 2017-7-31 09:39
crossin
今天在写这个代码 一个问题:
为什么要 有两个获取位置的方法啊:

解决了 是可以的

bullet.move() 少了括号
作者: IRVING_LEE    时间: 2018-2-23 15:51
  1. import  pygame
  2. from sys import exit

  3. class Bullet():
  4.     def __init__(self):
  5.         self.x=0
  6.         self.y=-1
  7.         self.image=pygame.image.load('bullet.png').convert_alpha()
  8.         self.width=self.image.get_width()/2
  9.         self.height=self.image.get_height()/2

  10.     def move(self):
  11.         if self.y<0:
  12.             mouse_x,mouse_y=pygame.mouse.get_pos()
  13.             self.x=mouse_x-self.width
  14.             self.y=mouse_y-self.height
  15.         else:
  16.             self.y-=5

  17. class Plane():
  18.     def __init__(self):
  19.         self.image=pygame.image.load('plane.png').convert_alpha()
  20.         self.width = self.image.get_width() / 2
  21.         self.height = self.image.get_height() / 2

  22.     def postion(self):
  23.         mouse_x, mouse_y = pygame.mouse.get_pos()
  24.         x = mouse_x - self.width
  25.         y = mouse_y - self.height
  26.         return (x,y)

  27. pygame.init()
  28. screen=pygame.display.set_mode((450,600),0,32)
  29. pygame.display.set_caption('飞机大战')
  30. background=pygame.image.load('back.jpg')
  31. bullet=Bullet()
  32. plane=Plane()

  33. while True:
  34.     for event in pygame.event.get():
  35.         if event.type==pygame.QUIT:
  36.             pygame.quit()
  37.             exit()
  38.     screen.blit(background,(0,0))
  39.     bullet.move()
  40.     plane.postion()
  41.     screen.blit(bullet.image,(bullet.x,bullet.y))
  42.     screen.blit(plane.image,plane.postion())
  43.     pygame.display.update()

复制代码

作者: IRVING_LEE    时间: 2018-2-23 15:51
集各家所长,嘿嘿
作者: 花花啊    时间: 2018-4-11 10:28
     self.y -= 5,这个5是什么单位,是像素点吗?
作者: crossin先生    时间: 2018-4-11 11:54
花花啊 发表于 2018-4-11 10:28
self.y -= 5,这个5是什么单位,是像素点吗?


作者: Joseph丶Joe    时间: 2018-4-22 09:26
我想问一下子弹的向上运动为什么是bullet_y -= 5呢?而不是 bullet_y += 5呢,我试了一下 这种情况没有子弹出现。而且我这样发出子弹不是一颗,而是一连串的
作者: crossin先生    时间: 2018-4-22 18:11
Joseph丶Joe 发表于 2018-4-22 09:26
我想问一下子弹的向上运动为什么是bullet_y -= 5呢?而不是 bullet_y += 5呢,我试了一下 这种情况没有子弹 ...

坐标原点在左上角

一连串是你没控制好间隔的数值
作者: koalaoycx    时间: 2018-6-5 16:56
import pygame
from sys import exit

pygame.init()
screen=pygame.display.set_mode((600,170),0,32)
pygame.display.set_caption('Game in object!')
bg=pygame.image.load('./Figure/back.jpg').convert()

#定义子弹的类
class Bullet:
    def __init__(self):
        self.x=0
        self.y=-1
        self.image=pygame.image.load('./Figure/bullet.png').convert_alpha()

    def move(self):
        if self.y < 0:
            mouseX,mouseY=pygame.mouse.get_pos()
            self.x=mouseX-self.image.get_width()/2
            self.y=mouseY-self.image.get_height()/2
        else:
            self.y -= 5

class Plane:
    def __init__(self):
        self.image=pygame.image.load('./Figure/plane.png').convert_alpha()

    def move(self):
        mouseX, mouseY = pygame.mouse.get_pos()
        self.x = mouseX-self.image.get_width()/2
        self.y = mouseY-self.image.get_height()/2

#调用飞机的函数
plane=Plane()
#调用子弹的函数
bullet=Bullet()

while True:
    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            pygame.quit()
            exit()

    #根据图片调整窗口位置并显示背景图片
    width=bg.get_width()
    height=bg.get_height()
    screen_size=(width,height)
    screen=pygame.display.set_mode(screen_size, 0, 32)
    screen.blit(bg, (0,0))

    #调用子弹的Move方法
    bullet.move()
    #绘制子弹
    screen.blit(bullet.image,(bullet.x-22,bullet.y))
    screen.blit(bullet.image, (bullet.x+24, bullet.y))

    #调用飞机的Move方式
    plane.move()
    #绘制飞机
    screen.blit(plane.image,(plane.x,plane.y))

    #更新
    pygame.display.update()
修改了一下,封装了子弹跟飞机,双弹齐发,同时跟前面已经发过的朋友又有点小小的区别。




作者: crossin先生    时间: 2018-6-5 23:36
koalaoycx 发表于 2018-6-5 16:56
import pygame
from sys import exit

不错
作者: ericlinmk2    时间: 2018-8-18 17:34

  1. class Bullet:
  2.     x = 0
  3.     y = -1
  4.     image = pygame.image.load('bullet.png').convert_alpha()

  5.     def move(self):
  6.         if self.y < 0:
  7.             bulletX, bulletY = pygame.mouse.get_pos()
  8.             self.x = bulletX - self.image.get_width() / 2
  9.             self.y = bulletY - self.image.get_height() / 2
  10.         else:
  11.             self.y -= 1
复制代码
我嘗試了在Class裡不要使用__init__,
但都會出錯顯示
    image = pygame.image.load('bullet.png').convert_alpha()
pygame.error: cannot convert without pygame.display initialized

感覺只有 image = pygame.image.load('bullet.png').convert_alpha()才有問題
所以寫成下面這樣子就沒錯了
  1. class Bullet:
  2.     x = 0
  3.     y = -1
  4.     def __init__(self):
  5.         self.image = pygame.image.load('bullet.png').convert_alpha()
复制代码
但是我不懂為什麼子彈圖片一定要初始化?
不是給了圖片就好了嗎?
想了很久  没有搞明白
谢谢老师
作者: crossin先生    时间: 2018-8-19 14:34
ericlinmk2 发表于 2018-8-18 17:34
我嘗試了在Class裡不要使用__init__,
但都會出錯顯示
    image = pygame.image.load('bullet.png').conve ...

执行先后顺序不一样
不放在 __init__ 里,你这句话执行的时候,pygame还没初始化
作者: ericlinmk2    时间: 2018-8-19 19:55
crossin先生 发表于 2018-8-19 14:34
执行先后顺序不一样
不放在 __init__ 里,你这句话执行的时候,pygame还没初始化 ...

原來如此
谢谢老师




欢迎光临 Crossin的编程教室 (https://bbs.crossincode.com/) Powered by Discuz! X2.5