文章目錄
- 一、小游戲簡介
- 1、起源
- 2、玩法
- 二、編程思路
- 三、各個功能的實作
- 1、圖片的制作
- 2、創建2048.h頭檔案
- 3、加載圖片資源
- 4、繪制地圖
- 5、在二維陣列中產生亂數
- 6、按鍵操作
- Ⅰ、數的合并操作
- Ⅱ、數的移動
- 7、勝局判斷
- 8、敗局判斷
- 四、整體代碼
- 五、總結
一、小游戲簡介
1、起源
??2048小游戲最早于2014年3月20日發行,原版的2048最早于GitHub上發行,后被移植到了各個平臺,是基于《1024》開發的新型數字游戲
2、玩法
??這款游戲的玩法很簡單,每次可以選擇上下左右滑動,每滑動一次,所有的數字方塊都會往滑動的方向靠攏,系統也會在空白的地方亂數出現一個數字方塊,相同數字的方塊在靠攏、相撞時會相加,不斷的疊加最終拼湊出2048這個數字就算成功,
二、編程思路

三、各個功能的實作
1、圖片的制作
每個正方形數字圖片的大小都為60 * 60像素
最后游戲表單的大小為240 * 300像素、
2、創建2048.h頭檔案
??由于是使用分檔案撰寫的方式(主要是所有函式都寫在一個檔案里面實在是不方便看),所以需要創建一個頭檔案來宣告我們需要使用到的庫、變數和自定義函式,代碼如下:
#ifndef SMALLGAME
#define SMALLGAME
#include <cstdio>
#include <conio.h>
#include <cstdlib>
#include <graphics.h> //用到的第一個非C++自帶庫--->Easyz的圖片庫
#include <ctime>
//由于分檔案撰寫的
//所以注意我們定義的變數都要使用extern型別
extern IMAGE img[15];//存放的是我們的15張圖片
extern int map[4][4];//4*4的地圖
extern int Imagine_number[12];
void Loadimage();//加載所有的圖片
void DrawMap();//根據二維陣列中的資料來貼上相應的圖片
void KeyDown();//模擬按鍵
int RandNum();//在地圖中隨機產生資料
int MoveRight();//向右走
int MoveLeft();//向左走
int MoveUp();//向上走
int MoveDown();//向下走
void GAMEOVER();//游戲結束
void PLAYERWIN();//游戲獲勝
int ScanMap2();//掃描判斷是否獲勝
bool ScanMap();//掃描判斷是否游戲結束
#endif
3、加載圖片資源
Easyx開發的圖片庫可以進行圖片的操作,在使用圖片之前,需要加載要使用的圖片,一共需要加載15張圖,所以直接撰寫一個加載圖片的函式利用回圈加載即可,代碼如下:
#include "2048.h"
#define _CRT_SECURE_NO_WARNINGS //去除內擴增的影響
void Loadimage(){ //加載所有的圖片
for (int i = 0; i < 12; i++) {
char FileName[200]={};
sprintf(FileName,"%d.jpg",Imagine_number[i]);
loadimage(img + i,FileName);
}
loadimage(img + 12, "111.jpg");
loadimage(img + 13, "12345.jpg");
loadimage(img + 14, "444.jpg");
}
注意:sprintf具有不安全性,如果將一個長度大于原定字符陣列大小的字串存放進字符陣列,就會發生越界的現象,可以使用snprintf替換sprintf
4、繪制地圖
遍歷二維陣列中的資料,根據二維陣列中每個元素的資料來判斷應該在某個位置貼上對應的圖片,注意需要根據圖片的尺寸來計算每個點的x,y坐標,
代碼如下:
#include "2048.h"
void DrawMap(){//畫地圖
setbkcolor(RGB(244,215,215)); //設定表單背景顏色
cleardevice();//更新一遍表單
settextcolor(WHITE);//設定文本顏色
settextstyle(30,0,"圓體");//字體大小
outtextxy(50,10,"2048小游戲");//文字內容
int x,y,k;
for(int i = 0;i < 4;i++){
for(int j = 0;j < 4;j++){
x=60*j;//列
y=60*i+60;//行
//printf("%d %d\n",x,y);
for(k = 0;k < 12;k++){//遍歷12張圖片
if(Imagine_number[k] == map[i][j])
//判斷應該貼哪一張圖片
break;//退出回圈
}
putimage(x,y,img+k);//在(x,y)的位置貼上第k張圖
}
}
}
5、在二維陣列中產生亂數
C++中產生亂數需要使用到亂數函式srand();(包含于cstdlib庫中)
用法:srand((unsigned int)亂數種子(NULL))
若亂數種子保持不變,那么所生成的亂數也不會改變(感覺沒說清楚,只能自行理解了),故此我們一般使用電腦的系統時間作為亂數種子,也就是:
srand((unsigned int)time(NULL));
由于這里使用到了系統時間,所以需要包含C++的系統庫---->ctime
在2048小游戲中,每次產生的亂數為2或4,
那么如何產生這種亂數?可以這么做👇
temp=(rand()%3) * 2;
注意:rand() % n得到的就是[0,n)的資料
??那么不可避免地會生成0這么一個亂數,但我們只需要在生成后判斷一次即可,如果是0,那么我們就在其他的區域內繼續生成一個亂數,
代碼如下:
#include "2048.h"
int RandNum(){//在地圖中隨機產生數字
srand((unsigned int)time(NULL));
for(int i = 0;i < 4;i++){
for(int j = 0;j < 4;j++){
if(map[i][j] == 0){
map[i][j] = (rand()%3)*2;
if(map[i][j] == 0) continue; //如果產生的亂數為0 那么就在其他的區域內繼續產生亂數
return 0;
}
}
}
return 0;
}
6、按鍵操作
Ⅰ、數的合并操作
??在上下左右的移動程序中,如果移動路徑上有可以合并的數,按照規則我們需要進行合并操作;
Ⅱ、數的移動
??在移動的程序中,如果某個方塊在移動路徑上沒有數字方塊,那么我們就需要對該方塊進行移動操作;
不同方向上的合并和移動操作的代碼是有些許不同的,但是其思想都是一樣的
下面就以按下右鍵向右移動為例:
#include "2048.h"
int MoveRight(){
bool flag = false;
for(int i = 0;i < 4;i++){ //判斷是否可以合并
for(int j = 3;j >= 0;j--){
int k = j - 1;//取當前元素的前一個元素
int NowKey = map[i][j];//取當前的元素
if(NowKey != 0){ // 等于0的時候再去判斷是沒有意義的
while(k >= 0){ //列指標移動
int NextKey = map[i][k];//取當前元素的下一個元素
if(NextKey != 0){ //如果下一個塊已經有資料
if(NextKey == NowKey){ //如果下一塊和當前塊的資料是相同的
flag = true;
map[i][j]*=2;//那么就進行合并
map[i][k] = 0;
}
break; //按照規則,每次只能合并兩個方塊
}
k--;
}
}
}
}
for(int i = 0;i<4;i++){
for(int j = 3;j>=0;j--){
int nowkey = map[i][j];
if(map[i][j] == 0){
int k = j - 1;
while(k >= 0){
int nowkeynext = map[i][k];
if(map[i][k] != 0){ //如果下一個元素不等于0,那么就需要進行合并的操作
flag = true;
map[i][j]=nowkeynext;
map[i][k]=0; //非0元素移動之后重新置為0
break;
}
k--;//繼續去判斷其他的位置
}
}
}
}
if (flag) return 0;
else return 4;
}
??其他三個方向的操作與向右移動類似,不再贅述;此時二維陣列中的資料已經發生了更新;(其實如果你不想用圖形庫的話,可以簡單的通過不斷的列印二維陣列實作類似的效果)更新以后,我們再根據新的資料在對應的位置來貼上對應的圖片(可以理解為又覆寫上了一層圖片)也就達到了移動的效果,
7、勝局判斷
??每次按鍵+列印地圖之后對二維陣列進行掃描,一旦發現了2048這個資料,那么直接跳到PLAYERWIN()這個函式,
while (1)
{
int WINNERCHECK;
KeyDown();//按下按鍵
DrawMap();//列印一遍地圖
WINNERCHECK = ScanMap2();
if (WINNERCHECK == 1)
{
putimage(0, 0, img + 13);//貼上游戲獲勝的圖片
break;
}//游戲獲勝
}
8、敗局判斷
??掃描地圖,對四個方向嘗試移動(只是嘗試!沒有更新二維數陣列中的資料!可以想想該怎么做!),如果有一個方向還可以移動,那么就未達到敗局,如果四個方向都已經嘗試過且無法移動,那么就已是敗局,貼上游戲失敗的圖片
四、整體代碼
?1、頭檔案
#ifndef SMALLGAME
#define SMALLGAME
#include <cstdio>
#include <conio.h>
#include <cstdlib>
#include <graphics.h>
#include <ctime>
extern IMAGE img[15];
extern int map[4][4];
extern int Imagine_number[12];
void Loadimage();//加載所有的圖片
void DrawMap();//根據二維陣列中的資料來貼上相應的圖片
void KeyDown();//模擬按鍵
int RandNum();//在地圖中隨機產生資料
int MoveRight();//向右走
int MoveLeft();//向左走
int MoveUp();//向上走
int MoveDown();//向下走
void GAMEOVER();
void PLAYERWIN();
int ScanMap1();
int ScanMap2();
bool ScanMap();
#endif
?2、源檔案
#include "2048.h"
int Imagine_number[12]={0,2,4,8,16,32,64,128,256,512,1024,2048};
int map[4][4]={0};//地圖默認全部為0
IMAGE img[15];
/*****************************************************/
/*****************************************************/
//主函式
int main(){
initgraph(240,300); //創建游戲表單
Loadimage();//加載圖片資源
DrawMap();//列印一遍默認的地圖
while(1){
int WINNERCHECK;
KeyDown();//按下按鍵
DrawMap();//列印一遍地圖
if (ScanMap() == false) {
getchar();
GAMEOVER();
system("cls");
//putimage(0, 0, img+14);
getchar();
break;
}
WINNERCHECK= ScanMap2();
if (WINNERCHECK== 1) { putimage(0, 0, img + 13); break; }//游戲獲勝
}
//getchar();//暫停表單
//closegraph();//關閉表單
system("pause");
return 0;
}
/*****************************************************/
/*****************************************************/
//畫地圖
void DrawMap(){//畫地圖
setbkcolor(RGB(244,215,215)); //設定表單背景顏色
cleardevice();//更新一遍表單
settextcolor(WHITE);//設定文本資料
settextstyle(30,0,"楷體");
outtextxy(50,10,"2048小游戲");
int x,y,k;
for(int i = 0;i < 4;i++){
for(int j = 0;j < 4;j++){
x=60*j;//列
y=60*i+60;//行
//printf("%d %d\n",x,y);
for(k = 0;k < 12;k++){//遍歷12張圖片
if(Imagine_number[k] == map[i][j])//如果圖片的標號等于地圖中的資料
break;//退出回圈
}
putimage(x,y,img+k);//在x,y的位置貼上第k章圖
}
}
}
/*****************************************************/
/*****************************************************/
//游戲結束
void GAMEOVER() {
closegraph();
initgraph(240, 300); //創建游戲表單
putimage(0, 0, img + 12);
getchar();
}
/*****************************************************/
/*****************************************************/
//模擬按鍵
void KeyDown(){
char Key = _getch();//_getch()可以從鍵盤接受一個字符且不需要按enter鍵就可以執行
switch(Key)
{
case 'w':
case 'W':
case 72:
RandNum();//每次移動都會伴隨著移動
MoveUp();
break;
case 's':
case 'S':
case 80:
RandNum();
MoveDown();
break;
case 'a':
case 'A':
case 75:
RandNum();
MoveLeft();
break;
case 'd':
case 'D':
case 77:
RandNum();
MoveRight();
break;
}
}
/*****************************************************/
/*****************************************************/
//加載圖片
#define _CRT_SECURE_NO_WARNINGS //去掉sprintf內擴增的影響
void Loadimage(){ //加載所有的圖片
loadimage(img + 12, "111.jpg");
loadimage(img + 13, "12345.jpg");
loadimage(img + 14, "444.jpg");
for (int i = 0; i < 12; i++) {
char FileName[200]={};
sprintf(FileName,"%d.jpg",Imagine_number[i]);
loadimage(img + i,FileName);
}
}
/*****************************************************/
/*****************************************************/
//移動
//不同方向的移動
int MoveRight(){
bool flag = false;
for(int i = 0;i < 4;i++){ //判斷是否可以合并
for(int j = 3;j >= 0;j--){
int k = j - 1;
int NowKey = map[i][j];//取當前的元素
if(NowKey != 0){ // 等于0的時候再去判斷是沒有意義的
while(k >= 0){ //列指標移動
int NextKey = map[i][k];//取當前元素的下一個元素
if(NextKey != 0){ //如果下一個塊已經有資料
if(NextKey == NowKey){ //如果下一塊和當前塊的資料是相同的
flag = true;
map[i][j]*=2;//那么就進行合并
map[i][k] = 0;
}
//k=-1;
break; //每次只能合并兩個方塊
}
k--;
}
}
}
}
for(int i = 0;i<4;i++){
for(int j = 3;j>=0;j--){
int nowkey = map[i][j];
if(map[i][j] == 0){
int k = j - 1;
while(k >= 0){
int nowkeynext = map[i][k];
if(map[i][k] != 0){ //如果下一個元素不等于0,那么就需要進行合并的操作
flag = true;
map[i][j]=nowkeynext;
map[i][k]=0; //非0元素移動之后重新置為0
break;
}
k--;//繼續去判斷其他的位置
}
}
}
}
if (flag) return 0;
else return 4;
}
int MoveLeft(){ //向左走
bool flag = false;
for(int i = 0;i < 4;i++){
for(int j = 0;j < 4;j++){ //遍歷整個地圖
int NowKey = map[i][j];
if(NowKey != 0){
int k = j + 1;
while(k < 4){ //不可以越界
int NextKey = map[i][k];
if(NextKey != 0){ //如果下一個塊已經有資料
if(NextKey == NowKey){ //如果下一塊和當前塊的資料是相同的
flag = true;
map[i][j]*=2;//那么就進行合并
map[i][k] = 0;
}
//k=4;
break; //每次只能合并兩個方塊
}
k++;
}
}
}
}
for(int i = 0;i<4;i++){
for(int j = 0;j<4;j++){
int nowkey = map[i][j];
if(map[i][j] == 0){
int k = j + 1;
while(k < 4){
int nowkeynext = map[i][k];
if(map[i][k] != 0){ //如果下一個元素不等于0,那么就需要進行合并的操作
flag = true;
map[i][j]=nowkeynext;
map[i][k]=0; //非0元素移動之后重新置為0
break;
}
k++;//繼續去判斷其他的位置
}
}
}
}
if (flag) return 0;
else return 4;
}
int MoveUp(){
bool flag = false;
for(int i = 0;i < 4;i++){ //i是行 j是列
for(int j = 0;j < 4;j++){ //遍歷整個地圖
int NowKey = map[i][j];
if(map[i][j] != 0){
int k = i + 1;
while(k < 4){ //不可以越界
int NextKey = map[k][j];//取相同列的下一行元素
if(NextKey != 0){ //如果下一個塊已經有資料
if(NextKey == NowKey){ //如果下一塊和當前塊的資料是相同的
flag = true;
map[i][j]*=2;//那么就進行合并
map[k][j] = 0;
}
//k=4;
break; //每次只能合并兩個方塊
}
k++;
}
}
}
}
for(int i = 0;i<4;i++){
for(int j = 0;j<4;j++){
int nowkey = map[i][j];//取當前元素
if(map[i][j] == 0){
int k = i+1;
while(k < 4){
int nowkeynext = map[k][j];//取相同列的下一行元素
if(map[k][j] != 0){ //如果下一個元素不等于0,那么就需要進行合并的操作
flag = true;
map[i][j]=nowkeynext;
map[k][j]=0; //非0元素移動之后重新置為0
break;
}
k++;//繼續去判斷其他的位置
}
}
}
}
if(flag) return 0;
else return 4;
}
int MoveDown(){
bool flag = false;
for(int i = 3;i >= 0;i--){ //i是行 j是列
for(int j = 0;j < 4;j++){ //遍歷整個地圖
int NowKey = map[i][j];
if(NowKey != 0){
int k = i - 1;//
while(k >= 0){ //不可以越界
int NextKey = map[k][j];//取相同列的下一行元素
if(NextKey != 0){ //如果下一個塊已經有資料
if(NextKey == NowKey){ //如果下一塊和當前塊的資料是相同的
flag = true;
map[i][j]*=2;//那么就進行合并
map[k][j] = 0;
}
//k=-1;
break; //每次只能合并兩個方塊
}
k--;
}
}
}
}
for(int i = 3;i>=0;i--){
for(int j = 0;j<4;j++){
int nowkey = map[i][j];//取當前元素
if(map[i][j] == 0){
int k = i-1;
while(k >= 0){
int nowkeynext = map[k][j];//取相同列的下一行元素
if(map[k][j] != 0){ //如果下一個元素不等于0,那么就需要進行合并的操作
flag = true;
map[i][j]=nowkeynext;
map[k][j]=0; //非0元素移動之后重新置為0
break;
}
k--;//繼續去判斷其他的位置
}
}
}
}
if(flag) return 0;
else return 4;
}
/*****************************************************/
/*****************************************************/
//生成亂數
int RandNum(){//在地圖中隨機產生數字
srand((unsigned int)time(NULL));
for(int i = 0;i < 4;i++){
for(int j = 0;j < 4;j++){
if(map[i][j] == 0){
map[i][j] = (rand()%3)*2;
if(map[i][j] == 0) continue; //如果產生的亂數為0 那么就在其他的區域內繼續產生亂數
return 0;
}
}
}
return 0;
}
/*****************************************************/
/*****************************************************/
//敗局判斷
bool ScanMap() {
bool flag = false;
/*向右走*/
for (int i = 0; i < 4; i++) { //判斷是否可以合并
for (int j = 3; j >= 0; j--) {
int k = j - 1;
int NowKey = map[i][j];//取當前的元素
if (NowKey != 0) { // 等于0的時候再去判斷是沒有意義的
while (k >= 0) { //列指標移動
int NextKey = map[i][k];//取當前元素的下一個元素
if (NextKey != 0) { //如果下一個塊已經有資料
if (NextKey == NowKey) { //如果下一塊和當前塊的資料是相同的
flag = true;
//map[i][j] *= 2;//那么就進行合并
//map[i][k] = 0;
}
//k=-1;
break; //每次只能合并兩個方塊
}
k--;
}
}
}
}
for (int i = 0; i < 4; i++) {
for (int j = 3; j >= 0; j--) {
int nowkey = map[i][j];
if (map[i][j] == 0) {
int k = j - 1;
while (k >= 0) {
int nowkeynext = map[i][k];
if (map[i][k] != 0) { //如果下一個元素不等于0,那么就需要進行合并的操作
flag = true;
//map[i][j] = nowkeynext;
//map[i][k] = 0; //非0元素移動之后重新置為0
break;
}
k--;//繼續去判斷其他的位置
}
}
}
}
/*向左走*/
for (int i = 0; i < 4; i++) {
for (int j = 0; j < 4; j++) { //遍歷整個地圖
int NowKey = map[i][j];
if (NowKey != 0) {
int k = j + 1;
while (k < 4) { //不可以越界
int NextKey = map[i][k];
if (NextKey != 0) { //如果下一個塊已經有資料
if (NextKey == NowKey) { //如果下一塊和當前塊的資料是相同的
flag = true;
///map[i][j] *= 2;//那么就進行合并
//map[i][k] = 0;
}
//k=4;
break; //每次只能合并兩個方塊
}
k++;
}
}
}
}
for (int i = 0; i < 4; i++) {
for (int j = 0; j < 4; j++) {
int nowkey = map[i][j];
if (map[i][j] == 0) {
int k = j + 1;
while (k < 4) {
int nowkeynext = map[i][k];
if (map[i][k] != 0) { //如果下一個元素不等于0,那么就需要進行合并的操作
flag = true;
//map[i][j] = nowkeynext;
//map[i][k] = 0; //非0元素移動之后重新置為0
break;
}
k++;//繼續去判斷其他的位置
}
}
}
}
/*向上走*/
for (int i = 0; i < 4; i++) { //i是行 j是列
for (int j = 0; j < 4; j++) { //遍歷整個地圖
int NowKey = map[i][j];
if (map[i][j] != 0) {
int k = i + 1;
while (k < 4) { //不可以越界
int NextKey = map[k][j];//取相同列的下一行元素
if (NextKey != 0) { //如果下一個塊已經有資料
if (NextKey == NowKey) { //如果下一塊和當前塊的資料是相同的
flag = true;
//map[i][j] *= 2;//那么就進行合并
//map[k][j] = 0;
}
//k=4;
break; //每次只能合并兩個方塊
}
k++;
}
}
}
}
for (int i = 0; i < 4; i++) {
for (int j = 0; j < 4; j++) {
int nowkey = map[i][j];//取當前元素
if (map[i][j] == 0) {
int k = i + 1;
while (k < 4) {
int nowkeynext = map[k][j];//取相同列的下一行元素
if (map[k][j] != 0) { //如果下一個元素不等于0,那么就需要進行合并的操作
flag = true;
//map[i][j] = nowkeynext;
//map[k][j] = 0; //非0元素移動之后重新置為0
break;
}
k++;//繼續去判斷其他的位置
}
}
}
}
/*向下走*/
for (int i = 3; i >= 0; i--) { //i是行 j是列
for (int j = 0; j < 4; j++) { //遍歷整個地圖
int NowKey = map[i][j];
if (NowKey != 0) {
int k = i - 1;//
while (k >= 0) { //不可以越界
int NextKey = map[k][j];//取相同列的下一行元素
if (NextKey != 0) { //如果下一個塊已經有資料
if (NextKey == NowKey) { //如果下一塊和當前塊的資料是相同的
flag = true;
//map[i][j] *= 2;//那么就進行合并
//map[k][j] = 0;
}
//k=-1;
break; //每次只能合并兩個方塊
}
k--;
}
}
}
}
for (int i = 3; i >= 0; i--) {
for (int j = 0; j < 4; j++) {
int nowkey = map[i][j];//取當前元素
if (map[i][j] == 0) {
int k = i - 1;
while (k >= 0) {
int nowkeynext = map[k][j];//取相同列的下一行元素
if (map[k][j] != 0) { //如果下一個元素不等于0,那么就需要進行合并的操作
flag = true;
//map[i][j] = nowkeynext;
//map[k][j] = 0; //非0元素移動之后重新置為0
break;
}
k--;//繼續去判斷其他的位置
}
}
}
}
if (flag == false) return false;
else return true;
}
/*****************************************************/
/*****************************************************/
//勝局判斷
int ScanMap2() {
for (int i = 0; i < 3; i++) {
for (int j = 0; j < 3; j++) {
if (map[i][j] == 2048) return 1;
}
}
return 0;
}
/*****************************************************/
五、總結
??進作業室以來第一次寫小游戲,這一次寫小游戲的程序中識訓到了不少新東西:
????1、Easyx的圖形庫的使用
????2、亂數的生成
????3、_getch()和getchar()的區別
????4、sprintf()的用法以及其不安全性
??學會了Easyx的圖形庫以后可以學習做其他的小游戲,然后感覺自己還是有很多不會的東西,圖書管理系統寫了一小半但是感覺沒有API的話寫出來也沒什么意思,等以后學了Java再繼續完成吧,這學期剩下的時間好好復習的同時學習一點Linux服務器的知識,看能不能做出一個屬于自己的博客吧!
??沖沖沖!
轉載請註明出處,本文鏈接:https://www.uj5u.com/qita/232097.html
標籤:其他
