我是一個典型的80後,年輕時玩過了特別多的遊戲,所以這幾天用Python3+pygame實現了一個另外小遊戲”坦克大戰“(其他的遊戲,請翻閱我的部落格)

本例項程式碼量有些多,完整的版本在1000行左右(當然瞭如果再次最佳化的話 會減少一部分)

分享出來,希望能幫助到大家,畢竟自己做教育行業做了這麼多年,還是教育情懷的,哈哈哈哈哈經典遊戲90坦克大戰分享出來,希望能幫助到大家,畢竟自己做教育行業做了這麼多年,還是教育情懷的,哈哈哈哈哈

一、顯示效果

Python3+pygame實現的90坦克大戰 程式碼完整 有演示效果

Python3+pygame實現的90坦克大戰 程式碼完整 有演示效果

Python3+pygame實現的90坦克大戰 程式碼完整 有演示效果

二、程式碼

下面程式碼用到了一些素材(遊戲背景音樂、圖片等等),可以到我的網站下載,謝謝大家的支援

完整程式碼如下(注意:為了方便下載以及編寫更簡單,沒有采用多模組的方式,全部程式碼全部放到main。py檔案中)

“”“

作者:it專案例項網

網站:wwww。itprojects。cn

”“”

import

random

import

sys

import

pygame

# 螢幕的寬、高

WIDTH

=

630

HEIGHT

=

630

# 邊界值

BORDER_LEN

=

3

# 字型

FONTPATH

=

‘resources/font/font。ttf’

class

Iron

pygame

sprite

Sprite

):

“”“

鐵牆類

”“”

# 定義精靈組,將所有的磚牆例項物件新增到裡面

group

=

pygame

sprite

Group

()

def

__init__

self

position

):

# 呼叫父類的初始化方法,這樣才能夠實現必要的初始化操作

super

()

__init__

()

self

image

=

pygame

image

load

“resources/images/scene/iron。png”

# 當使用碰撞判斷方法時,pygame就需要知道當前要檢測的物體的位置,所以這個rect屬性一定要設定

self

rect

=

self

image

get_rect

()

self

rect

topleft

=

position

# 新增到精靈組

self

group

add

self

@classmethod

def

show

cls

screen

):

for

temp

in

cls

group

screen

blit

temp

image

temp

rect

class

Ice

pygame

sprite

Sprite

):

“”“

冰類

”“”

# 定義精靈組,將所有的例項物件新增到裡面

group

=

pygame

sprite

Group

()

def

__init__

self

position

):

# 呼叫父類的初始化方法,這樣才能夠實現必要的初始化操作

super

()

__init__

()

# 因為是12x12的小圖片,所以需要製作一個24x24的image

image

=

pygame

Surface

((

24

24

))

for

i

in

range

2

):

for

j

in

range

2

):

image

blit

pygame

image

load

“resources/images/scene/ice。png”

),

12

*

i

12

*

j

))

self

image

=

image

# 當使用碰撞判斷方法時,pygame就需要知道當前要檢測的物體的位置,所以這個rect屬性一定要設定

self

rect

=

self

image

get_rect

()

self

rect

topleft

=

position

# 新增到精靈組

self

group

add

self

@classmethod

def

show

cls

screen

):

for

temp

in

cls

group

screen

blit

temp

image

temp

rect

class

River

pygame

sprite

Sprite

):

“”“

河流類

”“”

# 定義精靈組,將所有的例項物件新增到裡面

group

=

pygame

sprite

Group

()

def

__init__

self

position

):

# 呼叫父類的初始化方法,這樣才能夠實現必要的初始化操作

super

()

__init__

()

# 因為是12x12的小圖片,所以需要製作一個24x24的image

image

=

pygame

Surface

((

24

24

))

for

i

in

range

2

):

for

j

in

range

2

):

image

blit

pygame

image

load

“resources/images/scene/river1。png”

),

12

*

i

12

*

j

))

self

image

=

image

# 當使用碰撞判斷方法時,pygame就需要知道當前要檢測的物體的位置,所以這個rect屬性一定要設定

self

rect

=

self

image

get_rect

()

self

rect

topleft

=

position

# 新增到精靈組

self

group

add

self

@classmethod

def

show

cls

screen

):

for

temp

in

cls

group

screen

blit

temp

image

temp

rect

class

Tree

pygame

sprite

Sprite

):

“”“

樹類

”“”

# 定義精靈組,將所有的例項物件新增到裡面

group

=

pygame

sprite

Group

()

def

__init__

self

position

):

# 呼叫父類的初始化方法,這樣才能夠實現必要的初始化操作

super

()

__init__

()

# 因為是12x12的小圖片,所以需要製作一個24x24的image

image

=

pygame

Surface

((

24

24

))

for

i

in

range

2

):

for

j

in

range

2

):

image

blit

pygame

image

load

“resources/images/scene/tree。png”

),

12

*

i

12

*

j

))

self

image

=

image

# 當使用碰撞判斷方法時,pygame就需要知道當前要檢測的物體的位置,所以這個rect屬性一定要設定

self

rect

=

self

image

get_rect

()

self

rect

topleft

=

position

# 新增到精靈組

self

group

add

self

@classmethod

def

show

cls

screen

):

for

temp

in

cls

group

screen

blit

temp

image

temp

rect

class

Brick

pygame

sprite

Sprite

):

“”“

磚牆類

”“”

# 定義精靈組,將所有的磚牆例項物件新增到裡面

group

=

pygame

sprite

Group

()

def

__init__

self

position

):

# 呼叫父類的初始化方法,這樣才能夠實現必要的初始化操作

super

()

__init__

()

self

image

=

pygame

image

load

“resources/images/scene/brick。png”

# 當使用碰撞判斷方法時,pygame就需要知道當前要檢測的物體的位置,所以這個rect屬性一定要設定

self

rect

=

self

image

get_rect

()

self

rect

topleft

=

position

# 新增到精靈組

self

group

add

self

@classmethod

def

show

cls

screen

):

for

temp

in

cls

group

screen

blit

temp

image

temp

rect

class

Bullet

pygame

sprite

Sprite

):

“”“

子彈類

”“”

# 定義精靈組,將所有的磚牆例項物件新增到裡面

group

=

pygame

sprite

Group

()

group_enemy

=

pygame

sprite

Group

()

def

__init__

self

_type

direction

position

):

super

()

__init__

()

# 子彈圖片

if

direction

==

“up”

image_path

=

“resources/images/bullet/bullet_up。png”

elif

direction

==

“down”

image_path

=

“resources/images/bullet/bullet_down。png”

elif

direction

==

“left”

image_path

=

“resources/images/bullet/bullet_left。png”

elif

direction

==

“right”

image_path

=

“resources/images/bullet/bullet_right。png”

self

image

=

pygame

image

load

image_path

# 子彈位置

self

rect

=

self

image

get_rect

()

self

rect

center

=

position

# 設定子彈的初始位置的中心點

# 子彈方向

self

direction

=

direction

# 子彈移動速度

self

speed

=

8

# 將子彈新增到精靈組

if

_type

==

“player”

self

group

add

self

else

self

group_enemy

add

self

@classmethod

def

auto_move

cls

):

for

temp

in

cls

group

if

temp

rect

x

<

BORDER_LEN

or

temp

rect

x

>

WIDTH

-

BORDER_LEN

or

temp

rect

y

<

BORDER_LEN

or

temp

rect

y

>

HEIGHT

-

BORDER_LEN

cls

group

remove

temp

print

“子彈超出邊界,移除子彈”

continue

if

temp

direction

==

“up”

temp

rect

=

temp

rect

move

((

0

-

temp

speed

))

elif

temp

direction

==

“down”

temp

rect

=

temp

rect

move

((

0

temp

speed

))

elif

temp

direction

==

“left”

temp

rect

=

temp

rect

move

((

-

temp

speed

0

))

elif

temp

direction

==

“right”

temp

rect

=

temp

rect

move

((

temp

speed

0

))

for

temp

in

cls

group_enemy

if

temp

rect

x

<

BORDER_LEN

or

temp

rect

x

>

WIDTH

-

BORDER_LEN

or

temp

rect

y

<

BORDER_LEN

or

temp

rect

y

>

HEIGHT

-

BORDER_LEN

cls

group_enemy

remove

temp

print

“子彈超出邊界,移除子彈”

continue

if

temp

direction

==

“up”

temp

rect

=

temp

rect

move

((

0

-

temp

speed

))

elif

temp

direction

==

“down”

temp

rect

=

temp

rect

move

((

0

temp

speed

))

elif

temp

direction

==

“left”

temp

rect

=

temp

rect

move

((

-

temp

speed

0

))

elif

temp

direction

==

“right”

temp

rect

=

temp

rect

move

((

temp

speed

0

))

# 子彈碰磚牆(如果相碰,那麼就移除當前子彈以及磚牆)

pygame

sprite

groupcollide

cls

group

Brick

group

True

True

pygame

sprite

groupcollide

cls

group_enemy

Brick

group

True

True

# 子彈碰鐵牆(如果相碰,那麼只移除子彈)

for

bullet

in

cls

group

if

pygame

sprite

spritecollide

bullet

Iron

group

False

None

):

cls

group

remove

bullet

for

bullet

in

cls

group_enemy

if

pygame

sprite

spritecollide

bullet

Iron

group

False

None

):

cls

group_enemy

remove

bullet

@classmethod

def

show

cls

screen

):

“”“

顯示子彈

”“”

for

temp

in

cls

group

screen

blit

temp

image

temp

rect

for

temp

in

cls

group_enemy

screen

blit

temp

image

temp

rect

@classmethod

def

move_and_show

cls

screen

):

“”“

移動、顯示子彈

”“”

cls

auto_move

()

cls

show

screen

class

PlayerTank

pygame

sprite

Sprite

):

“”“

我方坦克類

”“”

# 定義類屬性,儲存我方坦克(如果是單人模式就只有1個,如果是雙人模式就有2個)

player_group

=

list

()

# 定義精靈組,用來碰撞等判斷

group

=

pygame

sprite

Group

()

def

__init__

self

player

top_left

):

“”“

實現初始化功能

”“”

# 呼叫父類的初始化方法,這樣才能夠實現必要的初始化操作

super

()

__init__

()

# 坦克的圖片

image_path

=

“resources/images/playerTank/tank_T1_0。png”

if

player

==

“player1”

else

“resources/images/playerTank/tank_T2_0。png”

self

tank_all_image

=

pygame

image

load

image_path

convert_alpha

()

self

image

=

self

tank_all_image

subsurface

((

0

0

),

48

48

))

# 當使用碰撞判斷方法時,pygame就需要知道當前要檢測的物體的位置,所以這個rect屬性一定要設定

self

rect

=

self

image

get_rect

()

self

rect

topleft

=

top_left

# 記錄初始位置,以便在被擊中後能夠重新在預設位置出現

self

origin_position

=

top_left

# 定義移動的步長

self

step_length

=

8

# 坦克的預設方向

self

direction

=

“up”

# 預設朝上

# 移動緩衝, 用於避免坦克連續移動過快導致不方便調整位置

self

move_cache_time

=

4

self

move_cache_count

=

0

# 坦克輪子轉動效果

self

switch_count

=

0

self

switch_time

=

2

self

switch_image_index

=

False

self

image_postion_index

=

0

# 發射子彈的間隔

self

is_bullet_cooling

=

False

# 如果是第一次發射子彈,則不在冷卻時間內,可以正常發射

self

bullet_cooling_count

=

0

self

bullet_cooling_time

=

30

# 我方坦克生命次數

self

life_num

=

3

# 標記此坦克是否顯示

self

is_show_flag

=

True

# 將當前物件新增到類屬性中,這樣就可以透過類物件訪問到我方坦克

self

__class__

player_group

append

self

# 或者self。player_group。append(self)也是可以的

# 新增到精靈組

self

group

add

self

def

update_direction

self

):

“”“

更新坦克的朝向

”“”

if

self

direction

==

‘up’

self

image

=

self

tank_all_image

subsurface

((

0

0

),

48

48

))

self

image_postion_index

=

0

elif

self

direction

==

‘down’

self

image

=

self

tank_all_image

subsurface

((

0

48

),

48

48

))

self

image_postion_index

=

48

elif

self

direction

==

‘left’

self

image

=

self

tank_all_image

subsurface

((

0

96

),

48

48

))

self

image_postion_index

=

96

elif

self

direction

==

‘right’

self

image

=

self

tank_all_image

subsurface

((

0

144

),

48

48

))

self

image_postion_index

=

144

def

move

self

direction

group_list

):

“”“

根據鍵盤調整坦克方向,然後移動

”“”

# 如果要移動的方向與當前坦克的朝向不同,則先調整朝向

if

self

direction

!=

direction

self

direction

=

direction

self

update_direction

()

return

# 移動緩衝

self

move_cache_count

+=

1

if

self

move_cache_count

<

self

move_cache_time

return

else

self

move_cache_count

=

0

# 移動坦克

# 複製一份當前玩家坦克的座標,如果碰到障礙物之後,可以進行恢復

rect_ori

=

self

rect

if

direction

==

“up”

self

rect

=

self

rect

move

((

0

-

self

step_length

))

elif

direction

==

“down”

self

rect

=

self

rect

move

((

0

self

step_length

))

elif

direction

==

“left”

self

rect

=

self

rect

move

((

-

self

step_length

0

))

elif

direction

==

“right”

self

rect

=

self

rect

move

((

self

step_length

0

))

# 檢測碰撞“磚牆”、“鐵牆”、“冰”、“河流”。“樹”無需檢查

for

group

in

group_list

if

pygame

sprite

spritecollide

self

group

False

None

):

self

rect

=

rect_ori

# 判斷碰撞到邊界

if

self

rect

top

<

BORDER_LEN

self

rect

top

=

BORDER_LEN

elif

self

rect

bottom

>

HEIGHT

-

BORDER_LEN

self

rect

bottom

=

HEIGHT

-

BORDER_LEN

elif

self

rect

left

<

BORDER_LEN

self

rect

left

=

BORDER_LEN

elif

self

rect

right

>

WIDTH

-

BORDER_LEN

self

rect

right

=

WIDTH

-

BORDER_LEN

# 為坦克輪動特效切換圖片

self

switch_count

+=

1

if

self

switch_count

>

self

switch_time

self

switch_count

=

0

self

switch_image_index

=

not

self

switch_image_index

self

image

=

self

tank_all_image

subsurface

((

48

*

int

self

switch_image_index

),

self

image_postion_index

),

48

48

))

def

fire

self

):

“”“

發射子彈

”“”

if

not

self

is_bullet_cooling

if

self

direction

==

“up”

position

=

self

rect

centerx

self

rect

y

elif

self

direction

==

“down”

position

=

self

rect

centerx

self

rect

y

+

48

elif

self

direction

==

“left”

position

=

self

rect

x

self

rect

centery

elif

self

direction

==

“right”

position

=

self

rect

x

+

48

self

rect

centery

Bullet

“player”

self

direction

position

print

“我方坦克發射子彈”

@classmethod

def

move_player_tank

cls

is_dual_mode

group_list

):

“”“

控制我方坦克移動

”“”

# 檢查使用者按鍵,從而控制坦克移動

key_pressed

=

pygame

key

get_pressed

()

# 定義移動的步長

step_length

=

8

# 複製一份當前玩家1的座標,如果碰到障礙物之後,可以進行恢復

# rect_ori = cls。player_group[0]。rect

# 玩家一, ASWD移動

if

key_pressed

pygame

K_w

]:

cls

player_group

0

move

“up”

group_list

elif

key_pressed

pygame

K_s

]:

cls

player_group

0

move

“down”

group_list

elif

key_pressed

pygame

K_a

]:

cls

player_group

0

move

“left”

group_list

elif

key_pressed

pygame

K_d

]:

cls

player_group

0

move

“right”

group_list

elif

key_pressed

pygame

K_SPACE

]:

# 如果按下了空格鍵,那麼就發射子彈

cls

player_group

0

fire

()

# 檢查玩家1是否碰撞到障礙物

# # 檢測碰撞“磚牆”

# if pygame。sprite。spritecollide(cls。player_group[0], brick_group, False, None):

# print(“玩家1碰到了磚牆”, cls。player_group[0]。rect)

# cls。player_group[0]。rect = rect_ori

# 玩家二, ↑↓←→移動

if

is_dual_mode

# 複製一份當前玩家2的座標,如果碰到障礙物之後,可以進行恢復

# rect_ori = cls。player_group[1]。rect

if

key_pressed

pygame

K_UP

]:

cls

player_group

1

move

“up”

group_list

elif

key_pressed

pygame

K_DOWN

]:

cls

player_group

1

move

“down”

group_list

elif

key_pressed

pygame

K_LEFT

]:

cls

player_group

1

move

“left”

group_list

elif

key_pressed

pygame

K_RIGHT

]:

cls

player_group

1

move

“right”

group_list

elif

key_pressed

pygame

K_KP0

]:

# 如果按下了數字0,那麼就發射子彈

cls

player_group

1

fire

()

# 檢查玩家2是否碰撞到障礙物

# 檢測碰撞“磚牆”

# if pygame。sprite。spritecollide(cls。player_group[1], brick_group, False, None):

# cls。player_group[1]。rect = rect_ori

def

bullet_cooling

self

):

“”“

判斷髮射子彈的冷卻時間是否達到

”“”

# 對發射子彈的冷卻時間計數

self

bullet_cooling_count

+=

1

if

self

bullet_cooling_count

>

self

bullet_cooling_time

self

is_bullet_cooling

=

False

# 不在冷卻狀態,即意味著可以發射子彈

self

bullet_cooling_count

=

0

print

“冷卻完畢。。。”

else

self

is_bullet_cooling

=

True

# 不能發射,正在冷卻

def

judge_bomb

self

):

“”“

判斷是否被擊中

”“”

# 判斷碰撞到敵方坦克子彈

if

pygame

sprite

spritecollide

self

Bullet

group_enemy

True

None

):

self

life_num

-=

1

# 如果被擊中,那麼就生命值-1

if

self

life_num

==

0

self

is_show_flag

=

False

# 如果已經沒有了生命值,那麼就標記為不顯示

# 重新設定位置為初始位置

self

rect

topleft

=

self

origin_position

@classmethod

def

show

cls

screen

is_dual_mode

):

“”“

顯示我方坦克

”“”

if

cls

player_group

if

cls

player_group

0

is_show_flag

screen

blit

cls

player_group

0

image

cls

player_group

0

rect

# 對發射子彈的冷卻時間計數

cls

player_group

0

bullet_cooling

()

# 判斷是否被擊中

cls

player_group

0

judge_bomb

()

if

is_dual_mode

and

cls

player_group

1

is_show_flag

# 如果是雙人模式

screen

blit

cls

player_group

1

image

cls

player_group

1

rect

# 對發射子彈的冷卻時間計數

cls

player_group

1

bullet_cooling

()

# 判斷是否被擊中

cls

player_group

1

judge_bomb

()

class

PlayerHome

pygame

sprite

Sprite

):

“”“

我方大本營

”“”

home

=

None

def

__init__

self

position

):

pygame

sprite

Sprite

__init__

self

self

image

=

pygame

image

load

“resources/images/home/home1。png”

self

rect

=

self

image

get_rect

()

self

rect

left

self

rect

top

=

position

self

__class__

home

=

self

@classmethod

def

show

cls

screen

):

“”“

顯示大本營

”“”

screen

blit

cls

home

image

cls

home

rect

class

EnemyTank

pygame

sprite

Sprite

):

“”“

敵人坦克類

”“”

# 定義精靈組,將所有的例項物件新增到裡面

group

=

pygame

sprite

Group

()

def

__init__

self

position

):

# 呼叫父類的初始化方法,這樣才能夠實現必要的初始化操作

super

()

__init__

()

# 坦克預設的朝向

self

direction

=

random

choice

([

“up”

“down”

“left”

“right”

])

self

tank_all_image

=

pygame

image

load

“resources/images/enemyTank/enemy_1_0。png”

self

image

=

None

self

update_direction

()

# 根據隨機朝向,計算出要使用的坦克圖片

self

rect

=

self

image

get_rect

()

self

rect

topleft

=

position

# 坦克預設的速度

self

speed

=

random

choice

([

2

4

])

# 發射子彈的間隔

self

is_bullet_cooling

=

True

self

bullet_cooling_count

=

0

self

bullet_cooling_time

=

5

# 新增到精靈組

self

group

add

self

def

update_direction

self

):

“”“

更新坦克的朝向

”“”

if

self

direction

==

‘up’

self

image

=

self

tank_all_image

subsurface

((

0

0

),

48

48

))

elif

self

direction

==

‘down’

self

image

=

self

tank_all_image

subsurface

((

0

48

),

48

48

))

elif

self

direction

==

‘left’

self

image

=

self

tank_all_image

subsurface

((

0

96

),

48

48

))

elif

self

direction

==

‘right’

self

image

=

self

tank_all_image

subsurface

((

0

144

),

48

48

))

@classmethod

def

show

cls

screen

):

for

temp

in

cls

group

screen

blit

temp

image

temp

rect

def

move

self

group_list

):

“”“

敵方坦克自動移動

:return:

”“”

# 記錄位置,以便在碰到障礙物之後,能夠恢復

rect_ori

=

self

rect

if

self

direction

==

“up”

self

rect

=

self

rect

move

((

0

-

self

speed

))

elif

self

direction

==

“down”

self

rect

=

self

rect

move

((

0

self

speed

))

elif

self

direction

==

“left”

self

rect

=

self

rect

move

((

-

self

speed

0

))

elif

self

direction

==

“right”

self

rect

=

self

rect

move

((

self

speed

0

))

# 檢測碰撞“磚牆”、“鐵牆”、“冰”、“河流”。“樹”無需檢查

for

group

in

group_list

if

pygame

sprite

spritecollide

self

group

False

None

):

self

rect

=

rect_ori

# 隨機得到新的朝向

self

direction

=

random

choice

([

“up”

“down”

“left”

“right”

])

self

update_direction

()

# 碰撞到其他敵方坦克

# 先將本坦克從精靈組中移除

self

group

remove

self

# 然後再判斷是否碰撞到其他敵方坦克

if

pygame

sprite

spritecollide

self

self

group

False

None

):

self

rect

=

rect_ori

# 隨機得到新的朝向

self

direction

=

random

choice

([

“up”

“down”

“left”

“right”

])

self

update_direction

()

# 判斷之後再將本坦克新增到精靈組

self

group

add

self

# 碰撞到玩家坦克

if

pygame

sprite

spritecollide

self

PlayerTank

group

False

None

):

self

rect

=

rect_ori

# 隨機得到新的朝向

self

direction

=

random

choice

([

“up”

“down”

“left”

“right”

])

self

update_direction

()

# 碰撞到我方子彈

if

pygame

sprite

spritecollide

self

Bullet

group

True

None

):

self

group

remove

self

# 判斷碰撞到邊界,然後再次隨機新朝向

if

self

rect

top

<

BORDER_LEN

self

rect

top

=

BORDER_LEN

# 隨機得到新的朝向

self

direction

=

random

choice

([

“up”

“down”

“left”

“right”

])

self

update_direction

()

elif

self

rect

bottom

>

HEIGHT

-

BORDER_LEN

self

rect

bottom

=

HEIGHT

-

BORDER_LEN

# 隨機得到新的朝向

self

direction

=

random

choice

([

“up”

“down”

“left”

“right”

])

self

update_direction

()

elif

self

rect

left

<

BORDER_LEN

self

rect

left

=

BORDER_LEN

# 隨機得到新的朝向

self

direction

=

random

choice

([

“up”

“down”

“left”

“right”

])

self

update_direction

()

elif

self

rect

right

>

WIDTH

-

BORDER_LEN

self

rect

right

=

WIDTH

-

BORDER_LEN

# 隨機得到新的朝向

self

direction

=

random

choice

([

“up”

“down”

“left”

“right”

])

self

update_direction

()

@classmethod

def

auto_move

cls

group_list

):

for

temp

in

cls

group

temp

move

group_list

@classmethod

def

auto_move_and_show

cls

screen

group_list

):

cls

auto_move

group_list

cls

show

screen

def

judge_cooling_and_fire

self

):

“”“

判斷是否達到冷卻時間(即發射子彈要有間隔),然後發射

”“”

self

bullet_cooling_count

+=

1

if

self

bullet_cooling_count

>

self

bullet_cooling_time

self

bullet_cooling_count

=

0

if

random

randint

1

10

==

6

# 如果隨機得到的數字恰巧是6,那麼就表示冷卻時間到

self

is_bullet_cooling

=

False

if

not

self

is_bullet_cooling

# 建立子彈物件

if

self

direction

==

“up”

position

=

self

rect

centerx

self

rect

y

elif

self

direction

==

“down”

position

=

self

rect

centerx

self

rect

y

+

48

elif

self

direction

==

“left”

position

=

self

rect

x

self

rect

centery

elif

self

direction

==

“right”

position

=

self

rect

x

+

48

self

rect

centery

Bullet

“enemy”

self

direction

position

# 發射完畢後,立刻設定為冷卻狀態

self

is_bullet_cooling

=

True

@classmethod

def

fire

cls

):

“”“

發射子彈

”“”

for

temp

in

cls

group

temp

judge_cooling_and_fire

()

class

Game

object

):

“”“

遊戲控制類

”“”

def

__init__

self

):

“”“

初始化工作

”“”

# 遊戲初始化

pygame

init

()

# 建立用來顯示畫面的物件(理解為相框)

self

screen

=

pygame

display

set_mode

((

WIDTH

HEIGHT

))

def

game_start_interface

self

):

“”“

顯示遊戲開始畫面(讓使用者選擇遊戲人數)

:return:False為單人模式,True為雙人模式

”“”

# 準備用到的圖片

background_img

=

pygame

image

load

“resources/images/others/background。png”

logo_img

=

pygame

image

load

“resources/images/others/logo。png”

logo_img

=

pygame

transform

scale

logo_img

446

70

))

# 圖片縮小1倍,即將892x140——>446x70

logo_rect

=

logo_img

get_rect

()

# 得到這個圖片的左上角的座標(預設是(0,0)點),以及寬高

# 為了能夠讓logo圖片在合適的位置顯示,我們可以設定它的中心點的座標,它會根據自身的寬高自動計算出左上角的座標

logo_rect

centerx

logo_rect

centery

=

WIDTH

/

2

HEIGHT

//

4

# print(logo_rect。topleft) # 在終端中看到,此時輸出的值是:(92, 122)

# 準備要顯示文字(1player、2players)

# 字型

font

=

pygame

font

Font

FONTPATH

60

# 60表示要顯示的字型大小

font_color_white

=

255

255

255

# 要顯示的字型顏色為白色

# 1player

one_player_text

=

font

render

‘1 PLAYER’

True

font_color_white

one_player_rect

=

one_player_text

get_rect

()

one_player_rect

left

one_player_rect

top

=

WIDTH

/

2

-

50

HEIGHT

/

2

-

60

# 2players

two_players_text

=

font

render

‘2 PLAYERS’

True

font_color_white

two_players_rect

=

two_players_text

get_rect

()

two_players_rect

left

two_players_rect

top

=

WIDTH

/

2

-

50

HEIGHT

/

2

# 遊戲人數選擇時的圖片

select_player_num_tank

=

pygame

image

load

“resources/images/playerTank/tank_T1_0。png”

convert_alpha

()

subsurface

((

0

144

),

48

48

))

select_player_num_tank_rect

=

select_player_num_tank

get_rect

()

# 遊戲開始提示

game_tip

=

font

render

‘press to start’

True

font_color_white

game_tip_rect

=

game_tip

get_rect

()

game_tip_rect

centerx

game_tip_rect

top

=

WIDTH

/

2

HEIGHT

/

1。4

# 建立一個計時器,用來實現更加方便的延時(防止while迴圈過快,佔用太多CPU的問題)

clock

=

pygame

time

Clock

()

# 儲存遊戲人數(False單人,True雙人)

is_dual_mode

=

False

# 主迴圈

while

True

# 事件檢測(例如點選了鍵盤、滑鼠點選等)

for

event

in

pygame

event

get

():

if

event

type

==

pygame

QUIT

# 如果用滑鼠點選了❌,那麼就退出程式

pygame

quit

()

sys

exit

()

# 退出程式

elif

event

type

==

pygame

KEYDOWN

if

event

key

==

pygame

K_RETURN

return

is_dual_mode

elif

event

key

==

pygame

K_UP

or

event

key

==

pygame

K_DOWN

or

event

key

==

pygame

K_w

or

event

key

==

pygame

K_s

is_dual_mode

=

not

is_dual_mode

# print(“當前選擇的遊戲人數是(True雙人、False單人)”, is_dual_mode)

# 從(0,0)點(即左上角)開始貼一張圖片(理解為在screen這個相框中從左上角開始貼一張照片)

self

screen

blit

background_img

0

0

))

# 顯示logo

self

screen

blit

logo_img

logo_rect

# 顯示遊戲人數文字

self

screen

blit

one_player_text

one_player_rect

self

screen

blit

two_players_text

two_players_rect

# 顯示標記選擇的人數的tank

if

is_dual_mode

# 雙人模式

select_player_num_tank_rect

right

select_player_num_tank_rect

top

=

two_players_rect

left

-

10

two_players_rect

top

self

screen

blit

select_player_num_tank

select_player_num_tank_rect

else

# 單人模式

select_player_num_tank_rect

right

select_player_num_tank_rect

top

=

one_player_rect

left

-

10

one_player_rect

top

self

screen

blit

select_player_num_tank

select_player_num_tank_rect

# 顯示提示

self

screen

blit

game_tip

game_tip_rect

# 顯示screen這個相框的內容(此時在這個相框中的內容像照片、文字等會顯示出來)

pygame

display

update

()

# FPS(每秒鐘顯示畫面的次數)

clock

tick

60

# 透過一定的延時,實現1秒鐘能夠迴圈60次

def

game_end_interface

self

is_win

):

“”“

顯示遊戲結束畫面

”“”

# 背景

background_img

=

pygame

image

load

“resources/images/others/background。png”

# 遊戲失敗圖

game_over_img

=

pygame

image

load

“resources/images/others/gameover。png”

game_over_img

=

pygame

transform

scale

game_over_img

150

75

))

game_over_img_rect

=

game_over_img

get_rect

()

game_over_img_rect

midtop

=

WIDTH

/

2

HEIGHT

/

8

# 遊戲勝利、失敗字型

color_white

=

255

255

255

font

=

pygame

font

Font

FONTPATH

60

# 遊戲勝利與否的提示

if

is_win

font_render

=

font

render

‘Congratulations, You win!’

True

color_white

else

font_render

=

font

render

‘Sorry, You fail!’

True

color_white

font_rect

=

font_render

get_rect

()

font_rect

centerx

font_rect

centery

=

WIDTH

/

2

HEIGHT

/

3

# 用於選擇退出或重新開始

# 用於選擇的坦克游標

tank_cursor

=

pygame

image

load

“resources/images/playerTank/tank_T1_0。png”

convert_alpha

()

subsurface

((

0

144

),

48

48

))

tank_rect

=

tank_cursor

get_rect

()

# 重新執行

restart_render_white

=

font

render

‘RESTART’

True

color_white

restart_rect

=

restart_render_white

get_rect

()

restart_rect

left

restart_rect

top

=

WIDTH

/

2。4

HEIGHT

/

2

# 退出

quit_render_white

=

font

render

‘QUIT’

True

color_white

quit_rect

=

quit_render_white

get_rect

()

quit_rect

left

quit_rect

top

=

WIDTH

/

2。4

HEIGHT

/

1。6

# 標記當前選擇的是退出還是繼續遊戲

is_quit_game

=

False

# 建立計時器物件,用於控制重新整理頻率

clock

=

pygame

time

Clock

()

# 主迴圈

while

True

# 檢查鍵盤事件

for

event

in

pygame

event

get

():

if

event

type

==

pygame

QUIT

pygame

quit

()

sys

exit

()

elif

event

type

==

pygame

KEYDOWN

if

event

key

==

pygame

K_RETURN

return

is_quit_game

elif

event

key

==

pygame

K_UP

or

event

key

==

pygame

K_DOWN

or

event

key

==

pygame

K_w

or

event

key

==

pygame

K_s

is_quit_game

=

not

is_quit_game

# 顯示背景

self

screen

blit

background_img

0

0

))

self

screen

blit

game_over_img

game_over_img_rect

self

screen

blit

font_render

font_rect

if

not

is_quit_game

tank_rect

right

tank_rect

top

=

restart_rect

left

-

10

restart_rect

top

self

screen

blit

tank_cursor

tank_rect

self

screen

blit

quit_render_white

quit_rect

self

screen

blit

restart_render_white

restart_rect

else

tank_rect

right

tank_rect

top

=

quit_rect

left

-

10

quit_rect

top

self

screen

blit

tank_cursor

tank_rect

self

screen

blit

quit_render_white

quit_rect

self

screen

blit

restart_render_white

restart_rect

# 重新整理顯示畫面,此時才會真正的顯示

pygame

display

update

()

# 控制頻率,FPS為60,每秒鐘60次重新整理

clock

tick

60

def

parse_game_level_file

self

):

“”“

解析關卡檔案

”“”

# 每個地圖元素佔用的畫素(配置檔案,例如1。lvl中註釋裡說明了Grid SIZE: 24 * 24 pixels)

grid_size

=

24

# 定義大本營

# home_dict = dict()

with

open

“。/levels/3。lvl”

errors

=

‘ignore’

as

f

num_row

=

-

1

# 用來標記地圖元素是整個地圖的第幾行(總共26行,26列)

for

line

in

f

readlines

():

line

=

line

strip

\n

# 切除每行行尾的換行符

# 如果當前要處理的行是地圖元素,那麼就繼續處理

if

line

0

in

“S”

“B”

“I”

“R”

“C”

“T”

]:

# 地圖元素

num_row

+=

1

# print(“當前是第%d行” % num_row)

for

num_col

elem

in

enumerate

line

split

‘ ’

)):

# print(“當前是第%d行,第%d列” % (num_row, num_col))

position

=

BORDER_LEN

+

num_col

*

grid_size

BORDER_LEN

+

num_row

*

grid_size

if

elem

==

‘B’

# 建立磚牆物件,然後新增到精靈組

Brick

position

elif

elem

==

‘I’

# 建立鐵牆物件,然後新增到精靈組

Iron

position

elif

elem

==

‘R’

# 建立河流物件,然後新增到精靈組

River

position

elif

elem

==

‘C’

# 建立冰物件,然後新增到精靈組

Ice

position

elif

elem

==

‘T’

# 建立樹物件,然後新增到精靈組

Tree

position

elif

line

startswith

‘%HOMEPOS’

):

# 大本營位置

home_position

=

line

split

‘:’

)[

-

1

home_position

=

int

home_position

split

‘,’

)[

0

]),

int

home_position

split

‘,’

)[

1

])

home_position

=

BORDER_LEN

+

home_position

0

*

grid_size

BORDER_LEN

+

home_position

1

*

grid_size

# 建立大本營類

PlayerHome

home_position

elif

line

startswith

‘%PLAYERTANKPOS’

):

# 我方坦克初始位置

player_tank_positions

=

line

split

‘:’

)[

-

1

player_tank_positions

=

[(

int

pos

split

‘,’

)[

0

]),

int

pos

split

‘,’

)[

1

]))

for

pos

in

player_tank_positions

split

‘ ’

)]

player_tank_positions

=

[[

BORDER_LEN

+

pos

0

*

grid_size

BORDER_LEN

+

pos

1

*

grid_size

for

pos

in

player_tank_positions

# 從這個圖片中切除一部分來當做要使用的圖片,即玩家1的坦克

# image = pygame。image。load(“resources/images/playerTank/tank_T1_0。png”)。convert_alpha()

# image = image。subsurface((0, 0), (48, 48))

# player1_dict = {

# “image”: image,

# “top_left”: player_tank_positions[0]

# }

# 建立我方玩家1的坦克,然後新增到列表中

PlayerTank

“player1”

player_tank_positions

0

])

# 從這個圖片中切除一部分來當做要使用的圖片,即玩家2的坦克

# image = pygame。image。load(“resources/images/playerTank/tank_T2_0。png”)。convert_alpha()

# image = image。subsurface((0, 0), (48, 48))

# player2_dict = {

# “image”: image,

# “top_left”: player_tank_positions[1]

# }

# player_group。append(player1_dict)

# player_group。append(player2_dict)

# 建立我方玩家2的坦克,然後新增到列表中

PlayerTank

“player2”

player_tank_positions

1

])

elif

line

startswith

%E

NEMYTANKPOS’

):

# 敵方坦克初始位置

position

=

line

split

‘:’

)[

-

1

position

=

[[

int

pos

split

‘,’

)[

0

]),

int

pos

split

‘,’

)[

1

])]

for

pos

in

position

split

‘ ’

)]

position

=

[(

BORDER_LEN

+

pos

0

*

grid_size

BORDER_LEN

+

pos

1

*

grid_size

for

pos

in

position

# 根據敵方坦克的初始位置建立多個坦克

for

pos

in

position

EnemyTank

pos

def

game_run_level

self

is_dual_mode

):

“”“

運行遊戲

”“”

# 背景圖片

background_img

=

pygame

image

load

“resources/images/others/background。png”

# 呼叫解析關卡配置檔案

self

parse_game_level_file

()

# 幀率控制物件

clock

=

pygame

time

Clock

()

# 運行遊戲的主迴圈

is_win

=

False

is_running

=

True

while

is_running

# 事件檢測(例如點選了鍵盤、滑鼠點選等)

for

event

in

pygame

event

get

():

if

event

type

==

pygame

QUIT

pygame

quit

()

sys

exit

()

# 透過鍵盤控制坦克移動

PlayerTank

move_player_tank

is_dual_mode

Brick

group

River

group

Ice

group

Iron

group

])

# 敵方坦克開發

EnemyTank

fire

()

# 碰撞檢測

# 子彈碰大本營(無論是我方還是敵方子彈,只要碰到都認為本關卡遊戲結束)

if

pygame

sprite

spritecollide

PlayerHome

home

Bullet

group

True

None

or

pygame

sprite

spritecollide

PlayerHome

home

Bullet

group_enemy

True

None

):

is_running

=

False

# 如果碰撞到大本營,那麼透過修改這個變數為False讓while迴圈結束,遊戲即將結束執行

is_win

=

False

# 如果敵方坦克沒有了,則認為我方勝利

if

len

EnemyTank

group

==

0

is_running

=

False

is_win

=

True

# 如果我方坦克沒有了生命值,則認為遊戲輸了

if

not

is_dual_mode

and

PlayerTank

player_group

0

life_num

==

0

or

\

is_dual_mode

and

PlayerTank

player_group

0

life_num

==

0

and

PlayerTank

player_group

1

life_num

==

0

):

is_running

=

False

is_win

=

False

# 顯示遊戲背景

self

screen

blit

background_img

0

0

))

# 顯示磚牆

Brick

show

self

screen

# 顯示鐵牆

Iron

show

self

screen

# 顯示河流

River

show

self

screen

# 顯示冰

Ice

show

self

screen

# 顯示樹

Tree

show

self

screen

# 顯示大本營

PlayerHome

show

self

screen

# 顯示我方坦克

PlayerTank

show

self

screen

is_dual_mode

# 顯示我方坦克發射的子彈

Bullet

move_and_show

self

screen

# 顯示敵方坦克

EnemyTank

auto_move_and_show

self

screen

Brick

group

River

group

Ice

group

Iron

group

])

# 重新整理要顯示的內容,從而真正的顯示

pygame

display

update

()

# 每秒鐘控制為60幀

clock

tick

60

return

is_win

def

clean

self

):

“”“

清理上一次遊戲留下的殘留

”“”

EnemyTank

group

empty

()

PlayerTank

group

empty

()

PlayerTank

player_group

clear

()

Brick

group

empty

()

Ice

group

empty

()

River

group

empty

()

Iron

group

empty

()

Tree

group

empty

()

Bullet

group

empty

()

Bullet

group_enemy

empty

()

def

run

self

):

# 顯示遊戲開始畫面,讓使用者選擇遊戲人數

is_dual_mode

=

self

game_start_interface

()

# 呼叫遊戲關卡函式,從而開始遊戲

is_win

=

self

game_run_level

is_dual_mode

# 接下來根據is_win來顯示對應的輸贏介面

while

True

if

self

game_end_interface

is_win

):

# 如果返回為True,那麼就意味著退出遊戲,否則繼續遊戲

break

# 繼續重新遊戲

# 清理上一次遊戲的殘留

self

clean

()

# 建立用來顯示畫面的物件(理解為相框)

pygame

display

set_mode

((

WIDTH

HEIGHT

))

# 顯示遊戲開始畫面,讓使用者選擇遊戲人數

is_dual_mode

=

self

game_start_interface

()

# 呼叫遊戲關卡函式,從而開始遊戲

is_win

=

self

game_run_level

is_dual_mode

if

__name__

==

‘__main__’

“”“

整體流程的控制

”“”

game

=

Game

()

game

run

()