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.
$(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: