Dziś wieczorem miałem chwilę wolnego czasu na zabawę z kodem na napisanie jakiejś bardziej “technicznej” notki. Ten mój freecoding doprowadził mnie do pomysłu na stworzenie bardzo prostej gry z wykorzystaniem HTML, CSS oraz jQuery. Tak, nie czysty JS, nie dedykowany pod to Phaser, ani reszta frameworków w stylu React, Angular, Vue czy Backbone. Bo gra ma być prosta jak cep, a taki jest jQuery.

Gra oparta jest o kilka klocków- jedne to potwory do ustrzelenia, inne to ozdobniki, a inne to przeszkody za którymi potwory się chowają. Ustrzelenie stwora dodaje nam punkt, punkty tracimy jeśli próbujemy oszukać klikając kilkukrotnie na potwory- 2 ostrzeżenia i za trzecim dostajemy karę w postaci -10 punktów.
Jeśli punkty zejdą poniżej 0 to gra się kończy i zaczynamy od nowa.

I nie - nie robiłem odejmowania punktów za niecelny strzał, ani ilości nabojów, ani menu- to jest gra stworzona na szybkości, ale nie jest powiedziane że tego nie dorobię- w końcu powinno być więcej opcji przegranie ;)
Sama gra nie jest skomplikowana- większość kodu to tak naprawde... CSS. Tak, dokładnie- style.

Sam “świat” gry to tak naprawdę trochę div’ów (a jak, canvas to to nie jest), parę grafik robionych na szybko w MS Paint oraz z FlatIcon.com. To wszystko na sztywno wypozycjonowane na stronie.

Tak wygląda HTML


	<div class="wrapper">
		<div class="game-window">
			<div class="game-bg">
				<div class="grass grass-1"></div>
				<div class="grass grass-2"></div>
				<div class="grass grass-3"></div>
				<div class="grass grass-4"></div>
				<div class="grass grass-5"></div>
			</div>
			<div class="element-1 with-monster tree-element">
				<div class="tree nature"></div>
				<div class="monster monster-1">
					<img src="/img/monster.png" alt="monster">
				</div>
			</div>
			<div class="element-2 tree-element">
				<div class="tree nature"></div>
			</div>
			<div class="element-3 with-monster tree-element">
				<div class="tree nature"></div>
				<div class="monster monster-2">
					<img src="/img/monster.png" alt="monster">
				</div>
			</div>
			<div class="element-4 bush-element">
				<div class="bush nature"></div>
			</div>
			<div class="element-5 with-monster bush-element">
				<div class="bush nature"></div>
				<div class="monster monster-3">
					<img src="/img/monster.png" alt="monster">
				</div>
			</div>
			<div class="element-6 bush-element">
				<div class="bush nature"></div>
			</div>
			<div class="element-7 with-monster stone-element">
				<div class="stone nature"></div>
				<div class="monster monster-4">
					<img src="/img/monster.png" alt="monster">
				</div>
			</div>
			<div class="element-8 stone-element">
				<div class="stone nature"></div>
			</div>
			<div class="element-9 with-monster stone-element">
				<div class="stone nature"></div>
				<div class="monster monster-5">
					<img src="/img/monster.png" alt="monster">
				</div>
			</div>
			<div class="element-10 with-monster free-element">
				<div class="monster monster-6">
					<img src="/img/monster.png" alt="monster">
				</div>
			</div>
		</div>
		<div class="score">Twoje punkty: <span class="points">0</span></div>
	</div>

a tak CSS:


* {
	padding: 0px;
	margin: 0px;
	outline: none;
}

body {
	font-family: 'Arial', sans-serif;
	font-size: 20px;
	line-height: 25px;
	color: #00293c;
	width: 100%;
	height: 100%;
	background: #ffffff;
	position: relative;
	word-wrap: break-word;
	text-align: justify;
	-webkit-font-smoothing: antialiased!important;
	font-smoothing: antialiased!important;
	-moz-osx-font-smoothing: grayscale!important;
	font-smooth: always!important;
}

a,
.monster,
.aim * {
	-webkit-transition: all 0.7s ease-out;
	-moz-transition: all 0.7s ease-out;
	-o-transition: all 0.7s ease-out;
	transition: all 0.7s ease-out;
}

.author {
	padding: 5px 5%;
	z-index: 9;
	position: fixed;
	bottom: 0px;
	width: 90%;
	background: #eeeeee;
	font-size: 13px;
	line-height: 15px;
	border-top: 1px solid #cccccc;
	height: 33px;
}

.author p {
	margin: 0px;
}

.wrapper {
	width: 800px;
	height: 600px;
	position: fixed;
	top: 0;
	left: 50%;
	margin: 0 0 0 -400px;
	overflow: hidden;
}

.game-window {
	position: relative;
	cursor: url('../img/target.png'), auto;
}

.game-window:before {
	display: none;
	content: '';
	position: absolute;
	top: 0;
	left: 0;
	width: 100%;
	height: 100%;
	background: rgba(255, 255, 255, 0.5);
	z-index: 999999;
}

.game-window.hit:before {
	display: block;
}

.game-window,
.game-bg {
	width: 100%;
	height: 100%;
}

.game-bg {
	background: #b5e61d;
}

.grass,
[class^='element'],
.monster,
.score,
.monster.hit:before,
.monster.hit:after {
	position: absolute
}

.grass {
	background: url('../img/grass.png') center center no-repeat;
	width: 30px;
	height: 30px
}

.grass-1 {
	top: 20px;
	left: 475px;
}

.grass-2 {
	top: 350px;
	left: 115px;
}

.grass-3 {
	top: 115px;
	left: 190px;
}

.grass-4 {
	bottom: 67px;
	left: 240px;
}

.grass-5 {
	bottom: 120px;
	right: 275px;
}

[class^='element'] {
	z-index: 99;
}

[class~='with-monster'] {
	z-index: 999;
}

.element-1 {
	top: 150px;
	right: 20px;
}

.element-2 {
	top: 50px;
	left: 50px;
}

.element-3 {
	top: 250px;
	left: 320px;
}

.element-4 {
	top: 90px;
	left: 250px;
}

.element-5 {
	bottom: 25px;
	left: 10px;
}

.element-6 {
	bottom: 50px;
	right: 55px;
}

.element-7 {
	top: 80px;
	left: 540px;
}

.element-8 {
	bottom: 150px;
	left: 190px;
}

.element-9 {
	top: 275px;
	right: 210px;
}

.element-10 {
	bottom: 150px;
	left: -100px;
}

.tree {
	background: #804500;
	width: 110px;
	height: 230px;
}

.bush {
	background: #115800;
	width: 175px;
	height: 120px;
}

.stone {
	background: #5a5a5a;
	width: 100px;
	height: 110px;
}

.monster {
	line-height: 0;
	z-index: 1;
	top: 0;
	left: 50%;
	margin-left: -50px;
	cursor: url('../img/target_pointed.png'), auto;
	overflow: hidden
}

.monster.active {
	top: -100px
}

.monster-2 {
	top: 0;
	left: 0;
	-webkit-transform: rotate(-90deg);
	-moz-transform: rotate(-90deg);
	-ms-transform: rotate(-90deg);
	-o-transform: rotate(-90deg);
	transform: rotate(-90deg);
	margin-left: 0;
}

.monster-2.active {
	top: 0;
	left: -100px;
}

.monster-6 {
	top: 40px;
	left: 0;
	margin-left: 0;
}

.monster-6.active {
	top: 40px;
	left: 375px;
	margin-left: 0;
}

.monster.hit:before,
.monster.hit:after {
	display: block;
	content: '';
	width: 2px;
	height: 200px;
	background: #f00;
	top: -20px;
	left: 50%;
	margin-top: -30px
}

.monster.hit:before {
	-webkit-transform: rotate(-45deg);
	-moz-transform: rotate(-45deg);
	-ms-transform: rotate(-45deg);
	-o-transform: rotate(-45deg);
	transform: rotate(-45deg);
}

.monster.hit:after {
	-webkit-transform: rotate(45deg);
	-moz-transform: rotate(45deg);
	-ms-transform: rotate(45deg);
	-o-transform: rotate(45deg);
	transform: rotate(45deg);
}

.score {
	bottom: 5px;
	right: 15px;
	z-index: 5;
	color: #e80000;
	font-size: 18px;
}

.points {
	font-size: 1.2em;
	line-height: 1.5em;
	font-weight: bold
}

.nature {
	position: relative;
	z-index: 9
}

Skrypt jQuery co określony czas (500ms dokładniej) losuje liczbę z przedziału 1-6 (bo tyle jest stworów) i na podstawie tej liczby dany potwór dostaje klasę active, która powoduje zmianę jego położenia.
Po strzale jeśli trafimy stworka to dostajemy punkt. Żeby było ładnie i płynnie wszystko dodana została regułka CSS3 o nazwie transition, zapewniająca płynne przejście.

Kod Javascript przedstawia się w calości tak:

$(document).ready(function () {
	var points = 0;
	var alerts = 0;
	setInterval(
		function () {
			var el = Math.floor((Math.random() * 6) + 1);
			$('.monster-' + el).removeClass('hit').toggleClass('active');
		}, 500
	);
	$('.monster').click(function(){
		if ($(this).hasClass('active')){
			$(this).removeClass('active').addClass('hit');
			points++;
			$('.points').html(points);
			$('.game-window').addClass('hit');
			setTimeout(
				function(){
					$('.game-window').removeClass('hit');
				},50
			);
		}else{
			alert('Nie oszukuj- klikanie wiele razy nic nie da, bo zlicza tylko pojedyncze trafienie');
			alerts++;
			if(alerts > 2){
				alert('3 razy wykryta proba oszustwa... minus 10 punktów');
				alerts = 0;
				points -= 10;
				if (points < 0){
					alert('Game over');
					location.reload();
				}else{
					$('.points').html(points);
				}
			}
		}
	});
});

Gdybym chciał stworzyć grę, w której za każdym razem generowany jest inny układ, inne opcje- z jQuery mogłoby się to średnio udać.

Podcza spisania tego tekstu pomyślałem- cholera, może faktycznie warto troszkę to ulepszyć? Jakieś dźwięki, różne stwory o różnej prędkości i wartości punktowej, kule, ekran powitalny...

Niemniej to już zostawiam na inny czas, kiedy będę miał więcej wolnego czasu na eksperymenty. Podobnie jak czyszczenie CSS i ew. lekka przeróbka HTML, bo wiem, że idealnie nie jest- całość zajęla godzinę wraz z przemyśleniami co i jak.

Porady, pytania, krytyka i propozycje mile widziane :)

Oczywiście standardowo linki:

GRA NA POLIGONIE
GRA I KOD NA CODEPEN