天天看點

【C++】2048遊戲系列---優化子產品第一稿【加載圖檔】一、圖檔的命名二、圖檔加載三、圖檔的尺寸四、圖檔的釋放五、所有的代碼六、運作結果

【C++】2048遊戲系列---優化子產品第一稿【加載圖檔】

(更新中……)

參考部落格:

https://blog.csdn.net/qq_39151563/article/details/104283217

https://blog.csdn.net/qq_39151563/article/details/104342530

由于放在一篇會導緻篇幅太長,是以分成了幾篇。(可能有個10篇吧=.=)

目前寫完的:

  • 2048遊戲系列—總覽篇
  • 2048遊戲系列—功能子產品第一稿【矩陣操作】
  • 2048遊戲系列—功能子產品第二稿【鍵盤輸入】
  • 2048遊戲系列—功能子產品第三稿【添加新數】
  • 2048遊戲系列—功能子產品第四稿【結束檢測】

本篇介紹如何把數字轉化為圖檔輸出。

相關圖檔資源下載下傳:2048圖檔 提取碼:u0h3

一、圖檔的命名

【C++】2048遊戲系列---優化子產品第一稿【加載圖檔】一、圖檔的命名二、圖檔加載三、圖檔的尺寸四、圖檔的釋放五、所有的代碼六、運作結果

2的1次方 到 2的17次方,本篇用到背景圖、格子圖和

gameOver

圖, 共19張圖。

二、圖檔加載

  1. sprintf

    格式化字元串
#include <iostream>
#include "graphics.h" 
using namespace std;
int main()
{
	char imgAdress[40];
	//PIMAGE BlockImgs[18];//EGE圖檔形式 
	for(int i=1,num=2; i<18; i++,num*=2)
	{
		sprintf(imgAdress,"image\\block_%d.png",num);	
		cout << imgAdress << endl;
	}
	
	return 0;
} 
           

運作結果是圖檔的相對路徑:

【C++】2048遊戲系列---優化子產品第一稿【加載圖檔】一、圖檔的命名二、圖檔加載三、圖檔的尺寸四、圖檔的釋放五、所有的代碼六、運作結果
  1. EGE

    圖檔操作
    • 擷取圖像

      getimage()

    參考部落格裡寫了很多,這裡這用

    getimage(BlockImgs[i], imgAdress);

    第一個參數是

    PIMAGE

    對象,第二個參數是圖檔位址(建議用相對路徑,這樣友善移動檔案夾)

    要把圖檔檔案夾放在于

    main.cpp

    同一目錄下

    imgAddress

    位址的圖檔”賦 給“

    BlockImgs[i]

    對象,這樣我們就可以在程式中進行引用了。
    • 繪制圖像

      putimage()

    最常用的,

    putimage(x, y, pimg);

    本篇用的:

    putimage_withalpha(NULL,pimg,x,y)

    void EGEAPI putimage
    (
    	int dstX,
    	int dstY,				//目标位置(X,Y)
    	const PIMAGE pSrcImg,	//源圖像
    	DWORD dwRop = SRCCOPY	//最後一個參數是三元光栅操作碼,不用管它,不寫就行,
        						//因為它有預設參數SRCCOPY (預設參數就是如果不寫,就傳入預設的值)。
    ); 
    
    int putimage_withalpha(
    	PIMAGE imgdest,		// 目标圖像
    	PIMAGE imgsrc,		// 源圖像
        int dstX,    		// 目标圖像左上角x坐标 
        int dstY,    		// 目标圖像左上角y坐标
    	int srcX = 0,		// 源圖像左上角x坐标
        int srcY = 0,		// 源圖像左上角y坐标
        int srcWidth = 0,   // 原圖像混合區域寬度
        int srcHeight = 0   // 源圖像混合區域高度
    );
               

三、圖檔的尺寸

  1. 背景圖檔尺寸:500*500
    【C++】2048遊戲系列---優化子產品第一稿【加載圖檔】一、圖檔的命名二、圖檔加載三、圖檔的尺寸四、圖檔的釋放五、所有的代碼六、運作結果
  2. Block圖檔尺寸:106*106
    【C++】2048遊戲系列---優化子產品第一稿【加載圖檔】一、圖檔的命名二、圖檔加載三、圖檔的尺寸四、圖檔的釋放五、所有的代碼六、運作結果
  3. 尺寸計算:
【C++】2048遊戲系列---優化子產品第一稿【加載圖檔】一、圖檔的命名二、圖檔加載三、圖檔的尺寸四、圖檔的釋放五、所有的代碼六、運作結果

圖中紅色的點

(x,y)

就是我們要放照片函數

putimage_withalpha(NULL,pimg,x,y)

裡面的參數

  1. 周遊紅點放照片
    const int DEVIDE = 15;
    const int GRID_WIDTH = 106;
    
    void Draw()
    {
    	putimage_withalpha(NULL,BlockImgs[0],0,0);//放背景
    	for(int i=0;i<4;i++)
    	{
    		
    		for(int j=0;j<4;j++)
    		{
    			int x = (j+1)*DEVIDE + j*GRID_WIDTH;
    			int y = (i+1)*DEVIDE + i*GRID_WIDTH;
    			//cout << "(x,y) = " << "(" << x << ","<< y << ")" << endl;
    			if(grid[i][j]!=0)
    			{
    				putimage_withalpha(NULL,BlockImgs[grid[i][j]],x,y);
    			}
    		}
    	}
    	
    }
    
               
    運作結果:
    【C++】2048遊戲系列---優化子產品第一稿【加載圖檔】一、圖檔的命名二、圖檔加載三、圖檔的尺寸四、圖檔的釋放五、所有的代碼六、運作結果

四、圖檔的釋放

在圖檔無用後或遊戲結束後,應該釋放圖檔記憶體空間

對應的函數:

delimage(pimg);

void ReleaseImgs()
{
	for(int i=0; i<18; i++)
	{
		delimage(BlockImgs[i]);
	}
	delimage(GameOverImg);
}
           

五、所有的代碼

【C++】2048遊戲系列---優化子產品第一稿【加載圖檔】一、圖檔的命名二、圖檔加載三、圖檔的尺寸四、圖檔的釋放五、所有的代碼六、運作結果
#include <iostream>
#include "graphics.h"
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();
} 

//移動函數 
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]++;
                    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;
//加載圖檔
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();
	getimage(BlockImgs[0],"image\\background.png");	
	getimage(GameOverImg,"image\\gameOver.png"); 
	cout<< "讀取圖檔成功" << endl;	
}
//釋放圖檔 
void ReleaseImgs()
{
	for(int i=0; i<18; i++)
	{
		delimage(BlockImgs[i]);
	}
	delimage(GameOverImg);
}

const int DEVIDE = 15;
const int GRID_WIDTH = 106;
void Draw()
{
	cleardevice();
	putimage_withalpha(NULL,BlockImgs[0],0,0);
	for(int i=0;i<4;i++)
	{	
		for(int j=0;j<4;j++)
		{
			int x = (j+1)*DEVIDE + j*GRID_WIDTH;
			int y = (i+1)*DEVIDE + i*GRID_WIDTH;
			//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;
}
 
int main()
{
	initgraph(500, 500);
	setbkcolor(WHITE);
	
	Addnum(2); //在随機2個位置添加新數 
	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 << "gameover: " << (gameOver()?"是":"否") << endl; 
			Draw();
			dir = -1;//将 dir 置為無效,否則控制台會一直重新整理 
		}
		if(gameOver())
		{
			cout << "Game Over!" << endl;
			//putimage(150,150,GameOver);
			putimage_withalpha(NULL,GameOverImg,120,200);
			break;
		}		
	} 
	ReleaseImgs();
	getch();
    closegraph();  
    return 0;
}
           

六、運作結果

【C++】2048遊戲系列---優化子產品第一稿【加載圖檔】一、圖檔的命名二、圖檔加載三、圖檔的尺寸四、圖檔的釋放五、所有的代碼六、運作結果

本篇就結束了,介紹了加載圖檔的原理和操作,不過是不是感覺有點單調呢?那是因為我們沒有計分,沒有“正向回報”,缺少了遊戲的樂趣,下一部分的優化,就是加入計分子產品,加油!

【C++】2048遊戲系列---優化子產品第一稿【加載圖檔】一、圖檔的命名二、圖檔加載三、圖檔的尺寸四、圖檔的釋放五、所有的代碼六、運作結果