【C++】2048游戏系列---优化模块第二稿【计分模块】

2048游戏系列---优化模块第二稿【计分模块】

(更新中……)
参考博客:https://blog.csdn.net/qq_39151563/article/details/104283217
参考博客:https://blog.csdn.net/qq_39151563/article/details/104342730
由于放在一篇会导致篇幅太长,所以分成了几篇。(可能有个10篇吧=.=)

目前写完的:


本篇讲解计分模块的激活。


一、布局和计算

从参考博客偷来的布局图:
布局

1.1-通过图片尺寸计算

布局和计算

1.2-一些定义的含义

我的思路是,先把它们紧密地拼凑在一起,再计算图片之间的间隔,只要加 DEVIDE 的就好了

const int WIDTH  = 500+2*DEVIDE;//画面总体宽度
const int HEIGHT = 660+2*DEVIDE;//画面总体长度

const int DEVIDE = 15;//间隔宽度
const int GRID_WIDTH = 106;//数字块宽度(正方形)

const int BOARD_X = 0+DEVIDE;//棋盘起始点
const int BOARD_Y = 160+DEVIDE

const int LOGO_X = 25+DEVIDE;//Logo图起始点
const int LOGO_Y = 20;

const int RESTART_X = 0+DEVIDE;//restart图起始点
const int RESTART_Y = 90; 

const int SCOREGB_X = 230+DEVIDE;//计分背景图起始点
const int SCOREGB_Y = 0; 

1.3-一些关键的修改

具体的可以参考后面的源码

initgraph(WIDTH, HEIGHT);
//图片
PIMAGE GameLogoImg;
PIMAGE RestartImg;
PIMAGE ScoreBgImg;

GameLogoImg = newimage();
RestartImg = newimage();
ScoreBgImg = newimage();	
getimage(GameOverImg,  "image\\gameOver.png");
getimage(GameLogoImg,  "image\\gamelogo.png"); 
getimage(RestartImg,   "image\\restart.png"); 
getimage(ScoreBgImg,   "image\\scorebg.png"); 

delimage(GameLogoImg);
delimage(RestartImg);
delimage(ScoreBgImg);

//Draw函数里面
putimage_withalpha(NULL, GameLogoImg,  LOGO_X,    LOGO_Y);//Logo图 
putimage_withalpha(NULL, RestartImg,   RESTART_X, RESTART_Y);//重新开始图 
putimage_withalpha(NULL, ScoreBgImg,   SCOREGB_X, SCOREGB_Y);//计分背景图 
int x = (j+1)*DEVIDE + j*GRID_WIDTH + BOARD_X;
int y = (i+1)*DEVIDE + i*GRID_WIDTH + BOARD_Y;

1.4-运行结果

改布局调试1

后来我又在上面添加了一个 DEVIDE,也就是每一张图片的 y + DEVIDE,大家可以根据自己喜欢调整布局,感觉有那味了

二、激活重新开始按键

现在的重新开始按键就是摆设,鼠标点击它并没有什么反应,我们要做的工作就是激活它!!!

2.1-鼠标检测模板

bool flag = false;//判断标志位
int xClick = yClick = 0;//记录鼠标位置
for(;is_run();delay_fps())
{
    flag = false;
    while (mousemsg())
    {
			mouse_msg msg = getmouse();
    		if (msg.is_left() && msg.is_down())
    		{
                isClick = true;
                xClick = msg.x;
                yClick = msg.y;
    		} 
    }
    if(flag)
    {
        ...//具体操作
    }
}

具体操作原理可以参看参考博客,与键盘检测逻辑类似

2.2-检测是否点击在矩形区域内

const int RESTART_X = 0+DEVIDE;
const int RESTART_Y = 90+DEVIDE;
const int RESTART_WIDTH = 220;//restart 宽度
const int RESTART_HEIGHT = 50;//restart 高度 
bool isClicInRectangle(int xClick,int yClick,int Rectangle_X,int Rectangle_Y,
                       int Rectangle_WIDTH,int Rectangle_HEIGHT)
{
	if(xClick>Rectangle_X&&xClick<Rectangle_X+Rectangle_WIDTH && 
       yClick>Rectangle_Y&&yClick<Rectangle_Y+Rectangle_HEIGHT)
	return true;
	return false;
}

2.3-重新开始函数

//重新开始
void RestartDate()
{
	for(int i=0; i<4; i++)
		for(int j=0; j<4; j++)
			grid[i][j] = 0;
	Addnum(2);	
} 

2.4-主函数修改代码

//鼠标检测 
bool Clickflag= false;
while (mousemsg())
{
    mouse_msg msg = getmouse();
    if (msg.is_left() && msg.is_down()&&
        isClicInRectangle(msg.x,msg.y,RESTART_X,RESTART_Y,RESTART_WIDTH,RESTART_HEIGHT))
    {
        Clickflag = true;
    } 
}
if(Clickflag)
{
    RestartDate();
    Draw();
}

2.5-运行结果

激活重新开始按键

三、激活合成最大值输出

3.1-计算合成的最大值

只有在合并的时候才要比较一下最大值,所以要添加MaxValue = GetMaxValue();Move()函数合并时的情况

int MaxValue = 0;//记录合成的最大值
//计算合成的最大值
int GetMaxValue() 
{
	int temp=0;
	for(int i=0; i<4; i++)
		for(int j=0; j<4; j++)
		{
			if(grid[i][j]>temp)	temp = grid[i][j];
		}
	return temp;
}

//Move()函数中
...
else if(grid[t1y][t1x]==grid[t2y][t2x])
{

    grid[t1y][t1x]++;//合并 
    
    MaxValue = GetMaxValue();//更新最大值
    
    grid[t2y][t2x] = 0;
    t1x += firstOffset[dir][0];
    t1y += firstOffset[dir][1];
}
...

3.2-图形输出最大值

文字输出函数:xyprintf()

记得设置背景模式:setbkmode(TRANSPARENT)

const int MAXVALUE_X = SCOREGB_X+160;
const int MAXVALUE_Y = SCOREGB_Y+112; 
const color_t TEXT_COLOR = EGERGB(241, 231, 214); //保持与面板字体一样的颜色
const color_t BG_COLOR = EGERGB(250,248, 239);//背景色:淡黄色 

//Draw()函数中
setcolor(TEXT_COLOR);
xyprintf(MAXVALUE_X, MAXVALUE_Y,"%4d",(int)pow(2,MaxValue));//要添加<cmath>库

//main()函数中
setbkcolor(BG_COLOR);
setfont(25,0,"黑体"); 
setbkmode(TRANSPARENT);//文字输出为透明,默认带背景 

3.3-运行结果

最大值输出调试

四、激活本次分数输出

4.1-计算和输出最大值

加的分数就是合成的数字值,要改 Draw() 函数 和 Move() 函数

int Score = 0;//记录分数 
const int SCORE_X = MAXVALUE_X;
const int SCORE_Y = MAXVALUE_Y-45;

//Draw()函数中
...
xyprintf(SCORE_X,    SCORE_Y,   "%4d",Score);
...
    
//Move()函数中
...
else if(grid[t1y][t1x]==grid[t2y][t2x])
{

    grid[t1y][t1x]++;//合并 
    MaxValue = GetMaxValue();
    
    Score += (int)pow(2,grid[t1y][t1x]); 
    
    grid[t2y][t2x] = 0;
    t1x += firstOffset[dir][0];
    t1y += firstOffset[dir][1];
}
...

4.2-运行结果

激活本次分数输出

五、激活最高分数输出

(脑子有点晕,放到下次更新内容中吧)

重点在于记录下历史最高分,所以涉及到文件操作

九层之台,起于垒土。还是一步步来吧

``

六、全部代码和运行结果

#include <iostream>
#include "graphics.h"
#include <cmath>
#include <fstream> 
#include <cstdio>
using namespace std;
//测试矩阵 
int grid[4][4] = {0
//    {1,2,3,4},
//    {4,5,6,7},
//    {7,8,9,10},
//    {1,1,1,0}
};

int EmptyBlock = 4 ;	//空格数 
int dir = -1;				// 0-左,1-上,2-右,3-下 
//打印函数 
void PrintGrid()
{
    for(int i=0; i<4; i++)
    {
        for(int j=0; j<4; j++)
            cout << grid[i][j] << " ";
        cout << endl;
    }
    cout << endl;
}
//计算空格函数 
int CalculateEmpty()
{
    int cnt = 0;
    for(int i=0; i<4; i++)
        for(int j=0; j<4; j++)
            if(grid[i][j]==0)   cnt++;
    return cnt;
}

//显示信息
void ShowInfo()
{
	cout << "dir = " << dir << endl;
    cout<< "EmptyBlock = " << CalculateEmpty() << endl;
	cout << "grid[4][4] = " << endl;
	PrintGrid();
} 
const int DEVIDE = 15;
const int GRID_WIDTH = 106;

const int BOARD_X = 0+DEVIDE;//棋盘起始点x
const int BOARD_Y = 160+2*DEVIDE;//棋盘起始点y
const int LOGO_X = 25+DEVIDE;
const int LOGO_Y = 20+DEVIDE;

const int RESTART_X = 0+DEVIDE;
const int RESTART_Y = 90+DEVIDE;
const int RESTART_WIDTH = 220;//restart 宽度
const int RESTART_HEIGHT = 50;//restart 高度 
 
const int SCOREGB_X = 230+DEVIDE;
const int SCOREGB_Y = 0+DEVIDE; 

const color_t TEXT_COLOR = EGERGB(241, 231, 214); //字体颜色

int MaxValue = 0;//记录合成的最大值
const int MAXVALUE_X = SCOREGB_X+160;
const int MAXVALUE_Y = SCOREGB_Y+112; 

int Score = 0;//记录分数 
const int SCORE_X = MAXVALUE_X;
const int SCORE_Y = MAXVALUE_Y-45; 

int TopScore = 0;//记录最高分数 
const int TOP_SCORE_X = SCORE_X;
const int TOP_SCORE_Y = SCORE_Y-45;

//计算合成的最大值
int GetMaxValue() 
{
	int temp=0;
	for(int i=0; i<4; i++)
		for(int j=0; j<4; j++)
		{
			if(grid[i][j]>temp)	temp = grid[i][j];
		}
	return temp;
}
void GameSave();
//移动函数 
static int x0[4] = {0, 0, 3, 0};
static int y0[4] = {0, 0, 0, 3};
static int firstOffset[4][2]  = {{1,0},{0,1},{-1,0},{0,-1}};
static int secondOffset[4][2] = {{0,1},{1,0},{0,1} ,{1,0}};
void Move(int dir)
{
	//bool moved = false; 
	if(dir==-1)	return;
    int tx, ty;
    int t1x, t1y;
    int t2x, t2y;
    for(int i=0; i<4; i++)
    {
    	
        tx = x0[dir] + i*secondOffset[dir][0];
        ty = y0[dir] + i*secondOffset[dir][1];
       //cout << "(" << tx << ", " << ty << ")" << endl;
       
        t1x = tx;
        t1y = ty;
        t2x = tx + firstOffset[dir][0]; 
        t2y = ty + firstOffset[dir][1];
        for( ;t2x>=0&&t2x<=3&&t2y>=0&&t2y<=3; t2x+=firstOffset[dir][0],t2y+=firstOffset[dir][1])
        {
            if(grid[t2y][t2x]!=0)
            {
                if(grid[t1y][t1x]==0)
                {
                    grid[t1y][t1x] = grid[t2y][t2x];
                    grid[t2y][t2x] = 0;
                   // moved = true;
                }
                else if(grid[t1y][t1x]==grid[t2y][t2x])
                {
                	
                    grid[t1y][t1x]++;//合并 
                    
                    if(MaxValue<GetMaxValue())	
					{
						MaxValue=GetMaxValue();
						cout << "teststst" << endl;
						GameSave();	
					}
                    Score += (int)pow(2,grid[t1y][t1x]); 
                    
					if(TopScore<Score)	
					{
						TopScore=Score;
						GameSave();	
					}
                    
                    grid[t2y][t2x] = 0;
                    t1x += firstOffset[dir][0];
                    t1y += firstOffset[dir][1];
                   // moved = true;
                }
                else if(t1x+firstOffset[dir][0]!=t2x||t1y+firstOffset[dir][1]!=t2y)
                {
                    grid[t1y+firstOffset[dir][1]][t1x+firstOffset[dir][0]] = grid[t2y][t2x];
                    grid[t2y][t2x] = 0;
                    t1x += firstOffset[dir][0];
                    t1y += firstOffset[dir][1];
                    //cout << "Move Test" << endl;
                   // moved = true;
                }
                else
                {
                    t1x += firstOffset[dir][0];
                    t1y += firstOffset[dir][1];
                }
            }
            
        }
    }
    //return moved;
}

//添加新数
void Addnum(int n)
{	
	while(n--)//添加n个
	{
		EmptyBlock = CalculateEmpty();
		if(EmptyBlock<=0)	
		{
			//cout << "addnum EmptyBlock = " << EmptyBlock << endl; 
			//cout << "addnum Test1" << endl;
			return;	
		}
		int cnt = random(EmptyBlock)+1;//随机得到一个空格数以内的数	
		//cout << "找到第" << cnt << "个空位" << endl; 
		//cout << "cnt = " << cnt << endl;

		int *p = &grid[0][0]-1;//记录矩阵首地址前一个 ,因为后面的 p 在找到时还会 ++ 
		//cout << "n = "<< n <<endl;
		for(int i=0; i<4&&cnt; i++)
			for(int j=0; j<4&&cnt; j++)
			{
				if(grid[i][j]==0 && cnt)//如果有空格并且cnt有效	
				{
					//cout << "cnt = " << cnt << endl;
					cnt--;//找到一个划一个
				}
                p++;//p 指向下一个再进行判断
			}
        //循环结束时 p 指向我们之前随机指定的空格
		*p = (random(10)==0)?2:1;// 0.1 的概率为2,0.9 的概率为1
		//cout << "插入成功" << endl; 
		//*p = (random(10)==0)+1;//这样写也可以
		EmptyBlock--;
	}
}

PIMAGE BlockImgs[18];//EGE图片形式 
PIMAGE GameOverImg;
PIMAGE GameLogoImg;
PIMAGE RestartImg;
PIMAGE ScoreBgImg;
//加载图片
void LoadImgs()
{
	char imgAdress[40];
	for(int i=1,num=2; i<18; i++,num*=2)
	{
		
		sprintf(imgAdress,"image\\block_%d.png",num);
		BlockImgs[i] = newimage();	
      	getimage(BlockImgs[i], imgAdress);
		//cout << imgAdress << endl;
	}
	BlockImgs[0] = newimage();
	GameOverImg = newimage();
	GameLogoImg = newimage();
	RestartImg = newimage();
	ScoreBgImg = newimage();
	getimage(BlockImgs[0], "image\\background.png");	
	getimage(GameOverImg,  "image\\gameOver.png");
	getimage(GameLogoImg,  "image\\gamelogo.png"); 
	getimage(RestartImg,   "image\\restart.png"); 
	getimage(ScoreBgImg,   "image\\scorebg.png"); 
	cout<< "读取图片成功" << endl;	
}
//释放图片 
void ReleaseImgs()
{
	for(int i=0; i<18; i++)
	{
		delimage(BlockImgs[i]);
	}
	delimage(GameOverImg);
	delimage(GameLogoImg);
	delimage(RestartImg);
	delimage(ScoreBgImg);
}

//绘制图像 
void Draw()
{
	cleardevice();
	putimage_withalpha(NULL, BlockImgs[0], BOARD_X,   BOARD_Y);//格子背景图 
	putimage_withalpha(NULL, GameLogoImg,  LOGO_X,    LOGO_Y);//Logo图 
	putimage_withalpha(NULL, RestartImg,   RESTART_X, RESTART_Y);//重新开始图 
	putimage_withalpha(NULL, ScoreBgImg,   SCOREGB_X, SCOREGB_Y);//计分背景图 
	//MaxValue = GetMaxValue();//更新最大值
	 
	setcolor(TEXT_COLOR);
	xyprintf(MAXVALUE_X, MAXVALUE_Y,"%4d",(int)pow(2,MaxValue));
	xyprintf(SCORE_X,    SCORE_Y,   "%4d",Score);

	for(int i=0;i<4;i++) 
	{	
		for(int j=0;j<4;j++)
		{
			int x = (j+1)*DEVIDE + j*GRID_WIDTH + BOARD_X ;
			int y = (i+1)*DEVIDE + i*GRID_WIDTH + BOARD_Y ;
			//cout << "(x,y) = " << "(" << x << ","<< y << ")" << endl;
			if(grid[i][j]!=0)
			{
				putimage_withalpha(NULL,BlockImgs[grid[i][j]],x,y);
			}
		}
	}
	
}
//游戏结束 
bool gameOver()
{
	EmptyBlock = CalculateEmpty();
	if(EmptyBlock>0)	return false;
	for(int i=0;i<4;i++)
	{
		int t1=0,t2=1;
		while(t2<=3)
		{
			if(grid[i][t1]==grid[i][t2] || grid[t1][i]==grid[t2][i])//  横 ||纵 
			{
				return false;
			}
			else
			{
				t1++;
				t2++;
			}
		}
	}
	return true;
}

//重新开始
void RestartDate()
{
	for(int i=0; i<4; i++)
		for(int j=0; j<4; j++)
			grid[i][j] = 0;
	Addnum(2);
	MaxValue = GetMaxValue();	
} 
//检测是否点击在矩形区域内
bool isClicInRectangle(int xClick,int yClick,int Rectangle_X,int Rectangle_Y,
                       int Rectangle_WIDTH,int Rectangle_HEIGHT)
{
	if(xClick>Rectangle_X&&xClick<Rectangle_X+Rectangle_WIDTH && 
       yClick>Rectangle_Y&&yClick<Rectangle_Y+Rectangle_HEIGHT)
	return true;
	return false;
}

const char RECORD_FILE_NAME[15] = "RECORD.txt";
void GameSave()
{
	ofstream ofile(RECORD_FILE_NAME);	
	char RecordString[30];
	sprintf(RecordString, "TopScore:%d\nMaxValue:%d\n",TopScore,MaxValue);
	ofile << RecordString << endl;
	ofile.close();
}

void LoadRecord()
{
	ifstream ifile(RECORD_FILE_NAME);						//定义输入文件,并打开文件 FILE_NAME
	char InputRecordString[50];						//暂存字符串	
	int i=0;	
	int cnt = 0;
    while(true)										
	{    
		if(ifile.get(InputRecordString[i]))	cnt++;	//读取到一个字符并给 InputRecordString[i]
        if(InputRecordString[i]=='\0') break;
        i++;
    }
    ifile.close();
    cout << InputRecordString << endl;
    
    int num[15];
	int NumCnt = 0;
	int flag = 0;
	TopScore = MaxValue = 0;
	for(int i=0; i<cnt; i++)//初始值为 8 因为 "TopSocre" 有 7 个字符(从 0 开始算), 第 8 个正好是 ':' 
	{
		cout << i << ": " << InputRecordString[i]<< endl;
		if(InputRecordString[i]==':')
		{	
			cout << "i = " << i <<  endl;
			flag++;
			i++;
			while((int)InputRecordString[i]>=48&&(int)InputRecordString[i]<=57)//'0'的 AscII 码 = 48,'9'为57
			{
				
				num[NumCnt] = (int)InputRecordString[i]-48;
				//cout << NumCnt << ": " << num[NumCnt] << endl;
				NumCnt++;
				i++;
			}
			cout << NumCnt << endl;
			for(int i=0; i<NumCnt; i++)//根据 flag 的值分别给 变量 1,2...赋值 
			{
				if(flag==1)
				{
					cout << "num[i] = " << num[i] << endl;
					cout << "(int)pow(10,NumCnt-i-1) = " << (int)pow(10,NumCnt-i-1) << endl;
					
					TopScore += num[i] * (int)pow(10,NumCnt-i-1); 
					cout << "TopScore = " << TopScore << endl;
				}
				else if(flag==2)
				{
					MaxValue += num[i] * (int)pow(10,NumCnt-i-1); 
				}
			}
		}	      
		NumCnt = 0;	//置 0 准备下次读值 
	}

    cout << "TopScore = " << TopScore << endl;
	cout << "MaxValue = " << MaxValue << endl; 	
}

const int WIDTH  = 500+2*DEVIDE;
const int HEIGHT = 660+3*DEVIDE; 
const color_t BG_COLOR = EGERGB(250,248, 239);//淡黄色 
int main()
{
	initgraph(WIDTH, HEIGHT);
	setbkcolor(BG_COLOR);
	setfont(25,0,"黑体"); 
	setbkmode(TRANSPARENT);//文字输出为透明 
	//GameSave();
	LoadRecord();
	Addnum(2); //在随机2个位置添加新数 
	MaxValue = GetMaxValue();
	ShowInfo();
	LoadImgs();
	Draw();   
    for ( ; is_run(); delay_fps(60) )
	{
		//cleardevice();
		
		// todo: 逻辑更新(数据更新)
		//按键检测	
		while(kbmsg())
		{
		    key_msg keyMsg = getkey();
		    if(keyMsg.msg == key_msg_down)
		    {
		        switch(keyMsg.key)
		        {
		            case 'A':case key_left  : dir = 0; break;//左 
		            case 'W':case key_up  	: dir = 1; break;//上 
		            case 'D':case key_right : dir = 2; break;//右 
		            case 'S':case key_down	: dir = 3; break;//下 
		        }
		    }
		}   
		// todo: 图形更新
		if(dir!=-1)
		{
			system("cls");
			switch(dir)
		        {
		            case  0: cout << "按下了 A/左 键" << endl; break;//左 
		            case  1: cout << "按下了 W/上 键" << endl; break;//上 
		            case  2: cout << "按下了 D/右 键" << endl; break;//右 
		            case  3: cout << "按下了 S/下 键" << endl; break;//下 
		        }
		               
		    bool flag = false;    //移动标志位 
			int tempGrid[4][4];	 //暂存数组 
			int i,j; 
			for(i=0;i<4;i++)
				for(j=0;j<4;j++)
					tempGrid[i][j] = grid[i][j];
			Move(dir);
			//比较 
			for(i=0; i<4; i++)
				for(j=0; j<4; j++)
					if(grid[i][j]!=tempGrid[i][j])	
					{
						flag = true;
						break;
					} 
			if(flag)
			{
				cout << "有效移动" << endl;
				Addnum(1);
			}	
			else	cout << "无效移动" << endl;					 
			ShowInfo();
			cout << "最大值:" << pow(2,GetMaxValue())<< endl; 
			cout << "gameover: " << (gameOver()?"是":"否") << endl; 
			Draw();
			dir = -1;//将 dir 置为无效,否则控制台会一直刷新 
		}
		
		//鼠标检测 
		bool Clickflag= false;
		while (mousemsg())
	    {
				mouse_msg msg = getmouse();
	    		if (msg.is_left() && msg.is_down()&&
					isClicInRectangle(msg.x,msg.y,RESTART_X,RESTART_Y,RESTART_WIDTH,RESTART_HEIGHT))
	    		{
	                Clickflag = true;
	    		} 
	    }
	    if(Clickflag)
	    {
	       RestartDate();
	       Draw();
	    }
	        
		if(gameOver())
		{
			cout << "Game Over!" << endl;
			//putimage(150,150,GameOver);
			putimage_withalpha(NULL,GameOverImg,120,200);
			break;
		}		
	} 
	ReleaseImgs();
	getch();
    closegraph();  
    return 0;
}

全部代码运行结果

状态不太好,要考试了!本篇有些细节没有处理好,等以后有时间再细细更改。

未来可期

  • 2
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值