问小白 wenxiaobai
资讯
历史
科技
环境与自然
成长
游戏
财经
文学与艺术
美食
健康
家居
文化
情感
汽车
三农
军事
旅行
运动
教育
生活
星座命理

EasyX入门教程:开发一个简单的连连看游戏

创作时间:
2025-01-21 18:19:13
作者:
@小白创作中心

EasyX入门教程:开发一个简单的连连看游戏

EasyX 是针对 C++ 的图形库,可以帮助 C/C++ 初学者快速上手图形和游戏编程。本文将详细介绍如何使用C语言和EasyX库实现一个简单的连连看小游戏,包括游戏初始化、图形绘制、鼠标事件处理、图片消除判断等关键功能模块。

代码部分

头文件和宏定义

先是一些头文件、宏定义等,方便后续修改游戏相关设置

#include<time.h>
#define MAP_SIZE 10 //每一行有多少张图片
#define IMG_SIZE 39 //每张图片的宽度
#define WIDTH (MAP_SIZE +2) * IMG_SIZE //窗口的宽度
#define ANIMAL_NUM 42 //图片的总个数
int map[MAP_SIZE + 2][MAP_SIZE + 2];
int way[MAP_SIZE + 2][MAP_SIZE + 2];
IMAGE img_bk;
IMAGE img_Animal[ANIMAL_NUM][2];
struct Index
{
    int row;
    int col;
}begin = { 1, -1 }, end = { -1, -1 };
enum Cur
{
    BEGIN,
    END
};
int step = 0;
Cur state = BEGIN;

游戏初始化函数

这部分是游戏初始化的函数

void gameInit()
{
    //设置随机数种子
    srand((unsigned)time(NULL));
    //用不同的数据表示不同的图片,每种图片来10个,一共需要10种图片
    for (int i = 1; i <= MAP_SIZE; i++)
    {
        for (int k = 1; k <= MAP_SIZE; k++)
        {
            map[i][k] = i; //10个i
        }
    }
    //加载图片
    loadimage(&img_bk,"./ref/bk.jpg",WIDTH+80,WIDTH+50);   //后两个参数用来调整背景图片的位置
    IMAGE animal;
    loadimage(&animal, "./ref/animal.bmp"); //bmp
    //如何切割图片
    SetWorkingImage(&animal);  //设置工作区
    for (int i = 0; i < ANIMAL_NUM; i++)
    {
        for (int k = 0; k < 2; k++)
        {
            getimage(&img_Animal[i][k], k * IMG_SIZE, i * IMG_SIZE, IMG_SIZE, IMG_SIZE);
        }
    }
    SetWorkingImage();  //恢复默认工作区
    //打乱数组排列,让图片也随机排列
    for (int i = 1; i <= MAP_SIZE; i++)
    {
        for (int k = 1; k <= MAP_SIZE; k++)
        {
            //随即找到两个游戏区域的下标位置,然后交换值 随机函数 rand()
            int x = rand() % MAP_SIZE + 1;
            int y=  rand() % MAP_SIZE + 1;
            int _swap = map[i][k];
            map[i][k] = map[x][y];
            map[x][y] = _swap;
        }
    }
}

我们使用一个二维数组来存储图片的位置信息,为了使每次运行程序时图片的排列方式不同,因此设置了一个随机数种子。

游戏图形的绘制

void gameDraw()
{
    putimage(0, 0, &img_bk);
    for (int i = 1; i <= MAP_SIZE; i++)
    {
        for (int k = 1; k <= MAP_SIZE; k++)
        {
            if (map[i][k] != 0)
            {
                putimage(k * IMG_SIZE, i * IMG_SIZE, &img_Animal[map[i][k]][1], SRCAND);    //原图
                putimage(k * IMG_SIZE, i * IMG_SIZE, &img_Animal[map[i][k]][0], SRCPAINT);  //掩码图
            }
        }
    }
}

获取鼠标消息

获取鼠标消息,获取点击的两个位置的下标

void mouseEvent()
{
    if (MouseHit())
    {
        MOUSEMSG msg = GetMouseMsg();
        //判断鼠标点击的位置(begin,end)
        if (msg.uMsg == WM_LBUTTONDOWN && state == BEGIN)
        {
            //把鼠标的坐标转成对应的数组的下标
            begin.col = msg.x / IMG_SIZE;
            begin.row = msg.y / IMG_SIZE;
            end.col = -1;
            end.row = -1;
            state = END;  //改变状态
        }
        else if (msg.uMsg == WM_LBUTTONDOWN && state == END)
        {
            end.col = msg.x / IMG_SIZE;
            end.row = msg.y / IMG_SIZE;
            state = BEGIN; //改变状态
        }
        if (msg.uMsg == WM_LBUTTONDOWN)
        {
            printf("begin(%d,%d) end(%d,%d)\n", begin.col, begin.row, end.col, end.row);
        }
    }
}

辅助函数

判断某个位置是否有图片

bool isBlock(int row, int col)
{
    if (row < 0 || col < 0 || row >= MAP_SIZE + 2 || col >= MAP_SIZE + 2)
        return 1;
    return map[row][col] + way[row][col];
}

在控制台打印出二维数组(为了后续观察寻路器的运作)

void showMap()
{
    for (int i = 0; i < MAP_SIZE + 2; i++)
    {
        for (int k = 0; k < MAP_SIZE + 2; k++)
        {
            printf("%-2d ", map[i][k]);
        }
        printf("\n");
    }
}

判断鼠标点击的起点和终点是否一致

bool same(int begin_x, int begin_y, int end_x, int end_y) 
{
    if (begin_x < 0 || begin_y < 0 || end_x < 0 || end_y < 0 || end_x >= MAP_SIZE + 2 || end_y >= MAP_SIZE + 2 || begin_x >= MAP_SIZE + 2 || begin_y >= MAP_SIZE + 2)
        return false;
    if (begin_x == end_x && begin_y == end_y)
        return true;
    return false;
}

判断是否满足消除条件

bool can_pass(int begin_x, int begin_y, int end_x, int end_y) 
{
    if (state != BEGIN || begin_x < 0 || begin_y < 0 || end_x < 0 || end_y < 0 || end_x >= MAP_SIZE + 2 || end_y >= MAP_SIZE + 2 || begin_x >= MAP_SIZE + 2 || begin_y >= MAP_SIZE + 2)
        return false;
    if (begin_x == end_x && begin_y == end_y)
        return false;
    if (map[begin.row][begin.col] != map[end.row][end.col])
        return false;
    return true;
}

使用A*寻路算法进行递归寻路消除

bool can(int begin_x, int begin_y, int end_x, int end_y) 
{
    printf("step:%d (%d,%d)->(%d,%d)\n", step, begin_x, begin_y, end_x, end_y);
    if (step++ > 2000) //当找路太多了
        return false;
    if (begin_x < 0 || begin_y < 0 || end_x < 0 || end_y < 0 || end_x >= MAP_SIZE + 2 || end_y >= MAP_SIZE + 2 || begin_x >= MAP_SIZE + 2 || begin_y >= MAP_SIZE + 2)
        return false;
    if (same(begin_x, begin_y, end_x, end_y) || same(begin_x - 1, begin_y, end_x, end_y) || same(begin_x + 1, begin_y, end_x, end_y) || same(begin_x, begin_y - 1, end_x, end_y) || same(begin_x, begin_y + 1, end_x, end_y))
        return true;
    if (!isBlock(begin_x - 1, begin_y)) //1
    {
        way[begin_x - 1][begin_y] = 1;
        if (can(begin_x - 1, begin_y, end_x, end_y))
            return true;
        way[begin_x - 1][begin_y] = 0;
    }
    if (!isBlock(begin_x, begin_y - 1)) //3
    {
        way[begin_x][begin_y - 1] = 1;
        if (can(begin_x, begin_y - 1, end_x, end_y))
            return true;
        way[begin_x][begin_y - 1] = 0;
    }
    if (!isBlock(begin_x + 1, begin_y)) //2
    {
        way[begin_x + 1][begin_y] = 1;
        if (can(begin_x + 1, begin_y, end_x, end_y))
            return true;
        way[begin_x + 1][begin_y] = 0;
    }
    if (!isBlock(begin_x, begin_y + 1)) //4
    {
        way[begin_x][begin_y + 1] = 1;
        if (can(begin_x, begin_y + 1, end_x, end_y))
            return true;
        way[begin_x][begin_y + 1] = 0;
    }
    return false;
}

主函数

int main()
{
    //首先需要我们自己创建一个图形窗口
    initgraph(WIDTH, WIDTH, SHOWCONSOLE);
    gameInit();
    showMap();
    while (1)
    {
        //开始双缓冲绘图
        BeginBatchDraw();
        gameDraw();
        //结束双缓冲绘图
        EndBatchDraw();
        if (can_pass(begin.row, begin.col, end.row, end.col))
        {
            memset(way, 0, sizeof(way));
            way[begin.row][begin.col] = 1;
            step = 0;
            printf("inininininin");
            if (can(begin.row, begin.col, end.row, end.col))
            {
                map[begin.row][begin.col] = 0;
                map[end.row][end.col] = 0;
                begin.row = -1;
                begin.col = -1;
                end.row = -1;
                end.col = -1;
            }
        }
        mouseEvent();
    }
    getchar();
    return 0;
}

使用双缓冲绘图防止闪烁

运行结果

(背景图片和连连看内容图片可以自行更换,这里背景我随便放了一张球星照片,注意连连看图片要同时制作其掩码图以实现透明效果)

© 2023 北京元石科技有限公司 ◎ 京公网安备 11010802042949号