HTML5 wraz z nowymi elementami pomagającymi w budowie stron internetowych, przyniósł też duży krok w stronę animacji i gier przeglądarkowych, pozwalając na tworzenie tego, do czego do tej pory używano Flasha, aby móc korzystać z aplikacji bez jej pobierania.
My, w dzisiejszym temacie, skupimy się na aspekcie tworzenia gier z wykorzystaniem elementu canvas, na którym, za pomocą JavaScriptu, możemy rysować obraz – w naszym przypadku, klatki gry. Aby nasza gra cieszyła oko i była bardziej żywa, dostarczyć musimy jej więcej ruchu, animacji, grafiki. Jednym z elementów, które są takim "szczegółem" wpływającym na wygląd naszej gry, są efekty cząsteczkowe. Tym razem pokażę sposób, w jaki mnie udało się to wykorzystać w swoich grach.
Pomijając podstawowe czynności (takie jak ustawienia naszego canvasa, czy sam sposób renderowania klatek) zabiorę się za wyjaśnienie głównej funkcji, która sprawia, że każda z cząsteczek się rusza. Gdy ją wywołamy za pierwszym razem, utworzy nam cząsteczki. Potem będziemy uruchamiać instrukcję z każdą klatką, aby zmienić ich położenie, co da nam płynną animację.
function particleEffect(particleSet, xStart = false, yStart = false, particleImage = false, particleNumber=50, lifetimeValue=1){
if(particleSet === undefined || particleImage != false){
particleSet = new Array();
for(var i=0;i<=particleNumber;i++){
particleSet[i] = {
x: xStart,
y: yStart,
counter: -40,
lifetime: lifetimeValue,
xChange: Math.random() * (1 - (-1)) + (-1),
yChange: Math.random() * (2 - 1) + 1,
img: particleImage,
};
}
}
else{
particleSet.forEach(function(thisEle){
thisEle.x += thisEle.xChange;
thisEle.counter += thisEle.yChange;
thisEle.y = thisEle.y + thisEle.counter/10;
if(thisEle.lifetime > 0){
thisEle.lifetime -= 0.01;
ctx.globalAlpha = Math.abs(thisEle.lifetime);
}else{
ctx.globalAlpha = 0;
}
var angle = (frameElapsed % 360) *3;
drawRotate(thisEle.img, thisEle.x, thisEle.y, angle);
ctx.globalAlpha = 1;
});
}
return particleSet;
}
Funkcja zawiera kilka elementów, które były potrzebne w konkretnym efekcie, którego używałem w grach. Jak można zauważyć, sporo argumentów tej funkcji jest już przypisana domyślnie. Te argumenty prześlemy tylko za pierwszym razem, żeby wiadomo było, w którym miejscu ma rozpocząć efekt, jakiego obrazka użyć, oraz dodatkowo ile ma być cząsteczek i jak długo mają się pokazywać na ekranie.
Funkcja zwraca tablicę z wszystkimi cząsteczkami i ich położeniem, które się zmienia. W ten sposób przy każdej klatce animacji możemy nadpisywać ją nową tablicą ze zmienionymi wartościami, rysując jednocześnie każdą z cząsteczek na ekranie.
Napisałem też krótką funkcję drawRotate(), która rysuje obrazek obrócony o konkretny stopień:
function drawRotate(targetImage, x, y, angle){
ctx.save();
ctx.translate(x+targetImage.width/2, y+targetImage.height/2);
ctx.rotate(angle*Math.PI/180);
ctx.drawImage(targetImage, -(targetImage.width/2), -(targetImage.height/2));
ctx.restore();
};
Kiedy mamy już te dwie funkcje, możemy utworzyć tablicę ze wszystkimi efektami cząsteczkowymi, których obecnie używamy:
var effectsArray = new Array();
A w funkcji renderującej całość zamieszczamy pętlę, która zajmie się każdym z efektów.
for(var key in effectsArray){
effectsArray[key] = particleEffect(effectsArray[key]);
}
Ostatni element, którego nam brakuje, to rozpoczęcie efektu. W dowolnym miejscu w naszym kodzie (np. po wykryciu kolizji między graczem a bonusem do zebrania) zapiszemy w którymś z elementów tablicy funkcję, która rozpisze dla nas cząsteczki. Przykładowo wygląda to tak:
effectsArray[0] = particleEffect(effectsArray[0], 75, 75, particle, 50);
Kiedy podaliśmy funkcji wszystkie potrzebne informacje, otrzymamy gotową tablicę, która w każdej kolejnej klatce będzie na nowo rysowana wraz z postępującymi zmianami.
Poniżej możecie znaleźć cały kod w działaniu. Poza wyżej opisanymi elementami, znajdują się tam jeszcze części odpowiadające za rysowanie klatek. W poniższym przykładzie efekt cząsteczkowy resetuje się co trzy sekundy.
Jeśli chodzi o wydajność tego skryptu, to w moim przypadku przy około 1000 cząsteczek było widać brak płynności. Może macie jakieś inne sposoby na wykonanie podobnego efektu?