Пользователь
0,1
рейтинг
5 декабря 2012 в 15:17

Разработка → Создаём сложный лабиринт в фоне веб-страницы из песочницы

CSS*, PHP*

Находясь под приятным впечатлением от кратенькой, но весьма остроумной и, не побоюсь этого слова, «культовой» программы, задался вопросом: «А можно ли сгенерировать подобную структуру в фоновом изображении сайта?». Захотелось создать бесконечный лабиринт, не повторяющийся в любом направлении. Вспомнил, где-то уже встречал метод, который и поможет мне любому стать Дедалом веб-дизайна.



html {background: url('fon.gif') repeat;}



Первое, что приходит на ум: нарисовать кусок лабиринта и просто размножить его. Лабиринт выйдет «ненастоящий» – повторение рисунка при «паркетной укладке» моментально бросится в глаза.

Усложним подход. Можно создать два фоновых изображения разного размера. В каждом из них в шахматном порядке будем чередовать прозрачные области и стенки лабиринта. И затем одно подложим под другое. Разноразмерность и случайная генерация стенок позволит создавать различные комбинации ходов при наложении:

html {
	background-image: url('fon1.gif');
	background-repeat: repeat;
}
		
body {
	background-image: url('fon2.gif');
	background-repeat: repeat;
}	



Уже теплее, но если неудачно выбрать размеры, то это не даст принципиального выигрыша по сравнению с background-repeat одного изображения. Пусть и не сразу, но структура лабиринта снова начнёт повторяться.

Кто ещё помнит математику за 7-й класс, сразу сообразит, что коэффициент «неповторения» будет равен частному от деления наименьшего общего кратного (НОК) на наибольший общий делитель (НОД) от длин сторон изображений. То есть, если одно изображение 3х3 элемента, а второе 6х6 то уже через 2 итерации лабиринт «начинается сначала».

Из этого следует печальный вывод, что бесконечно неповторяющегося узора достичь нельзя. Во всяком случае методами CSS. Можно рисовать Ява-скриптом, но… Вы понимаете…

Если уж бесконечный лабиринт воздвигнуть не судьба, то хотя бы сделаем фон как можно дольше неповторяющимся. Для этого нужно чтобы стороны обоих изображений представляли собой взаимно простые числа. В этом случае НОК равно произведению сторон, НОД — единице. А количество итераций «неповторения» в этом случае уже будет равно НОК, что весьма немало даже при небольших значениях. Если, например, взять 3х3 и 4х4, то только через 12 наложений мы обнаружим в затейливом узоре знакомые очертания. А если взять 13х23 и 17х19, то уникальность замысловатых маршрутов обеспечена для страницы любого размера.

Ободрившись подобными арифметическими выкладками, может показаться, что пора праздновать победу. Но коварная математика ставит ещё одну подножку.


Шахматные поля удобно накладывать друг на друга в случае чётности сторон. Поскольку мы хотим чтобы соотношение сторон у двух картинок были взаимно простым, то как минимум одна из них должна быть «нечётной». Эта нечётность и преподносит неприятный сюрприз в виде нерегулярного наложения не «теми» квадратиками. Уже при втором прогоне пустые квадратики ложатся на пустые, зарисованные — на зарисованные.

Смещение «нечётной» картинки на один ряд вправо/вниз ничего не решает. Покрутив-повертев варианты с различными комбинациями сторон я не придумал ничего лучшего, как делать нижнюю картинку без прозрачных ячеек. В этом случае 100% гарантия, что в фоне не будет зияющих пустот. Но php-скрипт частично работает вхолостую, отрисовывая стенки, которые всё равно будут не видны. Если есть идеи, как обойтись без «ненужной работы» — предлагайте. Помните, соотношение сторон разных изображений должно быть взаимно простым.

Собственно, всё. Демку смотрите здесь. Если дадут вожделенный инвайт на случай хабраэффекта я отключил генерацию каждый раз заново при загрузке страницы нового лабиринта. Для интересующихся прилагаю полный листинг кода с php-скриптом, генерирующий фоновые пинги для логова Минотавра.

index.php
<?php include_once "generate_fon.php"; ?>
<html>
	<title>10 PRINT CHR$(205.5+RND(1));: GOTO 10</title>
	<style type='text/css'>
		html, body {
			padding: 0px; margin: 0px;
			width: 100%; height: 100%;
		}
		
		html {
			background-image: url('fon1.png');
			background-repeat: repeat;
		}
		
		body {
			background-image: url('fon2.png');
			background-repeat: repeat;
		}	

		#outer {
			height: 300px;
			line-height: 300px;
			text-align: center;			
		}
		
		#inner {
			outline: solid #99c medium;
			padding: 50px;			
			background: #336;
			color: #99c;
			font-weight: bold;
			font-size: 16pt;
		}
	</style>
<body>
<div id='outer'><span id='inner'>10 PRINT CHR$(205.5+RND(1));: GOTO 10</span></div>
</body>
</html>



generate_fon.php
<?php 
	function create_background_image ($w, $h, $quadro, $is_chess, $numder) {
		//w, h - кратность ширины/высоты фонового изображения
		//$quadro - длина стороны одного квадратного элемента "стены лабиринта", px
		//$is_chess - true/false, в шахматном ли порядке заполнять фон
		//$numder - номер фонового файла для его названия (их нужно 2)
		
		$img = imageCreate($w*$quadro, $h*$quadro);//Заготовка изображения, размеры
		$color_transparent = imagecolorallocate ($img, 0, 0, 0);//Чёрный цвет будет выполнять роль прозрачного
		$color_background =  imagecolorallocate ($img, 51, 51, 102);//Цвет фона		
		$color_line =  imagecolorallocate ($img, 153, 153, 204);//Цвет линий
		imagesetthickness($img, 4);//Толщина "стенок"
		
		//Рисуем "стенки лабиринта"
		for ($i = 0; $i < $w; $i++) {
			for ($j = 0; $j < $h; $j++) {				
				$sparseness = mt_rand(0, 8);//"Разреженность" лабиринта. 1 шанс из 9-ти что не станем рисовать "стенку"
				if ($sparseness) {
					//Не забываем про "шахматный порядок"
					if (!($is_chess && ($i % 2 ==  $j % 2))) {
						//Заливаем очередной квадратик фоновым цветом
						imagefilledrectangle (
							$img, 
							$i * $quadro, $j * $quadro, //Верхний левый угол
							($i + 1) * $quadro, ($j + 1) * $quadro, //Нижний правый угол
							$color_background //Цвет фона
						);					
						//"Кидаем монетку", чтобы решить направление "стенки"
						$direction = mt_rand(0, 1);
						//Рисуем линию (случайно выбираем: \ или /)
						if ($direction) {
							imageline (
								$img, 
								$i * $quadro, $j * $quadro, //Из левого верхнего угла...
								($i + 1) * $quadro, ($j + 1) * $quadro, //... в правый нижний угол
								$color_line //Цвет линий
							);				
						} else {
							imageline (
								$img, 
								($i + 1) * $quadro, $j * $quadro, //Из правого верхнего угла...
								$i * $quadro, ($j + 1) * $quadro, //... в левый нижний угол
								$color_line //Цвет линий
							);						
						}
					} else {
						//Этот квадратик должен быть прозрачным					
						imagefilledrectangle (
							$img, 
							$i * $quadro, $j * $quadro, //Верхний левый угол
							($i + 1) * $quadro, ($j + 1) * $quadro, //Нижний правый угол
							$color_transparent //Цвет фона - прозрачный
						);				
					}
				} else {
					//А вот и "разреженность" лабиринта проявилась
					//"Стенку" не рисуем, но фон заливаем
					imagefilledrectangle (
						$img, 
						$i * $quadro, $j * $quadro, //Верхний левый угол
						($i + 1) * $quadro, ($j + 1) * $quadro, //Нижний правый угол
						$color_background //Цвет фона
					);					
				}
			}
		}
		imagecolortransparent ($img, $color_transparent);//Прозрачный цвет фона
		imagepng ($img, "fon$numder.png");//Сохраняем фон в файл
		imageDestroy($img);//Для очистки памяти "уничтожаем" переменную-картинку		
	}
	
	$w1 = 17; $h1 = 19;//Кратность сторон первого изображения
	$w2 = 23; $h2 = 13;//Кратность сторон второго изображения
	$wall = 30; //Длина стороны одного квадратного элемента "стены лабиринта", px

	$img1 = create_background_image($w1, $h1, $wall, false, 1);//Создаём нижнее фоновое изображение
	$img2 = create_background_image($w2, $h2, $wall, true, 2);//Создаём верхнее фоновое изображение 
?>



В скрипте я «проредил» генерацию стенок. В оригинальном алгоритме получается слишком много «слепых ходов», в которые невозможно попасть. Пробовал также развернуть на 45 градусов к зрителю, но вертикальные и горизонтальные линии стандартными средствами php рисуются как-то неказисто. В общем, для желающих внести в лабиринтостроительство инновации и улучшения – работы непочатый край.
Валерий Макаров @valemak
карма
60,2
рейтинг 0,1
Реклама помогает поддерживать и развивать наши сервисы

Подробнее
Реклама

Самое читаемое Разработка

Комментарии (14)

  • +6
    Посмотрел на повёрнутый на 45 градусов лабиринт и первая же мысль: «Здесь есть несущие колонны, но большинство стен можно снести. Получился бы не плохой оупен-спейс».
    • 0
      Ну, да. А если снести всё, то получится футбольное поле или место для бассейна.
  • +4
    Не о лабиринте, но суть та же, генерация фона, который долго не повторяется:
    Принцип цикады и почему он важен для веб-дизайнеров
    • +3
      Автор в самом начале упоминает эту статью)
      • 0
        Хм, действительно, тогда непонятно почему у автора возникли проблемы с наложение пустого на пустое и т.п.
        В исходной статье момент был обсосан, это и ввело в заблуждение.
        • 0
          Топик про принцип цикады прочитал «по диагонали». Запомнил сам метод, но в подробности статьи не вникал. Что поделать — информации очень много, приходится усваивать именно так.

          Лабиринт рисовался «just for fun», поэтому я не стал лишать себя удовольствия на досуге изобрести трёхколёсный велосипед.
  • 0
    Я не понимаю, зачем усложнять?
    В любом случае имеет смысл использовать или принцип цикады, тогда есть несколько фоновых изображений, которые объединяются уже в клиенте, или же просто сгенерировать лабиринт заранее с помощью великого рандома и вставлять его на страницу целиком, тогда будет одно большое фоновое изображение во весь экран.

    Тут же получается что-то среднее. И преимуществ перед первыми двумя способами как-то не очень заметно.
    • 0
      Да здесь, в общем-то, первый способ.

      Рисовать громадный фон для сайта в одном файле просто нецелесообразно. Во-первых, не обрадуются пользователи у которых медленный/лимитный Интернет. Во-вторых, придётся учитывать крупные экраны (1920x1080 и иже с ними), но в этом случае будет генерироваться большая картинка которая заведомо будет видна частично на подавляющем большинстве мониторов.
  • +5
    Сделал за 10 минут с генерацией фона через канвас:

    bolknote.ru/files/bg-slash-maze.html
    • +1
      … а теперь связный %)
    • +1
      Сначала подумал что «а в Опере не работает!», потом присмотрелся повнимательнее :)

      Глядя на Ваш варинат у меня возникла сырая идея браузерной игры. На странице генерируется лабиринт и нужно с помощью мышки переместиться из пункта A в пункт B. С помощью тех же Ява-скриптов отслеживать траекторию курсора.

      • 0
        Бесконечный пакмэн!
  • +1
    Я бы-таки в фоне несложным javascript-ом генерил случайный фон, пример — maze.ragneta.com/. Кому как, а мне накладывать две текстуры и думать об кратностях их сторон сложнее, чем уложить в голове одну простую рекурсию.
    • +1
      На данный момент уже предложено несколько разных решений на Java-Script (включая Ваше). Так что написание сего топика было не напрасно.

Только зарегистрированные пользователи могут оставлять комментарии. Войдите, пожалуйста.