python黑白棋 pygame_玩转pygame——游戏开发

使用pygame进行游戏开发

Pygame是一个开源的Python模块,专门用于多媒体应用(如电子游戏)的开发,其中包含对图像、声音、视频、事件、碰撞等的支持。Pygame建立在SDL的基础上,SDL是一套跨平台的多媒体开发库,用C语言实现,被广泛的应用于游戏、模拟器、播放器等的开发。而Pygame让游戏开发者不再被底层语言束缚,可以更多的关注游戏的功能和逻辑。

具体功能可以查看pygame的官方网站

五子棋游戏制作

制作游戏窗口

可以通过pygame中draw模块的函数在窗口上绘图,可以绘制的图形包括:线条、矩形、多边形、圆、椭圆、圆弧等。需要说明的是,屏幕坐标系是将屏幕左上角设置为坐标原点(0, 0),向右是x轴的正向,向下是y轴的正向,在表示位置或者设置尺寸的时候,我们默认的单位都是像素。所谓像素就是屏幕上的一个点,你可以用浏览图片的软件试着将一张图片放大若干倍,就可以看到这些点。pygame中表示颜色用的是色光三原色表示法,即通过一个元组或列表来指定颜色的RGB值,每个值都在0~255之间,因为是每种原色都用一个8位(bit)的值来表示,三种颜色相当于一共由24位构成,这也就是常说的“24位颜色表示法”。

import pygame

def main():

# 初始化导入的pygame中的模块

pygame.init()

screen = pygame.display.set_mode([640, 640]) # 初始化用于显示的窗口并设置窗口尺寸

pygame.display.set_caption('五子棋') # 设置当前窗口的标题

screen.fill([218, 165, 105]) # 填充背景色 红绿蓝三原色

running = True

while running: # 开启一个事件循环处理发生的事件

for event in pygame.event.get(): # 从消息队列中获取事件并对事件进行处理

if event.type == pygame.QUIT:

running = False

if __name__ == '__main__':

main()

画棋盘

创建棋子类对象和棋盘类对象,棋盘类里面创建一个15*15的数组的属性,再写一个重置棋盘的方法,令这个棋盘数组全部等于空,还有画棋盘的方法,通过pygame的draw模块在窗口上绘画棋盘、天元、边框和棋子,还有一个判断该位置是否有棋子,可以下的方法

class Chess(object):

"""棋子类"""

def __init__(self, x, y, is_black):

self._is_black = is_black

self._row = x

self._col = y

@property

def is_black(self):

return self._is_black

@property

def row(self):

return self._row

@property

def col(self):

return self._col

class RenjuBoard(object):

"""棋盘类"""

def __init__(self):

self._board = [[]] * 15

self.reset()

def reset(self):

"""重置棋盘"""

for row in range(len(self._board)):

self._board[row] = [EMPTY] * 15

def move(self, row, col, is_black):

"""判断是否有棋子"""

if self._board[row][col] == EMPTY:

self._board[row][col] = BLACK if is_black else WHITE

return True

return False

def draw(self, screen):

"""画棋盘"""

for i in range(1, 16): # 画格子:窗口,颜色,起始位置,结束位置,粗细

pygame.draw.line(screen, black_color, [40, 40 * i], [600, 40 * i], 1)

pygame.draw.line(screen, black_color, [40 * i, 40], [40 * i, 600], 1)

pygame.draw.rect(screen, black_color, [36, 36, 568, 568], 4) # 画边框

# 画圆 窗口,颜色,位置,半径,0实心

pygame.draw.circle(screen, black_color, [320, 320], 4, 0)

pygame.draw.circle(screen, black_color, [160, 160], 3, 0)

pygame.draw.circle(screen, black_color, [160, 480], 3, 0)

pygame.draw.circle(screen, black_color, [480, 480], 3, 0)

pygame.draw.circle(screen, black_color, [480, 160], 3, 0)

for row in range(len(self._board)):

for col in range(len(self._board[row])):

if self._board[row][col] != EMPTY:

ccolor = black_color if self._board[row][col] == BLACK else white_color

pos = [40 * (col + 1), 40 * (row + 1)]

pygame.draw.circle(screen, ccolor, pos, 20, 0)

完善主函数 事件处理

判断输赢,绘画文字,获取鼠标点击事件,绘画棋盘等

def main():

board = RenjuBoard() # 创建棋盘对象

chess = [[EMPTY] * 16 for _ in range(16)] # 棋子位置

is_black = True # 黑白棋分辨变量

pygame.init() # 初始化pygame

screen = pygame.display.set_mode([640, 640]) # 初始化用于显示的窗口并设置窗口尺寸

pygame.display.set_caption('五子棋') # 设置当前窗口的标题

screen.fill([218, 165, 105]) # 填充背景色 红绿蓝三原色

board.draw(screen) # 画棋盘

pygame.display.flip() # 刷新窗口

runing = True # 是否退出游戏变量

gameover = True # 游戏是否结束变量

while runing: # 开启一个事件循环处理发生的事件

for event in pygame.event.get(): # 从消息队列中获取事件并对事件进行处理

if event.type == pygame.QUIT:

runing = False

elif event.type == pygame.KEYUP:

if event.key == pygame.K_F2:

board.reset()

gameover = True

is_black = True

pygame.display.flip()

elif event.type == pygame.MOUSEBUTTONDOWN and event.button == 1 and gameover: # 鼠标点击事件

x, y = event.pos

if 20 < x <= 600 and 20 < y < 600:

row = round((y - 40) / 40)

col = round((x - 40) / 40)

if board.move(row, col, is_black):

screen.fill([218, 165, 105])

board.draw(screen)

chess[col][row] = 1 if is_black else 2

pygame.display.flip()

if board.who_wins(col, row, chess):

my_font = pygame.font.SysFont("宋体", 60) # 字体,大小

if is_black:

position = my_font.render('game over, black win', True,

(255, 0, 0)) # 内容,抗锯齿,颜色,背景色(可选)

else:

position = my_font.render('game over, white win', True, (255, 0, 0))

screen.blit(position, (120, 310))

pygame.display.flip()

gameover = False

chess = [[EMPTY] * 16 for _ in range(16)]

is_black = not is_black

pygame.quit()

判断输赢

def who_wins(self, x, y, chess):

"""判断输赢"""

# 三维数组记录横向,纵向,左斜,右斜

dir = [[[-1, 0], [1, 0]], [[0, -1], [0, 1]], [[-1, -1], [1, 1]], [[1, -1], [-1, 1]]]

tempx = x

tempy = y

# 循环四个大方向

for i in range(4):

count = 1

# 循环两边方向

for j in range(2):

flag = True # 一直向一个方向遍历,有相同的,count+1,否则置flag为False

while flag:

tempx += dir[i][j][0]

tempy += dir[i][j][1]

if chess[x][y] == chess[tempx][tempy]:

count += 1

else:

flag = False

tempx = x

tempy = y

if count >= 5:

return True

return False

还可以有很多拓展,比如添加背景音乐和下棋时的音乐,可以使用pygame的music和mixer,还可以写成联网游戏,用网络编程来实现

如果开发3d游戏,可以使用Panda3D

运行结果图

sjx.png

飞机大战游戏

我自己结合所学知识用pygame做的一个小游戏,用了一些老师没就讲的碰撞精灵和音乐知识

首先,我创建了一个游戏对象的大类,有横坐标,纵坐标,速度,图片四个公共属性,并写成抽象类,然后分别创建了玩家类,敌机类,背景类,子弹类,并让他们继承来游戏对象类,除背景外,因为要用到碰撞精灵,所以使用了多重继承让他们还继承了pygame.sprite.Sprite。并重写了update精灵行为方法

from abc import ABCMeta,abstractclassmethod

import pygame

class GameObject(object, metaclass=ABCMeta):

"""游戏对象"""

def __init__(self, x=0, y=0,speed=0, image=None):

self._x = x

self._y = y

self._speed = speed

self._image = image

@property

def image(self):

return self._image

@property

def x(self):

return self._x

@property

def y(self):

return self._y

@property

def speed(self):

return self._speed

@x.setter

def x(self,x):

self._x = x

@y.setter

def y(self, y):

self._y = y

@speed.setter

def speed(self, speed):

self._speed = speed

@image.setter

def image(self, image):

self._image = image

class Player(GameObject, pygame.sprite.Sprite):

"""玩家"""

def __init__(self):

super().__init__(260, 600, 6)

pygame.sprite.Sprite.__init__(self)

self._image = pygame.image.load('picture/Player01.png')

self.rect = self._image.get_rect()

self.rect.topleft = (self._x, self._y)

self._score = 0 # 分数

def draw(self, screen):

"""绘制玩家"""

screen.blit(self._image, (self.rect.left, self.rect.top))

def update(self,dir,screen):

"""玩家移动"""

if dir[pygame.K_d] and self.rect.left + self._speed < 520:

self.rect.left += self._speed

if dir[pygame.K_a] and self.rect.left + self._speed > 0:

self.rect.left -= self._speed

if dir[pygame.K_w] and self.rect.top > 0:

self.rect.top -= self._speed

if dir[pygame.K_s] and self.rect.top +self._speed < 645:

self.rect.top += self._speed

self.draw(screen)

@property

def score(self):

return self._score

@score.setter

def score(self,score):

self._score = score

class Enemy(GameObject,pygame.sprite.Sprite):

"""敌机类"""

def __init__(self, x, y, speed):

super().__init__(x, y, speed)

pygame.sprite.Sprite.__init__(self)

self._image = pygame.image.load('picture/enemy_0.png')

self.rect = self._image.get_rect()

self.rect.topleft = (self._x,self._y)

def update(self):

self.rect.top += self._speed

if self.rect.top > 680:

self.kill()

class Background(GameObject):

"""背景类"""

def __init__(self):

super().__init__(0, 0, 2)

bg_color1 = pygame.image.load('picture/Background.png')

bg_color2 = pygame.image.load('picture/Background.png')

self._image = [bg_color1, bg_color2]

def move(self, screen):

"""背景移动"""

self._y += self._speed

if self._y >= 960:

self._image = self._image[1:] + self._image[:1]

self._y = 0

screen.blit(self._image[0], (self._x, self._y))

screen.blit(self._image[1], (self._x, self._y - 960))

class Bullet(GameObject,pygame.sprite.Sprite):

"""子弹类"""

def __init__(self, x, y, image, speed, speed2=0):

super().__init__(x, y, speed, image)

pygame.sprite.Sprite.__init__(self)

self._speed2 = speed2

self.rect = self._image.get_rect()

self.rect.topleft = (self._x, self._y)

def update(self):

self.rect.top -= self._speed

self.rect.left += self._speed2

if self.rect.top < 0 or self.rect.top > 680:

self.kill()

主函数

首先用pygame创建窗口,窗口名,背景色,玩家对象,玩家子弹精灵组,敌机子弹精灵组,敌机精灵组,死亡敌机精灵组(因为要在死亡时产生爆炸效果),爆炸效果图片数组

创建了一个播放音乐的方法,然后用线程启动它,首先用mixer.music.load('dfj.MP3')来加载音乐文件,然后用mixer.music.play启动音乐

创建背景对象,在while循环里调用对象的move进行移动

编写方法:创建玩家子弹并移动和绘画,创建敌机子弹并移动和绘画,创建敌机并移动和绘画,绘画使用精灵的内部方法draw方法,把精灵组的全部精灵绘画到窗口上

然后在主函数判断游戏是否结束,如果没有,就获取键盘输入事件,再调用上面所创建的方法进行绘画和移动

接着检测玩家子弹与敌机是否相碰,两个精灵组相碰用的是sprite的groupcollide方法,相碰的话两者都消失,并把相碰的敌机添加到死亡敌机精灵组,再遍历敌机精灵组,绘画爆炸效果,就是五张图片连续播放出来

继续检测敌机子弹或敌机是否与玩家相撞,一个精灵与一组精灵相撞,使用sprite的spritecollideany的方法,如果相撞,gameover置为True游戏结束,游戏结束会绘画一段字体在屏幕上,按f2会重开游戏,鼠标点击窗口关闭按钮会退出游戏

代码如下:

import pygame

from threading import Thread

from random import *

import GameObject

def main():

def creat_player_bullet(dir):

"""创建玩家子弹"""

nonlocal count2

count2 += 1

if dir[pygame.K_j] and count2 % 10 == 0:

speed = 3

image = pygame.image.load('picture/shotw_0.png')

bullet_player = GameObject.Bullet(player.rect.left + 14, player.rect.top, image, speed, 0)

bullet_player_group.add(bullet_player)

bullet_player_group.update() # 玩家子弹移动

bullet_player_group.draw(screen) # 绘制玩家子弹

def creat_enemy():

"""创建敌机"""

if randint(1, 20) == 1:

x = randint(20, 520)

enemy_group.add(GameObject.Enemy(x, 0, randint(3, 5)))

enemy_group.update() # 敌机移动

enemy_group.draw(screen) # 绘制敌机

def creat_enemy_bullet():

"""创建敌机子弹"""

nonlocal count1

count1 += 1

if count1 % 50 == 0:

for i in enemy_group:

speed = -6

speed2 = int((player.rect.left - i.rect.left) * 0.008571428571428572)

image = pygame.image.load('picture/Bullet.png')

bullet_enemy = GameObject.Bullet(i.rect.left, i.rect.top, image, speed, speed2)

bullet_enemy_group.add(bullet_enemy)

bullet_enemy_group.update()

bullet_enemy_group.draw(screen)

def resetgame():

"""重置游戏"""

nonlocal player, bullet_enemy_group, bullet_player_group, enemy_group, gameover

player = GameObject.Player()

bullet_player_group = pygame.sprite.Group()

bullet_enemy_group = pygame.sprite.Group()

enemy_group = pygame.sprite.Group()

pygame.display.update()

gameover = False

def music_play():

"""播放音乐"""

pygame.mixer.music.load('dfj.MP3')

pygame.mixer.music.play(loops=0, start=0.0)

pygame.init()

player = GameObject.Player() # 创建玩家

bullet_player_group = pygame.sprite.Group() # 创建玩家子弹精灵组

bullet_enemy_group = pygame.sprite.Group() # 创建敌机子弹精灵组

enemy_group = pygame.sprite.Group() # 创建敌机精灵组

enemy_hit_group = pygame.sprite.Group() # 死亡敌机精灵组

enemy_bomb_picture = [] # 爆炸效果图片数组

for i in range(6):

enemy_bomb_picture.append(pygame.image.load('picture/bomb_enemy_%d.png' % i))

background = GameObject.Background() # 创建背景

screen = pygame.display.set_mode((540, 680)) # 创建游戏窗口

pygame.display.set_caption('雷电') # 窗口名

screen.fill((230, 230, 230)) # 填充背景色

Thread(target=music_play, daemon=True).start()

gameover = False

running = True

count = count1 = count2 = 0

while running:

background.move(screen) # 背景移动

for event in pygame.event.get():

if event.type == pygame.QUIT:

running = False

if event.type == pygame.KEYUP:

if event.key == pygame.K_F2: # f2重置游戏

resetgame()

if not gameover:

dir = pygame.key.get_pressed() # 获取键盘事件

player.update(dir,screen) # 玩家移动并绘画

creat_player_bullet(dir) # 创建玩家子弹并移动和绘画

creat_enemy() # 创建敌机并移动和绘画

creat_enemy_bullet() # 创建敌机子弹

if enemy_hit_group.add(pygame.sprite.groupcollide\

(enemy_group, bullet_player_group, True, True)): # 检测玩家子弹与敌机碰撞

player.score += 1

if pygame.sprite.spritecollideany(player,bullet_enemy_group) \

or pygame.sprite.spritecollideany(player, enemy_group): # 检测玩家与敌机和敌机子弹碰撞

gameover = True

for enemy_hit in enemy_hit_group: # 爆炸

screen.blit(enemy_bomb_picture[count], enemy_hit.rect)

# time.sleep(0.001)

if count < 5:

count += 1

else:

enemy_hit_group.remove(enemy_hit)

pygame.display.flip()

else:

my_font1 = pygame.font.SysFont("宋体", 60)

position1 = my_font1.render('game over', True, (255, 0, 0))

screen.blit(position1, (200, 300))

pygame.display.flip()

pygame.quit()

if __name__ == '__main__':

main()

游戏运行截图:

dfj.png

贪吃蛇

上面两个一个没动,一个连续移动,所以没用帧数控制,而贪吃蛇需要一节一节的动,就需要用帧数控制

蛇是由一个一个蛇节点对象构成,移动的时候把最后的节点删除,然后在头部新增一个头结点

吃食物使用头结点判断,然后添加在尾节点上

其他逻辑没什么想说的,直接上代码。。。

import pygame

import json

from threading import Thread

from random import *

from abc import ABCMeta, abstractclassmethod

BLACK_COLOR = (0, 0, 0)

FOOD_COLOR = (236, 189, 187)

GREEN_COLOR = (0, 255, 0)

UP = 0

RIGHT = 1

DOWN = 2

LEFT = 3

class GameObject(object, metaclass=ABCMeta):

"""游戏对象"""

def __init__(self, x=0, y=0, color=BLACK_COLOR):

self._x = x

self._y = y

self._color = color

@abstractclassmethod

def draw(self, screen):

pass

@property

def x(self):

return self._x

@property

def y(self):

return self._y

class Wall(GameObject):

"""围墙类"""

def __init__(self, x, y, lenth, width, color=BLACK_COLOR):

super().__init__(x, y, color)

self._lenth = lenth

self._width = width

def draw(self, screen):

pygame.draw.rect(screen, self._color, (self._x, self._y, self._width, self._lenth), 4)

@property

def lenth(self):

return self._lenth

@property

def width(self):

return self._width

class Food(GameObject):

"""食物类"""

def __init__(self, x, y, length, color=FOOD_COLOR):

super().__init__(x, y, color)

self._length = length

self._hidden = False # 是否隐藏

def draw(self, screen):

if not self._hidden:

pygame.draw.circle(screen, self._color,

(self._x + self._length // 2, self._y + self._length // 2),

self._length // 2, 0)

self._hidden = not self._hidden

class SnakeNode(GameObject):

"""蛇身节点类"""

def __init__(self, x, y, size, color=GREEN_COLOR):

super().__init__(x, y, color)

self._size = size

def draw(self, screen):

pygame.draw.rect(screen, self._color,

(self._x, self._y, self._size, self._size), 0)

pygame.draw.rect(screen, BLACK_COLOR,

(self._x, self._y, self._size, self._size), 1)

@property

def size(self):

return self._size

class Snake(GameObject):

"""蛇"""

def __init__(self):

super().__init__()

self._dir = LEFT # 方向

self._nodes = [] # 身体数组

for index in range(5):

node = SnakeNode(290 + index * 20, 250, 20)

self._nodes.append(node)

def collide(self, wall):

"""撞墙"""

head = self.head

return head.x < wall.x or head.y < wall.y or head.x + head.size > wall.x + wall.lenth\

or head.y + head.size > wall.y + wall.width

def eat_food(self, food):

"""吃食物"""

if self.head.x == food.x and self.head.y == food.y:

tail = self._nodes[-1]

self._nodes.append(tail)

return True

return False

def eat_self(self):

for i in range(4, len(self.nodes)):

if self.head.x == self.nodes[i].x and self.head.y == self.nodes[i].y:

return True

return False

def draw(self, screen):

"""画蛇"""

for node in self._nodes:

node.draw(screen)

def move(self):

"""移动"""

head = self.head

snake_dir = self._dir

x, y, size = head.x, head.y, head.size

if snake_dir == UP:

y -= size

elif snake_dir == RIGHT:

x += size

elif snake_dir == DOWN:

y += size

else:

x -= size

new_head = SnakeNode(x, y, size)

self._nodes.insert(0, new_head)

self._nodes.pop()

@property

def dir(self):

return self._dir

@property

def nodes(self):

return self._nodes

@property

def head(self):

return self._nodes[0]

def change_dir(self, new_dir):

if (self._dir + new_dir) % 2 != 0:

self._dir = new_dir

def main():

def refresh():

"""刷新游戏窗口"""

screen.fill((242, 242, 242))

wall.draw(screen) # 画围墙

food.draw(screen) # 画食物

snake.draw(screen) # 画蛇

show_info(screen, snake)

pygame.display.flip()

def reset_game():

nonlocal food, snake, gameover, rank

food = creat_food()

snake = Snake()

gameover = False

def handle_key_event(key_event):

"""处理按键事件"""

key = key_event.key

new_dir = snake.dir

if key == pygame.K_w or key == pygame.K_UP:

new_dir = UP

if key == pygame.K_d or key == pygame.K_RIGHT:

new_dir = RIGHT

if key == pygame.K_s or key == pygame.K_DOWN:

new_dir = DOWN

if key == pygame.K_a or key == pygame.K_LEFT:

new_dir = LEFT

if key == pygame.K_F2:

reset_game()

return

if new_dir != snake.dir:

snake.change_dir(new_dir)

def creat_food():

"""吃食物"""

row = randint(0, 29)

col = randint(0, 29)

return Food(10 + 20 * col, 10 + 20 * row, 20)

def show_info(screen1, snake1):

"""展示分数"""

my_font2 = pygame.font.SysFont("宋体", 30)

position3 = my_font2.render('high score: %d' % max(rank), True, (255, 0, 0))

screen1.blit(position3, (453, 10))

position2 = my_font2.render('score: %d' % (len(snake1.nodes) - 5), True, (255, 0, 0))

screen1.blit(position2, (500, 30))

pygame.display.flip()

try:

with open('rank.json', 'r', encoding='utf-8') as fs:

if len(fs.read()) == 0:

rank = [0] # 排行榜

else:

with open('rank.json', 'r', encoding='utf-8') as fs2:

rank = json.load(fs2)

except IOError as e:

print(e)

pygame.init()

screen = pygame.display.set_mode((620, 620)) # 创建游戏窗口

pygame.display.set_caption('贪吃蛇') # 窗口名

screen.fill((242, 242, 242)) # 填充背景色

pygame.display.flip() # 刷新窗口

clock = pygame.time.Clock() # 创建计时器,控制帧数

wall = Wall(10, 10, 600, 600) # 围墙

food = creat_food() # 食物

snake = Snake() # 蛇

running = True

gameover = False

while running:

for event in pygame.event.get():

if event.type == pygame.QUIT:

try:

with open('rank.json', 'w', encoding='utf-8') as fs:

json.dump(rank, fs)

except IOError as e:

print(e)

running = False

elif event.type == pygame.KEYDOWN:

handle_key_event(event)

elif event.type == pygame.MOUSEBUTTONDOWN:

pass

if not gameover:

Thread(target=refresh,daemon=True).start()

snake.move()

if snake.collide(wall) or snake.eat_self():

gameover = True

if snake.eat_food(food):

food = creat_food()

if gameover:

rank.append(len(snake.nodes) - 5)

else:

my_font1 = pygame.font.SysFont("宋体", 60)

position1 = my_font1.render('game over', True, (255, 0, 0))

screen.blit(position1, (200, 300))

pygame.display.flip()

clock.tick(6) # 6帧

pygame.quit()

if __name__ == '__main__':

main()

游戏运行结果:

snake.png

你可能感兴趣的:(python黑白棋,pygame)