使用 WinBGIm 來製作俄羅斯方塊 (Tetris)

如果你完成了上面這個文字版的俄羅斯方塊, 覺得這樣子的介面不夠炫, 想要看看下一步能做什麼, 可以嘗試用 WinBGIm 函式庫來改進它的介面, 只有輸出的函式改變了, 運用 WinBGIm 函式庫來寫的時候, 基本模型和文字版沒有差異, 如果你前面物件化做得好的話, 修改的地方應該蠻有限的 (當我們換到 Java 或是 MFC 這種事件驅動的視窗介面時, 就會有一些基本模型的改變)

目標是寫出 WinBGIm 版本的俄羅斯方塊

BGI

BGIBorland Graphic Interface 繪圖函式庫的縮寫, 在標準的 C 語言中, 是沒有繪圖函數的, 早年 (~1985) Borland 公司為 Turbo C/C++ 系統寫的繪圖函式庫, 那個年代 PC 是只有文字介面的 (24列80行), 640x480 的解析度就叫做高解析度圖形介面了XD。視窗系統在 1990 年代出現以後, 當然 BGI 也早就進入歷史了... Borland C/C++ 是 Turbo C/C++ 的後續版本, 不過在 1997 年也走入歷史, 最後的版本是 Borland C/C++ 5.02。Borland 公司在 1997 開發一套快速化程式開發 (RAD, Rapid Application Development) 的整合開發環境 (IDE, Integrated Development Environment) C++ Builder (BCB) 接續 Borland C++, 在 2008 年這個產品以及整個軟體部門 CodeGear 賣給了 Embarcadero Technologies。

簡史: Turbo C++Borland C++ → Borland C++Builder → CodeGear C++Builder → Embarcadero C++Builder

聽起來就是很有歷史的感覺, 我們還用這個嗎? 老實說, 不太用了, 不過要上手實在簡單, 和文字介面的程式運作模型是一致的, 大家應該可以很快開始, 門檻很低, 畫出來的效果其實也還不錯... 還有環境也很簡單, 不論你是用 gcc/g++ 或是 visual C/C++, 都只要下載一個 graphics.h 檔案, 一個 libbgi.lib 檔案就 OK 了, 準備好開始了, 不小心我們就介紹完了

WinBGIm

BGI 是過去的東西了, 可是這麼容易使用的東西, 還是很好的練習工具, 我在 Dept. CS of Univ. Colorado 網站上找到這個 WinBGIm, 用 Win32 API 重新實作了所有的函式, 是給 Dev-C/C++ 4.9.9.2 的, 不過我在 Dev-C/C++ 5.7.1, 5.11, MinGW 4.8.1, VC6, VC2008, VC2010 中編譯都只要修改非常少數的東西

  1. graphics.h
  2. libbgi.lib or libbig.a
    VC6 libbgi.lib
    VC08 libbgi.lib
    VC10 libbgi.lib, libbgi_d.lib
    DevC++4.9.9.2 libbgi.a
    MinGW g++4.8.1(32bit) libbgi.a
    MinGW g++4.8.1(64bit) libbgi.a
    DevC++5.7.1(64bit) libbgi.a
    DevC++5.11(64bit) libbgi.a
  3. 基本測試程式 testBGIm.cpp
  4. 編譯器/連結器參數
    VC/C++ 6

    命令列
    cl -GX testBGIm.cpp libbgi.lib gdi32.lib comdlg32.lib uuid.lib oleaut32.lib ole32.lib user32.lib

    VC/C++ 2008 命令列
    cl -EHsc testBGIm.cpp libbgi.lib gdi32.lib comdlg32.lib uuid.lib oleaut32.lib ole32.lib user32.lib
    VC/C++ 2010 IDE整合環境

    檔案/新增/專案, 空專案, testBGIm
    拷貝 testBGIm.cpp 到 testBGIm 資料匣
    方案總管/右鍵點 testBGIm/加入現有項目, 選擇 testBGIm.cpp
    專案/屬性/組態屬性/連結器/命令列/其他選項: /NODEFAULTLIB:LIBCMT

    建置/組態管理員/使用中的方案組態: Debug (預設是 Debug 版本)
    專案/屬性/組態屬性/連結器/輸入/其他相依性:加入 libbgi_d.lib;

    建置/組態管理員/使用中的方案組態: Release (如果你需要建置 Release 版本)
    專案/屬性/組態屬性/連結器/輸入/其他相依性:加入 libbgi.lib;

    命令列
    cl -EHsc testBGIm.cpp libbgi.lib gdi32.lib comdlg32.lib uuid.lib oleaut32.lib ole32.lib user32.lib

    DevC++ 4.9.9.2

    工具/編譯器選項/在連結器命令列中加入以下的命令
    Tools/Compiler Options/Add these commands to the linker command line
    -lbgi -lgdi32 -lcomdlg32 -luuid -loleaut32 -lole32

    MinGW g++ 4.8.1(32bit) g++ testBGIm.cpp -o testBGIm.exe -lbgi -L. -lgdi32 -lcomdlg32 -luuid -loleaut32 -lole32
    MinGW g++ 4.8.1(64bit) g++ testBGIm.cpp -o testBGIm.exe -lbgi -L. -lgdi32 -lcomdlg32 -luuid -loleaut32 -lole32 -Wno-write-strings
    DevC++ 5.7.1(64bit)

    工具/編譯器選項/在連結器命令列中加入以下的命令
    -L. -lbgi -lgdi32 -lcomdlg32 -luuid -loleaut32 -lole32

    DevC++ 5.11(64bit) 工具/編譯器選項/在連結器命令列中加入以下的命令
    -L. -lbgi -lgdi32 -lcomdlg32 -luuid -loleaut32 -lole32
  5. 說明文件 bgi.chm (Windows 中如果看不到內容的話, 請以滑鼠右鍵點選檔案 bgi.chm /內容/取消封鎖) (線上版本)
  6. 主要範例 bgidemo.exe, BGIDemo.zip

BGIm 基本功能說明:

程式中如何 進入結束 繪圖模式?

initwindow(640, 480, "First Sample"); // graphics.h 進入繪圖模式, 開啟 640 x 480 大小的繪圖視窗
...
繪圖動作
...
closegraph(); // graphics.h 中結束繪圖模式, 這一列請放在 system("pause") 之後

程式如何知道螢幕寬度及高度各有幾點呢?


程式在執行的時候可以藉由 graphics.h 中定義的函式 getmaxx() 及 getmaxy() 來得到螢幕的寬度及高度,例如:

int maxX, maxY;
maxX = getmaxx(); // graphics.h
maxY = getmaxy();

螢幕上可以在 x 軸(正向朝右) 0 到 maxX 以及 y 軸(正向朝下) 0 到 maxY 的範圍中作畫。

如何在螢幕上畫線呢?

請運用 graphics.h 函式庫中

  1. void line(int x1, int y1, int x2, int y2);
  2. void lineto(int x2, int y2);
  3. void moveto(int x2, int y2);

三個函式在螢幕上畫線, line() 這個函式可以指定起始點座標 (x1, y1) 及終點座標 (x2, y2), 在這之間畫一條直線。 moveto() 及 lineto() 這一組函式配合目前的游標位置 CP 也可以用來畫直線, 例如:

moveto(200, 20); // CP 移到 (200,20)
lineto(200, 80); // 畫直線到 (200,80)
lineto(300, 80); // 畫直線到 (300,80)

line(200, 20, 200, 80);
line(200, 80, 300, 80);

的效果完全一樣, moveto(200, 20) 將游標位置 CP 移動到 (200, 20) 的地方, lineto(200, 80) 則由 CP 的位置畫直線到 (200, 80), 並設定 CP 為 (200, 80)。

使用 lineto() 函式畫連續線段比起 line() 函式最大的好處就是程式中不需要記錄上一點的座標, 而由繪圖系統中的 CP 來記住。

注意:

如果你希望設定線段的顏色, 請以 void setcolor(int color); 函式來改變, 其中 color 的數值可以是 graphics.h 中定義的 BLACK(0), BLUE(1), GREEN(2), CYAN(3), RED(4), MAGENTA(5), BROWN(6), LIGHTGRAY(7), DARKGRAY(8), LIGHTBLUE(9), LIGHTGREEN(10), LIGHTCYAN(11), LIGHTRED(12), LIGHTMAGENTA(13), YELLOW(14), WHITE(15) 等等常數之一, 例如:


setcolor(LIGHTGREEN);
line(320, 0, 320, 479);
outtextxy(100, 200, "hello");
setcolor(LIGHTRED);
outtextxy(120, 200, "world");


就會 畫出一條亮綠色的直線, 亮綠色的文字 hello 以及亮紅色的文字 world


如何畫 sine 函式

一個 sine 函式圖形是一個二維平面上的圖形, x 軸範圍在 -π 到 π 之間, y 軸範圍在 -1 到 1 之間, 因為這是一個連續函數, 所以不論 x 軸或是 y 軸, 我們都知道其上應該有無窮多個點。

但是在電腦螢幕上要顯示這樣子的圖形, 不可能也不需要使用無窮多個點,

不可能的原因是螢幕的解析度有限, 在文字 模式視窗只能顯示 80 個字元寬、24 個字元高, 每一個方格裡你只能顯示一個字元或是不顯示, 沒有太多的選擇。 在簡單的 BGIm 圖形模式視窗中允許你在指定的 寬度及高度 的視窗中作圖, 每一個點除了允許你畫點或是不畫點之外, 也容許你用 16 個顏色來畫點。

不需要使用無窮多點的原因是: 你的眼睛不夠好, 根本無法在螢幕上分辨距離太近的點

下圖在 BGIm 繪圖模式視窗中繪製的 sine 函數:

由於解析度比文字模式提高很多, 在上面視窗中繪製的曲線, 你已經慢慢感覺不出來它是折線了。

座標軸繪製如下:

line(0, 240, 640, 240);
line(320, 0, 320, 480);

以線段來畫出 sine 函式, 程式如下:

    ix = (int) ((x = -PI) / PI * 319 + 320 + 0.5);
    iy = (int) (-sin(x) * 239 + 240 + 0.5);
    moveto(ix, iy);
    for (i=-318; i<=319; i++)
    {
        x = PI / 319 * i;
        y = sin(x);
        ix = i + 320;
        iy = (int) (-y * 239 + 240 + 0.5);
        lineto(ix, iy);
    }

座標轉換公式:

繪製 sine 函數圖形在寬 640, 高 480 的視窗中, 需要設計座標點的轉換公式如下:

實數平面
寬:2π 高:2
繪圖模式視窗
寬:640 高:480
(x,y) (x2,y2)
(0, 0) (320, 240)
(π, 0) (639, 240)
(-π, 0) (1, 240)
(0, 1) (320, 1)
(0, -1) (320, 479)

一般化的公式應該是

實數平面
寬:2π 高:2
繪圖模式視窗
寬:640 高:480
x
y
x2 = x / π * 319 + 320
y2 = -y * 239 + 240

請注意

  1. 由於 x, y 是實數, 上列公式算出的 x1, y1, x2, y2 也是實數, 而我們知道不管在 文字模式視窗或是繪圖模式視窗下, 都只能將螢幕畫面切割為整數個點, 其座標也需要是整數, 所以我們計算出來的 x1, y1, x2, y2 要經過四捨五入來找到最接近的整數座標值。

  2. 如何將一個浮點變數內的浮點數四捨五入為一個整數變數內的整數資料呢? 則整數變數 i 內的數值為 3, j 內的數值為 2。

 

如何在視窗中畫出有顏色的矩形方塊?

setfillstyle(SOLID_FILL, RED); // 紅色
bar(100, 150, 300, 250); // left, top, right, bottom

如果想要加畫邊框,

setcolor(WHITE); // 白色
rectangle(100, 150, 300, 250); // left, top, right, bottom

C++ 物件導向程式設計課程 首頁

製作日期: 05/01/2016 by 丁培毅 (Pei-yih Ting)
E-mail: [email protected] TEL: 02 24622192x6615
海洋大學 電機資訊學院 資訊工程學系 Lagoon