四国军棋界面开发(2) 让棋子动起来

在上一篇文章讲了如何显示棋盘和棋子,接下来开始实现鼠标点击棋子时移动棋子。

1.获取鼠标位置

整个棋盘都是在fixed容器里,而fixed容器不可以与鼠标点击事件直接绑定,这里我们用到了消息事件的容器,fixed不是直接放在盒状容器里,而是先放在消息盒子里,再把消息盒子放在盒状容器里,然后把事件盒状与鼠标点击的事件绑定在一起,这样当鼠标点击棋盘时,就会触发鼠标事件的回调函数。

    GtkWidget *event_box = gtk_event_box_new();
    gtk_box_pack_start (GTK_BOX (vbox), event_box, TRUE, TRUE, 0);
    GtkWidget *fixed = gtk_fixed_new();
    gtk_container_add(GTK_CONTAINER(event_box),fixed);
    g_signal_connect(gtk_widget_get_parent(fixed), "button-press-event",
            G_CALLBACK(deal_mouse_press), pJunqi);

此时我们就可以在deal_mouse_press的回调函数里获取鼠标的坐标,再由鼠标的坐标计算出棋盘上对应的棋子的坐标

void deal_mouse_press(GtkWidget *widget, GdkEventButton *event, gpointer data)
{

    int x,y;
    BoardChess *pChess;
    Junqi *pJunqi = (Junqi *)data;
    x = event->x;
    y = event->y;

    pChess=GetChessPos(pJunqi,x,y);
    ... ...
}

在获取棋盘位置时,先把棋盘分割成5个区域,如下图
这里写图片描述
然后根据鼠标的位置,算出落在哪个区域。在计算时每个矩形的左上角坐标和长宽要事先用宏定义好,判断代码如下:

    if( (x>=MIDX) && (x<= MIDX+HORIZONTAL_LONG1)  )
    {
        if( (y>=BOTTOMY) && (y<=BOTTOMY+VERTICAL_LONG) )
        {
            自家范围
        }
        else if( (y>=TOPY) && (y<=TOPY+VERTICAL_LONG) )
        {
            对家范围
        }
        else if( (y>=NINE_GRID_Y) && (y<=NINE_GRID_Y+HORIZONTAL_LONG2) )
        {
            九宫范围
        }
    }
    else if( (y>=MIDY) && (y<=MIDY+HORIZONTAL_LONG2)  )
    {
        if((x>=RIGHTX) && (x<= RIGHTX+VERTICAL_LONG))
        {
            下家范围
        }
        else if( (x>=LEFTX) && (x<= LEFTX+VERTICAL_LONG) )
        {
            上家范围
        }
    }

知道在哪一家区域后,再把区域划分成6*5的方阵,在每个格子里填放着棋子。首先确定鼠标在哪个格子里,取得格子的左上角坐标,一个棋子并没有填满格子,还需判断鼠标是否落在棋子上而不是落在旁边的空隙里,此后返回选中的棋子。

2.初始化棋盘元素

棋盘相当于存放棋子的容器,用slot来表示存放棋子的格子,总共有4*30+9个格子,slot的位置是固定不变的,而slot里存放的棋子随着棋子的移位是会发生变化的,相关定义如下:

typedef struct ChessLineup
{
    enum ChessType type;
    GtkWidget *pImage[4];
}ChessLineup;

typedef struct BoardChess BoardChess;
struct BoardChess
{
    enum ChessType type;
    enum ChessDir iDir;
    ChessLineup *pLineup;
    int xPos;
    int yPos;
};

    ChessLineup Lineup[4][30];
    BoardChess ChessPos[4][30];
    BoardChess NineGrid[9];

ChessLineup定义棋子结构体,ChessLineup.type是棋子类型,ChessLineup.pImage表示同一个棋子在四家区域的不同显示方式。BoardChess定义了棋盘的slot,xPos和yPos为slot的左上角左边,在布阵时初始化,此后保持不变,iDir定义了slot所在区域,这个变量是为了显示棋子用的,pLineup就是当前slot存放的棋子。

3.移动棋子

有了上面的基础后就可以移动棋子了,目前先不管军棋的移动规则。实现的功能为,鼠标点击棋子,则棋子显示白色选中框,在点击目标位置,棋子移动到目标位置,如果目标位置有棋子则被覆盖掉。

相关代码如下,说明见注释

void deal_mouse_press(GtkWidget *widget, GdkEventButton *event, gpointer data)
{

    int x,y;
    BoardChess *pChess;
    BoardChess *pSrc, *pDst;
    Junqi *pJunqi = (Junqi *)data;
    x = event->x;
    y = event->y;
    //获取鼠标点击时的棋盘slot元素
    pChess=GetChessPos(pJunqi,x,y);
    if( pChess==NULL )
    {
        //为空说明点到了棋盘有效区之外的地方
        return;
    }
    //此时获得了棋盘的slot,此条件表示棋盘当前未处于选中状态
    if(!pJunqi->bSelect)
    {
        //没有棋子选中时,点到没有棋子的slot则返回
        if( pChess->type==NONE )
        {
            return;
        }
        pJunqi->bSelect = 1;
        pJunqi->pSelect = pChess;

        //显示白色选择框
        gtk_widget_hide(pJunqi->redRectangle[0]);
        gtk_widget_hide(pJunqi->redRectangle[1]);

        ShowRectangle(pJunqi, pChess, RECTANGLE_WHITE);
    }
    else
    {
        //此时棋子处于选中状态,点击目标位置进入这里,先隐藏白色选中框
        gtk_widget_hide(pJunqi->whiteRectangle[0]);
        gtk_widget_hide(pJunqi->whiteRectangle[1]);

        pJunqi->bSelect = 0;
        if(pChess==pJunqi->pSelect)
        {
            //选中的是当前棋子,则取消选中
            pJunqi->pSelect = NULL;
            return;
        }

        pDst = pChess;
        //如果目标位置上有棋子则隐藏棋子
        HideChess(pDst,pDst->iDir);
        pSrc = pJunqi->pSelect;
        //把目标棋子替换为选中的棋子
        pDst->pLineup = pSrc->pLineup;
        把选中棋子移动到目标位置上
        gtk_fixed_move(GTK_FIXED(pJunqi->fixed),
                       pSrc->pLineup->pImage[pDst->iDir],
                       pDst->xPos,pDst->yPos);
        //替换目标棋子类型,注意!这部分代码有很强的顺序关系
        pDst->type = pSrc->type;
        //隐藏选中的棋子
        HideChess(pSrc,pSrc->iDir);
        //将原来选中棋子位置上的棋子类型置为空,表示该位置上无棋子
        pSrc->type = NONE;
        //将移动后的棋子显示出来
        gtk_widget_show(pSrc->pLineup->pImage[pDst->iDir]);
        //显示红色矩形框
        ShowRectangle(pJunqi, pChess, RECTANGLE_RED);
    }
}

4.画矩形框

矩形框是通过cairo来画图的,先新建一块画布surface ,大小随意,再在画布上新建一支画笔,为了使背景色透明,需要选择CAIRO_FORMAT_ARGB32

    surface = cairo_image_surface_create ( CAIRO_FORMAT_ARGB32, 800, 900) ;
    cr = cairo_create (surface);

接下来把矩形画在surface上,从surface里提取pixbuf,要获取竖直矩形框,还需要对pixbuf进行旋转,使用gdk_pixbuf_rotate_simple()函数,最后把pixbuf转成图像即可

image = gtk_image_new_from_pixbuf(pixbuf);

效果如下
这里写图片描述

5.源代码

https://github.com/pfysw/JunQi

  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值