pygame写贪吃蛇

python小白尝试写游戏..

学了点pygame不知道那什么练手好,先拿贪吃蛇开刀吧.

一个游戏可以粗略的分为两个部分:

  • 数据(变量)
  • 处理数据(函数,方法)

设计变量

首先预想下,画面的那些部分需要存储在变量里

整个画面上只会有矩形,而且这些矩形整整齐齐,大小相等,原本一个矩形需要四个变量表示位置,这里,只需要两个变量(行数,列数)就能表示方块的位置

蛇头,食物可以用二元元组表示,蛇身的数量不确定,只能用包含数个元组的列表表示

另外设定窗口大小800x600,每个方块都是50x50

import pygame
import sys

pygame.init()
screen = pygame.display.set_mode((800, 600))
pygame.display.set_caption("贪吃蛇")

food = (4, 5)
body = [(1, 1),(1,2)]
head = (1, 3)

BLOCK = 0, 0, 0
GREEN = 0, 255, 0
RED = 255, 0, 0
BLUE = 0, 0, 255
WHITE = 255, 255, 255

while 1:
    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            sys.exit()

变量设定好了,游戏已经完成了一半( ̄▽ ̄)~*

下一步

变量到画面

pygame.draw.rect()是根据矩形四元数组绘制图像的,那就写个函数"对接"下我的二元坐标

这里就成数学的问题了...

def new_draw_rect(zb, color,screen):
    pygame.draw.rect(screen,color,((zb[1]-1)*50+1,(zb[0]-1)*50+1,48,48))

鉴于50x50时相邻方块们会"粘"在一起,方块向里收一下成48x48        ↑

绘制图形,

...while 1:
    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            sys.exit()

    screen.fill(WHITE)
    new_draw_rect(food, RED, screen)
    for i in body:
        new_draw_rect(i, BLUE, screen)
    new_draw_rect(head, GREEN, screen)

    pygame.display.update()

 由静到动

两个问题:

1.什么时候动

2.怎么动

问题1,什么时候动,这里有两个思路,

  • 间隔固定时间(1秒),动一次
  • 按一次键动一次,无操作一定时间(1秒)后,重复最后一次操作

看起来第一种方案 简单 不错

首先,先用pygame的clock类限制帧率(100帧),以方便计时

再者,加入新变量times,times每次加1,超过一百就"动一动"

加入新变量direction,表示蛇头朝向,衔接键盘操作和"怎么动"

PS:程序结束之前,很难知道要用多少变量

import pygame
import sys

pygame.init()
screen = pygame.display.set_mode((800, 600))
pygame.display.set_caption("贪吃蛇")

fclock = pygame.time.Clock()

food = (4, 5)
body = [(1, 1)]
head = (1, 2)
times = 0
direction = ‘right‘

BLOCK = 0, 0, 0
GREEN = 0, 255, 0
RED = 255, 0, 0
BLUE = 0, 0, 255
WHITE = 255, 255, 255

def new_draw_rect(zb, color,screen):
    pygame.draw.rect(screen,color,((zb[1]-1)*50+1,(zb[0]-1)*50+1,48,48))
    pass

while 1:
    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            sys.exit()
        elif event.type == pygame.KEYDOWN:
            if event.key == pygame.K_UP:
                direction = "up"
            elif event.key == pygame.K_LEFT:
                direction = "left"
            elif event.key == pygame.K_DOWN:
                direction = "down"
            elif event.key == pygame.K_RIGHT:
                direction = "right"
    if times >= 100:
        pass#动一动
        times = 0
    else:
        times += 1

    screen.fill(WHITE)
    new_draw_rect(food, RED, screen)
    for i in body:
        new_draw_rect(i, BLUE, screen)
    new_draw_rect(head, GREEN, screen)

    fclock.tick(100)
    pygame.display.update()

蛇头的运动规律 : 向临近的格子移动,上下左右具体那个格子由键盘确定

那就写个新函数去生成蛇头的新位置

def get_front(head,direction):
    x, y = head
    if direction == "up":
        return x-1, y
    elif direction == "left":
        return x, y-1
    elif direction == "down":
        return x+1, y
    elif direction == "right":
        return x, y+1

然后

head = get_front(head,direction)

但是蛇蛇一头扎墙里怎么办.......

你的好友【front : 临时记下蛇头前方的位置】已上线

你的好友【alive : 记录存活信息】已上线

PS:front可以不是全局变量

def ask_alive(front,body):
    x, y = front
    if x < 0 or x > 12 or y < 0 or y >16 :
        return False
    if front in body:
        return False
    return True

然后这样用

front = get_front(head,direction)
alive = ask_alive(front,body)
if alive:
  head = front

人话 :先看看蛇头前面是否有危险,有危险就死了 , 不动啦

另外alive得加到前面的if里面当限制条件,死了就不能乱动啦~~~~~

蛇身动的规律 : 近头端跟头走,尾端也跟着走--向程序靠拢-->>用过的head加入body,同时删去body最老的成员

这里可以看出body必须有序,可变.python里面就用列表了

如果使用list的append方法,head加在body的末尾,那么body[0]就会是"最老的成员"就得使用pop(0)删去

PS. body.append(head)得写在head=front前面,在head更新前加进body

    if times >= 100 and alive:
        front = get_front(head,direction)
        alive = ask_alive(front,body)
        if alive:
            body.append(head)
            head = front
            body.pop(0)
        times = 0
    else:
        times += 1

食物的运动规律:被吃掉后,随机位置再出现 --向程序靠拢-->> 当head== food为真时 food随机选择一个蛇之外的地方出现

def new_food(head,body):
    while 1:
        x = random.randint(1, 12)
        y = random.randint(1, 16)
        if (x, y) != head and (x, y) not in body:
            return x, y

这里存在一个隐式BUG,要是蛇充满了每一个角落,那这就是死循环 ...........然后整个程序卡在这里....

版本1 : 我选择没看见,不会有什么人能吃到"全屏"的(′⊙ω⊙`)    --来自开发者的懒惰

版本2 【我的选择】: 修了这个BUG

def new_food(head,body):
    i = 0
    while i < 100:
        x = random.randint(1, 12)
        y = random.randint(1, 16)
        if (x, y) != head and (x, y) not in body:
            return (x, y), True
        i += 1
    else:
        return (0, 0), False

food, alive = new_food()

100次机会 否则就死( ̄へ ̄)     --来自开发者的恶意

另外,蛇吃了食物就要长一格====>>蛇头前进一格,蛇尾不动,蛇就因此长了一格====>>当head = food 为真时body.pop(0)不用执行

        if alive:
            body.append(head)
            head = front
            if food == head:
                food = new_food(head, body)
            else:
                body.pop(0)

完结撒花

细节优化

1,

什么?游戏结束了?

黑个屏,提醒下

你的好友【back_color】已上线

back_color一开始等于WHITE , alive为假时变为BLOCK

标题栏变成"游戏结束"

    if times >= 100 and alive:
        front = get_front(head, direction)
        alive = ask_alive(front, body)
        if alive:
            body.append(head)
            head = front
            if food == head:
                food, alive = new_food(head, body)
            else:
                body.pop(0)
        else:
            back_color = BLOCK
            pygame.display.set_caption("游戏结束")
        times = 0
    else:
        times += 1

2,

要是一开始按了一下left......

恭喜你获得技能【撞脖子自杀】

要么那就

你的好友【old_direction】已上线

old_direction = "right"

def direction_yes_no(direction,old_direction):
    d = {"up": "down", "down": "up", "left": "right", "right": "left"}
    if d[direction] == old_direction:
        return old_direction
    return direction

PS : 字典代替if-elif结构省心省时

然后

  if times >= 100 and alive:
        direction = direction_yes_no(direction, old_direction)
        old_direction = direction
        front = get_front(head, direction)
        alive = ask_alive(front, body)

人话 : 添加一个变量记录上一次有效的输入,两个方向关系不正确时,以老变量为准--向人靠拢-->>向右跑时,不准向左!!!

Q : direction也可以在按键事件处理时限制呀,例如

        elif event.type == pygame.KEYDOWN:
            if event.key == pygame.K_UP:
                direction = "up"
                   """改为"""
        elif event.type == pygame.KEYDOWN:
            if event.key == pygame.K_UP:
                if direction != "down"
                    direction = "up"

A:在发现这个方法的BUG之前,我也是这样想的

还是上面的例子

直接按left行不通,right--XX-->left

先按down,迅速再按left ,,right --pass-->> down --pass-->> left

恭喜你获得技能【绕过开发者防护,高水平撞脖子自杀】

3,

Q : 我要暂停!!!

A : 好好玩游戏,不要动不动就暂停

你的好友【pause】已上线

逻辑值,P键控制

pause = False

            elif event.key == pygame.K_p:
                pause = not pause    

if times >= 100 and alive and (not pause):
    ....

下面我发一下全部的代码,代码包含上面的优化

↓↓↓↓

完结撒花

import pygame
import sys
import random

pygame.init()
screen = pygame.display.set_mode((800, 600))
pygame.display.set_caption("贪吃蛇")

fclock = pygame.time.Clock()

food = (4, 5)
body = [(1, 1)]
head = (1, 2)
times = 0
direction = "right"
old_direction = "right"
alive = True
pause = False

BLOCK = 0, 0, 0
GREEN = 0, 255, 0
RED = 255, 0, 0
BLUE = 0, 0, 255
WHITE = 255, 255, 255
back_color = WHITE

def new_draw_rect(zb, color,screen):
    pygame.draw.rect(screen,color,((zb[1]-1)*50+1,(zb[0]-1)*50+1,48,48))

def get_front(head, direction):
    x, y = head
    if direction == "up":
        return x-1, y
    elif direction == "left":
        return x, y-1
    elif direction == "down":
        return x+1, y
    elif direction == "right":
        return x, y+1

def ask_alive(front, body):
    x, y = front
    if x < 0 or x > 12 or y < 0 or y >16 :
        return False
    if front in body:
        return False
    return True

def new_food(head, body):
    i = 0
    while i < 100:
        x = random.randint(1, 12)
        y = random.randint(1, 16)
        if (x, y) != head and (x, y) not in body:
            return (x, y), True
        i += 1
    else:
        return (0, 0), False

def direction_yes_no(direction, old_direction):
    d = {"up": "down", "down": "up", "left": "right", "right": "left"}
    if d[direction] == old_direction:
        return old_direction
    return direction

#food = new_food(head,body)

while 1:
    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            sys.exit()
        elif event.type == pygame.KEYDOWN:
            if event.key == pygame.K_UP:
                direction = "up"
            elif event.key == pygame.K_LEFT:
                direction = "left"
            elif event.key == pygame.K_DOWN:
                direction = "down"
            elif event.key == pygame.K_RIGHT:
                direction = "right"
            elif event.key == pygame.K_p:
                pause = not pause
    if times >= 100 and alive and (not pause):
        direction = direction_yes_no(direction, old_direction)
        old_direction = direction
        front = get_front(head, direction)
        alive = ask_alive(front, body)
        if alive:
            body.append(head)
            head = front
            if food == head:
                food, alive = new_food(head, body)
            else:
                body.pop(0)
        else:
            back_color = BLOCK
            pygame.display.set_caption("游戏结束")
        times = 0
    else:
        times += 1

    screen.fill(back_color)
    new_draw_rect(food, RED, screen)
    for i in body:
        new_draw_rect(i, BLUE, screen)
    new_draw_rect(head, GREEN, screen)

    fclock.tick(100)
    pygame.display.update()

↑↑↑↑

↑↑↑↑

↑↑↑↑

4,

Q : 游戏结束或者暂停后因为times >= 100 and alive and (not pause)始终为假

times += 1 一直运行,,,,会不会不太妥当

A : 又不会溢出,,,,,,,,取消暂停时蛇能迅速跑起来要是time==100那问题就大了,,幸亏当初留了些余地写成 >=

(~ ̄▽ ̄)~

5,

Q : 蛇跑的太慢我想加速

A : 上面的times>=100的100随便改一下就行

0->100动一动   变成    0->80 动一动

或者动前面的数

0->100动一动   变成    20->100动一动

或者将它设置成变量让它随蛇的长度变化而变化

6,

打字太累,,

direction那里可以使用0,1,2,3

代替 up left down right

键盘事件和get_front()得用同一套词

7,

Q : 第一个food的位置是固定的,不能"动"吗?

A : 因为我是先定义变量,再定义函数

food = new_food(head,body)

就得写在定义函数后面,我不忍心让它一个变量孤单,,,,怕你看不到(写了但注释掉了)

动手术

前面说过

问题1,什么时候动,这里有两个思路,

  • 间隔固定时间(1秒),动一次
  • 按一次键动一次,无操作一定时间(1秒)后,重复最后一次操作

看起来第一种方案 简单 不错

当初我以为第二种方案很难,,,

写了这个博客后实力大增?( ? ? ? )?

现在我不怕了

第一个方法,

处理按键事件后times = 100 ,例

    if event.key == pygame.K_UP:
        direction = "up"
        times = 100
    elif event.key == pygame.K_LEFT:
        direction = "left"
        times = 100  

while循环当轮就能动一动

隐藏操作 : 在0.01s内按下两个键,第一个按的不会"生效"

pygame : 我得跑完事件列表的每个元素

这是假的第二方案!!!

第二个方法,

把"动一动"的全部代码,打包成函数或者直接写在按键处理的后面

就是上面times = 100的位置 例

    if event.key == pygame.K_UP:
        direction = "up"
        ‘动一动‘
    elif event.key == pygame.K_LEFT:
        direction = "left"
        ‘动一动‘

记住关键的一点times = 0也算"动一动"的内容,第二方案里,按键后计时需要清零!!!

    if times >= 100 and alive and (not pause):
        direction = direction_yes_no(direction, old_direction)
        old_direction = direction
        front = get_front(head, direction)
        alive = ask_alive(front, body)
        if alive:
            body.append(head)
            head = front
            if food == head:
                food, alive = new_food(head, body)
            else:
                body.pop(0)
        else:
            back_color = BLOCK
            pygame.display.set_caption("游戏结束")
        times = 0
    else:
        times += 1

为了防止"诈尸"

"动一动"前还得加上if alive and (not pause):

times没必要加

我就懒得管了(╥╯^╰╥)这篇文章写了太长时间,现在已经看不懂当初的代码了

Q : 不知道你是不是记得我,优化2就是我问的.....是这样的,我对我的方法还不死心...."动手术"改第二方案后能用吗?

        elif event.type == pygame.KEYDOWN:
            if event.key == pygame.K_UP:
                direction = "up"
                   """改为"""
        elif event.type == pygame.KEYDOWN:
            if event.key == pygame.K_UP:
                if direction != "down"
                    direction = "up"

A : 对于第一方法实现的第二方案还是会有小毛病

while循环当轮就能动一动

隐藏操作 : 在0.01s内按下两个键,第一个按的不会"生效"

pygame : 我得跑完事件列表的每个元素

在0.01s内,按下两个键,就可以照旧触发【秘技: 撞脖子自杀】

第二方法实现的应该没问题.......

完结撒花??ヽ(°▽°)ノ?

Q : 我想在加宽屏幕在右侧显示一下时间分数之类的信息,你还有什么"交待"吗?

A : 在pygame显示字体比较有难度....我只能祝你程序不出BUG...另外点(800,0)到点(800,600)别忘了画到线,提醒玩家"边界"还是存在的,让玩家摔键盘动怒就不好了

Q : 我就是对你的颜色搭配有意见,颜色有点扎眼....

A : 我又不是美工,,,,,,,,,,,,颜色搭配的问题,应该,,,,,,,,,应该可以原谅,,,,,,,,,,

Q : 游戏太没挑战性,加点障碍物呗~~

A : walls 会在ask_alive()和new_food()用到,加个if的问题,,,,另外绘制屏幕那里多加个for,,,,,

我就懒得管了(╥╯^╰╥)这篇文章写了太长时间,现在已经看不懂当初的代码了

完结撒花??ヽ(°▽°)ノ?

这次真没了..??ヽ(°▽°)ノ?

如有疏漏,欢迎补充

原文地址:https://www.cnblogs.com/ansver/p/8438830.html

时间: 2024-08-07 07:22:21

pygame写贪吃蛇的相关文章

C语言学习必须拥有学习路线,不然你就只会写贪吃蛇

C语言学习必须拥有学习路线,不然你就只会写贪吃蛇这些天一直有人问我,c语言好学吗?我是个新手,该如何学习? 其实,这类问题困扰着很多新手.在如何学习之前,我们想简单的了解一下什么是C语言: C语言是一种计算机程序设计语言.它既有高级语言的特点,又具有汇编语言的特点.它可以作为系统设计语言,编写工作系统应用程序,也可以作为应用程序设计语言,编写不依赖计算机硬件的应用程序.创一个小群,供大家学习交流聊天如果有对学C++方面有什么疑惑问题的,或者有什么想说的想聊的大家可以一起交流学习一起进步呀.也希望

用原生Canvas写贪吃蛇及问题解决

为了学习Canvas,写了这个小游戏贪吃蛇供自己和大家学习 Github: https://github.com/zhiyishou/Gsnake Play On: http://zhiyishou.github.io/Gsnake 游戏截图: 前言: 为了方便加载转移,我把整个js都写在了html里,为了方便阅读,将函数结构在html里清晰地分开, 并在代码里有足够注释. 函数结构如下: |----script |----Definations |----Global Snake variab

Python入门学习:一步步教你怎么用Python写贪吃蛇游戏

前几天,有人提到贪吃蛇,一下子就勾起了我的兴趣,毕竟在那个Nokia称霸的年代,这款游戏可是经典中的经典啊!而用Python(蛇)玩Snake(贪吃蛇),再合适不过了. 这里通过一个Python入门学习的例子跟大家详细讲解一下! 先通过下面这个效果图来感受下吧! 1 环境 操作系统:Windows Python版本:3.7.3 2 需求分析 我们先来回顾下贪吃蛇中的游戏元素及游戏规则. 首先呢,需要有贪吃蛇.有食物:需要能控制贪吃蛇来上下移动获取食物:贪吃蛇在吃取食物后,自身长度增加,同时食物消

自己写贪吃蛇的一下感悟

贪吃蛇 思想 先建立一个大的JFrame框架 建立一个Jpanel中级容器 ,将JPanel放入框架中 建立一个Data类放置各种需要用的图片 具体实现 让Jpanel类继承JPanel public class gameJpan extends JPanel 重写JPanel中的方法 protected void paintComponent(Graphics g) 开始在JPanel上添加贪吃蛇以外的东西 super.paintComponent(g);        this.setBac

python写贪吃蛇小游戏

python的pygame模块写的. 运行界面: # 没搞素材,丑了点 身体跟随蛇头的实现思路: 1. 通过双向链表+两个标志. 链表是为了,蛇头改变方向后,告诉后一个身体,移动后跟着主蛇方向改变,之后身体在告诉后一个身体,跟随前一个身体方向改变. 两个标志(start_flag,change_flag)是为了,每一帧依次改变方向,而不是一个帧内就循环告诉完全部身体方向改变. 2. 加上两个标志,只记录一次前者方向,那么主体发生连续两次改变会出现的情况 解决方式:身体有存前者改变方向的列表 #

13行js写贪吃蛇游戏

先上源码,版本是ES6 13行常规(700bytes) shortest snake game.html 压缩后的500bytes(当然两处document还是可以用eval压缩的) index.500bytes.html 之前很火的20行代码地址(有BUG)(900bytes) hj7jay/article/details/51011269 一维数组700char (0,0)位置的蛇身用0表示,(0,1)用1,(1,0)用10表示,以此类推 因为就13行js, 第4行 是声明 第5行 比较难理

python之pygame实现贪吃蛇小游戏

1 安装pygame pip install pygame 2 代码如下 import pygame import sys import time import random from pygame.locals import * # 初始化pygame pygame.init() # 这是游戏框 fps_clock = pygame.time.Clock() play_sur_face = pygame.display.set_mode((640, 480)) # 设置标题 pygame.di

java 简单贪吃蛇

说了是简单版贪吃蛇... 就3个类+方向枚举变量(本来想写贪吃蛇斜着走的..想象我的蛇是矩形 斜着难看就没写) 上下左右键控制移动 空格暂停 SnackClient类 package com.xynu.snaker; import java.awt.Color; import java.awt.Font; import java.awt.Graphics; import java.awt.event.ActionEvent; import java.awt.event.ActionListene

小项目特供 贪吃蛇游戏(基于C语言)

C语言写贪吃蛇本来是打算去年暑假写的,结果因为ACM集训给耽搁了,因此借寒假的两天功夫写了这个贪吃蛇小项目,顺带把C语言重温了一次. 是发表博客的前一天开始写的,一共写了三个版本,第一天写了第一版,第二天写了第二版和第三版. 相信C语言写个小游戏或小项目是大多数计算机相关专业的学生都做的事情,但是作为一个数学专业的学生,我们教研室的老师对C语言的要求也就比较低了,大一没有让我们做个小项目实践一次.至今为止用C/C++做过的三个小项目(大作业),一个是外校同学让我帮忙写的学生信息管理系统(天呐,这