Python3+pygame實現的90坦克大戰 程式碼完整 有演示效果
我是一個典型的80後,年輕時玩過了特別多的遊戲,所以這幾天用Python3+pygame實現了一個另外小遊戲”坦克大戰“(其他的遊戲,請翻閱我的部落格)
本例項程式碼量有些多,完整的版本在1000行左右(當然瞭如果再次最佳化的話 會減少一部分)
分享出來,希望能幫助到大家,畢竟自己做教育行業做了這麼多年,還是教育情懷的,哈哈哈哈哈經典遊戲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
)
(
“子彈超出邊界,移除子彈”
)
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
)
(
“子彈超出邊界,移除子彈”
)
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
)
(
“我方坦克發射子彈”
)
@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
(
“冷卻完畢。。。”
)
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
,
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
()