SDL2 自建對話框

對話框就是一個簡單的窗口,僅包含標題、文字信息和一兩個特定文字的按鈕。windows

因此咱們先改造下上篇的按鈕,增長類型屬性,並添加幾個對話框專用的特定的按鈕。數組

// 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;
    // 一般對話框上的按鈕有三種形式:一、單「肯定」,二、「肯定」 + 「取消」,三、「是」 + 「否」。
    // 默認定義爲 二、「肯定」 + 「取消」
    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);  // 控制按鈕數量一個,則只有 一、單「肯定」
    int id;

    // 調整爲 三、「是」 和「否」
    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;

    // 初始化:SDL二、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 也貼出來吧ui

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)

數據處理部分、圖片文件、聲音和前面同樣。指針

另外,這個辦法在安卓下無效,緣由不明。code

相關文章
相關標籤/搜索