經典 Turbo C 圖形編程教學案例:使用 BGI 庫繪製模擬時鐘表盤,通過 dos.h 讀取系統實時時間,實現秒針動畫與時分針更新。代碼雖屬 DOS 時代產物,但其「幾何計算 + 雙緩衝動畫 + 硬件時間讀取」的框架思想仍具參考價值。
#include <graphics.h>
#include <conio.h>
#include <math.h>
#include <dos.h>
#include <stdio.h>
void main() {
int gd = DETECT, gm;
int x = 320, y = 240, r = 200; // 屏幕中心與表盤半徑
int i, h, m, s, thetamin, thetasec;
struct time t;
char n[12][3] = {"3","2","1","12","11","10","9","8","7","6","5","4"}; // 刻度標籤
initgraph(&gd, &gm, "g:\\tc\\bgi"); // BGI 路徑需按實際調整
circle(x, y, 210); // 外圓
setcolor(4); settextstyle(4, 0, 5); // 紅色、大字體
// 繪製 12 個刻度數字(極座標轉直角座標)
for (i = 0; i < 12; i++) {
int dx = (r - 14) * cos(M_PI / 6 * i);
int dy = (r - 14) * sin(M_PI / 6 * i);
int offset = (i == 3) ? 20 : 10; // "12" 兩位數需額外偏移
outtextxy(x + dx - offset, y - dy - 26, n[i]);
}
gettime(&t);
printf("Current: %2d:%02d:%02d.%02d\n", t.ti_hour, t.ti_min, t.ti_sec, t.ti_hund);
// ========== 主動畫循環 ==========
while (!kbhit()) {
// 1. 繪製中心固定圓
setcolor(5); setfillstyle(1, 5);
circle(x, y, 10); floodfill(x, y, 5);
// 2. 讀取最新時間
gettime(&t);
// 3. 分鐘變化時,清除舊時分針並重新計算角度
if (t.ti_min != m) {
setcolor(0); // 背景色擦除
// 擦除舊分针、時針(略,幾何計算同下)
}
// 4. 時針角度計算(12 小制,考慮分针影響)
if (t.ti_hour > 12) t.ti_hour -= 12;
h = (t.ti_hour < 4) ? abs(t.ti_hour - 3) : 15 - t.ti_hour;
m = t.ti_min;
thetamin = (t.ti_min <= 15) ? (15 - t.ti_min) * 6 : 450 - t.ti_min * 6;
thetasec = (t.ti_sec <= 15) ? (15 - t.ti_sec) * 6 : 450 - t.ti_sec * 6;
// 5. 繪製新時針(紅)、分针(紅)、秒針(白)
setcolor(4);
// 時針:長度 r-110,末端圓半徑 10
line(x, y, x + (r-110)*cos(M_PI/6*h - (m/2)*(M_PI/180)), y - (r-110)*sin(...));
circle(x + (r-130)*cos(...), y - (r-130)*sin(...), 10);
// 分针:長度 r-60
line(x, y, x + (r-60)*cos(thetamin*M_PI/180), y - (r-60)*sin(...));
circle(x + (r-80)*cos(...), y - (r-80)*sin(...), 10);
// 秒針:白色,長度 r-70
setcolor(15);
line(x, y, x + (r-70)*cos(thetasec*M_PI/180), y - (r-70)*sin(...));
delay(1000); // 粗略 1 秒
// 6. 擦除秒針(背景色),下一幀重繪
setcolor(0);
line(x, y, x + (r-70)*cos(thetasec*M_PI/180), y - (r-70)*sin(...));
}
closegraph();
}
屏幕座標系:X 右正,Y 下正。數學極座標:角度從 X 軸逆時針。
screen_x = center_x + R * cos(angle_rad);
screen_y = center_y - R * sin(angle_rad); // 注意負號翻轉 Y 軸
代碼中 M_PI/6 * i 即 30° 步進,thetamin/thetasec 將 0-59 映射到 0-360°。
時針不應跳躍,需隨分鐘連續轉動:
hour_angle = (hour % 12) * 30 + minute * 0.5; // 每分鐘 0.5°
代碼以 M_PI/6*h - (m/2)*(M_PI/180) 實現此邏輯。
無真正雙緩衝頁面,採用「前景色畫、背景色擦」模擬:
delay(1000) → 黑色擦。缺點:delay(1000) 阻塞主循環,無法響應鍵盤;系統調度誤差導致走時不準。
struct time { unsigned char ti_hour, ti_min, ti_sec, ti_hund; };
// ti_hund: 百分之一秒(0-99)
| 模塊 | Turbo C | 現代替代 |
|---|---|---|
| 圖形庫 | BGI (graphics.h) | SDL2, Raylib, OpenGL, Qt |
| 時間獲取 | dos.h gettime() | time.h localtime(), chrono (C++11), clock_gettime() |
| 延時 | dos.h delay() | SDL_Delay, std::this_thread::sleep_for, nanosleep |
| 鍵盤檢測 | conio.h kbhit() | SDL_PollEvent, GetAsyncKeyState, ncurses |
| 數學常數 | math.h M_PI | 自定義 const double PI = acos(-1); 或 C23 M_PI |
這個鬧鐘程序展示了「圖形初始化 → 靜態繪製 → 動態循環(讀取時間 → 幾何計算 → 增量重繪 → 幀率控制)」的經典遊戲/儀表循環架構。雖然 Turbo C/BGI 已退出歷史舞台,但掌握其中的座標變換、極坐標動畫、時間驅動邏輯,能無縫遷移至任何現代圖形框架。