PHP 不僅限於 Web 開發,配合 WinBinder 擴充套件,也能編寫原生 Windows 桌面應用程式。本文將透過一個經典的「21 點(Blackjack)」遊戲範例,展示如何使用 PHP 建立視窗、元件佈局、事件處理與遊戲邏輯。
php_winbinder.dll)專案包含三個主要檔案:
<?php
$winmain = wb_create_window(null, 'AppWindow', '21點遊戲', WBC_CENTER, WBC_CENTER, 800, 495, 0, 0);
// 建立標籤:莊家、玩家
wb_create_control($winmain, 'Label', '莊家', 20, 15, 40, 30, 'LB_1', 0, 0, 0);
wb_create_control($winmain, 'Label', '玩家', 20, 200, 40, 30, 'LB_2', 0, 0, 0);
// 建立撲克牌顯示框架(莊家 5 張、玩家 5 張)
for ($i = 1; $i <= 5; $i++) {
wb_create_control($winmain, 'Frame', '', 80 + ($i-1)*140, 40, 120, 150, "FR_PC_$i", 0, 0, 0); // 莊家
wb_create_control($winmain, 'Frame', '', 80 + ($i-1)*140, 230, 120, 150, "FR_U_$i", 0, 0, 0); // 玩家
}
// 按鈕:發牌、停止
wb_create_control($winmain, 'PushButton', '發牌', 320, 400, 70, 25, 'B_GO', 0x00000002, 0, 0);
wb_create_control($winmain, 'PushButton', '停止', 435, 400, 70, 25, 'B_CHANGE', 0x00000002, 0, 0);
?>
<?php
// 產生 52 張撲克牌點數陣列
$card = [];
for ($i = 1; $i <= 13; $i++) {
switch ($i) {
case 1: $j = 1; $key = 'A'; break;
case 11: $j = 10; $key = 'J'; break;
case 12: $j = 10; $key = 'Q'; break;
case 13: $j = 10; $key = 'K'; break;
default: $key = $j = $i;
}
foreach (['黑桃', '紅心', '梅花', '方塊'] as $suit) {
$card["$suit\n$key"] = $j;
}
}
// 控制項 ID 對照表
$control_id = [
'B_GO' => 1001, 'B_CHANGE' => 1002,
'ME_START' => 2001, 'ME_REMON' => 2002, 'ME_AUTHOR' => 2003, 'ME_QUIT' => IDCLOSE,
// ... 其餘標籤、框架 ID 略
];
foreach ($control_id as $key => $value) {
if (!defined(strtoupper($key))) define(strtoupper($key), $value);
}
?>
class Ponit {
private $user_point = 10000;
private $pc_point = 10000;
private $card;
private $player = 'user';
private $new_term = false;
public function __construct($winmain, $card) {
// 從檔案讀取雙方積分
list($this->user_point, $this->pc_point) = explode('|', file_get_contents('form/db.db'));
$this->window = $winmain;
$this->card = $card;
$this->prepare();
}
private function prepare() {
// 設定圖示、選單、字型、初始積分顯示
wb_set_image($this->window, 'form/hyper.ico');
// 建立選單:開局、重計分、作者、退出
wb_create_control($this->window, 'Menu', [ ... ]);
wb_set_text(wb_get_control($this->window, U_MON), "$this->user_point 分");
wb_set_text(wb_get_control($this->window, PC_MON), "$this->pc_point 分");
}
private function play() {
static $card_tem = [], $i = 0, $j = 0, $user_total = 0, $pc_total = 0;
if ($this->new_term) { // 新局初始化
$card_tem = $this->card;
$user_total = $pc_total = 0;
$i = $j = 0;
$this->new_term = false;
}
if ($this->player === 'user') { // 玩家抽牌
$i++;
$card_key = array_rand($card_tem);
$card_val = $card_tem[$card_key];
$user_total += $card_val;
// 更新介面顯示
wb_set_text(wb_get_control($this->window, LB_U_T), "$user_total 點");
wb_set_text(wb_get_control($this->window, 5000+$i), $card_key);
wb_set_text(wb_get_control($this->window, 3200+$i), $card_val);
// 判斷勝負:5 張牌未爆或爆牌
if ($i >= 5 && $user_total <= 21) { $this->total(20, 'U'); /* 詢問繼續 */ }
elseif ($user_total > 21) { $this->total(20, 'P'); /* 詢問繼續 */ }
} else { // 莊家抽牌(自動)
$j++;
$card_key = array_rand($card_tem);
$card_val = $card_tem[$card_key];
$pc_total += $card_val;
wb_set_text(wb_get_control($this->window, LB_P_T), "$pc_total 點");
wb_set_text(wb_get_control($this->window, 4000+$j), $card_key);
wb_set_text(wb_get_control($this->window, 3100+$j), $card_val);
// 莊家策略:未達 5 張且未爆則繼續抽牌(usleep 模擬思考)
if ($j >= 5 && $pc_total <= 21) { $this->total(20, 'P'); /* 詢問繼續 */ }
elseif ($pc_total > 21) { /* 比較點數判斷勝負 */ }
else { usleep(800000); $this->play(); }
}
unset($card_tem[$card_key]);
}
// 積分結算與存檔
private function total($num, $win) {
static $u_tem, $p_tem;
if (empty($u_tem) || $this->remon) { $u_tem = $this->user_point; $p_tem = $this->pc_point; $this->remon = false; }
$win === 'U' ? ($u_tem += $num, $p_tem -= $num) : ($u_tem -= $num, $p_tem += $num);
$this->user_point = $u_tem; $this->pc_point = $p_tem;
wb_set_text(wb_get_control($this->window, U_MON), "$u_tem 分");
wb_set_text(wb_get_control($this->window, PC_MON), "$p_tem 分");
file_put_contents('form/db.db', "$u_tem|$p_tem");
}
}
// 主程式進入點
$wbd = new Ponit($winmain, $card);
wb_set_handler($winmain, 'go_to');
wb_main_loop();
function go_to($window, $id) {
global $wbd;
$wbd->key_id = $id;
return $wbd->main();
}
?>
php 21point.phpw 或直接雙擊 .phpw 檔案(需關聯 php-win.exe)。此範例展示了 PHP 結合 WinBinder 開發桌面 GUI 應用的完整流程:視窗建立、元件佈局、事件驅動、遊戲狀態管理與本地資料持久化。雖然 WinBinder 目前已較少維護,但對於快速原型開發、內部小工具或教學示範,PHP 桌面開發仍具備獨特優勢。