陈建华的个人网站

PHP7 开发GUI应用程序:贪吃蛇

  • Jet Chen
  • 2017-11-30
  • 0

其实PHP不仅仅可以书写网站,还可以书写GUI桌面程序,从PHP5.2起就有这样的第三方扩展包

目前PHP7.x 的GUI扩展有良好的GUI扩展:

http://php.net/ui

开启PHP GUI,以Windows 10 64bit系统为例子

1.按照自己的PHP版本,编译的版本如PHP 7.1 x86的NTS版本,下载扩展插件

2.将插件的3个文件

php_ui.dll 
libui.dll 
pthreadVC2.dll

拷贝到与php.exe同位置的目录。

3.php.ini开启扩展

以下给出一个贪吃蛇的demo

<?php
define ("PHP_UI_SECOND",    1000000);
define ("PHP_UI_SNAKE_FPS", 30);

use UI\Window;
use UI\Point;
use UI\Size;
use UI\Area;
use UI\Key;

use UI\Controls\Box;

use UI\Draw\Pen;
use UI\Draw\Brush;
use UI\Draw\Path;
use UI\Draw\Color;
use UI\Draw\Stroke;
use UI\Draw\Matrix;

use UI\Draw\Text\Font;
use UI\Draw\Text\Font\Descriptor;

use UI\Executor;

$win = new class("Snake", new Size(640, 480), false) extends Window {
	public function addExecutor(Executor $executor) {
		$this->executors[] = $executor;
	}

	protected function onClosing() {
		foreach ($this->executors as $executor) {
			$executor->kill();
		}

		$this->destroy();

		UI\quit();
	}
};

$box = new Box(Box::Vertical);
$win->add($box);

$snake = new class($box) extends Area{

	public function __construct(Box $box) {
		$this->newSnake();

		$box->append($this, true);
	}

	public function setExecutor(Executor $executor) {
		$this->executor = $executor;
	}

	protected function onKey(string $char, int $key, int $flags) {
		if ($flags & Area::Down) {
			switch ($key) {
				case Key::Up:
					if ($this->direction == Key::Down)
						return;
					$this->direction = $key;
				break;

				case Key::Down:
					if ($this->direction == Key::Up)
						return;
					$this->direction = $key;
				break;

				case Key::Right:
					if ($this->direction == Key::Left)
						return;
					$this->direction = $key;
				break;

				case Key::Left:
					if ($this->direction == Key::Right)
						return;

					$this->direction = $key;
				break;

				default:
					if ($char == " ") {
						$this->pause = !$this->pause;

						if ($this->pause) {
							/* this allows the CPU to idle while paused */
							$this->executor->setInterval(0);
						} else {
							/* this will (re)start the game */
							$this->executor->setInterval(PHP_UI_SECOND/PHP_UI_SNAKE_FPS);
						}
					}
				break;
			}
		}
	}

	protected function onDraw(Pen $pen, Size $size, Point $clip, Size $clipSize) {
		$zero = new Point(0, 0);
		$frame = $zero + 40;
		$frameSize = $size - 80;

		$path = new Path();
		$path->addRectangle($zero, $size);
		$path->end();

		$pen->fill($path, 0xf5f5f5ff);

		$stroke = new Stroke();	

		$pen->stroke($path, 0x000000FF, $stroke);

		$path = new Path();
		$path->addRectangle($frame, $frameSize);
		$path->end();

		$pen->stroke($path, 0x000000FF, $stroke);

		$matrix = new Matrix();
		$matrix->translate($frame);

		$pen->transform($matrix);

		if (!$this->food) {
			$this->newFood($frameSize);
		}

		if (!$this->pause && ($run = microtime(true)) - $this->run > 0.1 / $this->level * 2) {
			$this->run = $run;

			$next = clone $this->snake[0];

			switch ($this->direction) {
				case Key::Right: $next->x++; break;
				case Key::Left:  $next->x--; break;
				case Key::Up: $next->y--; break;
				case Key::Down: $next->y++; break;
			}

			if ($next->x < 0 || $next->x >= ($frameSize->width)/10 || 
				$next->y < 0 || $next->y >= ($frameSize->height)/10) {
				$this->newSnake();
				$this->newFood($frameSize);

				foreach ($this->snake as $body) {
					$this->newCell($pen, $body);
				}

				$this->newCell($pen, $this->food);

				$this->pause = true;
				$this->direction = Key::Right;
				$this->score = 0;
				$this->level = 1;
				return;
			}

			if ($this->food == $next) {
				$tail = $next;
				$this->newFood($frameSize);
				$this->score += 10;
				$this->level = ceil($this->score / 100);
			} else {
				$tail = array_pop($this->snake);
				$tail->x = $next->x;
				$tail->y = $next->y;
			}

			array_unshift($this->snake, $tail);
		}

		foreach ($this->snake as $body) {
			$this->newCell($pen, $body);
		}
		
		$this->newCell($pen, $this->food);

		$matrix = new Matrix();
		$matrix->translate($zero - 40);
		$pen->transform($matrix);

		if ($this->pause) {
			$this->drawPause($pen, $size);
		} else $this->drawScore($pen, $size);
	}

	private function newSnake() {
		$this->snake = [];
		for ($i = 0; $i < 5; $i++)
			$this->snake[$i] = new Point($i, 0);
	}

	private function newFood(Size $size) {
		$this->food = new Point(
			floor(mt_rand(40, ($size->width ) - 10) / 10), 
			floor(mt_rand(40, ($size->height) - 10) / 10));
	}

	private function newCell(Pen $pen, Point $point) {
		$path = new Path();
		$path->addRectangle($point * 10, new Size(10, 10));
		$path->end();

		$pen->fill($path, 0x0000FFFF);
		
		$stroke = new Stroke();
		$stroke->setThickness(2);

		$pen->stroke($path, 0x000000FF, $stroke);
	}

	private function drawPause(Pen $pen, Size $size) {
		$layout = new UI\Draw\Text\Layout(sprintf(
			"Press space bar to play ...",
			$this->level,
			$this->score
		), new Font(new Descriptor("arial", 12)), $size->width);

		$layout->setColor(0x000000FF);

		$pen->write(new Point(20, 10), $layout);
	}

	private function drawScore(Pen $pen, Size $size) {
		$layout = new UI\Draw\Text\Layout(sprintf(
			"Level: %d Score: %d",
			$this->level,
			$this->score
		), new Font(new Descriptor("arial", 12)), $size->width);

		$layout->setColor(0x000000FF);
	
		$pen->write(new Point(20, 10), $layout);
	}
	
	private $snake;
	private $food;
	private $direction = Key::Right;
	private $level = 1;
	private $score = 0;
	private $pause = true;
	private $run = 0;
};

$animator = new class ($snake) extends Executor {

	public function __construct(Area $area) {
		$this->area = $area;

		/* construct executor with infinite timeout */
		parent::__construct();
	}

	protected function onExecute() {
		$this->area->redraw();
	}
};

$win->addExecutor($animator);

$snake->setExecutor($animator);

$win->show();

UI\run();

 

 

© 2023 陈建华的个人网站
Theme by Wing
  • {{ item.name }}
  • {{ item.name }}