在 PHP GTK 编程教程的这一部分中,我们将创建一个贪食蛇游戏克隆。
贪食蛇是较旧的经典视频游戏。 它最初是在 70 年代后期创建的。 后来它被带到 PC 上。 在这个游戏中,玩家控制蛇。 目的是尽可能多地吃苹果。 蛇每次吃一个苹果,它的身体就会长大。 蛇必须避开墙壁和自己的身体。
开发
蛇的每个关节的大小为 10px。 蛇由光标键控制。 最初,蛇具有三个关节。 游戏立即开始。 游戏结束后,我们在窗口中心显示"Game Over"消息。
该代码分为两个文件。 board.php和nibbles.php。
<?php// board.phpdefine("WIDTH", 300);define("HEIGHT", 270);define("DOT_SIZE", 10);define("ALL_DOTS", WIDTH * HEIGHT / (DOT_SIZE * DOT_SIZE));define("RAND_POS", 26);class Board extends GtkDrawingArea {public function __construct() {parent::__construct();$this->modify_bg(Gtk::STATE_NORMAL, new GdkColor(6400, 6400, 6440));$this->connect('expose_event', array($this, 'on_expose'));$this->init_game();}public function init_game() {$this->x = array_fill(0, ALL_DOTS, 0);$this->y = array_fill(0, ALL_DOTS, 0);$this->left = false;$this->right = true;$this->up = false;$this->down = false;$this->inGame = true;$this->dots = 3;for ($i=0; $i<=$this->dots; $i++) {$this->x[$i] = 50 - $i * 10;$this->y[$i] = 50;}try {$this->dot = CairoImageSurface::createFromPng("dot.png");$this->head = CairoImageSurface::createFromPng("head.png");$this->apple = CairoImageSurface::createFromPng("apple.png");} catch( Exception $e) {echo $e->getMessage();echo "cannot load images";exit;}$this->locate_apple();$this->set_can_focus(true);Gtk::timeout_add(100, array($this, 'on_timer'));}public function on_timer() {if ($this->inGame) {$this->check_apple();$this->check_collision();$this->move();$this->queue_draw();return true;} else {return false;}}public function on_expose() {$cr = $this->window->cairo_create();if ($this->inGame) {$this->draw_objects($cr);} else {$this->game_over($cr);}}public function draw_objects($cr) {$cr->SetSourceRgb(0, 0, 0);$cr->paint();$cr->setSourceSurface($this->apple,$this->apple_x, $this->apple_y);$cr->paint();for ($z=0; $z<=$this->dots; $z++) {if ($z == 0) {$cr->setSourceSurface($this->head,$this->x[$z], $this->y[$z]);$cr->paint();} else {$cr->setSourceSurface($this->dot,$this->x[$z], $this->y[$z]);$cr->paint();}}}public function game_over($cr) {$c_x = $this->get_allocation()->width/2;$c_y = $this->get_allocation()->height/2;$cr->SetFontSize(15);$cr->SetSourceRgb(65535, 65535, 65535);$te = $cr->TextExtents("Game Over");$cr->MoveTo($c_x - $te['width']/2, $c_y);$cr->ShowText("Game Over");}public function check_apple() {if ($this->x[0] == $this->apple_xand $this->y[0] == $this->apple_y) {$this->dots = $this->dots + 1;$this->locate_apple();}}public function move() {$z = $this->dots;while ($z > 0) {$this->x[$z] = $this->x[($z - 1)];$this->y[$z] = $this->y[($z - 1)];$z--;}if ($this->left) {$this->x[0] -= DOT_SIZE;}if ($this->right) {$this->x[0] += DOT_SIZE;}if ($this->up) {$this->y[0] -= DOT_SIZE;}if ($this->down) {$this->y[0] += DOT_SIZE;}}public function check_collision() {$z = $this->dots;while ($z > 0) {if ($z > 4 and $this->x[0] == $this->x[$z]and $this->y[0] == $this->y[$z]) {$this->inGame = false;}$z--;}if ($this->y[0] > HEIGHT - DOT_SIZE) {$this->inGame = false;}if ($this->y[0] < 0) {$this->inGame = false;}if ($this->x[0] > WIDTH - DOT_SIZE) {$this->inGame = false;}if ($this->x[0] < 0) {$this->inGame = false;}}public function locate_apple() {$r = rand(0, RAND_POS);$this->apple_x = $r * DOT_SIZE;$r = rand(0, RAND_POS);$this->apple_y = $r * DOT_SIZE;}public function on_key_down($event) {$key = $event->keyval;if ($key == Gdk::KEY_Left and !$this->right) {$this->left = true;$this->up = false;$this->down = false;}if ($key == Gdk::KEY_Right and !$this->left) {$this->right = true;$this->up = false;$this->down = false;}if ($key == Gdk::KEY_Up and !$this->down) {$this->up = true;$this->right = false;$this->left = false;}if ($key == Gdk::KEY_Down and !$this->up) {$this->down = true;$this->right = false;$this->left = false;}}}?>
这是 board.php 文件。
define("WIDTH", 300);define("HEIGHT", 270);define("DOT_SIZE", 10);define("ALL_DOTS", WIDTH * HEIGHT / (DOT_SIZE * DOT_SIZE));define("RAND_POS", 26);
WIDTH和HEIGHT常数确定电路板的大小。 DOT_SIZE是苹果的大小和蛇的点。 ALL_DOTS常数定义了板上可能的最大点数。 RAND_POS常数用于计算苹果的随机位置。
$this->x = array_fill(0, ALL_DOTS, 0);$this->y = array_fill(0, ALL_DOTS, 0);
这两个数组存储蛇的所有可能关节的 x,y 坐标。
init_game()方法初始化变量,加载图像并启动超时功能。
if ($this->inGame) {$this->check_apple();$this->check_collision();$this->move();$this->queue_draw();return true;} else {return false;}
每 140 毫秒,将调用on_timer()方法。 如果我们参与了游戏,我们将调用三种构建游戏逻辑的方法。 queue_draw()方法强制重新绘制窗口小部件。 这将反映游戏板上的变化。 否则,我们返回false,它将停止计时器事件。
$cr = $this->window->cairo_create();if ($this->inGame) {$this->draw_objects($cr);} else {$this->game_over($cr);}
在on_expose()方法内部,我们检查$this->inGame变量。 如果为真,则绘制对象。 苹果和蛇的关节。 否则,我们显示"Game Over"文本。
public function draw_objects($cr) {$cr->SetSourceRgb(0, 0, 0);$cr->paint();$cr->setSourceSurface($this->apple,$this->apple_x, $this->apple_y);$cr->paint();for ($z=0; $z<=$this->dots; $z++) {if ($z == 0) {$cr->setSourceSurface($this->head,$this->x[$z], $this->y[$z]);$cr->paint();} else {$cr->setSourceSurface($this->dot,$this->x[$z], $this->y[$z]);$cr->paint();}}}
draw_objects()方法绘制苹果和蛇的关节。 蛇的第一个关节是其头部,用红色圆圈表示。
如果游戏结束,则调用game_over()方法。 此方法在窗口中心显示"Game Over"。
$c_x = $this->get_allocation()->width/2;$c_y = $this->get_allocation()->height/2;
在这里,我们获得窗口的中心点。
$cr->SetFontSize(15);$cr->SetSourceRgb(65535, 65535, 65535);
我们设置文本的字体大小和颜色。 背景为黑色,因此字体将为白色。
$te = $cr->TextExtents("Game Over");
我们得到字符串的文本范围。 为了将文本放置在窗口的中央,这是必需的。
$cr->MoveTo($c_x - $te['width']/2, $c_y);$cr->ShowText("Game Over");
我们移到中心并显示文本。
public function check_apple() {if ($this->x[0] == $this->apple_xand $this->y[0] == $this->apple_y) {$this->dots = $this->dots + 1;$this->locate_apple();}}
check_apple()方法检查蛇是否击中了苹果对象。 如果是这样,我们添加另一个蛇形关节并调用locate_apple()方法,该方法将随机放置一个新的Apple对象。
在move()方法中,我们有游戏的关键算法。 要了解它,请看一下蛇是如何运动的。 您控制蛇的头。 您可以使用光标键更改其方向。 其余关节在链上向上移动一个位置。 第二关节移动到第一个关节的位置,第三关节移动到第二个关节的位置,依此类推。
while ($z > 0) {$this->x[$z] = $this->x[($z - 1)];$this->y[$z] = $this->y[($z - 1)];$z--;}
该代码将关节向上移动。
if ($this->left) {$this->x[0] -= DOT_SIZE;}
将头向左移动。
在check_collision()方法中,我们确定蛇是否击中了自己或撞墙之一。
while ($z > 0) {if ($z > 4 and $this->x[0] == $this->x[$z]and $this->y[0] == $this->y[$z]) {$this->inGame = false;}$z--;}
如果蛇用头撞到关节之一,我们就结束游戏。
if ($this->y[0] > HEIGHT - DOT_SIZE) {$this->inGame = false;}
如果蛇击中了棋盘的底部,我们就结束了游戏。
locate_apple()方法在板上随机放置一个苹果。
$r = rand(0, RAND_POS);
我们得到一个从 0 到RAND_POS-1的随机数。
$this->apple_x = $r * DOT_SIZE;...$this->apple_y = $r * DOT_SIZE;
这些行设置了apple对象的 x,y 坐标。
在Board类的on_key_down()方法中,我们确定按下的键。
if ($key == Gdk::KEY_Left and !$this->right) {$this->left = true;$this->up = false;$this->down = false;}
如果单击左光标键,则将$this->left变量设置为 true。 在move()方法中使用此变量来更改蛇对象的坐标。 还要注意,当蛇向右行驶时,我们不能立即向左转。
<?php/*ZetCode PHP GTK tutorialIn this program, we create a Nibblesgame clone.author: Jan Bodnarwebsite: www.zetcode.comlast modified: September 2011*/include 'board.php';class Example extends GtkWindow {public function __construct() {parent::__construct();$this->init_ui();}private function init_ui() {$this->set_title('Nibbles');$this->connect_simple('destroy', array('gtk', 'main_quit'));$this->board = new Board();$this->board->connect('key-press-event', array($this, 'on_key_down'));$this->add($this->board);$this->set_default_size(300, 270);$this->set_position(GTK::WIN_POS_CENTER);$this->show_all();}public function on_key_down($sender, $event) {$key = $event->keyval;$this->board->on_key_down($event);}}new Example();Gtk::main();?>
这是nibbles.php文件。 在此文件中,我们设置了贪食蛇游戏。
public function on_key_down($sender, $event) {$key = $event->keyval;$this->board->on_key_down($event);}
在这个类中,我们捕获按键事件。 并将处理委托给板类的on_key_down()方法。

图:贪食蛇
这是用 GTK 库和 PHP 编程语言编程的贪食蛇电脑游戏。
