张银峰的编程课堂

实现游戏逻辑

角色的位置

在让角色动起来之前,我们需要先知道他在哪,在哪这个位置性概念我们通过定义一个POS结构来个表达。

// 以行列存储的位置信息
typedef struct tagPOS
{
    char r;
    char c;
} POS;

现在我们来定位它在哪?从之前的分析中可以知道,关卡中数字4代表的是角色,为此我们就可以在载入关卡时保存这个位置。

// 玩家位置
POS g_player_pos;

// 载入关卡
bool Load_Map(int level)
{   
    // 记录玩家初始位置
    if (map[r][c] == PLAYER)
    {
        g_player_pos.r = r;
        g_player_pos.c = c;
    }
}

游走起来

角色在我们的操控下可以向四个方向游走,这些方向我们通过枚举来表达。

enum MOVEDIR
{
    MOVE_LEFT,
    MOVE_RIGHT,
    MOVE_UP,
    MOVE_DOWN
};

角色的移动是通过键盘操控来实现的;我们使用上下左右键实现四个方向的游走。

void Keyboard(E2DKeyboardEvent kbe, E2DScancode sc)
{
    if (kbe == E2D_KEYDOWN)
    {
        switch (sc)
        {
        case E2D_SCANCODE_LEFT:
            Player_Move(MOVE_LEFT);
            break;
        case E2D_SCANCODE_RIGHT:
            Player_Move(MOVE_RIGHT);
            break;
        case E2D_SCANCODE_UP:
            Player_Move(MOVE_UP);
            break;
        case E2D_SCANCODE_DOWN:
            Player_Move(MOVE_DOWN);
            break;
        }
    }
}

函数Player_Move是整个控制的核心.

void Player_Move(MOVEDIR dir)
{
    POS p1 = g_player_pos;
    POS p2 = g_player_pos;

    switch (dir)
    {
    case MOVE_LEFT:
        p1.c -= 1;
        p2.c -= 2;
        g_player_tex = TEX_PLAYER_LEFT;
        break;

    case MOVE_RIGHT:
        p1.c += 1;
        p2.c += 2;
        g_player_tex = TEX_PLAYER_RIGHT;
        break;

    case MOVE_UP:
        p1.r -= 1;
        p2.r -= 2;
        g_player_tex = TEX_PLAYER_UP;
        break;

    case MOVE_DOWN:
        p1.r += 1;
        p2.r += 2;
        g_player_tex = TEX_PLAYER_DOWN;
        break;
    }

    if (p1.c < 0) return;
    if (p1.r < 0) return;
    if (p1.c >= MAP_COLUMNS) return;
    if (p1.r >= MAP_ROWS) return;

    if (g_map[p1.r][p1.c] == WALL)
        return;

    if (g_map[p1.r][p1.c] == BOX || g_map[p1.r][p1.c] == GOAL)
    {
        int e2 = g_map[p2.r][p2.c];
        if (e2 == WALL || e2 == BOX || e2 == GOAL)
            return;

        if (e2 == ROAD)
            g_map[p2.r][p2.c] = BOX;
        else if (e2 == TARGET)
            g_map[p2.r][p2.c] = GOAL;
    }

    // 玩家移动到此位置
    g_map[p1.r][p1.c] = PLAYER;

    // 通过测试
    if (Level_Clear())
    {
        Load_Map(g_cur_level + 1);
    }
}

函数一开始定义了p1、p2两个位置信息,分别表示玩家所处方向的下一个及下下一个位置。然后依据这些位置在索引地图中的元素,以此判断是行走、推箱子或其它行为,如下一个位置是障碍物时,角色不可移动。

if (g_map[p1.r][p1.c] == WALL)
        return;

当角色移动了一步后,我们就可以判断是否通关,Level_Clear的逻辑比较简单,如果所有的目标点上有箱子,就说明通关了,然后程序载入下一关。

显示更新

当玩家将箱子推到目标点后,渲染的结果依然是箱子,也就是说箱子在与不在目标点上,看不出差别。为此我们增加一点渲染效果,以区分两者,增加游戏体验,这里的做法是,当箱子在目标点上时,为箱子混入红色。

void Render()
{
    if (!blend)
    {
        E2D_RenderTexture(t->tex, x - t->dx, y - t->dy, E2D_FLIP_NONE);
    }
    else
    {
        E2DColor color = {255, 0, 0, 255};
        E2D_SetTextureBlend(t->tex, color, E2D_BLENDMODE_BLEND);
        E2D_RenderTexture(t->tex, x - t->dx, y - t->dy, E2D_FLIP_NONE);
        E2D_ResetTextureBlend(t->tex);
    }
}

glimix.com