简单贪吃蛇模拟(C语言版本·)

简单贪吃蛇模拟(C语言版本·)

  • 一、所需win32 API知识
  • 二、游戏逻辑实现

一、所需win32 API知识

1.在这儿,直接弱化概念,把在贪吃蛇中用到的API知识说一下!
 1.1用cmd命令来设置控制台窗口的长宽
1
1

  1.2.用title 指令设置控制台窗口的名字
1

#include <stdio.h>
#include <windows.h>
int main()
{//设置控制台尺寸system("mode con cols=100 cols=30");//设置控制台名字system("title 贪吃蛇");system("pause");return 0;
}

 1.3.表示一个字符在控制台屏幕上的坐标COORD

可以设置一个坐标COORD pos = {10,20};

 1.4.GetStdHandle函数介绍!
  *此函数检索指定设备的句柄!(标准输入,标准输出,标准错误)
原型是:HANDLE WINAPI GetStdHandle(
In DWORD nStdHandle
);

1
1

  1.5.GetConsoleCursorInfo
 *检索有关指定控制台屏幕缓冲区的游标大小和游标可见性的信息。
BOOL WINAPI GetConsoleCursorInfo(
In HANDLE hConsoleOutput,
Out PCONSOLE_CURSOR_INFO lpConsoleCursorInfo
);


1

  *参数作用: dwSize:由光标填充的字符单元格的百分⽐。 此值介于1到100之间。 光标外观会变化,范围从完全填充单元格到单元底部的⽔平线条!
  bVisible:游标的可⻅性。 如果光标可⻅,则此成员为TRUE

 1.6.SetConsoleCursorInfo
BOOL WINAPI SetConsoleCursorInfo(
In HANDLE hConsoleOutput,
In const CONSOLE_CURSOR_INFO *lpConsoleCursorInfo
);

1
1
 1.7.SetConsoleCursorPosition
BOOL WINAPI SetConsoleCursorPosition(
In HANDLE hConsoleOutput,
In COORD dwCursorPosition
);
设置指定控制台屏幕缓冲区中的光标位置,我们将想要设置的坐标信息放在COORD类型的pos中,调⽤SetConsoleCursorPosition函数将光标位置设置到指定的位置

//设置一个函数,用于定位位置
void SetPos(short x, short y)
{ COORD  pos = { x,y };HANDLE handle = GetStdHandle(STD_OUTPUT_HANDLE);//设置位置SetConsoleCursorPosition(handle, pos);
}

 1.8.GetAsyncKeyState
SHORT GetAsyncKeyState(
[in] int vKey
);
将键盘上每个键的虚拟键值传递给函数,函数通过返回值来分辨按键的状态。GetAsyncKeyState 的返回值是short类型,在上⼀次调⽤ GetAsyncKeyState 函数后,如果返回的16位的short数据中,最⾼位是1,说明按键的状态是按下,如果最⾼是0,说明按键的状态是抬起;如果最低位被置为1则说明,该按键被按过,否则为0。
如果我们要判断⼀个键是否被按过,可以检测GetAsyncKeyState返回值的最低值是否为1.

  要判断一个键盘上的键是否按过,要看GetAsyncKeyState返回值的最低位是否是1,如果最低位是1,则表示按键按过,做出相应的操作,如果最低位是0,则表示按键没按过,不执行任何操作!

#define KEY_PRESS(VK)  ((GetAsyncKeyState(VK) & 0x1)? 1:0)
int main()
{//按键控制模块while (1){if (KEY_PRESS(VK_NUMPAD0)){printf("0\n");}else if (KEY_PRESS(VK_NUMPAD1)){printf("1");}}return 0;
}

  1.9.打印宽字符
宽字符x轴占两格子,Y轴占一个格子

#include <locale.h>
int main() {setlocale(LC_ALL, "");wchar_t ch1 = L'●';wchar_t ch2 = L'★';printf("%c%c\n", 'a', 'b');wprintf(L"%c\n", ch1);wprintf(L"%c\n", ch2);return 0;
}

二、游戏逻辑实现

 首先,对于现在的我来说,写这样一个贪吃蛇还是很难的,写这个东西,让我感觉最深刻的是枚举类型的运用,这是一个好东西,还有结构体的运用,能够学到大概一个简答的东西,怎么组织,怎么来写,这是很重要的一点,看整个代码,不是很难,但是如果你自己要组织出来,要写出来还是很难得,特别是枚举的应用!
 1.数据存储结构的设
  首先蛇的每一个节点都要存储,选用链表结构来存储蛇的每一个节点的数据!蛇吃的食物也是一个节点,所以可以直接定义!

typedef struct SnakeNode
{//存储坐标信息int x;int y;struct SnakeNode* next;
}SnakeNode,*pSnakeNode;

  蛇的方向和游戏状态可以用枚举类型一一列举,我认为这说的这个游戏设计最好的地方,这个方面跟能让你感受枚举的作用!!!

enum DIRECTION
{UP = 1,DOWN,LEFT,LEFT
};enum GAME_STATE
{OK = 1,//正常运行ESC,//按ESC退出游戏KILL_BY_WALL,//撞墙死亡退出游戏KILL_BY_SELF //撞自己死亡退出游戏
};

  这儿再说一下,枚举这样写,也看不出来,有什么作用,但是到后面设计的时候有奇效!!!
  下面来定义一个结构体存放整个贪吃蛇游戏中所用到的所有信息!!!

struct Snake
{//定义蛇头pSnakeNode pSnake;//定义食物pSnakeNode pFood;//方向 DIRECTION Direction;//状态GAME_STATE GameState;//获得的总分int Score;//食物的分数int FoodWeight;//睡眠时间//决定贪吃蛇速度的快慢int SleepTime;
};

2整个思路设计
1
3.游戏逻辑实现

//先写测试代码,Test.c
void test()
{//创建贪吃蛇int ch = 0;do{Snake snake = { 0 };GameStart(&snake);GameRun(&snake);GameEnd(&snake);SetPos(20, 15);printf("再来一局吗?(Y/N):");ch = getchar();getchar();//清理\n} while (ch == 'Y'||ch == 'y');
}
int main()
{//修改适配本地中文环境srand((unsigned int)time(NULL));setlocale(LC_ALL, "");//测试贪吃蛇test();SetPos(0, 28);return 0;
}
//写Snake.c文件,各个逻辑的写法,时间不够,详细不说了
#include "Snake.h"//设置光标位置的函数void SetPos(short x, short y)
{COORD pos = {x,y};HANDLE houtput = NULL;houtput = GetStdHandle(STD_OUTPUT_HANDLE);SetConsoleCursorPosition(houtput, pos);
}
void WelcomeToGame()
{//欢迎信息SetPos(40, 12);printf("欢迎来到贪吃蛇游戏!\n");SetPos(41, 18);system("pause");system("cls");//功能介绍信息SetPos(29, 12);printf("用↑.↓.←.→分别控制蛇的移动,F3为加速,F4为减速\n");SetPos(29, 13);printf("加速将能得到更高分数!");SetPos(29,20);system("pause");system("cls");
}void CreatMap()
{//上SetPos(0, 0);for (int i = 0; i <= 56; i += 2){wprintf(L"%lc", WALL);}//SetPos(57, 0);//printf("---->X");//wprintf(L"%lc", L'┉');//wprintf(L"%lc", L'▶');//下SetPos(0, 26);for (int i = 0; i <= 56; i += 2){wprintf(L"%lc", WALL);}//左SetPos(0, 1);for (int i = 1; i <= 25; i++){wprintf(L"%lc\n", WALL);}//SetPos(0, 27);//wprintf(L"%lc\n", L'┋');//wprintf(L"%lc", L'▼'); //printf("Y");//右SetPos(56, 1);for (int i = 1; i <= 25; i++){wprintf(L"%lc\n", WALL);SetPos(56, 1 + i);}
}void InitSnake(pSnake ps)
{//创建5个蛇身节点pSnakeNode cur = NULL;for (int i = 0; i < 5; i++){cur = (pSnakeNode)malloc(sizeof(SnakeNode));if (cur == NULL){perror("InitSnake malloc fail!");return;}//申请成功cur->x = POS_X + 2 * i;cur->y = POS_Y;cur->next = NULL;//头插if (ps->pSnake == NULL){ps->pSnake = cur;}else{cur->next = ps->pSnake;ps->pSnake = cur;}}//打印蛇身cur = ps->pSnake;while (cur){SetPos(cur->x, cur->y);wprintf(L"%lc", BOOY);cur = cur->next;}//贪吃蛇其它信息初始化ps->dir = RIGHT;ps->FoodWeight = 10;ps->pFood = NULL;ps->Score = 0;ps->SleepTime = 400; //msps->status = OK;
}void CreatFood(pSnake ps)
{int x = 0;int y = 0;//处理随机生成的坐标,不能在墙外面,x必须是2的倍数
again:do {x = rand() % 53 + 2;y = rand() % 25 + 1;} while (x % 2!=0);//食物不能是蛇的节点pSnakeNode cur = ps->pSnake;while (cur){if ((x == cur->x) && (y == cur->y)){goto again;}cur = cur->next;}//创建食物pSnakeNode pFood = (pSnakeNode)malloc(sizeof(SnakeNode));if (pFood == NULL){perror("CreatFood malloc fail!");return;}pFood->x = x;pFood->y = y;pFood->next = NULL;ps->pFood = pFood;SetPos(x, y);wprintf(L"%lc", FOOD);
}
void PrintHelpInfo()
{SetPos(62, 15);printf("1.不能穿墙,不能咬到自己");SetPos(62, 16);printf("2.用↑.↓.←.→分别控制蛇的移动");SetPos(62, 17);printf("  F3为加速,F4为减速");SetPos(62, 18);
}void pause()
{//while (1){Sleep(200);//如果按键按下,跳出循环if (KEY_PRESS(VK_SPACE)){break;}}
}
int NextIsFood(pSnake ps, pSnakeNode pnext)
{if ((ps->pFood->x == pnext->x) && (ps->pFood->y == pnext->y)){return 1;}return 0;
}void EatFood(pSnake ps, pSnakeNode pnext)
{//头插pnext->next = ps->pSnake;ps->pSnake = pnext;//打印蛇身pSnakeNode cur = ps->pSnake;while (cur){SetPos(cur->x, cur->y);wprintf(L"%lc", BOOY);cur = cur->next;}ps->Score += ps->FoodWeight;//释放旧的食物free(ps->pFood);//创造新的食物CreatFood(ps);
}void NotEatFood(pSnake ps, pSnakeNode pnext)
{//头插pnext->next = ps->pSnake;ps->pSnake = pnext;//找到倒数第二个节点pSnakeNode cur = ps->pSnake;while (cur->next->next){//打印感觉有问题SetPos(cur->x, cur->y);wprintf(L"%lc", BOOY);cur = cur->next;}//打印倒数第二个节点SetPos(cur->x, cur->y);wprintf(L"%lc", BOOY);//最后一个节点pSnakeNode tail = cur->next;cur->next = NULL;//将尾结点的地方打印成空格SetPos(tail->x, tail->y);printf("  ");free(tail);tail = NULL;//打印蛇身
}
//检测撞墙
KillByWall(pSnake ps)
{if ((ps->pSnake->x == 0) || (ps->pSnake->x == 56) ||(ps->pSnake->y == 0) || (ps->pSnake->y == 26)){ps->status = KILL_BY_WALL;}
}
//检测撞到自己
KillBySelf(pSnake ps)
{pSnakeNode cur = ps->pSnake->next; //从第二个开始遍历while (cur){if ((cur->x == ps->pSnake->x) &&(cur->y == ps->pSnake->y)){ps->status = KILL_BY_SELF;//结束break;}cur = cur->next;}
}
void SnakeMove(pSnake ps)
{pSnakeNode pnext = (pSnakeNode)malloc(sizeof(SnakeNode));if (pnext == NULL){perror("SnakeMove malloc fail");return;}pnext->next = NULL;switch (ps->dir){case UP:pnext->x = ps->pSnake->x;pnext->y = ps->pSnake->y - 1;break;case DOWN:pnext->x = ps->pSnake->x;pnext->y = ps->pSnake->y + 1;break;case LEFT:pnext->x = ps->pSnake->x-2;pnext->y = ps->pSnake->y;break;case RIGHT:pnext->x = ps->pSnake->x+2;pnext->y = ps->pSnake->y;break;}//下一个坐标是否是食物if (NextIsFood(ps, pnext)){//是食物就吃掉EatFood(ps,pnext);}else{//不是食物正常走NotEatFood(ps,pnext);}//检测撞墙KillByWall(ps);//检测撞到自己KillBySelf(ps);
}void GameStart(pSnake ps)
{system("mode con cols=100 lines=30");system("title 贪吃蛇");//隐藏光标HANDLE handle = GetStdHandle(STD_OUTPUT_HANDLE);CONSOLE_CURSOR_INFO cursorinfo;//获取控制台光标信息GetConsoleCursorInfo(handle, &cursorinfo);cursorinfo.bVisible = false;SetConsoleCursorInfo(handle, &cursorinfo);//打印欢迎信息WelcomeToGame();//绘制地图CreatMap();//初始化蛇InitSnake(ps);//创建食物CreatFood(ps);
}void GameRun(pSnake ps)
{//打印帮助信息PrintHelpInfo();do{//do...while循环先进去,打印分数//分数SetPos(62, 10);printf("总分:%5d\n", ps->Score);SetPos(62, 11);printf("食物的分值:%02d\n", ps->FoodWeight);//检测按键//上 下 左 右 ESC 空格 F3 F4//下面怎么控制按下按键,做出对应的响应的操作//它是通过枚举来控制按键对应的操作if (KEY_PRESS(VK_UP) && ps->dir != DOWN){//按下上键,则方向变为上ps->dir = UP;}else if (KEY_PRESS(VK_DOWN) && ps->dir != UP){ps->dir = DOWN;}else if (KEY_PRESS(VK_LEFT) && ps->dir != RIGHT){ps->dir = LEFT;}else if (KEY_PRESS(VK_RIGHT) && ps->dir != LEFT){ps->dir = RIGHT;}else if (KEY_PRESS(VK_ESCAPE)){ps->status = ESC;//按下ESC做出的响应是跳出这个游戏的循环break;  }else if (KEY_PRESS(VK_SPACE)){//暂停pause();  //暂停和恢复暂停//第一次按下空格键,检测到则调用了pause函数//程序死循环睡眠//再次按下则在pause函数里面响应,跳出死循环,执行其它操作}else if (KEY_PRESS(VK_F3)){//还是通过枚举来控制if (ps->SleepTime >= 80){ps->SleepTime -= 30;ps->FoodWeight += 2;}}else if (KEY_PRESS(VK_F4)){//F4按下对应的响应if (ps->FoodWeight > 2){ps->SleepTime += 30;ps->FoodWeight -= 2;}}//睡眠一下//停留一下,执行一下,停留时间的长短由Sleep控制Sleep(ps->SleepTime);// 走一步SnakeMove(ps);} while (ps->status == OK);
}void GameEnd(pSnake ps)
{SetPos(25, 12);switch (ps->status){case ESC:printf("主动退出游戏!");break;case KILL_BY_WALL:printf("撞墙而死!");break;case KILL_BY_SELF:printf("撞自身而死!");break;}//释放所有申请的节点pSnakeNode cur = ps->pSnake;while (cur){pSnakeNode next = cur->next;free(cur);cur = next;}ps->pSnake = NULL;free(ps->pFood);ps->pFood = NULL;
}
//写Snake.h文件
#pragma once
#include <stdio.h>
#include <locale.h>
#include <windows.h>
#include <stdlib.h>
#include <stdbool.h>
#include <time.h>#define KEY_PRESS(VK)  ((GetAsyncKeyState(VK) & 0x1)? 1:0)#define WALL L'□'
#define BOOY L'●'
#define FOOD L'★'
//蛇起始位置
#define POS_X  24
#define POS_Y  5//控制游戏状态
enum GAME_STATUS
{OK = 1,ESC, //退出KILL_BY_WALL, //撞墙KILL_BY_SELF //撞自身
};//控制方向来用
enum DIRECTION
{UP=1,DOWN,LEFT,RIGHT
};//蛇身节点
typedef struct Snakenode
{int x;int y;struct Snakenode* next;
}SnakeNode,*pSnakeNode;//维护整个贪吃蛇游戏
typedef struct Snake
{pSnakeNode pSnake; //蛇头pSnakeNode pFood;  //食物指针int Score;   //累计分数int FoodWeight; //一个食物分数int SleepTime; //蛇休眠的时间,休眠的时间越短,蛇的速度越快,休眠的时间越长,蛇的速度越慢enum GAME_STATUS status; //游戏当前状态enum DIRECTION dir;  //方向
}Snake,*pSnake;void GameStart(pSnake ps);
void SetPos(short x, short y);
void WelcomeToGame();
void CreatMap();
void InitSnake(pSnake ps);
void CreatFood(pSnake ps);
void GameRun(pSnake ps);
void PrintHelpInfo();
void SnakeMove(pSnake ps);
int NextIsFood(pSnake ps, pSnakeNode pnext);
void EatFood(pSnake ps, pSnakeNode pnext);
void NotEatFood(pSnake ps, pSnakeNode pnext);
//检测撞墙
KillByWall(pSnake ps);
//检测撞到自己
KillBySelf(pSnake ps);
void GameEnd(pSnake ps);
void SetPos(short x, short y);

完结!!!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/690107.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

如何使用Pycharm中的image模块以及导入打开图片(属性)

在学习pytorch深度学习的过程中&#xff0c;通常会使用到大量的数据集&#xff0c;包括训练集和测试集。 以下是pytorch加载数据集的流程&#xff1a; 在notebook中使用help方法查看Dataset类的功能以及操作&#xff1a; 使用dataset需要继承Dataset父类 重写__getitem__方法和…

Android使用shape定义带渐变色的背景

在drawable目录下创建文件bg_gradient.xml 文件内的内容如下&#xff1a; <?xml version"1.0" encoding"utf-8"?> <shape android:shape"rectangle" xmlns:android"http://schemas.android.com/apk/res/android"> <…

更换笔记本电脑内存条

写在前头 本人笔记本电脑早年购买&#xff0c;随着学习工作量的增大&#xff0c;我在使用电脑时往往需要同时打开多个软件&#xff0c;这时电脑的表现就难以言论了。虽然这个电脑在我的手中表现出其“志在千里”的鸿志&#xff08;毕竟它主人是谁里&#xff09;&#xff0c;但…

前端秘法基础式(CSS)(第二卷)

目录 一.字体 1.字体的设置 2.字体的颜色 2.1预定义的颜色值 2.2十六进制 2.3rgb表示法 3.字体粗细及样式 4.文本 4.1text-align 4.2text-indent 4.3text-decoration 二.背景属性 三.圆角矩形 四.元素显示模式 五.盒模型 六.弹性布局 七.Chrome调试工具 一.字…

普通人steam搬砖到底能不能做,一定谨记这4点

steam/csgo搬砖项目拥有小投资收益稳定的特点&#xff0c;多搬多赚&#xff0c;这个项目已存在很多年&#xff0c;是非常老牌的阳光项目&#xff0c;很多人比较关心普通人steam搬砖到底能不能做&#xff0c;这里想提醒你谨记这4点。 1&#xff1a;steam/csgo搬砖项目不需要玩游…

java.sql.SQLException: No operations allowed after statement closed.

背景 某天下午&#xff0c;客服反馈线上服务出现问题&#xff0c;不能分配了。于是我登录到系统上&#xff0c;进行同样的操作发现也不行。当然同时我已经登录到服务器打开了日志&#xff0c;发现报错了&#xff0c;下面就是日志的错误信息&#xff1a; java.sql.SQLExceptio…

AI嵌入式K210项目(29)-模型加载

文章目录 前言一、下载部署包二、C部署三、搭建文件传输环境四、文件传输五、调试六、MicroPython部署总结 前言 上一章节介绍了如何进行在线模型训练&#xff0c;生成部署包后&#xff0c;本章介绍加载模型&#xff1b; 一、下载部署包 训练结束后&#xff0c;在训练任务条…

5 虚拟存储器

常规存储器管理方式和局部性原理 特征&#xff1a;一次性 驻留性局部性原理&#xff1a;较短的时间内程序的执行仅局限于某个部分 时间局限性&#xff1a;最近访问过的又被访问&#xff08;循环操作&#xff09;空间局限性&#xff1a;程序顺序执行 虚拟存储器&#xff1a;进程…

基于ESP32+Platformio的物联网RTOS_SDK-CC_Device

本项目基于ESP32以及Platformio平台开发&#xff0c;请自行查阅如何配置这个环境 开源gitee地址&#xff1a;cc_smart_device 如果愿意贡献项目or提出疑问和修改的&#xff0c;请在gitee上提issue 项目里的mqtt服务器是公共的 请大家最好换成私有的 否则容易收到其他用户的错误…

mybatis 基础入门使用

1、mybatis 简介 1.1、mybatis 特性 MyBatis 是支持定制化 SQL、存储过程以及高级映射的优秀的持久层框架&#xff1b; MyBatis 避免了几乎所有的 JDBC 代码和手动设置参数以及获取结果集&#xff1b; MyBatis可以使用简单的XML或注解用于配置和原始映射&#xff0c;将接口和…

【Java面试】MongoDB

目录 1、mongodb是什么&#xff1f;2、mongodb特点什么是NoSQL数据库&#xff1f;NoSQL和RDBMS有什么区别&#xff1f;在哪些情况下使用和不使用NoSQL数据库&#xff1f;NoSQL数据库有哪些类型?启用备份故障恢复需要多久什么是master或primary什么是secondary或slave系列文章版…

【shell】2、Makefile

文章目录 一、变量1.1 定义简单变量1.2 定义数组1.3 引用变量1.4 规则中定义变量 一、变量 1.1 定义简单变量 用变量名 值的语法来定义变量。这些变量可以用于存储命令、文件名、目录路径等信息&#xff0c;以便在Makefile规则中重复使用 # 定义一个变量 VAR_NAME value# …

Vue练习5:图片的引入

后续会补充 1.require引入 src -> asstes <template><img :src"url"> </template><script> export default {name: App,data(){return{url: require("./assets/logo.png"),}} } </script> 2.import引入 src…

Vue2:Vuex中使用mapState和mapGetters

一、情景说明 前面的Vuex案例中 我们在使用state中的变量或者getter中的变量时 使用插值语法&#xff0c;写法如下&#xff1a; <h1>当前求和为&#xff1a;{{$store.state.sum}}</h1><h1>当前求和放大10倍为&#xff1a;{{$store.getters.bigSum}}</h1&…

【医学大模型】MEDDM LLM-Executable CGT 结构化医学知识: 将临床指导树结构化,便于LLM理解和应用

MEDDM LLM-Executable CGT 结构化医学知识: 将临床指导树结构化&#xff0c;便于LLM理解和应用 提出背景对比传统医学大模型流程步骤临床指导树流程图识别临床决策支持系统 总结解决方案设计数据收集与处理系统实施临床决策支持 提出背景 论文&#xff1a;https://arxiv.org/p…

什么是DoS和DDoS攻击?

DoS和DDoS攻击是两种常见的网络攻击方式&#xff0c;它们通过向目标服务器发送大量的无效或高流量的网络请求&#xff0c;从而耗尽服务器的资源&#xff0c;使其无法正常处理有效的请求。 DoS攻击是一种单点攻击方式&#xff0c;通常是通过向目标服务器发送大量的请求&#x…

【c/c++】C++静态工具类和单例模式对比学习

文章目录 序言1. static静态成员2. C(伪)静态工具类3. 单例模式3.1 单例模式的特点3.2 单例模式的实现方式3.3 单例模式的缺点3.4 Meyer Singleton单例模式 4. (伪)静态工具类 vs 单例模式4.1 区别4.2 如何选择4.3 一些释疑 序言 比较C static&#xff08;伪&#xff09;静态&a…

在k8s中,使用DirectPV CSI作为分布式存储的优缺点

DirectPV 提供了一种直接将物理卷(Physical Volumes)与 Kubernetes 集群中的 Pod 绑定的机制。 利用 DirectPV,你可以将相应的 PV 直接与节点上的物理存储设备(如磁盘)进行绑定,而无需通过网络存储服务(如 NFS 或 Ceph)来提供存储。这种直接访问物理卷的方式,有助于提…

C语言---指针进阶

1.字符指针 int main() {char str1[] "hello world";char str2[] "hello world";const char* str3 "hello world.";const char* str4 "hello world.";if (str3 str4){//常量字符串在内存里面是无法修改的&#xff0c;所以没必要…

springboot/ssm校园菜鸟驿站管理系统Java校园快递取件管理系统

springboot/ssm校园菜鸟驿站管理系统Java校园快递取件管理系统 开发语言&#xff1a;Java 框架&#xff1a;springboot&#xff08;可改ssm&#xff09; vue JDK版本&#xff1a;JDK1.8&#xff08;或11&#xff09; 服务器&#xff1a;tomcat 数据库&#xff1a;mysql 5.…