SDL2 自建对话框

对话框就是一个简单的窗口,仅包含标题、文字信息和一两个特定文字的按钮。

所以我们先改造下上篇的按钮,增加类型属性,并添加几个对话框专用的特定的按钮。

// MySDL_Button.h
// SDL2 自定义部件 - 按钮


#include <SDL2/SDL.h>
#include <SDL2/SDL_ttf.h>

#ifndef MYSDL2_BUTTON_H
#define MYSDL2_BUTTON_H


// 按钮状态
typedef enum en_SDL_Button_State
{
    BTN_STATE_NORMAL,     // 正常
    BTN_STATE_DOWN,       // 按下
    BTN_STATE_UP          // 弹起
} SDL_Button_State;

// 按钮类型
typedef  enum en_SDL_Button_Type
{
    BT_TYPE_CUSTOM = 0, // 自定义类型,显示给定文字
    BT_TYPE_OK     = 1, // 其他类型,显示特定文字
    BT_TYPE_CANCEL = 2,
    BT_TYPE_YES    = 4,
    BT_TYPE_NO     = 8,
    BT_TYPE_OKCANCEL = BT_TYPE_OK | BT_TYPE_CANCEL,
    BT_TYPE_YESNO = BT_TYPE_YES | BT_TYPE_NO
} SDL_Button_Type;

// 按钮结构
typedef struct st_SDL_Button
{
    SDL_Button_Type type;       // 类型
    int id;                     // ID
    int x, y, w, h;             // 尺寸
    char *text;                 // 文字
    _Bool enable;               // 是否可用
    SDL_Button_State state;     // 状态
} SDL_Button;


// 画按钮
// 参数:pRen = 渲染器;pFont = 字体;pBtn = 按钮数组;btnNum = 按钮数量
void SDL_DrawButton(SDL_Renderer *pRen, TTF_Font *pFont, SDL_Button *pBtn, int btnNum);

// 坐标是否在有效按钮上
// 参数:x,y = 坐标;pBtn = 按钮数组;btnNum = 按钮数量
// 返回值:按钮ID,或者 -1(不在有效按钮上)
int SDL_isOnButton(int x, int y, SDL_Button *pBtn, int btnNum);

#endif
// MySDL_Button.h
// SDL2 自定义部件 - 按钮


#include "MySDL_Texture.h"
#include "MySDL_Button.h"


// 自定义按钮文字
static char *szOKCANCEL[] = {" 确定 ", " 取消 ",};
static char *szYESNO[]    = {"  是  ", "  否  "};


// 坐标是否在有效按钮上
// 参数:x,y = 坐标;pBtn = 按钮数组;btnNum = 按钮数量
// 返回值:按钮ID,或者 -1(不在有效按钮上)
int SDL_isOnButton(int x, int y, SDL_Button *pBtn, int btnNum)
{
    for(int i = 0; i < btnNum; i++)
        if(pBtn[i].enable && x >= pBtn[i].x && x <= pBtn[i].x + pBtn[i].w
                && y >= pBtn[i].y && y <= pBtn[i].y + pBtn[i].h)
            return pBtn[i].id;
    return -1;
}

// 画按钮
// 参数:pRen = 渲染器;pFont = 字体;pBtn = 按钮数组;btnNum = 按钮数量
void SDL_DrawButton(SDL_Renderer *pRen, TTF_Font *pFont, SDL_Button *pBtn, int btnNum)
{
    int bgc, tc;    // 背景颜色、文字颜色
    Uint8 ulc, dlc; // 按钮上、下线的颜色(单R、G、B)
    SDL_Texture *pBGTxt, *pTextTxt; // 背景、文字纹理
    SDL_Rect rt;

    for(int i = 0; i < btnNum; i++)
    {
        // 根据按钮是否可用及其状态决定底色、文字颜色
        if(pBtn[i].enable)
        {
            tc  = 0;
            switch(pBtn[i].state)
            {
            case BTN_STATE_NORMAL :
                bgc = 0xC5C5C5;
                ulc = 0xFF;
                dlc = 0;
                break;

            case BTN_STATE_DOWN :
                bgc = 0xA0A0A0;
                ulc = 0;
                dlc = 0xFF;
                break;

            case BTN_STATE_UP :
                bgc = 0xF1F1F1;
                ulc = 0xFF;
                dlc = 0;
                break;

            default :
                break;
            }
        }
        else
        {
            tc  = 0x989898;
            bgc = 0xF1F1F1;
            ulc = 0xFF;
            dlc = 0;
        }
        // 根据类型决定按钮上显示的文字
        pBGTxt = GetRGBTexture(pRen, pBtn[i].w, pBtn[i].h, bgc);
        switch(pBtn[i].type)
        {
        case BT_TYPE_OK :
            pTextTxt = GetTextTexture(pRen, pFont, szOKCANCEL[0], 0);
            break;
        case BT_TYPE_CANCEL :
            pTextTxt = GetTextTexture(pRen, pFont, szOKCANCEL[1], 0);
            break;
        case BT_TYPE_YES :
            pTextTxt = GetTextTexture(pRen, pFont, szYESNO[0], 0);
            break;
        case BT_TYPE_NO :
            pTextTxt = GetTextTexture(pRen, pFont, szYESNO[1], 0);
            break;
        case BT_TYPE_CUSTOM :
            pTextTxt = GetTextTexture(pRen, pFont, pBtn[i].text, tc);
            break;
        default :
            break;
        }

        if(pBGTxt != NULL && pTextTxt != NULL)
        {
            rt.x = pBtn[i].x;
            rt.y = pBtn[i].y;
            rt.w = pBtn[i].w;
            rt.h = pBtn[i].h;
            SDL_RenderCopy(pRen, pBGTxt, NULL, &rt);
            SDL_RenderCopy(pRen, pTextTxt, NULL, &rt);
            // 画上边线
            SDL_SetRenderDrawColor(pRen, ulc, ulc, ulc, SDL_ALPHA_OPAQUE);
            SDL_RenderDrawLine(pRen, rt.x, rt.y, rt.x + rt.w, rt.y);
            SDL_RenderDrawLine(pRen, rt.x, rt.y, rt.x, rt.y + rt.h);
            // 画下边线
            SDL_SetRenderDrawColor(pRen, dlc, dlc, dlc, SDL_ALPHA_OPAQUE);
            SDL_RenderDrawLine(pRen, rt.x + rt.w, rt.y, rt.x + rt.w, rt.y + rt.h);
            SDL_RenderDrawLine(pRen, rt.x, rt.y + rt.h, rt.x + rt.w, rt.y + rt.h);
        }
        if(pBGTxt != NULL)   SDL_DestroyTexture(pBGTxt);
        if(pTextTxt != NULL) SDL_DestroyTexture(pTextTxt);
    }
}

有了按钮,就可以建一个简单的窗口,只显示标题和文字内容,再加几个按钮。是窗口,当然也要自己处理消息了。

// MySDL_Dialog.h
// SDL2 自定义部件 - 对话框


#include <SDL2/SDL.h>
#include <SDL2/SDL_ttf.h>
#include "MySDL_Button.h"

#ifndef MYSDL2_DIALOG_H
#define MYSDL2_DIALOG_H


// 对话框结构
typedef struct st_SDL_Dialog
{
    char *title;                // 标题
    char *text;                 // 文字
    SDL_Button_Type btn_type;   // 按钮类型
} SDL_Dialog;

// 显示一个简单的对话框
// 参数:pWin = 父窗口;pFont = 字体;title = 标题;text = 文字;btn_type = 按钮类型
//返回值:被点击的按钮的类型值
extern SDL_Button_Type ShowDialog(SDL_Window *pWin, TTF_Font *pFont, char *title, char *text, SDL_Button_Type btn_type);

#endif
// MySDL_Dialog.c
// SDL2 自定义部件 - 对话框


#include <string.h>
#include "MySDL_Texture.h"
#include "MySDL_Dialog.h"


#define MARGIN 10      // 窗口边宽

static void UpdateWindow(SDL_Window *pWin, SDL_Renderer *pRen, SDL_Texture **pTxt,
        TTF_Font *pFont, char *title, char *text, SDL_Button *pBtn, int btnNum);

// 显示一个简单的对话框
// 参数:pWin = 父窗口;pFont = 字体;title = 标题;text = 文字;btn_type = 按钮类型
//返回值:被点击的按钮的类型值
extern SDL_Button_Type ShowDialog(SDL_Window *pWin, TTF_Font *pFont, char *title,
        char *text, SDL_Button_Type btn_type)
{
    int w, h;
    SDL_Window   *pThisWin;
    SDL_Renderer *pRen;
    SDL_Texture  *pTxt[4];  // 整体背景(标题栏、边框),文字背景,标题、文字的纹理
    SDL_Event    event;
    _Bool        bRun = 1;
    SDL_Button_Type ret = -1;
    // 通常对话框上的按钮有三种形式:1、单“确定”,2、“确定” + “取消”,3、“是” + “否”。
    // 默认定义为 2、“确定” + “取消”
    SDL_Button btn[2] =
    {
        {BT_TYPE_OK, 0, 0, 0, 0, 0, NULL, 1, BTN_STATE_NORMAL},
        {BT_TYPE_CANCEL, 1, 0, 0, 0, 0, NULL, 1, BTN_STATE_NORMAL}
    };
    int btnNum = (btn_type == BT_TYPE_OK ? 1 : 2);  // 控制按钮数量一个,则只有 1、单“确定”
    int id;

    // 调整为 3、“是” 和“否”
    if(btn_type == BT_TYPE_YESNO)
    {
        btn[0].type = BT_TYPE_YES;
        btn[1].type = BT_TYPE_NO;
    }

    SDL_GetWindowSize(pWin, &w, &h);
    if(SDL_CreateWindowAndRenderer(w / 2, h / 2, SDL_WINDOW_BORDERLESS
            | SDL_WINDOW_INPUT_GRABBED, &pThisWin, &pRen) == -1)
        goto label_error;

    pTxt[0] = GetRGBTexture(pRen, w / 2, h / 2, 0x00FFFF);
    pTxt[1] = GetRGBTexture(pRen, w / 2 - 2 * MARGIN, h / 2 - 5 * MARGIN, 0xFFFFFF);
    pTxt[2] = GetTextTexture(pRen, pFont, title, 0);
    pTxt[3] = GetTextTexture(pRen, pFont, text, 0);
    if(NULL == pTxt[0] || NULL == pTxt[1] || NULL == pTxt[2] || NULL == pTxt[3])
        goto label_error;

    while(bRun && SDL_WaitEvent(&event))
    {
        switch(event.type)
        {
        case SDL_MOUSEMOTION :      // 鼠标移动
            id = SDL_isOnButton(event.button.x, event.button.y, btn, btnNum);
            if(id >= 0) // 鼠标在某个按钮上
            {
                // 鼠标左键压下则该按钮处于凹状态,无鼠标键压下则该按钮处于凸状态
                if(event.motion.state == SDL_BUTTON_LMASK)
                    btn[id].state = BTN_STATE_DOWN;
                else
                    btn[id].state = BTN_STATE_UP;
            }
            else        // 鼠标不在按钮上,则所有按钮正常显示
            {
                for(int i = 0; i < btnNum; i++)
                    btn[i].state = BTN_STATE_NORMAL;
            }
            UpdateWindow(pThisWin, pRen, pTxt, pFont, title, text, btn, btnNum);
            break;

        case SDL_MOUSEBUTTONDOWN :  // 鼠标键按下
            id = SDL_isOnButton(event.button.x, event.button.y, btn, btnNum);;
            if(id >= 0) // 按下某个按钮,该按钮处于凹状态
            {
                btn[id].state = BTN_STATE_DOWN;
                UpdateWindow(pThisWin, pRen, pTxt, pFont, title, text, btn, btnNum);
            }
            break;

        case SDL_MOUSEBUTTONUP :    // 鼠标按键弹起
            id = SDL_isOnButton(event.button.x, event.button.y, btn, btnNum);
            if(id >= 0) // 在某个按钮上则结束对话框,返回按钮类型
            {
                ret = btn[id].type;
                bRun = 0;
            }
            break;

        case SDL_KEYUP :// 键盘的 Esc 键 也当“取消”处理
            if(event.key.keysym.sym == SDLK_ESCAPE)
            {
                ret = BT_TYPE_CANCEL;
                bRun = 0;
            }
            break;

        case SDL_WINDOWEVENT :      //  有窗口消息,重新计算窗口尺寸
            UpdateWindow(pThisWin, pRen, pTxt, pFont, title, text, btn, btnNum);
            break;

        default :
            break;
        }
    }

label_error:

    if(pTxt[0] != NULL) SDL_DestroyTexture(pTxt[0]);
    if(pTxt[1] != NULL) SDL_DestroyTexture(pTxt[1]);
    if(pTxt[2] != NULL) SDL_DestroyTexture(pTxt[2]);
    if(pTxt[3] != NULL) SDL_DestroyTexture(pTxt[3]);
    if(pRen != NULL)    SDL_DestroyRenderer(pRen);
    if(pThisWin != NULL) SDL_DestroyWindow(pThisWin);

    return ret;
}

// 重绘窗口
static void UpdateWindow(SDL_Window *pWin, SDL_Renderer *pRen, SDL_Texture **pTxt,
        TTF_Font *pFont, char *title, char *text, SDL_Button *pBtn, int btnNum)
{
    SDL_Rect rt;
    int w, h;

    SDL_GetWindowSize(pWin, &w, &h);
    SDL_RenderClear(pRen);

    // 整体背景(标题栏、边框)
    SDL_RenderCopy(pRen, pTxt[0], NULL, NULL);

    //文字背景
    rt.x = MARGIN;
    rt.y = 4 * MARGIN;
    rt.w = w - 2 * MARGIN;
    rt.h = h - 5 * MARGIN;
    SDL_RenderCopy(pRen, pTxt[1], NULL, &rt);

    // 标题
    rt.x = MARGIN;
    rt.y = MARGIN / 2;
    rt.w = MARGIN * strlen(title);
    rt.h = 3 * MARGIN;
    SDL_RenderCopy(pRen, pTxt[2], NULL, &rt);

    // 文字
    rt.y = 6 * MARGIN;
    rt.w = MARGIN * strlen(text);
    rt.h = 3 * MARGIN;
    SDL_RenderCopy(pRen, pTxt[3], NULL, &rt);

    // 按钮
    if(btnNum == 1)
    {
        pBtn[0].x = 3 * w / 4;
        pBtn[0].y = 3 * h / 4;
    }
    else
    {
        pBtn[0].x = w / 4;
        pBtn[1].x = 3 * w / 4;
        pBtn[0].y = pBtn[1].y = 3 * h / 4;
    }
    pBtn[1].w = pBtn[0].w = w / 6;
    pBtn[1].h = pBtn[0].h = h / 6;
    SDL_DrawButton(pRen, pFont, pBtn, btnNum);

    SDL_RenderPresent(pRen);
}

然后就可以在五子棋里测试了,给“悔棋”按钮加上显示对话框的功能

// Five.c
// SDL2 五子棋

//#define _DEBUG_


#include <SDL2/SDL.h>
#include <SDL2/SDL_image.h>
#include <SDL2/SDL_ttf.h>
#include <SDL2/SDL_mixer.h>
#include <stdio.h>
#include <string.h>
#include "MySDL_Texture.h"
#include "MySDL_Button.h"
#include "MySDL_Dialog.h"
#include "FiveData.h"


// 资源文件
int  BackColor      = 0xFFFFFF; // 棋子图片的背景色
char *ImgFileName[] =
{
    "Resource/BackGround.jpg",  // 棋盘背景图文件
    "Resource/BlackPiece.jpg",  // 黑棋子图文件
    "Resource/WhitePiece.jpg"   // 白棋子图文件
};
char SoundFileName[] = "Resource/Stone.mp3";        // 落子音效文件
char FontFileName[]  = "C:/Windows/Fonts/msyh.ttf"; // Windows 下字体文件

// 字符串常量
char szWinTitle[]  = "SDL2 五子棋";
char *szWho[]      = {"黑方", "白方"};
char *szGameTips[] = {"第 %d 手,轮到 %s 落子", "共 %d 手,%s 取得本局胜利"};


_Bool OnKeyUp(int x, int y, int nSpacing);
void DrawBoard(SDL_Renderer *pRen, int nSpacing, int c);
void DrawPieces(SDL_Renderer *pRen, int nSpacing, SDL_Texture **pImgTxt);
void PrintString(SDL_Renderer *pRen, int nSpacing, TTF_Font *pFont, char *text, int c);
static void UpdateWindow(SDL_Window *pWin, SDL_Renderer *pRen, int nSpacing,
        TTF_Font *pFont, SDL_Texture **pImgTxt, SDL_Button *pBtn, int n);

#undef main
int main(int argc, char **argv)
{
    int WinW = 640, WinH = 480; // 屏幕尺寸
    int nSpacing;               // 棋盘线距
    SDL_Window   *pWin;         // 主窗口
    SDL_Renderer *pRen;         // 主窗口渲染器
    SDL_Texture  *pImgTxt[3];   // 棋盘背景、黑白棋子图纹理
    TTF_Font     *pFont;        // 提示文字字体
    Mix_Music    *pMusic;       // 音效
    SDL_Event    event;         // 事件
    _Bool        bRun = 1;      // 持续等待事件控制循环标识
    // 按钮
    SDL_Button btn[] =
    {
        {BT_TYPE_CUSTOM, 0, 0, 0, 0, 0, " 新局 ", 0, BTN_STATE_NORMAL},
        {BT_TYPE_CUSTOM, 1, 0, 0, 0, 0, " 悔棋 ", 0, BTN_STATE_NORMAL},
    };
    int btnNum = sizeof(btn) / sizeof(SDL_Button);
    int id;

    // 初始化:SDL2、SDL_image(jpg)、SDL_ttf、SDL_mixer(mp3)
    if(SDL_Init(SDL_INIT_EVERYTHING) == -1 || IMG_Init(IMG_INIT_JPG) == -1 || TTF_Init() == -1
        || Mix_Init(MIX_INIT_MP3) == -1 || Mix_OpenAudio(MIX_DEFAULT_FREQUENCY,
            MIX_DEFAULT_FORMAT, MIX_DEFAULT_CHANNELS, 4096) == -1)
    {
#ifdef _DEBUG_
        fprintf(stderr, "1 %s", SDL_GetError());
#endif
        return 1;
    }

    // 创建主窗口及其渲染器
    if(SDL_CreateWindowAndRenderer(WinW, WinH, SDL_WINDOW_FULLSCREEN, &pWin, &pRen) == -1)
    {
#ifdef _DEBUG_
        fprintf(stderr, "2 %s", SDL_GetError());
#endif
        goto label_error;
    }
    SDL_SetWindowTitle(pWin, szWinTitle);

    // 加载图片文件
    if(NULL == (pImgTxt[0] = GetImageTexture(pRen, ImgFileName[0], 0, 0))
        || NULL == (pImgTxt[1] = GetImageTexture(pRen, ImgFileName[1], 1, BackColor))
        || NULL == (pImgTxt[2] = GetImageTexture(pRen, ImgFileName[2], 1, BackColor)))
    {
#ifdef _DEBUG_
        fprintf(stderr, "3 %s", IMG_GetError());
#endif
        goto label_error;
    }

    // 加载字体文件
    if(NULL == (pFont = TTF_OpenFont(FontFileName, 20)))
    {
#ifdef _DEBUG_
        fprintf(stderr, "4 %s", TTF_GetError());
#endif
        goto label_error;
    }

    // 加载声音文件
    if(NULL == (pMusic = Mix_LoadMUS(SoundFileName)))
    {
#ifdef _DEBUG_
        fprintf(stderr, "5 %s", Mix_GetError());
#endif
        goto label_error;
    }

    Five_ResetData();
    while(bRun && SDL_WaitEvent(&event))
    {
        switch(event.type)
        {
        case SDL_MOUSEMOTION :      // 鼠标移动
            id = SDL_isOnButton(event.button.x, event.button.y, btn, btnNum);
            // 鼠标在某个按钮上
            if(id >= 0)
            {
                // 鼠标左键压下则该按钮处于凹状态,无鼠标键压下则该按钮处于凸状态
                if(event.motion.state == SDL_BUTTON_LMASK)
                    btn[id].state = BTN_STATE_DOWN;
                else
                    btn[id].state = BTN_STATE_UP;
            }
            // 鼠标不在按钮上
            else
            {
                // 所有按钮正常显示
                for(int i = 0; i < btnNum; i++)
                    btn[i].state = BTN_STATE_NORMAL;
            }
            UpdateWindow(pWin, pRen, nSpacing, pFont, pImgTxt, btn, btnNum);
            break;

        case SDL_MOUSEBUTTONDOWN :  // 鼠标键按下
            id = SDL_isOnButton(event.button.x, event.button.y, btn, btnNum);;
            // 按下某个按钮,该按钮处于凹状态
            if(id >= 0)
            {
                btn[id].state = BTN_STATE_DOWN;
                UpdateWindow(pWin, pRen, nSpacing, pFont, pImgTxt, btn, btnNum);
            }
            break;

        case SDL_MOUSEBUTTONUP :    // 鼠标按键弹起
            id = SDL_isOnButton(event.button.x, event.button.y, btn, 2);
            // 在某个按钮上则响应功能,在棋盘则检测落子
            // 新局
            if(id == 0)
            {
                btn[0].enable = 0;
                btn[1].enable = 0;
                Five_ResetData();
            }
            // 悔棋
            else if(id == 1)
            {
                id = ShowDialog(pWin, pFont, "悔棋", "要悔棋吗?", BT_TYPE_YESNO);
                if(id == BT_TYPE_YES)
                    ShowDialog(pWin, pFont, "对不起", "还没实现悔棋功能呢", BT_TYPE_OKCANCEL);
                else if(id == BT_TYPE_NO)
                    ShowDialog(pWin, pFont, "还好", "你没点是", BT_TYPE_OK);
            }
            // 有效落子
            else if(id < 0 && g_iWho != NONE && OnKeyUp(event.button.x, event.button.y, nSpacing))
            {
                Mix_PlayMusic(pMusic, 0);
                btn[0].enable = 1;
                btn[1].enable = 1;
                if(Five_isFive())
                    g_iWho = NONE;
            }
            UpdateWindow(pWin, pRen, nSpacing, pFont, pImgTxt, btn, btnNum);
            break;

        case SDL_WINDOWEVENT :      //  有窗口消息,重新计算窗口尺寸
            SDL_GetWindowSize(pWin, &WinW, &WinH);
            nSpacing = SDL_min(WinW, WinH) / (MAX_LINES + 2);
            UpdateWindow(pWin, pRen, nSpacing, pFont, pImgTxt, btn, btnNum);
            break;

        case SDL_QUIT :
            bRun = 0;
            break;

        default :
            break;
        }
    }

label_error:
    // 清理
    if(pImgTxt[0] != NULL) SDL_DestroyTexture(pImgTxt[0]);
    if(pImgTxt[1] != NULL) SDL_DestroyTexture(pImgTxt[1]);
    if(pImgTxt[2] != NULL) SDL_DestroyTexture(pImgTxt[2]);
    if(pRen != NULL)   SDL_DestroyRenderer(pRen);
    if(pWin != NULL)   SDL_DestroyWindow(pWin);
    if(pFont != NULL)  TTF_CloseFont(pFont);
    if(pMusic != NULL) Mix_FreeMusic(pMusic);
    Mix_CloseAudio();
    TTF_Quit();
    IMG_Quit();
    SDL_Quit();
    return 0;
}

// 重绘窗口
static void UpdateWindow(SDL_Window *pWin, SDL_Renderer *pRen, int nSpacing,
        TTF_Font *pFont, SDL_Texture **pImgTxt, SDL_Button *pBtn, int btnNum)
{
    char szString[256];

    SDL_RenderClear(pRen);

    SDL_RenderCopy(pRen, pImgTxt[0], NULL, NULL);

    DrawBoard(pRen, nSpacing, 0);

    DrawPieces(pRen, nSpacing, pImgTxt);

    sprintf(szString, szGameTips[g_iWho == NONE],
            g_nHands + (g_iWho != NONE), szWho[(g_nHands + (g_iWho == NONE)) % 2]);
    PrintString(pRen, nSpacing, pFont, szString, 0);

    pBtn[1].x = pBtn[0].x = nSpacing * (MAX_LINES + 1);
    pBtn[0].y = nSpacing;
    pBtn[1].y = nSpacing * 3;
    pBtn[1].w = pBtn[0].w = nSpacing * 2;
    pBtn[1].h = pBtn[0].h = nSpacing;
    SDL_DrawButton(pRen, pFont, pBtn, btnNum);

    SDL_RenderPresent(pRen);
}

// 响应落子按键
// 参数:(x,y) = 被点击的窗口坐标;nSpacing = 棋盘线距
_Bool OnKeyUp(int x, int y, int nSpacing)
{
    // 计算落点棋盘坐标
    int m = (x - 0.5 * nSpacing) / nSpacing;
    int n = (y - 0.5 * nSpacing) / nSpacing;

    // 处理有效落点
    if(m >= 0 && m < MAX_LINES && n >= 0 && n < MAX_LINES && g_iBoard[m][n] == NONE)
    {
        Five_AddPiece(m, n, g_iWho);
        return 1;
    }
    return 0;
}

// 画圆(SDL2 没有画圆的函数,先用矩形框代替吧)
// 参数:pRen = 渲染器;(x,y) = 圆心坐标;r = 半径;c = 填充色
void FillCircle(SDL_Renderer *pRen, int x, int y, int r, int c)
{
    SDL_Rect rt = {x - r, y - r, 2 * r, 2 * r};

    SDL_SetRenderDrawColor(pRen, c >> 16, (c >> 8) & 0xFF, c & 0xFF, SDL_ALPHA_OPAQUE);
    SDL_RenderFillRect(pRen, &rt);
}

// 画棋盘
// 参数:pRen = 渲染器;nSpacing = 棋盘线距;c = 线及星颜色
void DrawBoard(SDL_Renderer *pRen, int nSpacing, int c)
{
    int r, x, y, z;

    // 棋盘线
    SDL_SetRenderDrawColor(pRen, c >> 16, (c >> 8) & 0xFF, c & 0xFF, SDL_ALPHA_OPAQUE);
    for(int i = 1; i <= MAX_LINES; i++)
    {
        SDL_RenderDrawLine(pRen, nSpacing, i * nSpacing, MAX_LINES * nSpacing, i * nSpacing);
        SDL_RenderDrawLine(pRen, i * nSpacing, nSpacing, i * nSpacing, MAX_LINES * nSpacing);
    }

    // 星位
    r = nSpacing * 0.2;                 // 星半径
    x = nSpacing * 4;                   // 第四线
    y = nSpacing * (MAX_LINES + 1) / 2; // 中线
    z = nSpacing * (MAX_LINES - 3);     // 倒数第四线
    FillCircle(pRen, x, x, r, c);
    FillCircle(pRen, y, x, r, c);
    FillCircle(pRen, z, x, r, c);
    FillCircle(pRen, x, y, r, c);
    FillCircle(pRen, y, y, r, c);
    FillCircle(pRen, z, y, r, c);
    FillCircle(pRen, x, z, r, c);
    FillCircle(pRen, y, z, r, c);
    FillCircle(pRen, z, z, r, c);
}

// 画棋子
// 参数:pRen = 渲染器;nSpacing = 棋盘线距;pImgTxt = 棋子纹理
void DrawPieces(SDL_Renderer *pRen, int nSpacing, SDL_Texture **pImgTxt)
{
    int r = 0.4 * nSpacing; // 棋子半径
    SDL_Rect rt = {0, 0, 2 * r, 2 * r};

    if(g_nHands <= 0)
        return;

    for(int i = 0; i < MAX_LINES; i++)
    {
        for(int j = 0; j < MAX_LINES; j++)
        {
            rt.x = (i + 1) * nSpacing - r;
            rt.y = (j + 1) * nSpacing - r;
            if(g_iBoard[i][j] == BLACK)
                SDL_RenderCopy(pRen, pImgTxt[1], NULL, &rt);
            else if(g_iBoard[i][j] == WHITE)
                SDL_RenderCopy(pRen, pImgTxt[2], NULL, &rt);
        }
    }
}

// 提示文字
// 参数:pRen = 渲染器;nSpacing = 棋盘线距;pFont = 字体;text = 文字内容;c = 文字颜色
void PrintString(SDL_Renderer *pRen, int nSpacing, TTF_Font *pFont, char *text, int c)
{
    SDL_Texture *pTextTxt;
    SDL_Rect rt;

    rt.x = nSpacing;
    rt.y = nSpacing * (MAX_LINES + 1);
    rt.w = nSpacing * strlen(text) / 4; // 这个 4 和字体大小有关
    rt.h = nSpacing;

    if((pTextTxt = GetTextTexture(pRen, pFont, text, c)) != NULL)
    {
        SDL_RenderCopy(pRen, pTextTxt, NULL, &rt);
        SDL_DestroyTexture(pTextTxt);
    }
}

为了统一,把获得纹理的模块也稍微改了下格式

// MySDL_Texture.h
// SDL2 公共函数 - 取得 RGB、图片、文字的纹理


#include <SDL2/SDL.h>
#include <SDL2/SDL_image.h>
#include <SDL2/SDL_ttf.h>

#ifndef MYSDL2_TEXTURE_H
#define MYSDL2_TEXTURE_H


// 取得 RGB 纹理
// 参数:pRen = 渲染器;w, h = 宽、高;c = 颜色
// 返回值:纹理指针
SDL_Texture *GetRGBTexture(SDL_Renderer *pRen, int w, int h, int c);

// 取得图片文件纹理
// 参数:pRen = 渲染器;FileName = 图片文件名;bTrn = 是否透明处理;c = 背景色
// 返回值:纹理指针
SDL_Texture *GetImageTexture(SDL_Renderer *pRen, char *FileName, _Bool bTrn, int c);

// 取得文字纹理
// 参数:pRen = 渲染器;pFont = 字体;text = 文字内容;c = 文字颜色
// 返回值:纹理指针
SDL_Texture *GetTextTexture(SDL_Renderer *pRen, TTF_Font *pFont, char *text, int c);


#endif
// MySDL_Texture.c
// SDL2 公共函数 - 取得 RGB、图片、文字的纹理


#include "MySDL_Texture.h"


// 取得 RGB 纹理
// 参数:pRen = 渲染器;w, h = 宽、高;c = 颜色
// 返回值:纹理指针
SDL_Texture *GetRGBTexture(SDL_Renderer *pRen, int w, int h, int c)
{
    SDL_Texture *pTexture;
    SDL_Surface *pSurface;

    if((pSurface = SDL_CreateRGBSurface(0, w, h, 32, 0, 0, 0, 0)) == NULL)
        return NULL;
    SDL_FillRect(pSurface, NULL, c);
    pTexture = SDL_CreateTextureFromSurface(pRen, pSurface);

    SDL_FreeSurface(pSurface);
    return pTexture;
}

// 取得图片文件纹理
// 参数:pRen = 渲染器;FileName = 图片文件名;bTrn = 是否透明处理;c = 背景色
// 返回值:纹理指针
SDL_Texture *GetImageTexture(SDL_Renderer *pRen, char *FileName, _Bool bTrn, int c)
{
    SDL_Texture *pTexture;
    SDL_Surface *pSurface;

    if((pSurface = IMG_Load(FileName)) == NULL)
        return NULL;
    if(bTrn)
        SDL_SetColorKey(pSurface, 1,
                SDL_MapRGB(pSurface->format, c>>16, (c>>8)&0xFF, c&0xFF));
    pTexture = SDL_CreateTextureFromSurface(pRen, pSurface);

    SDL_FreeSurface(pSurface);
    return pTexture;
}

// 取得文字纹理
// 参数:pRen = 渲染器;pFont = 字体;text = 文字内容;c = 文字颜色
// 返回值:纹理指针
SDL_Texture *GetTextTexture(SDL_Renderer *pRen, TTF_Font *pFont, char *text, int c)
{
    SDL_Texture *pTexture;
    SDL_Surface *pSurface;
    SDL_Color color = {c>>16, (c>>8)&0xFF, c&0xFF};

    if((pSurface = TTF_RenderUTF8_Blended(pFont, text, color)) == NULL)
        return NULL;
    pTexture = SDL_CreateTextureFromSurface(pRen, pSurface);

    SDL_FreeSurface(pSurface);
    return pTexture;
}

把 Makefile 也贴出来吧

SourceFile = Five.c FiveData.c MySDL_Dialog.c MySDL_Button.c MySDL_Texture.c
Library    = -lSDL2 -lSDL2main -lSDL2_image -lSDL2_ttf -lSDL2_mixer

ALL: $(SourceFile) Makefile
	gcc -mwindows -o Five $(SourceFile) $(Library)

数据处理部分、图片文件、声音和前面一样。

另外,这个办法在安卓下无效,原因不明。



转载于:https://my.oschina.net/u/580100/blog/614964

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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值