python學習筆記(七) 類和pygame實現打飛機游戲


python中類聲明如下:

class Student(object):
    def __init__(self, name, score):
        self.name = name
        self.score = score
    def printinfo(self):
        print('name is %s, score is %d'%(self.name, self.score))

Student類有兩個成員變量,name和score,類的成員函數第一個參數都為self,用來實現成員變量的賦值,__init__是類的初始化函數,初始化成員變量。

類的使用:

s1 = Student('niuniu',78)
print(s1.name)
print(s1.score)
s1.printinfo()

s2 = Student('gg',100)
s2.printinfo()
s2.age = 100
print('s2 age is %s'%(s2.age))

定義s2對象,並且通過s2.age=100,定義了s2的成員變量age,並且初始化為100

類的成員變量有兩種方式定義,一個是在__init__函數中,一個是通過類的對象初始化。

類的權限設置:

class Student2(object):
    def __init__(self, name, score):
        self.__name = name
        self.__score = score
    def getname(self):
        return self.__name
    def getscore(self):
        return self.__score 

    def printinfo(self):
        print('name is %s, score is %d' %(self.__name, self.__score))

__name通過在變量名前邊加上__表示該變量為私有變量,python沒有嚴格的權限限制,只不過通過重命名將__name變為其他的名字了,這樣在外部就訪問不到這個變量了。

通過添加getname和getscore函數獲取成員變量。

s3 = Student2('s3',99)
s3.printinfo()
name = s3.getname()
print(name)
s3.__name = 'iloveu'
print(s3.__name)
name = s3.getname()
print(name)
s3.printinfo()

 

雖然通過s3.__name = 'iloveu'賦值后,並沒有改變類的私有變量__name的數值,因為類的私有變量__name的名稱被改為其他的名字,用戶無法知道。所以打印出的名字和s3.__name數值不同。

python類同樣支持繼承

class Peaple(object):
    def __init__(self, name):
        self.__name = name
    def job(self):
        pass

class Worker(Peaple):
    def job(self):
        print("worker")

class Student(Peaple):
    def job(self):
        print("student")

s1 = Student('student')
s1.job

w1 = Worker('worker')
w1.job

p = Peaple("abc")
w = Worker("abc")
s = Student("abc")

 

可以通過isinstance判斷類對象是否是一個類型的實例

print(isinstance(p, Peaple))
print(isinstance(w,Peaple))
print(isinstance(s,Peaple))
print(isinstance(s,Worker))
print(isinstance(p,Student))

 

子類對象是基類類型的實例,而基類對象不一定是子類類型的實例。比如Student繼承於Peaple,學生是人,但是人不一定是學生。

類的屬性控制:

class Designer(Worker):
    def __init__(self,name,age):
        self.__name = name
        self.age = age
    def job(self):
        print("Designer")

designer = Designer('David',18) 

 

獲取屬性,設置屬性,判斷是否含有某個屬性

#判斷類中是否有某個實例print(hasattr(designer, 'age') )
print(hasattr(designer,'job'))
print(hasattr(designer,'name'))

#設置sex屬性,屬性值為'female'
setattr(designer, 'sex', 'female')

print(designer.sex)

#獲取job屬性,返回值為job函數對象
fn = getattr(designer, 'job')
#調用fn函數
fn()

 

類的公有屬性,為所有對象共有,類似於C++的static成員變量

#通過self變量或者實例自身可以實現實例屬性綁定
#在類中直接定義一個變量,這個屬性歸類所有,類似於C++的static變量。
class Temple(object):
    staticmember = 1000

temp1 = Temple()
#temp1沒有自身屬性成員staticmember,
#而Temple類含有共享屬性
#下面這種方式打印的是類的共有屬性
print(temp1.staticmember)
#為實例temp1綁定其自身的成員staticmember
#並且設置數值為2048
temp1.staticmember = 2048
#打印實例temp1的成員staticmember
print(temp1.staticmember)
#打印類的共享成員
print(Temple.staticmember)

#刪除實例的屬性staticmember
del temp1.staticmember
#打印出類共享的屬性
print(temp1.staticmember)

 可以為類綁定成員函數,也可以只為類的一個實例綁定成員函數

def setage(self, age):
    self.__age = age
def getage(self):
    return self.__age
###給實例綁定方法
temp1.setage = MethodType(setage, temp1)
temp1.getage = MethodType(getage,temp1)
temp1.setage(125)
print(temp1.getage())

def setname(self, name):
    self.__name = name

def getname(self):
    return self.__name

#給類綁定方法
Temple.setname= setname
Temple.getname = getname

temp2 = Temple()
temp2.setname('name')
print(temp2.getname())

 

可以通過@property的方式,通過屬性訪問的方式就可以調用函數

class Definetion(object):
    def __init__(self, member):
        self.__member = member
    @property
    def member(self):
        print("call getter")
        return self.__member
    @member.setter
    def member(self, member):
        print("call setter")
        if not isinstance(member,int):
            raise TypeError("member must be int type")
        self.__member = member
    @member.deleter
    def member(self):
        print("call deleter")
        raise AttributeError("Cann't delete member")
definetioner
= Definetion(3) print(definetioner.member) definetioner.member = 1024 print(definetioner.member)

 

將member分別實現為返回屬性__member,設置__member,以及刪除__member的函數。在每個member上添加對應格式的@property,@member.setter, @member.deleter。

通過屬性訪問的方式就可以調用對應的函數, definetioner.member返回__member值,  definetioner__member = 1024調用設置__member的函數。deleter definetioner.member調用的是刪除函數。

 

實戰:用pygame庫做一個打飛機的小游戲

pygame是python的一個做游戲的庫,安裝方法自行百度。

實現子彈類

# 設置游戲屏幕大小
SCREEN_WIDTH = 480
SCREEN_HEIGHT = 800

# 子彈類
class Bullet(pygame.sprite.Sprite):
    def __init__(self, bullet_img, init_pos):
        pygame.sprite.Sprite.__init__(self)
        self.image = bullet_img
        self.rect = self.image.get_rect()
        self.rect.midbottom = init_pos
        self.speed = 10

    def move(self):
        self.rect.top -= self.speed

 

寫玩家的飛機類

# 玩家飛機類
class Player(pygame.sprite.Sprite):
    def __init__(self, plane_img, player_rect, init_pos):
        pygame.sprite.Sprite.__init__(self)
        self.image = []                                 # 用來存儲玩家飛機圖片的列表
        for i in range(len(player_rect)):
            self.image.append(plane_img.subsurface(player_rect[i]).convert_alpha())
        self.rect = player_rect[0]                      # 初始化圖片所在的矩形
        self.rect.topleft = init_pos                    # 初始化矩形的左上角坐標
        self.speed = 8                                  # 初始化玩家飛機速度,這里是一個確定的值
        self.bullets = pygame.sprite.Group()            # 玩家飛機所發射的子彈的集合
        self.img_index = 0                              # 玩家飛機圖片索引
        self.is_hit = False                             # 玩家是否被擊中

 

實現飛機類的幾個功能函數

# 發射子彈
    def shoot(self, bullet_img):
        bullet = Bullet(bullet_img, self.rect.midtop)
        self.bullets.add(bullet)

    # 向上移動,需要判斷邊界
    def moveUp(self):
        if self.rect.top <= 0:
            self.rect.top = 0
        else:
            self.rect.top -= self.speed

    # 向下移動,需要判斷邊界
    def moveDown(self):
        if self.rect.top >= SCREEN_HEIGHT - self.rect.height:
            self.rect.top = SCREEN_HEIGHT - self.rect.height
        else:
            self.rect.top += self.speed

    # 向左移動,需要判斷邊界
    def moveLeft(self):
        if self.rect.left <= 0:
            self.rect.left = 0
        else:
            self.rect.left -= self.speed

    # 向右移動,需要判斷邊界        
    def moveRight(self):
        if self.rect.left >= SCREEN_WIDTH - self.rect.width:
            self.rect.left = SCREEN_WIDTH - self.rect.width
        else:
            self.rect.left += self.speed

實現敵方飛機類

# 敵機類
class Enemy(pygame.sprite.Sprite):
    def __init__(self, enemy_img, enemy_down_imgs, init_pos):
       pygame.sprite.Sprite.__init__(self)
       self.image = enemy_img
       self.rect = self.image.get_rect()
       self.rect.topleft = init_pos
       self.down_imgs = enemy_down_imgs
       self.speed = 2
       self.down_index = 0

    # 敵機移動,邊界判斷及刪除在游戲主循環里處理
    def move(self):
        self.rect.top += self.speed

 

實現游戲主邏輯

# 初始化 pygame
pygame.init()

# 設置游戲界面大小、背景圖片及標題
# 游戲界面像素大小
screen = pygame.display.set_mode((SCREEN_WIDTH, SCREEN_HEIGHT))

# 游戲界面標題
pygame.display.set_caption('飛機大戰')

# 背景圖
background = pygame.image.load('resources/image/background.png').convert()

# Game Over 的背景圖
game_over = pygame.image.load('resources/image/gameover.png')

# 飛機及子彈圖片集合
plane_img = pygame.image.load('resources/image/shoot.png')

 

由於資源采用大圖的方式,敵機和飛機,子彈都繪制在一站圖片上,需要裁剪,pygame提供裁剪函數

# 設置玩家飛機不同狀態的圖片列表,多張圖片展示為動畫效果
player_rect = []
player_rect.append(pygame.Rect(0, 99, 102, 126))        # 玩家飛機圖片
player_rect.append(pygame.Rect(165, 360, 102, 126))
player_rect.append(pygame.Rect(165, 234, 102, 126))     # 玩家爆炸圖片
player_rect.append(pygame.Rect(330, 624, 102, 126))
player_rect.append(pygame.Rect(330, 498, 102, 126))
player_rect.append(pygame.Rect(432, 624, 102, 126))
player_pos = [200, 600]
player = Player(plane_img, player_rect, player_pos)

# 子彈圖片
bullet_rect = pygame.Rect(1004, 987, 9, 21)
bullet_img = plane_img.subsurface(bullet_rect)

# 敵機不同狀態的圖片列表,多張圖片展示為動畫效果
enemy1_rect = pygame.Rect(534, 612, 57, 43)
enemy1_img = plane_img.subsurface(enemy1_rect)
enemy1_down_imgs = []
enemy1_down_imgs.append(plane_img.subsurface(pygame.Rect(267, 347, 57, 43)))
enemy1_down_imgs.append(plane_img.subsurface(pygame.Rect(873, 697, 57, 43)))
enemy1_down_imgs.append(plane_img.subsurface(pygame.Rect(267, 296, 57, 43)))
enemy1_down_imgs.append(plane_img.subsurface(pygame.Rect(930, 697, 57, 43)))

 

管理敵機和敵機被擊中的等對象

#存儲敵機,管理多個對象
enemies1 = pygame.sprite.Group()

# 存儲被擊毀的飛機,用來渲染擊毀動畫
enemies_down = pygame.sprite.Group()

# 初始化射擊及敵機移動頻率
shoot_frequency = 0
enemy_frequency = 0

# 玩家飛機被擊中后的效果處理
player_down_index = 16

# 初始化分數
score = 0

# 游戲循環幀率設置
clock = pygame.time.Clock()

# 判斷游戲循環退出的參數
running = True

 

通過循環控制游戲邏輯,不斷生成敵機和子彈,刷新場景等。

給大家個建議,也是忠告,pygame實現的游戲循環體中一定要捕捉事件消息,不然會因為死循環而一直卡頓,甚至崩潰。先實現循環體中事件捕捉

# 游戲主循環
while running:
    # 控制游戲最大幀率為 60
    clock.tick(60)

# 更新屏幕 pygame.display.update() # 處理游戲退出 for event in pygame.event.get(): if event.type == pygame.QUIT: pygame.quit() exit() # 獲取鍵盤事件(上下左右按鍵) key_pressed = pygame.key.get_pressed() # 處理鍵盤事件(移動飛機的位置) if key_pressed[K_w] or key_pressed[K_UP]: player.moveUp() if key_pressed[K_s] or key_pressed[K_DOWN]: player.moveDown() if key_pressed[K_a] or key_pressed[K_LEFT]: player.moveLeft() if key_pressed[K_d] or key_pressed[K_RIGHT]: player.moveRight()

 

在while running循環中添加子彈和敵機生成邏輯

# 生成子彈,需要控制發射頻率
    # 首先判斷玩家飛機沒有被擊中
    if not player.is_hit:
        if shoot_frequency % 15 == 0:
            player.shoot(bullet_img)
        shoot_frequency += 1
        if shoot_frequency >= 15:
            shoot_frequency = 0

    # 生成敵機,需要控制生成頻率
    if enemy_frequency % 50 == 0:
        enemy1_pos = [random.randint(0, SCREEN_WIDTH - enemy1_rect.width), 0]
        enemy1 = Enemy(enemy1_img, enemy1_down_imgs, enemy1_pos)
        enemies1.add(enemy1)
    enemy_frequency += 1
    if enemy_frequency >= 100:
        enemy_frequency = 0

 

在while running循環中子彈和敵機移動邏輯

for bullet in player.bullets:
        # 以固定速度移動子彈
        bullet.move()
        # 移動出屏幕后刪除子彈
        if bullet.rect.bottom < 0:
            player.bullets.remove(bullet)   

    for enemy in enemies1:
        #2. 移動敵機
        enemy.move()
        #3. 敵機與玩家飛機碰撞效果處理
        if pygame.sprite.collide_circle(enemy, player):
            enemies_down.add(enemy)
            enemies1.remove(enemy)
            player.is_hit = True
            break
        #4. 移動出屏幕后刪除飛機    
        if enemy.rect.top < 0:
            enemies1.remove(enemy)

    #敵機被子彈擊中效果處理
    # 將被擊中的敵機對象添加到擊毀敵機 Group 中,用來渲染擊毀動畫
    enemies1_down = pygame.sprite.groupcollide(enemies1, player.bullets, 1, 1)
    for enemy_down in enemies1_down:
        enemies_down.add(enemy_down)

 

在while running循環中添加自己飛機動態邏輯

# 繪制玩家飛機
    if not player.is_hit:
        screen.blit(player.image[player.img_index], player.rect)
        # 更換圖片索引使飛機有動畫效果
        player.img_index = shoot_frequency // 8
    else:
        # 玩家飛機被擊中后的效果處理
        player.img_index = player_down_index // 8
        screen.blit(player.image[player.img_index], player.rect)
        player_down_index += 1
        if player_down_index > 47:
            # 擊中效果處理完成后游戲結束
            running = False

    # 敵機被子彈擊中效果顯示
    for enemy_down in enemies_down:
        if enemy_down.down_index == 0:
            pass
        if enemy_down.down_index > 7:
            enemies_down.remove(enemy_down)
            score += 1000
            continue
        screen.blit(enemy_down.down_imgs[enemy_down.down_index // 2], enemy_down.rect)
        enemy_down.down_index += 1

 

 

效果顯示:

源碼下載地址:https://github.com/secondtonone1/python-/tree/master/plane

謝謝關注我的公眾號:

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM