pygame Sokoban prototype

Sokoban pygame pkinter

Foreword

At the beginning of 2020 is not good.
Are not play basketball in a small partner usually heard friends say, "You know I'm doing it four o'clock?", "Four o'clock I get up early in the learning." When this four-point statement comes from an interview with Bryant, the face of a reporter's question: "? Why are you able to achieve such a success of it," he indifferent smile, "You know, Los Angeles is four o'clock in the morning looks like?"
Countless people will be infected by Bryant's hard work, continue to work hard, I am no exception, highly talented big boys do not begrudge the sweat with his words and deeds inspired when I lost.
One day need farewell, but I did not think in such a way. mamba never out!
Closer to home, asked by a friend, I need to deliver a source, I selected as the game Sokoban (Source World War II aircraft is not good to change it ...), Sokoban meet the basic requirements (after all, take a friend to show its mettle in the past, that map and roles are allowed to set would be nice). in addition, in order to effect a better game, I added two to choose background music.
After a morning of code I promise down is complete, but there is no lack of self-control to change the fine, just free today, and quickly write down the time to think. Rough source, sprout new white, please forgive me. (Source code attached to the end of the article)
game is divided into menu interface and game interface.
Menu interface:
Here Insert Picture Description
the game interface:
Here Insert Picture Description

Menu interface

Use tkinter, music players use pygame.mixer.load
initialization - loading (Music Path) - Playback (-1 for infinite loop)

	pygame.mixer.init()
    pygame.mixer.music.load(music_path)
    pygame.mixer.music.play(-1)

game interface

On the basis of pygame code interpreter can look at my notes Wars prequel aircraft, not repeat them here.
Personal recommendations, even as the prototype code, but each type of game elements that temporarily without the need to rewrite should be a new category. Figuratively, this wall only for collision detection, then only need to add images to determine the initial coordinates (a, b) can.

wall= GameSprite(image_name,a,b)

Although compared to

class wall(GameSprite):
def __init__(self, x, y):
    super().__init__("./image/wall_image.png", x, y)
wall = wall(x,y)

简便了很多,但个人看来有两个好处:一是容易添删。若此游戏改为类似泡泡堂、QQ堂一样的游戏,那么墙体就多了一个能够被炸毁的属性,需要一个函数接收爆炸参数。二是美观好看,分类整洁,尤其于分文件后更易于阅读与debug。

主角

这里愤怒的小鸟是拥有四种形态(往上、下左、右走)。说明检测摁键后,不仅要改变rect,而且要对主角的image进行修改。

if self.num == 1:
    self.image_name = './image/birdup.png'

此外,摁键检测应该采取的是For event 的形式,毕竟推箱子是细致活,刷新帧率过高的情况下采取储存检测的方式容易把箱子逼上死路。

碰撞检测

推箱子的核心就是碰撞检测:主角和箱子,箱子和墙体,主角和墙体。
有很多其他的方法可以规避重合,但在这既运用到了pygame,我又熟悉碰撞检测的用法,自然偷懒。
难点在于,主角推箱子推上墙这个判断,原来是主角和箱子碰撞后,主角传递出位移的标志位,箱子接受并箱子位移。 但挨着墙的时候,又会形成墙体和箱子的碰撞事件,于此情况下箱子和主角要同时返回之前的位置。曾经想于箱子和主角类中加一个复位函数,但过于麻烦,于是便在主角的update函数中加上碰撞检测。主角位移有两个判断,一是前面有没有箱子,二是箱子后面有没有墙体。
以下是左移的情况:

        if self.num == 1:
        self.image_name = './image/birdup.png'
        self.rect.y -= 50
        if len(pygame.sprite.groupcollide(hero_group,wall_group,False,False)) > 0 :
            self.rect.y += 50
        if len(pygame.sprite.groupcollide(hero_group,box_group,False,False)) > 0:
            self.rect.y -= 50
            if len(pygame.sprite.groupcollide(hero_group,wall_group,False,False)) > 0:
                self.rect.y += 50
            self.rect.y += 50       

碰撞检测,要先进行碰撞方能检测,看起来复杂、蠢了点。
那么主角和箱子的碰撞检测自然十分容易:

    def check_collide(self):
    #返回的是精灵
    #英雄与箱子
    if len(pygame.sprite.spritecollide(self.hero,self.box_group,False)) > 0:
        if self.hero.move_num != 0:
            print('x : %d and y : %d and from%d' %(self.hero.rect.x,self.hero.rect.y,self.hero.move_num))
            for box in self.box_group:
                if box.rect.x == self.hero.rect.x and box.rect.y == self.hero.rect.y:
                    print(box.rect)
                    box.beenpush(self.hero.move_num)

游戏结束

箱子与终点碰撞即可(这里只有一个终点)

        if len(pygame.sprite.groupcollide(self.end_point_group,self.box_group,False,False)) > 0:
        result = tk.messagebox.askokcancel(title = '恭喜',message = '通关了')
        self.game_over()

不足

1、在游戏结束后,pygame.quit()会导致音乐关闭
2、能玩,但肯定有很多bug,一时间也说不上来,有问题或者想找原版菜鸡素材的可以找我要
3、我记笔记都是记每次的新内容,旧内容我会翻之前的笔记。

源代码

import tkinter as tk
import pygame
import time
import numpy as np

from PIL import ImageTk,Image,ImageGrab
from tkinter import StringVar,IntVar,messagebox,Radiobutton

pygame.init()

#屏幕大小的常量
SCREEN_RECT = pygame.Rect(0,0,1000,800)
#刷新的帧率
FRAME_PER_SEC = 60

class GameSprite(pygame.sprite.Sprite):  #(继承父类) 其中sprite是模块 Sprite是类名称
    '''精灵基类'''
    #构造函数/初始化
    image_name = 0
    def __init__(self,image_name,x=0,y=0):
        #调用父类初始化方法
        super().__init__()
        #定义对象的属性
        self.image_name = image_name
        self.image = pygame.image.load(self.image_name)
        self.rect = self.image.get_rect()  #图像的属性
        self.rect.x = x*50
        self.rect.y = y*50

class hero(GameSprite):
    '''英雄类'''
    def __init__(self,x,y):
        super().__init__("./image/birdleft.png",x,y)
        self.num = 0

    def move(self,num):
        self.num = num

    def update(self,hero_group,wall_group,box_group):
        # 传入 1 2 3 4 分别对应上下左右
        canpush = 0
        if self.num == 1:
            self.image_name = './image/birdup.png'
            self.rect.y -= 50
            if len(pygame.sprite.groupcollide(hero_group,wall_group,False,False)) > 0 :
                self.rect.y += 50

            if len(pygame.sprite.groupcollide(hero_group,box_group,False,False)) > 0:
                self.rect.y -= 50
                if len(pygame.sprite.groupcollide(hero_group,wall_group,False,False)) > 0:
                    self.rect.y += 50
                self.rect.y += 50


        elif self.num == 2:
            self.image_name = './image/birddown.png'
            self.rect.y += 50
            if len(pygame.sprite.groupcollide(hero_group,wall_group,False,False)) > 0 :
                self.rect.y -= 50
            if len(pygame.sprite.groupcollide(hero_group,box_group,False,False)) > 0:
                self.rect.y += 50
                if len(pygame.sprite.groupcollide(hero_group,wall_group,False,False)) > 0:
                    self.rect.y -= 50
                self.rect.y -= 50

        elif self.num == 3:
            self.image_name = './image/birdleft.png'
            self.rect.x -= 50
            if len(pygame.sprite.groupcollide(hero_group,wall_group,False,False)) > 0 :
                self.rect.x += 50
            if len(pygame.sprite.groupcollide(hero_group, box_group, False, False)) > 0:
                self.rect.x -= 50
                if len(pygame.sprite.groupcollide(hero_group, wall_group, False, False)) > 0:
                    self.rect.x += 50
                self.rect.x += 50

        elif self.num == 4:
            self.image_name = './image/birdright.png'
            self.rect.x += 50
            if len(pygame.sprite.groupcollide(hero_group,wall_group,False,False)) > 0 :
                self.rect.x -= 50

            if len(pygame.sprite.groupcollide(hero_group,box_group,False,False)) > 0:
                self.rect.x += 50
                if len(pygame.sprite.groupcollide(hero_group,wall_group,False,False)) > 0:
                    self.rect.x -= 50
                self.rect.x -= 50

        elif self.num == 0:
            pass
        self.move_num = self.num
        self.num = 0
        self.image = pygame.image.load(self.image_name)

    def goback(self,num):
        pass

class box(GameSprite):
    '''箱子类'''
    def __init__(self,x,y):
        self.num = 0
        super().__init__("./image/box.png",x,y)

    def beenpush(self,num):
        self.num = num

    def update(self,wall_group,box_group):
        # 传入 1 2 3 4 分别对应上下左右
        if self.num == 1:
            self.rect.y -= 50
            if len(pygame.sprite.groupcollide(wall_group,box_group,False,False)) > 0:
                self.rect.y += 50
                self.to_hero_goback = 1
        elif self.num == 2:
            self.rect.y += 50
            if len(pygame.sprite.groupcollide(wall_group,box_group,False,False)) > 0:
                self.rect.y -= 50
                self.to_hero_goback = 2
        elif self.num == 3:
            self.rect.x -= 50
            if len(pygame.sprite.groupcollide(wall_group,box_group,False,False)) > 0:
                self.rect.x += 50
                self.to_hero_goback = 3
        elif self.num == 4:
            self.rect.x += 50
            if len(pygame.sprite.groupcollide(wall_group,box_group,False,False)) > 0:
                self.rect.x -= 50
                self.to_hero_goback = 4
        elif self.num == 0:
            pass
        if self.num != 0:
            self.moved_num = self.num
            print('当前的movednum是%d'%self.moved_num)
        self.num = 0

class wall(GameSprite):
    def __init__(self, x, y):
        super().__init__("./image/wall_image.png", x, y)

class endpoint(GameSprite):
    def __init__(self, x, y):
        super().__init__("./image/end_point.png", x, y)

class gamepushbox():
    #推箱子主游戏
    wall = 0
    def __init__(self):
        print('初始化')
        #游戏窗口
        self.screen = pygame.display.set_mode(SCREEN_RECT.size)
        #游戏背景
        self.background = GameSprite("./image/game_background.jpg")
        self.back_group = pygame.sprite.Group(self.background)
        #英雄
        self.hero = hero(10,7)
        self.hero_group = pygame.sprite.Group(self.hero)
        #箱子
        self.box = box(8,7)
        self.box1 = box(8,6)
        self.box_group = pygame.sprite.Group(self.box,self.box1)
        #墙体
        self.wall0 = wall(2,2)
        self.wall_group = pygame.sprite.Group(self.wall0)
        for i in wall_list:
            self.wall_group.add(wall(i[0],i[1]))
        #终点
        self.end_point = endpoint(4,4)
        self.end_point_group = pygame.sprite.Group(self.end_point)

        #2.创建游戏的时钟
        self.clock = pygame.time.Clock()

    #更新精灵组
    def updata_sprites(self):
        self.back_group.draw(self.screen)

        self.wall_group.draw(self.screen)

        self.hero_group.update(self.hero_group,self.wall_group,self.box_group)
        self.hero_group.draw(self.screen)

        self.box_group.update(self.wall_group,self.box_group)
        self.box_group.draw(self.screen)

        self.end_point_group.draw(self.screen)

    def check_collide(self):
        #返回的是精灵
        #英雄与箱子
        if len(pygame.sprite.spritecollide(self.hero,self.box_group,False)) > 0:
            if self.hero.move_num != 0:
                print('x : %d and y : %d and from%d' %(self.hero.rect.x,self.hero.rect.y,self.hero.move_num))
                for box in self.box_group:
                    if box.rect.x == self.hero.rect.x and box.rect.y == self.hero.rect.y:
                        print(box.rect)
                        box.beenpush(self.hero.move_num)
                        # print('当前英雄移动状态:%d' %self.hero.move_num)
                        # print('当前箱子移动状态:%d' %box.num)
                        # print('当前box上移动状态:%d' %self.box.num)
                        # print('当前box下移动状态:%d' %self.box1.num)
                self.hero.move_num = 0
        #箱子与终点
        if len(pygame.sprite.groupcollide(self.end_point_group,self.box_group,False,False)) > 0:
            result = tk.messagebox.askokcancel(title = '恭喜',message = '通关了')
            self.game_over()


    #事件监听
    def event_handler(self):
        for event in pygame.event.get():
            # 退出事件
            if event.type == pygame.QUIT:
                print('游戏已结束')
                self.game_over()
            elif event.type == pygame.KEYDOWN:
                if event.key == pygame.K_RIGHT:
                    self.hero.move(4)
                    #print("向右移动")
                if event.key == pygame.K_LEFT:
                    self.hero.move(3)
                    #print("向左移动")
                if event.key == pygame.K_UP:
                    self.hero.move(1)
                    #print("向上移动")
                if event.key == pygame.K_DOWN:
                    self.hero.move(2)
                    #print("向下移动")


    def game_over(self):    #静态方法 就不接受self的东西
        print("游戏结束")
        pygame.quit()
        time.sleep()
    
class gamepushbox_menu():
    def draw_menu(self):
        self.window = tk.Tk()
        self.window.title('推箱子')
        Image_way = './image/star_main_background.jpg'
        img = Image.open(Image_way)
        window_background = ImageTk.PhotoImage(img)
        w = window_background.width()
        h = window_background.height()
        print(w,h)
        self.window.geometry('%dx%d+0+0' % (w, h))

        self.background_label = tk.Label(self.window, image=window_background)
        self.background_label.place(x=0, y=0, relwidth=1, relheight=1)

        self.gamestart_button = tk.Button(self.window, text='开始游戏', command=self.gamestart)
        self.gamestart_button.pack()

        self.gamestart_button = tk.Button(self.window, text='选择背景音乐', command=self.choose_music)
        self.gamestart_button.pack()

        #单选框
        """
        v = IntVar()
        Radiobutton(window, text='One', variable=v, value=1, ).pack()
        Radiobutton(window, text='Two', variable=v, value=2, ).pack()
        Radiobutton(window, text='Three', variable=v, value=3, ).pack()
        """
        self.window.mainloop()
    def choose_music(self):
        music_tk = tk.Toplevel()
        music_tk.title("选取音乐")
        self.v = IntVar()
        Radiobutton(music_tk, text='寻找爱情', variable=self.v, value=1, ).pack()
        Radiobutton(music_tk, text='爱河', variable=self.v, value=2, ).pack()
        music_play_button = tk.Button(music_tk, text='播放', command=self.play_music).pack()
        music_stop_button = tk.Button(music_tk, text='停止', command=self.stop_music).pack()
    def play_music(self):
        print(self.v.get())
        if (self.v.get() == 1):
            music_path = './music/find_happiness.mp3'
        elif (self.v.get() == 2):
            music_path = './music/love_river.mp3'
        pygame.mixer.init()
        pygame.mixer.music.load(music_path)
        pygame.mixer.music.play(-1)
    def stop_music(self):
        pygame.mixer.music.stop()
    def gamestart(self):
        gameaaa = gamepushbox()
        print('游戏开始了')
        while 1:
            #1.刷新频率
            gameaaa.clock.tick(FRAME_PER_SEC)
            #game.clock.tick(FRAME_PER_SEC)
            # 2.事件监听
            gameaaa.event_handler()
            # 3.碰撞检测 是否推动箱子
            gameaaa.check_collide()
            # # 4.更新/绘制精灵
            gameaaa.updata_sprites()
            # 5.更新检测
            pygame.display.update()
            # print(SCREEN_RECT.size)

if __name__ == '__main__':
    wall_list = []
    for i in range(16):
        wall_list.append((0, i))
        wall_list.append((19,i))
    for i in range(21):
        wall_list.append((i,0))
        wall_list.append((i,15))
    a = gamepushbox_menu()
    a.draw_menu()
Released four original articles · won praise 0 · Views 751

Guess you like

Origin blog.csdn.net/chunqingzhanna/article/details/104094245