Python命令行小游戏—贪吃蛇


前言

为了初步学习Python更有兴趣,决定做个学编程大多都会做的小游戏—贪吃蛇,因为是刚刚开始学习,不足之处,希望大家多多指正。


一、贪吃蛇游戏初始界面及地图

1.游戏初始界面

def mune():
    os.system("color 0a")
    dll.set_cmd(0, 2);print("□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□")
    dll.set_cmd(0, 3);print("□□□□□■■■■□□□□□□□贪吃蛇□□□□□□□□□■□□□    操作指南    □□□□")#7
    dll.set_cmd(0, 4);print("□□□□■■□□■■□□□□□□□□□□□□□□□□□□■□□□方向键  改变方向□□□□")#5
    dll.set_cmd(0, 5);print("□□□■■□□□□□□□□□□2021.05.06□□□□□□□□■□□□ESC     退出游戏□□□□□□")
    dll.set_cmd(0, 6);print("□□□□■■□□□□□□□□□□□□□□□□□□□□□□■□□□SPACE   暂停游戏□□□□□□□□□")
    dll.set_cmd(0, 7);print("□□□□□■■■■□□□□■■■■□□□□■■■□□□□■□□■■□□□□■■■■■□□")
    dll.set_cmd(0, 8);print("□■□□□□□■■■□□■□□□□■□□■□□□■□□□■■□□□□□□■□□□□□■□")
    dll.set_cmd(0, 9);print("□□■□□□□□■■■□■□□□□■□■□□□□■□□□■■□□□□□■□□■■■■□□")
    dll.set_cmd(0,10);print("□□□■■□□■■■□□■□□□□■□□■□□■□■□□■□■□□□□□■□□□□□□□")
    dll.set_cmd(0,11);print("□□□□■■■■□□□□■□□□□■□□□■■□□■■□■□□■■■□□□■■■■■□□")#24
    dll.set_cmd(0,12);print("□□□□□□□□□□□□□□□□□□按空格开始游戏□□□□□□□□□□□□□□□□□□□")

这里使用了os库ctypes库以及调用的.dll自建动态库,第二节会详细说明

2.游戏地图

def wall(x,y):#绘制围墙
    i=0;j=0
    while i<=x:
        print('■',end="")
        for j in range(y):
            if i==0:
                print('■', end="")
            elif i==20:
                print('■', end="")
            else:
                print("  ",end="")
            j+=1
        print('■')
        i+=1
        j=0

简单的使用Python的输出函数print()做一个围墙,默认情况下函数内的 end=“/n”

二、命令符的设置、输出刷新和按键检测

1.库支持

  • os库中的os.system(“命令行指令”) 函数可执行命令行中的指令,具体指令可在命令行中输入help回车获得相关信息
  • c语言能够更好的对命令行的输出进行刷新,这里通过调用.dll的方式调用c语言,具体的使用可观看此视频python和C语言混合编程

2.c语言代码

#include<stdio.h>
#include<stdlib.h>
#include<windows.h>
#include<conio.h>
//命令行光标显示(1)隐藏(0)
_declspec(dllexport) void _stdcall init_cmd(int x)
{
	HANDLE handle = GetStdHandle(STD_OUTPUT_HANDLE);
	CONSOLE_CURSOR_INFO CursorInfo;
	GetConsoleCursorInfo(handle, &CursorInfo);//获取控制台光标信息
	CursorInfo.bVisible = x; //控制台光标状态,x=1显示,x=0隐藏
	SetConsoleCursorInfo(handle, &CursorInfo);//设置控制台光标状态
}
//命令行光标移动函数
_declspec(dllexport) void _stdcall set_cmd(int x, int y)
{
	COORD pos;
	pos.X = x;
	pos.Y = y;
	SetConsoleCursorPosition(GetStdHandle(STD_OUTPUT_HANDLE), pos);
}
//获取键盘输入
_declspec(dllexport) int _stdcall c_getch()
{
	int key;
	if (_kbhit()) {//_kbhit()为按键检测,有按键输出1,没有输出0
	key = _getch();
	Sleep(10);
	return key;
      }
	else;
	
}

3.Python代码(变量初始化及游戏初始化)

import os,time,ctypes,random

wall_x,wall_y ,eat_flag= 20,21,1
move_up,move_down,move_left,move_right,esc,space = 72,80,75,77,27,32

def init_game():#游戏界面初始化
    global getkey, key, snake_x, snake_y, snake_list#全局变量声明
    os.system("mode con cols=48 lines=22")  # 设置命令符窗口大小
    os.system("cls")
    os.system("color 2b")  # 设置字体和背景颜色
    wall(wall_x, wall_y)  # 绘制围墙
    dll.set_cmd(0, 1)
    print("■□□□□○")
    snake_list = [[2, 1], [4, 1], [6, 1], [8, 1], [10, 1]]
    snake_x, snake_y ,getkey,key= 10, 1,0,0
def init():#命令符及游戏初始化
    ctypes.windll.kernel32.SetConsoleTitleW("贪吃蛇")  # 修改命令符标题
    os.system("mode con cols=88 lines=15")  # 设置命令符窗口大小
    dll.init_cmd(0)  # 设置光标状态

当定义函数内有全局变量的值发生变化时,需要进行全局变量声明

三、蛇的移动

def snake_move(*lis):#蛇移动
    dll.set_cmd(snake_list[0][0], snake_list[0][1])
    print("  ")
    snake_list.append(lis[0])
    if eat_flag==0:
        snake_list.pop(0)
    dll.set_cmd(snake_list[-1][0], snake_list[-1][1])
    print("○")
    n = 0
    for n in range(len(snake_list)-2):
        dll.set_cmd(snake_list[-2-n][0], snake_list[-2-n][1])
        print("□")
        n += 1
def move():#蛇移动防倒走
    global snake_x, snake_y
    if [snake_x, snake_y] in snake_list[-2:]:
        snake_x = snake_list[-1][0]
        snake_y = snake_list[-1][1]
    else:
        snake_move([snake_x, snake_y])
  • 蛇的身体的每个小块对应列表内的一个坐标,头的位置处于列表最后一项
  • 当移动时消除第一个坐标所在的位置,使用pop(0)方法弹出第一项
  • 将下一个头的位置(由 snake_ctrl 函数提供)使用append()方法添加到列表最后
  • 通过检测位置坐标(x in y)是否为靠近头的一段来防止蛇倒走。

四、蛇的方向控制

def get_key():#获得键盘输入
    global getkey,key
    a = dll.c_getch()
    if a in [esc,space]:
        getkey = a
    if a in [move_up,move_down,move_left,move_right]:
        if ((key == move_up) and (a != move_down))or ((key == move_down) and (a != move_up)) \
            or(key == 0) or((key == move_left) and (a != move_right)) \
                or ((key == move_right) and (a != move_left)):
            key = a
        else:
            pass
    else:
        pass
def snake_ctrl():#根据键盘输入判断方向已经功能操作
    global getkey, key, snake_x, snake_y
    if key == move_down:
        snake_y += 1
    elif key == move_up:
        snake_y -= 1
    elif key == move_right:
        snake_x += 2
    elif key == move_left:
        snake_x -= 2
    else:
        pass
    if getkey == space:
        time.sleep(1)
        getkey = 0
        while 1:
            getkey = dll.c_getch()
            if getkey == space:
                getkey = 0
                break
            elif getkey == esc:
                break
            else:
                pass
    else:
        pass

检测按键输入 get_key() 以及设置相应按键实现的功能 snake_ctrl()

五、食物的设置

def putfood():#放置食物
    global food_x,food_y,eat_flag
    if eat_flag==1:
        food_x = snake_x
        food_y = snake_y
        while [food_x,food_y] in snake_list:
            food_x = random.randint(1, 20) * 2
            food_y = random.randint(1, 19)
        eat_flag = 0
        dll.set_cmd(food_x,food_y)
        print("●")
    else :
        pass
def eatfood():#吃掉食物判断
    global eat_flag,snake_list,snake_x, snake_y
    if ([food_x,food_y] in snake_list)and(eat_flag==0) :
        eat_flag = 1
    else:
        pass

不得不说Python中成员判断真的很好用。

  • 通过食物坐标点点是否已经包含到蛇身体的列表中来判断是否吃到食物
  • 食物的产生通过随机函数(random.randint(x,y))来提供坐标
  • 是否放置食物由是否吃掉的标志(eat_flag)来判断

六、游戏结束

def gameover():#游戏结束判断
    if (snake_x>42)|(snake_y>19)\
        |(snake_x<2)|(snake_y==0):
        return 1
    elif snake_list.count(snake_list[-1])>1:
        return 1
    else :
        return 0
def over_next():#结束后的操作
    global getkey,eat_flag
    if gameover():
        dll.set_cmd(16, 9)
        print("得分:{}分".format(len(snake_list)-5))
        dll.set_cmd(6, 10)
        print("游戏结束,esc退出,space重新开始")
        while 1:
            getkey = dll.c_getch()
            if getkey == esc:
                os.system("cls")
                dll.init_cmd(1)
                os.system("exit")
                break
            elif getkey == space:
                init_game()
                eat_flag = 1
                break
            else:
                pass
    else:
        pass
  • 贪吃蛇撞墙的判断就简单粗暴的坐标判断,如果地图复杂可能需要使用列表将每部分墙体的起始坐标录入进行判断,贪吃蛇只需要判断头的位置就能够判断是否撞到了墙。
  • 使用列表的count方法能够很简单的判断序列中有没有两相同的元素,来检测蛇有没有吃到自己。

七、主程序

if __name__ == '__main__':#主函数main
  dll = ctypes.windll.LoadLibrary("set_cmd.dll")  # 调用c语言程序
  init()
  mune()
  while 1:
      getkey = dll.c_getch()
      if getkey == space:
          break
      else:
          pass
  init_game()
  while 1:
      get_key()
      putfood()
      eatfood()
      snake_ctrl()
      move()
      over_next()
      if getkey == esc:
          os.system("cls")
          dll.init_cmd(1)
          os.system("exit")
          break
      else:
          pass
      get_key()
      time.sleep(0.15)
  • 通过两次按键 get_key() 检测来减少按键的检测缺失
  • 可以通过调整循环等待时间 time.sleep(0.15) 来调整游戏内蛇的速度

八、演示效果

生成.exe文件需要用到pyinstaller,没有的话需要进行以下步骤

  1. 运行命令符输入 pip install pyinstaller
  2. 等待安装完成,输入 pyinstaller -F xxx.py (文件位置)

以下是演示效果
初始界面
在这里插入图片描述
评价:能玩

总结

虽然最后成品比较简陋,但还是有些成就感的,比之前光看书感觉效率提高很多。
下面是所有文件的网盘链接点击下载
提取码:6v5o


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值