【C语言】百行代码实现—俄罗斯方块

标签: 小游戏开发  C++编程

自述

这个代码是19年的末尾写的,最近就想着想把这个用博客分享出来,一方面是为了巩固自己的知识,另一方面也希望同学们能够因为这篇文章有所收获,所以也在原来代码的基础上添加了蛮多注释的(感觉就和重新写了一遍俄罗斯方块似的😂) ,如果对于这篇文章有任何问题都可以提出来,也希望这篇文章能够帮助到大家,谢谢大家。

整体框架和流程

开始游戏页面设计

欢迎界面
在这里插入图片描述
游戏开始界面
在这里插入图片描述
这些大家应该都可以知道这个页面的布局了吧,接下来我们来了解对于这些布局我们应该要利用哪些函数来设置


初始化画布
如果我们像要进行图形界面的编程和绘画的话就一定要去初始化画布

	//初始画布
	initgraph(550,660);//表示初始化长和宽

设置窗口标题:利用window接口

	//设置窗口标题:利用window接口
	HWND hwnd=GetHWnd();//hwnd变量来接收,句柄
	SetWindowText(hwnd,_T("俄罗斯方块  宋同学2019.12.18"));

对于显示中间的文字

	//设置文本的字体样式
	setfont(40,0,_T("微软雅黑"));//高度40个像素宽度自己适应
	setcolor(WHITE);
	outtextxy(205,200,_T("俄罗斯方块"));//利用坐标
	setfont(22,0,_T("楷体"));
	outtextxy(175,300,_T("编程从俄罗斯方块开始!"));
	Sleep(3000);//休眠3秒钟那样转到游戏界面就不会突兀,让玩家有准备时间

对于欢迎界面所有的代码:

void welcome(){
	//初始画布
	initgraph(550,660);

	//设置窗口标题:利用window接口
	HWND hwnd=GetHWnd();//hwnd变量来接收,句柄,用于
	SetWindowText(hwnd,_T("俄罗斯方块  宋同学2019.12.18"));

	//设置文本的字体样式
	setfont(40,0,_T("微软雅黑"));//高度40个像素宽度自己适应
	setcolor(WHITE);
	outtextxy(205,200,_T("俄罗斯方块"));
	setfont(22,0,_T("楷体"));
	outtextxy(175,300,_T("编程从俄罗斯方块开始!"));
	Sleep(3000);//休眠3秒钟那样转到游戏界面就不会突兀,让玩家有准备时间

}

初始化游戏界面
有些变量是设置为全局变量所以先不要太在意,等会儿都会有源码分享出来

//初始化游戏界面
void initGameScene(){
	char str[16];
	cleardevice();
	//绘制长方形框
	rectangle(27,27,336,635);
	rectangle(29,29,334,633);
	rectangle(370,50,515,195);

	setfont(24,0,_T("楷体"));
	setcolor(LIGHTGRAY);
	outtextxy(405,215,_T("下一个"));

	setcolor(RED);
	outtextxy(405,280,_T("分数"));
	//score是分数,定义为全局变量,等会回有源码分享
	sprintf(str,"%d",score);//把score写入到str中转换为字符串形式
	outtextxy(415,310,str);

	//和分数的设置同理
	outtextxy(405,375,_T("等级"));
	sprintf(str,"%d",rank);
	outtextxy(415,405,str);

	//操作说明
	setcolor(LIGHTBLUE);
	outtextxy(390,475,_T("操作说明"));
	outtextxy(390,500,"↑:旋转");
	outtextxy(390,525,"↓:下降");
	outtextxy(390,550,"←:左移");
	outtextxy(390,575,"→:右移");
	outtextxy(390,600,"空格:暂停");

}

由于代码量还是有一些的所以接下来就不每一个函数都进行分开讲,感觉那样那这篇博客就有点长了,后面有源码有挺多注释的,大家可以慢慢看。

游戏设计流程介绍

  • 设计图形界面
  • 更新"下一个"框框(利用随机取,当主窗口的图形落到最底下就更新(初始就直接先来一个))
  • 将下一个框框中的形态的图形复刻到主窗口的中
  • 实现主游戏窗口的方块进行移动
  • 进行消行处理
  • 更新分数以及等级

注意

其实这游戏设计起来其实不难,在我看来难点可能是出现在消行和更新方块的操作上,还有一点要注意的是,图形的更新可以通过先用黑方块把指定位置都给覆盖然后再重新画一个(这一点即适用于方块的下落也适用于下一个方块的更新上),对于消行的处理要注意的是:如果第i行的方块满了我们把这一行消了以后那么我们还要再次判断i行而不能直接判断i+1行,因为当我们第i行消了以后上面的方块压下来那么第i行可能还可以再消一次。


由于可能图形界面大家没怎么接触过所以我就单独的写了一下,其他的部分可能还是得靠大家自己的理解了,也没办法把所有的想法全部都写出来,下面的源码中加了蛮多的注释还是希望大家看看代码看看注释这样也能理解的深刻一些。

可执行源码-全部

头文件

#include<stdio.h>
#include<string>
#include<iostream>
#include<graphics.h>//图形界面编程
#include<time.h>
#include<conio.h>//kbhit()键盘捕捉的使用需要的头文件

各种宏定义及全局变量

#define BLOCK_COUNT 5
#define BlOCK_WIDTH 5
#define BLOCK_HEIGHT 5
#define UNIT_SIZE 20//右上角打印的方块的像素
#define START_X 130
#define START_Y 30
#define KEY_UP 72//向上按键ASCII码值为72
#define KEY_RIGHT 77
#define KEY_LEFT 75
#define KEY_DOWN 80
#define KEY_SPACE 32
int score=0;//分数
int rank=0;//等级

int speed=500;
int minX=30;
int minY=30;

//设置枚举值
typedef enum{
	BLOCK_UP,
	BLOCK_RIGHT,
	BLOCK_DOWN,
	BLOCK_LEFT
	
}block_dir_t;

typedef enum{
	MOVE_DOWN,
	MOVE_LEFT,
	MOVE_RIGHT
}move_dir_t;

int NextIndex=-1;//下一个方块的种类
int BlockIndex=-1;//当前方块的种类

int color[BLOCK_COUNT]={
	GREEN,CYAN,MAGENTA,BROWN,YELLOW
};

int visit[30][15];

int markcolor[30][15];//表示对应位置的方块的颜色

//初始化各种方块,包括其形态变换
int block[BLOCK_COUNT*4][BlOCK_WIDTH][BLOCK_HEIGHT]={
	// | 形方块
	{ 0,0,0,0,0,
	0,0,1,0,0,
	0,0,1,0,0,
	0,0,1,0,0,
	0,0,0,0,0 },

	{ 0,0,0,0,0,
	0,0,0,0,0,
	0,1,1,1,0,
	0,0,0,0,0,
	0,0,0,0,0 },

	{ 0,0,0,0,0,
	0,0,1,0,0,
	0,0,1,0,0,
	0,0,1,0,0,
	0,0,0,0,0 },

	{ 0,0,0,0,0,
	0,0,0,0,0,
	0,1,1,1,0,
	0,0,0,0,0,
	0,0,0,0,0 },

	// L 形方块
	{ 0,0,0,0,0,
	0,0,1,0,0,
	0,0,1,0,0,
	0,0,1,1,0,
	0,0,0,0,0 },

	{ 0,0,0,0,0,
	0,0,0,0,0,
	0,1,1,1,0,
	0,1,0,0,0,
	0,0,0,0,0 },

	{ 0,0,0,0,0,
	0,1,1,0,0,
	0,0,1,0,0,
	0,0,1,0,0,
	0,0,0,0,0 },

	{ 0,0,0,0,0,
	0,0,0,1,0,
	0,1,1,1,0,
	0,0,0,0,0,
	0,0,0,0,0 },

	// 田 形方块
	{ 0,0,0,0,0,
	0,1,1,0,0,
	0,1,1,0,0,
	0,0,0,0,0,
	0,0,0,0,0 },

	{ 0,0,0,0,0,
	0,1,1,0,0,
	0,1,1,0,0,
	0,0,0,0,0,
	0,0,0,0,0 },

	{ 0,0,0,0,0,
	0,1,1,0,0,
	0,1,1,0,0,
	0,0,0,0,0,
	0,0,0,0,0 },

	{ 0,0,0,0,0,
	0,1,1,0,0,
	0,1,1,0,0,
	0,0,0,0,0,
	0,0,0,0,0 },

	// T 形方块
	{ 0,0,0,0,0,
	0,1,1,1,0,
	0,0,1,0,0,
	0,0,0,0,0,
	0,0,0,0,0 },

	{ 0,0,0,0,0,
	0,0,0,1,0,
	0,0,1,1,0,
	0,0,0,1,0,
	0,0,0,0,0 },

	{ 0,0,0,0,0,
	0,0,1,0,0,
	0,1,1,1,0,
	0,0,0,0,0,
	0,0,0,0,0 },

	{ 0,0,0,0,0,
	0,1,0,0,0,
	0,1,1,0,0,
	0,1,0,0,0,
	0,0,0,0,0 },

	// Z 形方块
	{ 0,0,0,0,0,
	0,1,1,0,0,
	0,0,1,1,0,
	0,0,0,0,0,
	0,0,0,0,0 },

	{ 0,0,0,0,0,
	0,0,1,0,0,
	0,1,1,0,0,
	0,1,0,0,0,
	0,0,0,0,0 },

	{ 0,0,0,0,0,
	0,1,1,0,0,
	0,0,1,1,0,
	0,0,0,0,0,
	0,0,0,0,0 },

	{ 0,0,0,0,0,
	0,0,1,0,0,
	0,1,1,0,0,
	0,1,0,0,0,
	0,0,0,0,0 },
};//5种方块每种方块4种形态方块的表示是用5行5列来表示

各类方法

void welcome(){
	//初始画布
	initgraph(550,660);

	//设置窗口标题:利用window接口
	HWND hwnd=GetHWnd();//hwnd变量来接收
	SetWindowText(hwnd,_T("俄罗斯方块  宋同学2019.12.18"));

	//设置文本的字体样式
	setfont(40,0,_T("微软雅黑"));//高度40个像素宽度自己适应
	setcolor(WHITE);
	outtextxy(205,200,_T("俄罗斯方块"));
	setfont(22,0,_T("楷体"));
	outtextxy(175,300,_T("编程从俄罗斯方块开始!"));
	Sleep(3000);//休眠3秒钟那样转到游戏界面就不会突兀,让玩家有准备时间

}
//初始化游戏界面
void initGameScene(){
	char str[16];
	cleardevice();
	rectangle(27,27,336,635);
	rectangle(29,29,334,633);
	rectangle(370,50,515,195);

	setfont(24,0,_T("楷体"));
	setcolor(LIGHTGRAY);
	outtextxy(405,215,_T("下一个"));

	setcolor(RED);
	outtextxy(405,280,_T("分数"));

	sprintf(str,"%d",score);
	outtextxy(415,310,str);

	outtextxy(405,375,_T("等级"));
	sprintf(str,"%d",rank);
	outtextxy(415,405,str);

	//操作说明
	setcolor(LIGHTBLUE);
	outtextxy(390,475,_T("操作说明"));
	outtextxy(390,500,"↑:旋转");
	outtextxy(390,525,"↓:下降");
	outtextxy(390,550,"←:左移");
	outtextxy(390,575,"→:右移");
	outtextxy(390,600,"空格:暂停");

}
//清除右上角方块(用一个又一个的小黑方块把它遮住)
void clearBlock(){
	setcolor(BLACK);
	setfont(23,0,"楷体");//高度23

	for(int i=0;i<BLOCK_HEIGHT;i++){
		for(int j=0;j<BlOCK_WIDTH;j++){
			//"■"
			int x=391+UNIT_SIZE*j;
			int y=71+UNIT_SIZE*i;
			outtextxy(x,y, "■");	
		}
	}
}




//在右上角区域绘制下一个方块
void drawBlock(int x,int y){
	setcolor(color[NextIndex]);
	setfont(23,0,"楷体");//高度23

	for(int i=0;i<BLOCK_HEIGHT;i++){
		for(int j=0;j<BlOCK_WIDTH;j++){
			//"■"
			if(block[NextIndex*4][i][j]==1){//对应哪一种方块(NextIndex*4)

			int x2=x+UNIT_SIZE*j;
			int y2=y+UNIT_SIZE*i;
			outtextxy(x2,y2, "■");	
			}
		}

	}

}

//绘制方块,特定位置特定方向的方块
void drawBlock(int x,int y,int blockIndex,block_dir_t dir){
	setcolor(color[NextIndex]);
	setfont(23,0,"楷体");//高度23,宽度自动适应
	int id=blockIndex*4+dir;
	for(int i=0;i<BLOCK_HEIGHT;i++){
		for(int j=0;j<BlOCK_WIDTH;j++){
			//"■"
			if(block[id][i][j]==1){//对应哪一种方块(NextIndex*4)
			int x2=x+UNIT_SIZE*j;
			int y2=y+UNIT_SIZE*i;
			outtextxy(x2,y2, "■");	
			}
		}
	}
}

//下降过程中的清除方块的过程,一个一个的
//清除指定位置指定方向的方块
//参数x:方块的左上角的x坐标
//参数y:方块的左上角在游戏区域内的坐标,距离游戏区域顶部的距离
void clearBlock(int x,int y,block_dir_t dir){
	setcolor(BLACK);
	int id=BlockIndex*4+dir;
	y+=START_Y ;
	for(int i=0;i<5;i++){
		for(int j=0;j<5;j++){
			if(block[id][i][j]==1){ 
				//擦除该方块的第i行第j列
				outtextxy(x+20*j,y+20*i,"■");
			}
		}
	}
}

void nextblock(){
	clearBlock();//清除

	//随机选择一种方块
	srand(time(NULL));//使用时间函数的返回值来作为时间种子
	NextIndex=rand()%BLOCK_COUNT;
	drawBlock(391,71);//画方块(根据序号)
}

//如果可以在指定位置可以向指定位置移动就返回1,否则返回0
int moveAble(int x0,int y0,move_dir_t moveDir,block_dir_t blockDir){
	//计算当前方块的左上角在30*15的游戏区域中的位置(多少行多少列)
	int x=(y0-minY)/UNIT_SIZE;//一个方块表示20像素
	int y=(x0-minX)/UNIT_SIZE;
	int id=BlockIndex*4+blockDir;//哪个方块哪种状态
	int ret=1;
	if(moveDir==MOVE_DOWN){
		for(int i=0;i<5;i++){
			for(int j=0;j<5;j++){
				if(block[id][i][j]==1&&
					(x+i+1>=30||visit[x+i+1][y+j]==1)){
					ret=0;

				}
			}
		}
	}else if(moveDir==MOVE_LEFT){
		for(int i=0;i<5;i++){
			for(int j=0;j<5;j++){
				if(block[id][i][j]==1&&
					(y+j==0||visit[x+i][y+j-1]==1)){
					ret=0;		
				}
			}
		}
	}else if(moveDir==MOVE_RIGHT){
		for(int i=0;i<5;i++){
			for(int j=0;j<5;j++){
				if(block[id][i][j]==1&&
					(y+j+1==15||visit[x+i][y+j+1]==1)){
					ret=0;		
				}
			}
		}
	}

	return ret;
}

//检测函数是否结束
void failCheck(){
	//刚下来的时候都是向上的
	if(!moveAble(START_X,START_Y,MOVE_DOWN,BLOCK_UP)){
	setcolor(WHITE);
	setfont(45,0,"隶体");
	outtextxy(75,300,"GAME OVER!");
	Sleep(1000);
	system("pause");
	closegraph();
	exit(0);
	}
}

//等待函数,这样比sleep函数好的地方是不会卡顿
void wait(int interval){
	int count=interval/10;
	for(int i=0;i<count;i++){
		Sleep(10);
		if(kbhit()){
		return;
		}
	}
}

//很多时候可以利用反向思维去完成函数
//判断当前方块能否转向到指定方向
//注意此时还没开始转
int rotatable(int x,int y,block_dir_t Dir){
	int id=BlockIndex*4+Dir;
	int xIndex=(y-minY)/20;
	int yIndex=(x-minX)/20;
	if(!moveAble(x,y,MOVE_DOWN,Dir)){
		return 0;
	}
	
	for(int i=0;i<5;i++){
		for(int j=0;j<5;j++){
			if(block[id][i][j]==1&&(yIndex+j<0||yIndex+j>=15||visit[xIndex+i][yIndex+j]==1)){
				return 0;
			}
		}
	}
	return 1;
}

//方块固定函数
void mark(int x,int y,int blockIndex,block_dir_t dir){
	int id=blockIndex*4+dir;
	int x2=(y-minX)/20;
	int y2=(x-minY)/20;
	for(int i=0;i<5;i++){
		for(int j=0;j<5;j++){
			if(block[id][i][j]==1){
				visit[x2+i][y2+j]=1;
				markcolor[x2+i][y2+j]=color[blockIndex];
			}
		}
	}

}
//进行上下左右的移动
void move(){
	int x=START_X;
	int y=START_Y;
	int k=0;//偏移量
	block_dir_t blockDir=BLOCK_UP;
	int cur_speed=speed;
	//先检测游戏是否失败,再确认下降
	failCheck();
	//持续下降
	while(1){
		if(kbhit()){
			int key=getch();//和kbhit()组合使用
			if(key==KEY_SPACE){//碰到空格
				getch();//实现暂停
			}
		}
		//清除当前方块
		clearBlock(x,k,blockDir);//清除特定位置特定朝向的方块
		
		if(kbhit()){
			int key=getch();
			if(key==KEY_UP){
		   /********************
		    *变形
			*要求:要判断能不能翻转-》定义函数
			********************/
			block_dir_t nextDir=(block_dir_t)((blockDir+1)%4);
			if(rotatable(x,y+k,nextDir)){
				blockDir=nextDir;
			}
			}else if(key==KEY_DOWN){
			cur_speed=50;
			}else if(key==KEY_LEFT){
				if(moveAble(x,y+k+20,MOVE_LEFT,blockDir)){
					x-=UNIT_SIZE;
				}
			}else if(key==KEY_RIGHT){
				if(moveAble(x,y+k+20,MOVE_RIGHT,blockDir)){
					x+=UNIT_SIZE;
				}
			}
		}
		k+=20;
		//绘制当前方块
		drawBlock(x,y+k,BlockIndex,blockDir);
		wait(cur_speed);//利用sleep函数不太灵活容易出现卡顿所以就自己设计一个函数
		
		//方块的“固化”处理
		if(!moveAble(x,y+k,MOVE_DOWN,blockDir)){
			mark(x,y+k,BlockIndex,blockDir);//固定函数
			break;
		}
	}

}


void newblock(){
	//确定即将使用的方块的类别
	BlockIndex=NextIndex;

	//绘制刚从顶部下降的方块
	drawBlock(START_X,START_Y);

	//让新出现的方块停顿一会儿,让用户识别到
	Sleep(100);//0.1秒

	//右上角区域绘制下一个方块
	nextblock();

	//方块降落
	move();
}

void down(int x){
	for(int i=x;i>0;i--){
		//清除第i行,第j列的方块消失
		for(int j=0;j<15;j++){
		if(visit[i-1][j]){
			visit[i][j]=1;
			markcolor[i][j]=markcolor[i-1][j];
			setcolor(markcolor[i][j]);
			outtextxy(20*j+minX,20*i+minY,"■");
			}else{
				visit[i][j]=0;
				setcolor(BLACK);
				outtextxy(20*j+minX,20*i+minY,"■");
			}
		}
	
	}
	//清除最上面行
	setcolor(BLACK);
	for(int j=0;j<15;j++){
		visit[0][j]=0;
		outtextxy(20*j+minX,minY,"■");
	}
}

//更新分数
void addScore(int lines){
	char str[10];
	setcolor(RED);
	score+=lines*10;
	sprintf(str,"%d",score);
	outtextxy(415,310,str);
}

//更新等级
void updateGrade(){
	//更新等级提示
	//50分隔一级
	char str[16];
	rank=score/50+1;
	sprintf(str,"%d",rank);
	outtextxy(425,405,str);
	//更新速度,自己可调整
	speed=500-rank*100;
	if(speed<=100){
		speed=100;
	}

}

//消行处理
void check(){
	int i;
	int j;
	int clearLines=0;
	for(i=29;i>=0;i--){
		//利用visit函数可以提高效率
		for(j=0;j<15&&visit[i][j];j++);
			//执行到此处时有两种情况
			//1.i行没满,此时j<15
			//2.i行满了,此时j>=15
			if(j>=15){
				//此时第i行已经满了
				//此时来消除第i行
				down(i);
				i++;//可能那一行可能还是可以消的所以再检测一遍
				clearLines++;
			}
		}

	//更新分数
	addScore(clearLines);
	//更新等级
	updateGrade();
}

主方法:

int main(){
 	welcome();
	initGameScene();
	//产生新方块
	nextblock();
	Sleep(500);

	//初始化访问数组
	memset(visit,0,sizeof(visit));//从地址开始清为零大小为整个visit的数组大小
	
	while(1){
		newblock();
	//消除满行,并更新分数和等级
		check();
	}

	system("pause");
	closegraph();
	return 0;
}
版权声明:本文为Jacksqh原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/Jacksqh/article/details/108034749

智能推荐

laravel框架的课堂知识点概总

1. MVC 1.1 概念理解 MVC全名是Model View Controller,是模型(model)-视图(view)-控制器(controller)的缩写,一种软件设计典范,用一种业务逻辑、数据、界面显示分离的方法组织代码,将业务逻辑聚集到一个部件里面,在改进和个性化定制界面及用户交互的同时,不需要重新编写业务逻辑 MVC 是一种使用 MVC(Model View Controller ...

Unity人物角色动画系统学习总结

使用动画系统控制人物行走、转向、翻墙、滑行、拾取木头 混合树用来混合多个动画 MatchTarget用来匹配翻墙贴合墙上的某一点,人物以此为支点翻墙跳跃 IK动画类似于MatchTarget,控制两只手上的两个点来指定手的旋转和位置,使得拾取木头时更逼真 创建AnimatorController: 首先创建一个混合树,然后双击 可以看到该混合树有五种状态机,分别是Idle、WalkForward、...

Composer 安装 ThinkPHP6 问题

Composer 安装 ThinkPHP6 问题 先说说问题 一.运行环境要求 二.配置 参考: ThinkPHP6.0完全开发手册 先说说问题 执行ThinkPHP6的安装命令 遇到问题汇总如下: 看提示是要更新版本,执行命令更新。 更新之后,再次安装ThinkPHP,之后遇到如下问题。 尝试了很多方法,依然不能解决。其中包括使用https://packagist.phpcomposer.com...

Spring Boot 整合JDBC

今天主要讲解一下SpringBoot如何整合JDBC,没啥理论好说的,直接上代码,看项目整体结构 看一下对应的pom.xml 定义User.java 定义数据源配置,这里使用druid,所以需要写一个配置类 上面指定druid的属性配置,和用户登录的账号信息以及对应的过滤规则: 下面定义数据访问接口和对应的实现: 数据访问层很简单,直接注入JdbcTemplate模板即可,下面再看对应的servi...

html鼠标悬停显示样式

1.显示小手:     在style中添加cursor:pointer 实现鼠标悬停变成小手样式     实例:         其他参数: cursor语法: cursor : auto | crosshair | default | hand | move | help | wait | tex...

猜你喜欢

Yupoo(又拍网)的系统架构

Yupoo!(又拍网) 是目前国内最大的图片服务提供商,整个网站构建于大量的开源软件之上。以下为其使用到的开源软件信息: 操作系统:CentOS、MacOSX、Ubuntu 服务器:Apache、Nginx、Squid 数据库:MySQLmochiweb、MySQLdb 服务器监控:Cacti、Nagios、 开发语言:PHP、Python、Erlang、Java、Lua 分布式计算:Hadoop...

创建一个Servlet项目流程(入门)

版本 IDEA 2020.2 JDK1.8 apache-tomcat-9.0.36 项目流程 一、IDEA中新建JaveEE项目 项目起名,选择项目存放地址,点击finish创建成功 进入项目后,右键选择项目,选择add Framework Support 选择Web Application,点击OK 此时项目文件夹 在WEB-INF下创建两个目录classes和lib 按ctrl+alt+sh...

Docker部署SpringCloud ELK+RabbitMQ日志

Docker部署SpringCloud ELK+RabbitMQ日志  Im_Coder 原文:https://www.jianshu.com/p/f773f23096a9 一、效果图 image.png 二、ELK是什么? ELK由ElasticSearch、Logstash和Kiabana三个开源工具组成。 其中Elasticsearch是个开源分布式搜索引擎,它的特点有:分布式,索...

[译]高性能缓存库Caffeine介绍及实践

概览 本文我们将介绍Caffeine-一个Java高性能缓存库。缓存和Map之间的一个根本区别是缓存会将储存的元素逐出。逐出策略决定了在什么时间应该删除哪些对象,逐出策略直接影响缓存的命中率,这是缓存库的关键特征。Caffeine使用Window TinyLfu逐出策略,该策略提供了接近最佳的命中率。 添加依赖 首先在pom.xml文件中添加Caffeine相关依赖: 您可以在Maven Cent...

Django开发微信公众号

一、微信公众号的准备: 1. 注册 访问地址:https://mp.weixin.qq.com/  按照提示注册即可  注意:本文样例使用个人公众号,由于个人公众号没有接口权限,自定义菜单无法进行开发,同学们不要注册错!  2. 配置 需要准备好自己的服务器地址,可以使用腾讯云阿里云京东云等等皆可,没有域名用IP也可以。  在公众平台官网的开发-基本设置页面...