贪吃蛇小游戏C++实现

题目贪吃蛇游戏
借助Easyx图形界面库,利用鼠标和键盘,实现贪吃蛇的基本游戏功能。 可实现四种模式的切换,分别是三种固定速率刷新和一种变频加速刷新。 游戏过程中实现一键暂停和复原。
本文档仅作学习交流使用,任何转载引用请注明出处!文档末尾附有本文档的全部资源包括源程序代码,请合理使用。

一、概述

从简单的对数据进行处理,再到实现具体问题,我想这次利用计算机语言实现我一直梦寐以求的事情,做一个游戏。在做出成品之前,我自己甚至都不敢相信这件事对于我来说可行的。期间有多次陷入瓶颈,只知道一个大致方向,却不知如何描述自己的问题,然后导致寻求帮助无果。不过总会有“踏破铁鞋无觅处,得来全不费功夫”的激动,也会有“山重水复疑无路,柳暗花明又一村”的喜悦。在这一个过程中,我也学会了自己独立分析问题,查找资料并解决它。

通过这次课程设计,学会了许多老师不曾涉及的具体问题,比如画图这一应用,但是还是有些功能我想实现却未曾实现的地方,奈何水平不够,甚是遗憾。

二、内容具体描述

(一)、编译环境与语言

所使用编译软件为Visual Studio 2017
其中最关键的图形界面库文件由EsayX提供。开源且较为方便,头文件名为: #include <graphics.h>

所使用计算机语言为C++

(二)、概要设计

1)初始化蛇(数据)

注意到蛇身与蛇头具有不同的特性,故对蛇身与蛇头分别处理。

使用结构体mypoint保存蛇的节点坐标,且0号节点为蛇头。在本课设中,蛇的节点数据使用的是线性表,且最大表长为102。理论上可借助链表来达到蛇长不受限。蛇头需要控制移动方向,故利用5种枚举类型(分别为上、下、左、右、暂停)来区分实现对蛇头移动方向的不同处理方式。最后是蛇的移动方向,即所谓蛇的朝向,蛇下步该移动到的方向,初始化时,蛇的position为向右,蛇长为3,从窗口左上角开始移动。

2)初始化食物

2.1利用结构体mypoint作为食物的坐标,坐标通过随机函数生成。这里应当注意,随机函数有可能把食物坐标生成到蛇的节点坐标上,此时应当做判断,考虑重新生成。

2.2利用fillrerectangle()函数在窗口显示出食物。

3)蛇的移动(数据)

蛇身:前一个蛇身节点所在位置为后一个蛇身节点将要所在的位置。故利用for循环赋值即可实现。

蛇头:根据所获取到的按键信息,将之处理为5种枚举类型之一,再分别对不同的状态,进行不同的蛇头坐标处理。

4)蛇的可视化(图像)

在这里,只需根据前一步得到的蛇的节点坐标,利用for循环即可利用fillrerectangle()函数在窗口显示出蛇的模样。

5)游戏结束的判断

5.1蛇撞墙。即蛇头节点坐标超出窗口边界。

5.2蛇吃蛇身。即蛇头节点坐标与蛇身坐标重合。

6)控制蛇移动的速度

整个蛇移动就是不断重复上述操作,但是注意到计算机的运行速度远比人眼反映的快,故我们借助Sleep函数,通过控制程序休眠时长(以毫秒计),就可控制蛇的移动速度。

7)通关此游戏

贪吃蛇每吃一个食物,全局变量”score”就会加一,当计数器为100时,系统判定此玩家游戏通关,结束游戏。

(三)、操作集合

1)函数汇总:

    snake();					//构造函数,蛇的初始化
	void drawsnake();			//画蛇
	void movesnake();			//移动蛇
	void keydown();			//获取按键
	void judge();				//判断游戏结束
	void gameover();			//游戏结束输出信息
void graduated();		//判断游戏是否通关
	int& getsnakesize()  	//返回蛇长
	mypoint getsnakepos(int pos)//返回蛇的节点坐标
	food();					//构造函数,初始化食物
	void drawfood();	   		//画食物
	void setfoodpos(snake *psnake);	//若食物出现在蛇身上,则重新生成
	int& getflag()			//返回食物状态
	mypoint getfoodpos() 	//返回食物坐标
	void start()				//生成主界面
int help()				//帮助说明
int degreechoice()		//模式选择
void speed(int d)		//给各个模式分配速度
int mouse()				//捕获鼠标信息,并返回
void eatfood(snake *psnake, food *pfood)//判断是否吃到食物
void xiaochuang()					//游戏界面小窗输出信息

2)主函数:

int main()
{
	choice = 2;
	srand((unsigned)time(0));				//设置随机数种子
	initgraph(800, 480);					//开辟游戏窗口
	setbkcolor(RGB(51, 204, 235));			//填充背景颜色
	PlaySound(TEXT("Happy.wav"), NULL, SND_FILENAME | SND_ASYNC | SND_LOOP);		//播放背景音乐					
	while (choice == 2)
	{
		start();								//初始化主界面
		choice = mouse();
		if (choice == 1)
			degree = degreechoice();				//难度选择
		else if (choice == 4)
			choice = help();						//操作说明
		snake* psnake = new snake;
		food* pfood = new food;
		while (choice == 1)
		{
			BeginBatchDraw();					//开启批量绘图模式
			cleardevice();						//清屏函数
			xiaochuang();
			eatfood(psnake, pfood);				//判断是否吃到食物
			if (pfood->getflag() == 0)
				pfood->setfoodpos(psnake);//如果没有生成食物,则生成食物
			if (_kbhit())						//判断是否存在操作
				psnake->keydown();				//反馈控制信息
			psnake->movesnake();				//更新蛇的节点坐标
			psnake->drawsnake();		//根据蛇身坐标画蛇,使之可视化
			pfood->drawfood();		//根据食物坐标画食物,使之可视化
			psnake->judge();
//判断蛇是否撞墙或穿蛇身,并生成结束游戏界面
			psnake->graduated();				//判断是否通关
			FlushBatchDraw();					//绘制图形
			speed(degree);						
//利用程序休眠的时长来控制蛇移动的速度
		}
		delete(psnake);				//释放内存
		delete(pfood);				//释放内存
	}
	closegraph();				//关闭窗口
	return 0;
}

3)头文件:

#include <iostream>
#include <graphics.h>				//图形界面头文件
#include <conio.h>					//控制按键头文件				
#include <Windows.h>					//系统头文件
#include <time.h>						//时间头文件
#pragma comment(lib,"winmm.lib")	//音乐播放头文件

4)全局变量或常量:

int score = 0;
int choice;
int degree = 0;
const int N = 100;
int i;
MOUSEMSG m;							//鼠标指针

5)类:

class snake;
class food;

6)结构体:

struct mypoint						//坐标

(四)、调试分析

整个程序的难点在于(1)如何抽象出蛇的线性表(2)如何绘画出相应的图形(3)如何使界面更美观,更为人性化,游戏更为丰富多彩。

解决方案:

  1. 主要的贪吃蛇模型的搭建参考网上教程①,后再参考网上教程②,进行程序的优化和适当扩充。其中这两个教程贪吃蛇的状态均只有四种,即(right,left, down,up),(stop)状态为本人自行添加,起到程序暂停的一个作用。这也是当时想实现这个功能的解决方案。

  2. 绘图使用的是Easy-x中的图形界面库中的函数。<graphics.h>该头文件下有许多可直接调用的绘图函数,这也是编译器选择VS的原因之一。

  3. 对于这个问题,我创建过两个版本的贪吃蛇游戏,区别正是在于游戏界面的展示。早期是直接利用_getch()函数来获取键盘信息,再通过switch语句实现不同功能;后期是通过获取鼠标信息来控制不同功能的实现。

程序调试过程中发现的问题:
(1)在程序运行过程中,很大概率下会出现肉眼可察觉的闪屏现象。
(2)在不合法输入的条件下,即在主界面使用键盘输入,其中的输入信息会遗留至贪吃蛇的运动程序中去。

原因分析:

  1. 在使用Easy库的时候,由于每次都花大量时间绘制背景,导致闪烁厉害。这里用批量绘图方法将所有绘图一次性显示出来,以解决闪烁的问题。
  2. 未清空键盘缓冲区内的信息。

(五)、运行结果分析

1)主界面

在这里插入图片描述

2)操作说明

在这里插入图片描述

3)模式选择

在这里插入图片描述

4)贪吃蛇游戏

在这里插入图片描述

5)游戏结束显示

在这里插入图片描述

6)程序结构图

在这里插入图片描述

三、实现(源程序清单及注释)

#include <iostream>
#include <graphics.h>				//图形界面头文件
#include <conio.h>					//控制按键头文件				
#include <Windows.h>				//系统头文件
#include <time.h>					//时间头文件
#pragma comment(lib,"winmm.lib")	//音乐播放头文件
using namespace std;
int score = 0;
int choice;
int degree = 0;
const int N = 102;
int i;
MOUSEMSG m;					//鼠标指针
struct mypoint {					//定义节点坐标
	int x;
	int y;
};
class snake {
public:
	snake();						//构造函数,蛇的初始化
	void drawsnake();				//画蛇
	void movesnake();				//移动蛇
	void keydown();				//获取按键
	void gameover();				//游戏结束输出信息
	void judge();					//判断游戏结束
	void graduated();				//判断游戏是否通关
	int& getsnakesize() {
		return snakesize;			//返回蛇长
	}
	mypoint getsnakepos(int pos) {
		return snakepos[pos];		//返回蛇的节点坐标
	}
protected:
	mypoint snakepos[N];			//蛇的节点坐标
	int snakesize;					//蛇长
	char position;					//蛇的移动方向
	enum snakeposition { right, left, down, up, stop };//枚举类型
};
snake::snake()
{
	snakesize = 3;
	for (int i = snakesize - 1; i >= 0; i--)
	{
		snakepos[i].x = 10 * (3 - i);
		snakepos[i].y = 10;
	}
	position = right;
}
void snake::drawsnake()
{
	for (int i = 0; i < snakesize; i++)
	{

		if (i == 0)
			setfillcolor(GREEN);
		else
			setfillcolor(RGB(51, 153, 255));
		fillrectangle(snakepos[i].x, snakepos[i].y, snakepos[i].x + 10, snakepos[i].y + 10);
	}
}
void snake::movesnake()
{
	if (position != stop) {
		for (int i = snakesize - 1; i > 0; i--)
		{
			snakepos[i].x = snakepos[i - 1].x;
			snakepos[i].y = snakepos[i - 1].y;
		}
	}
	switch (position)
	{
	case right:
		snakepos[0].x += 10;
		break;
	case left:
		snakepos[0].x -= 10;
		break;
	case up:
		snakepos[0].y += 10;
		break;
	case down:
		snakepos[0].y -= 10;
		break;
	case stop:
		break;
	}
}
void snake::keydown()
{
	char userkey = 0;
	userkey = _getch();
	switch (userkey)
	{
	case 'W':
	case 'w':
	case 72:
		if (position != up)
			position = down;
		break;
	case 'S':
	case 's':
	case 80:
		if (position != down)
			position = up;
		break;
	case 'A':
	case 'a':
	case 75:
		if (position != right)
			position = left;
		break;
	case 'D':
	case 'd':
	case 77:
		if (position != left)
			position = right;
		break;
	case 'F':
	case 'f':
		position = stop;
		break;
	case 'L':
	case 'l':
		choice = 3;
		EndBatchDraw();						//关闭批量绘图模式
		break;
	case 'J':
	case 'j':
		score = 0;
		choice = 2;
		EndBatchDraw();						//关闭批量绘图模式
		break;
	}
}
void snake::gameover()
{
	EndBatchDraw();							//关闭批量绘图模式
	cleardevice();
	settextcolor(RED);
	settextstyle(50, 0, L"宋体");
	outtextxy(280, 130, L"Game Over!");
	settextcolor(BLACK);
	settextstyle(36, 0, L"宋体");
	outtextxy(340, 220, L"得分:");
	TCHAR s[5];
	_stprintf_s(s, _T("%d"), score);
	outtextxy(440, 220, s);
	settextstyle(24, 0, L"宋体");
	outtextxy(280, 300, L"请按J键回到主界面...");
	outtextxy(280, 324, L"或按L键退出游戏.....");
	char userkey = 0;
	while (1) {
		userkey = _getch();
		if (userkey == 'J' || userkey == 'j')
		{
			score = 0;
			choice = 2;
			break;
		}
		else if (userkey == 'L' || userkey == 'l')
		{
			choice = 3;
			break;
		}
	}
}
void snake::judge()
{
	if (snakepos[0].x < 10 || snakepos[0].x>550 ||
		snakepos[0].y < 10 || snakepos[0].y>460)			//判断蛇是否撞墙
		gameover();
	for (int i = snakesize - 1; i > 0; i--)			//判断蛇头是否碰蛇身
		if (snakepos[i].x == snakepos[0].x && snakepos[i].y == snakepos[0].y)
			gameover();
}
void snake::graduated()
{
	if (score == 100) {
		EndBatchDraw();					//关闭批量绘图模式
		cleardevice();
		settextcolor(RED);
		settextstyle(50, 0, L"宋体");
		outtextxy(220, 130, L"Congratulations!");
		settextcolor(BLACK);
		settextstyle(36, 0, L"宋体");
		outtextxy(300, 220, L"恭喜你通关了!");
		settextstyle(24, 0, L"宋体");
		outtextxy(260, 300, L"请按J键回到主界面...");
		outtextxy(260, 324, L"或按L键退出游戏.....");
		char userkey = 0;
		while (1) {
			userkey = _getch();
			if (userkey == 'J' || userkey == 'j')
			{
				score = 0;
				choice = 2;
				break;
			}
			else if (userkey == 'L' || userkey == 'l')
			{
				choice = 3;
				break;
			}
		}
	}
}
class food {
public:
	food();						//构造函数,初始化食物
	void drawfood();				//画食物
	void setfoodpos(snake* psnake);	//若食物出现在蛇身上,则重新生成
	int& getflag()
	{
		return foodflag;			//返回食物状态
	}
	mypoint getfoodpos()
	{
		return foodpos;				//返回食物坐标
	}
protected:
	mypoint foodpos;					//食物坐标
	int foodflag;						//食物状态
};
food::food()
{
	foodpos.x = (rand() % 55+1) * 10;
	foodpos.y = (rand() % 46+1) * 10;
	foodflag = 1;
}
void food::drawfood()
{
	setfillcolor(RGB(255, 0, 0));
	fillrectangle(foodpos.x, foodpos.y, foodpos.x + 10, foodpos.y + 10);
}
void food::setfoodpos(snake* psnake)
{
	int j;
	j = psnake->getsnakesize();
	while (foodflag != 1)
	{
		foodpos.x = (rand() % 55 + 1) * 10;
		foodpos.y = (rand() % 46 + 1) * 10;
		for (int i = 0; i < j; i++)
		{
			if (psnake->getsnakepos(i).x == foodpos.x
				&& psnake->getsnakepos(i).y == foodpos.y)
			{
				foodflag = 0;
				break;
			}
			foodflag = 1;
		}
	}

}
void eatfood(snake* psnake, food* pfood)			//判断是否吃到食物
{
	if (psnake->getsnakepos(0).x == pfood->getfoodpos().x
		&& psnake->getsnakepos(0).y == pfood->getfoodpos().y)
	{
		psnake->getsnakesize()++;
		pfood->getflag() = 0;
		score++;
	}
}
void start()				//生成主界面
{
	cleardevice();
	settextcolor(RED);
	settextstyle(18, 0, L"宋体");
	outtextxy(670, 455, L"power by Alan");
	settextcolor(BLUE);
	settextstyle(36, 0, L"宋体");
	outtextxy(200, 100, L"欢迎来到贪吃蛇的世界!");
	setlinecolor(RED);
	settextcolor(RED);
	settextstyle(24, 0, L"宋体");
	rectangle(310, 200, 422, 242);
	outtextxy(320, 210, L"开始游戏");
	rectangle(310, 250, 422, 292);
	outtextxy(320, 260, L"退出游戏");
	rectangle(310, 300, 422, 342);
	outtextxy(320, 310, L"操作说明");
}
int help()											//帮助说明
{
	cleardevice();
	settextstyle(20, 0, L"宋体");
	rectangle(0, 0, 90, 30);
	outtextxy(4, 4, L"退出说明");
	settextstyle(30, 0, L"宋体");
	outtextxy(120, 50, L"操作说明:请在英文模式下开始游戏。");
	settextstyle(20, 0, L"宋体");
	outtextxy(40, 175, L"W:向上,S:向下,A:向左,D:向右,F:暂停");
	outtextxy(40, 200, L"J:返回主界面,L:退出游戏");
	outtextxy(40, 225, L"或使用方向键盘。");
	outtextxy(40, 250, L"暂停后仅需重新按键控制蛇的运动即可恢复游戏。");
	settextcolor(RED);
	settextstyle(18, 0, L"宋体");
	outtextxy(670, 455, L"power by Alan");
	while (true)
	{
		m = GetMouseMsg();						//获取一条鼠标消息
		if (m.uMsg == WM_LBUTTONDOWN)
		{
			for (i = 0; i <= 10; i++)
			{
				setlinecolor(GREEN);				//设置圆颜色
				circle(m.x, m.y, 2 * i);
				Sleep(20);						//停顿20ms
				circle(m.x, m.y, 2 * i);				//抹去刚刚画的圆
			}
			if (m.x >= 0 && m.x <= 90 && m.y >= 0 && m.y <= 30)
				return 2;
			FlushMouseMsgBuffer();				//清空鼠标消息缓存区
		}
	}
}
int degreechoice()				//模式选择
{
	cleardevice();
	settextcolor(RED);
	settextstyle(18, 0, L"宋体");
	outtextxy(670, 455, L"power by Alan");
	settextcolor(BLUE);
	settextstyle(36, 0, L"宋体");
	outtextxy(0, 0, L"请选择难易度:");
	settextcolor(RED);
	setlinecolor(RED);
	settextstyle(30, 0, L"宋体");
	rectangle(215, 75, 285, 110);
	outtextxy(220, 80, L"普通");
	rectangle(215, 115, 285, 150);
	outtextxy(220, 120, L"一般");
	rectangle(215, 155, 285, 190);
	outtextxy(220, 160, L"高级");
	rectangle(215, 195, 285, 230);
	outtextxy(220, 200, L"进阶");
	settextcolor(BLACK);
	settextstyle(18, 0, L"宋体");
	outtextxy(0, 240, L"难度说明:");
	outtextxy(0, 258, L"进阶:会随着分数的提高而逐步加速。");
	while (true)
	{
		m = GetMouseMsg();						//获取一条鼠标消息
		if (m.uMsg == WM_LBUTTONDOWN)
		{
			for (i = 0; i <= 10; i++)
			{
				setlinecolor(GREEN);				//设置圆颜色
				circle(m.x, m.y, 2 * i);
				Sleep(20);						//停顿20ms
				circle(m.x, m.y, 2 * i);				//抹去刚刚画的圆
			}
			if (m.x >= 215 && m.x <= 285 && m.y >= 75 && m.y <= 110)
				return 1;
			else if (m.x >= 215 && m.x <= 285 && m.y >= 115 && m.y <= 150)
				return 2;
			else if (m.x >= 215 && m.x <= 285 && m.y >= 155 && m.y <= 190)
				return 3;
			else if (m.x >= 215 && m.x <= 285 && m.y >= 195 && m.y <= 230)
				return 4;
			FlushMouseMsgBuffer();				//清空鼠标消息缓存区
		}
	}
}
void speed(int d)									//给各个模式分配速度
{
	switch (d)
	{
	case 1:										//普通
		Sleep(140);
		break;
	case 2:										//一般
		Sleep(80);
		break;
	case 3:										//高级
		Sleep(60);
		break;
	case 4:										//进阶
		if (score < 5)
			Sleep(160);
		else if (score < 10 && score >= 5)
			Sleep(100);
		else if (score < 30 && score >= 10)
			Sleep(80);
		else
			Sleep(60);
		break;
	}
}
int mouse()									//捕获鼠标信息,并返回
{
	setrop2(R2_NOTXORPEN);					//二元光栅——NOT(屏幕颜色 XOR 当前颜色)
	while (true)
	{
		m = GetMouseMsg();					//获取一条鼠标消息
		if (m.uMsg == WM_LBUTTONDOWN)
		{
			for (i = 0; i <= 10; i++)
			{
				setlinecolor(GREEN);			//设置圆颜色
				circle(m.x, m.y, 2 * i);
				Sleep(20);					//停顿20ms
				circle(m.x, m.y, 2 * i);	//抹去刚刚画的圆
			}
			if (m.x >= 310 && m.x <= 422 && m.y >= 200 && m.y <= 242)
				return 1;
			if (m.x >= 310 && m.x <= 422 && m.y >= 250 && m.y <= 292)
				return 3;
			if (m.x >= 310 && m.x <= 422 && m.y >= 300 && m.y <= 342)
				return 4;
			FlushMouseMsgBuffer();			//清空鼠标消息缓存区
		}
	}
}
void xiaochuang()								//游戏界面小窗输出信息
{
	setfillcolor(RGB(227,187,223));
	solidrectangle(0, 0, 570, 10);
	solidrectangle(560, 0, 570, 480);
	solidrectangle(0, 0, 10, 480);
	solidrectangle(0, 470, 570, 480);
	settextstyle(20, 0, L"宋体"); 
	outtextxy(575, 5, L"操作说明:请在英文模式");
	outtextxy(575, 30, L"下开始游戏");
	outtextxy(575, 55, L"W:向上 A:向左 S:向下 ");
	outtextxy(575, 80, L"D:向右 F:暂停  ");
	outtextxy(575, 105, L"J:返回主界面");
	outtextxy(575, 130, L"L:退出游戏");
	outtextxy(670, 455, L"power by Alan");
	settextstyle(30, 0, L"宋体");
	outtextxy(575, 222, L"得分:");
	TCHAR s[5];
	_stprintf_s(s, _T("%d"), score);		
	outtextxy(655,222,s);
}
int main()
{
	choice = 2;
	srand((unsigned)time(0));					//设置随机数种子
	initgraph(800, 480);						//开辟游戏窗口
	setbkcolor(RGB(51, 204, 235));				//填充背景颜色
	PlaySound(TEXT("Happy.wav"), NULL, SND_FILENAME | SND_ASYNC | SND_LOOP);		//播放背景音乐					
	while (choice == 2)
	{
		start();								//初始化主界面
		choice = mouse();
		if (choice == 1)
			degree = degreechoice();			//难度选择
		else if (choice == 4)
			choice = help();					//操作说明
		snake* psnake = new snake;
		food* pfood = new food;
		while (choice == 1)
		{
			BeginBatchDraw();					//开启批量绘图模式
			cleardevice();						//清屏函数
			xiaochuang();
			eatfood(psnake, pfood);				//判断是否吃到食物
			if (pfood->getflag() == 0)
				pfood->setfoodpos(psnake);		//如果没有生成食物,则生成食物
			if (_kbhit())						//判断是否存在操作
				psnake->keydown();			//反馈控制信息
			psnake->movesnake();				//更新蛇的节点坐标
			psnake->drawsnake();				//根据蛇身坐标画蛇,使之可视化
			pfood->drawfood();					//根据食物坐标画食物,使之可视化
			psnake->judge();					//判断蛇是否撞墙或穿蛇身,并生成结束游戏界面
			psnake->graduated();				//判断是否通关
			FlushBatchDraw();					//绘制图形
			speed(degree);					//利用程序休眠的时长来控制蛇移动的速度
		}
		delete(psnake);						//释放内存
		delete(pfood);							//释放内存
	}
	closegraph();				//关闭窗口
	return 0;
}

四、参考资料

1. 《一起来面向对象编程吧!C++ 版本 多文件的贪吃蛇》
https://www.bilibili.com/video/BV1q4411X7RN

2.《C++语言实例_贪吃蛇小游戏制作_潭州教育》
https://www.bilibili.com/video/BV1DE411o7qM

3.《C语言图形化界面——含图形、按钮、鼠标、进度条等部件制作(带详细代码、讲解及注释)》
https://blog.csdn.net/weixin_44044411/article/details/104276757

4.《防闪屏批量绘图》
https://blog.csdn.net/weixin_43919932/article/details/94073937?utm_medium=distribute.pc_aggpage_search_result.none-task-blog-2~all~first_rank_v2~rank_v25-3-94073937.nonecase&utm_term=easyx%E6%80%8E%E4%B9%88%E9%98%B2%E6%AD%A2%E9%97%AA%E5%B1%8F&spm=1000.2123.3001.4430

5.《c语言中的rand()函数和srand()函数产生随机的整数》
https://blog.csdn.net/qin_zhangyongheng/article/details/8033936?utm_medium=distribute.pc_relevant.none-task-blog-BlogCommendFromMachineLearnPai2-1.channel_param&depth_1-utm_source=distribute.pc_relevant.none-task-blog-BlogCommendFromMachineLearnPai2-1.channel_param

五、资源下载

链接:https://pan.baidu.com/s/1_oTzhF8rNpB9XbUGiiwnvw
提取码:smay

  • 0
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值