İçeriğe geç

Callbacks

Bu bölüm veri toplama, efekt fonksiyonları oluşturma ve söz konusu fonksiyonları tetiklemek için Metre sınıfını kullanmayı ele almaktadır. Animasyonları veya efekt işleyicilerimizin özelliklerini açıklamak üzerine vurgu yapılmayacaktır; bu nedenle tazelemek istiyorsanız Eğitimler veya API Referansı bölümlerimize göz atmanızı öneririm.

Baştan sona bir animasyonu etkili şekilde tetikleme süreci:

  1. Ham metre verilerini Metre sınıfınızın bir örneğine eklemek için güncelleme fonksiyonunun içinde Meter.setValue(value) yöntemini kullanın.
  2. Metre kendini kararlı olarak değerlendirirse, ikinci parametre olarak aktarılan geri çağırma fonksiyonunu etkinleştirir.
  3. Bu geri çağırma fonksiyonu, Metre’nin durumunu değerlendirmek için koşullu mantık içerir. Durum doğruysa, efektiniz efektler dizisine (effects.push(new yourEffect())) veya durum işleyicisine (steMgr.Push(new yourEffect())) eklenmelidir.
  4. Güncelleme fonksiyonunun her çalışması, tam efektler dizisini ve durum işleyicisindeki en son öğeyi değerlendirir. Bu aşamada efektler durum değişkenlerine göre çizilir.
  5. Çizildikten sonra her efekt, ömrünü kontrol etmeye katkıda bulunur. Efektler dizisindeki efektler, sınıflarının örneğine ayarlanmış bir ömür değişkenini basitçe yineler. Ömür 0 veya daha az olduğunda, efekt efektler dizisinin değerlendirmesi sırasında bir splice ile kolayca kaldırılır. Durum işleyicisindeki efektler, durum işleyicisi bir yığın olarak çalıştığından ve bu efektler için koşullu mantık genellikle daha karmaşık olduğundan gerektiğinde kendilerini kaldırabilir.

Bu metre tipik bir sağlık çubuğunu arıyor ve eşleşen rengin yüzdesini 0 ile 1 arasında bir sayı olarak döndürecektir. HSL aralığı bu durumda, çubuğun rengindeki bir degradeyi hesaba katmak için oldukça geniştir. Bu bir sorun ortaya çıkarır — sağlık çubuğu küçüldükçe, renk kontrolünü geçen ve metremizi etkileyen yeşil öğeleri içerebilecek şeffaf bir arka planı açığa çıkarır.

<head>
<meta meter="health" tags="example" x= ".05" y=".9" width=".189" h="70-140" s="40-100" l="40-100" type="linear"/>
</head>

Burada, bir “hasar aldı” efektini etkinleştirmek için healthM adlı Metre sınıfının bir örneğini oluşturdum.

<script>
// Metre örneği
var healthM = new Meter(25, healthHelper);
function update () => {
healthM.setValue(health);
window.requestAnimationFrame(update);
}
function healthHelper () => {
// "Hasar Aldı" efekti sıradaki
}
// . . .
</script>

Yukarıdaki kararsızlık sorununu hesaba katmak için oldukça yüksek bir kararlılık gereksinimi (25) verdim. Ekli geri çağırma fonksiyonu yalnızca metre değeri 25 güncellemede kararlıysa etkinleştirilecektir; bu, oyuncu durağan olsa bile gerçek sağlık çubuğu olmayan verilerin çoğunu dışarıda bırakacaktır. Bunun tek dezavantajı, bu senaryoda her zaman sınırlayıcı faktör olacak büyüyen efekt gecikmesidir. Bu belirli sağlık çubuğu için bunu en az birkaç şekilde dengeleyebiliriz:

  • Vurulduğunda, yeşil çubuğun bir kısmı yeni yeşil sağlık seviyesine küçülmeden önce anında kırmızıya dönüşür. Sağlık metresindeki aynı noktada kırmızı arayan bir healthRed metresi eklersek daha hızlı tetikleme için değerlerini birleştirebiliriz. Koşullu if ( healthM.decreased && healthRed.increased ) bu durumda iyi doğruluk sağlamalıdır.
  • Ayrıca aynı renk ayarlarına sahip birkaç lineer metrenin okumalarını birleştirebiliriz. Şu anda sağlık çubuğunun ortasında bir lineer metre ayarlanmış. İkisi daha eklesem, biri orijinalin biraz üstünde ve biri altında ama yine de sağlık çubuğunun içinde, üç metre değerinin yakınlığı ek kararlılık kontrolü olarak kullanılabilir. Üç metrenin tamamının Meter.decreased okuması ve birbirinin değerleriyle küçük bir aralık içinde olması gerekir.

Bu geri çağırma fonksiyonu yalnızca metre, değer dizisindeki tüm öğelerin eşit olduğunu doğruladıktan sonra çalışır. Bu, metre değerlerinin kararlı veriye dayandığını garantiler. Geri çağırmanın içinde, efektin oynatılıp oynatılmayacağını belirlemek için koşullu bir ifade kullanacağım.

let healthPrev = 0;
function healthHelper () => {
if (healthM.decreased && healthM.value != healthPrev){
effects.push(new healthEffect());
healthPrev = healthM.value;
}
}

Koşullu ifade iki doğru koşul gerektirir: metrede kararlı bir azalma ve mevcut metre değeri ile önceden kaydedilmiş bir değer arasındaki uyumsuzluk. Bu ikinci koşulu bir hatadan kaçınmak için ekledim. Yalnızca azalmayı kontrol etseydik, efekt tekrar tekrar oynatılırdı. Meter.decreased, metre kararlılaştıktan sonra ayarlandığından, metre değişmediği sürece değeri aynı kalır. Bu sayede efekt yalnızca son kararlı değerden farklı bir azalma varsa oynatılacaktır.

Size iki efekt oluşturacağım: biri efektler dizisi için, diğeri durum işleyicisi için.

Bu ilk kod bloğu, efektler dizisi için tasarlanmış bir efekt fonksiyonunu tanımlar. Birden fazla küçük efektin eş zamanlı çalışmasına izin vermek için hızlı ve hafif olacak şekilde tasarlanmıştır. Sistem yükünü azaltmak için efekti yeniden kullanım için parametreler aracılığıyla kolayca özelleştirilebilir yaptım.

function healthEffect(color, direction, speed){
// Efektin ömrü çizim fonksiyonunda yinelenmelidir.
this.lifetime = 200;
this.speed = speed;
this.direction = direction;
this.color = color;
this.draw = () => {
// Parametre ile dolguyu ayarla
ctx.fillStyle = `hsl(${this.color}, 100%, 50%)`;
// Yönü kontrol et
if (this.direction == "up"){
ctx.fillRect(0, this.lifetime, width, 30);
} else {
ctx.fillRect(0, height - this.lifetime, width, 30);
}
// Ömür her güncellemede 5 azaldıkça dikdörtgen buna göre hareket eder.
this.lifetime -= speed;
}
}
function update() {
// Arka plan rengi
ctx.fillStyle = "black";
ctx.fillRect(0, 0, 320, 200);
// Dizideki tüm efektleri çiz ve bittiklerinde kaldır.
effects.forEach((ele, i) => {
ele.draw();
if (ele.lifetime == 0){
effects.splice(i, 1);
}
})
window.requestAnimationFrame(update);
}
function healthHelper () => {
if (healthM.decreased && healthM.value != healthPrev){
// Renk ve yön parametrelerini doğru şekilde ayarladığınızdan emin olun.
effects.push(new healthEffect(1, "down", 5));
healthPrev = healthM.value;
}
}

İşte çalışırken nasıl görünmesi gerektiği:

Fonksiyonun düzenlenebilir tasarımı sayesinde kolayca bir iyileşme efekti de oluşturabiliriz! Geri çağırma fonksiyonuna ek bir koşul ekleyin.

function healthHelper () => {
if (healthM.decreased && healthM.value != healthPrev){
// Hasar efekti
effects.push(new healthEffect(1, "down", 5));
healthPrev = healthM.value;
}
if (healthM.increased && healthM.value != healthPrev){
// İyileşme efekti
effects.push(new healthEffect(120, "up", 5));
healthPrev = healthM.value;
}
}

Bu efektin basitliği, bir LightScript geliştiricisi için en büyük avantajıdır. Her biri yüzlerce satır kod gerektiren özel tasarımcı efektleri oluşturmak yerine, belirli tetikleyiciler için yapı taşları olarak hizmet eden küçük, yeniden kullanılabilir efektler oluşturun. Sonraki üç efekt, az önce oluşturduğumuz küçük efekte dayalı varyasyonlardır. Denemekten çekinmeyin ve neler başarabileceğinizi görün!

Ben durum işleyicisini kişisel olarak çok spesifik durum mantığına sahip büyük, karmaşık efektler için rezerve ediyorum; özellikle efektin diğerleri üzerinde öncelikli olmasını istediğinizde. Durum işleyicisinin bir yığın gibi çalıştığını ve yalnızca en üstteki efekti çalıştırdığını unutmayın; her durum efekti bittiğinde kendini kaldırmaktan sorumludur. Bu örnek, arama yapıp yapmadığınıza bağlı olarak uyum sağlayan bir eşleşme lobisi animasyonunu göstermektedir. Ayrıca yardımcı bir efektle birlikte kendi küçük efektler dizisini de içermektedir. Burada kullanılan metreler, yalnızca eşleşme lobisinde etkinleşen inLobby ve yellowPlayButton’dır. yellowPlayButton, arama yapıp yapmadığınıza bağlı olarak renk değiştirir.

function lobbyAnimation(){
// Örnek değişkenler
this.start = new Date().getTime();
this.elapsed = 0;
this.radius = 0;
this.searching = 1000;
// Örnek efektler dizisi
this.effects = [];
// Process, bir durum efektinin kendini kaldırmasına olanak tanıyan yöntemdir
this.Process = function () {
// Lobi menüsü ve oynat düğmesi kaybolursa efekti kaldır
if (inLobby.decreased && yellowPlayButton.decreased) {
stateMgr.Pop();
// Durum işleyicisinde yinelenen lobi efektlerini önlemek için küresel boolean
lobbyAnim = false;
}
// Oynat düğmesi varsa, rengine göre this.searching'i düzenle
yellowPlayButton.value == 1 ? this.searching = 1000 : this.searching = 100;
// Efekte bağlı Draw fonksiyonunu çağır
this.Draw();
};
this.Draw = function(){
// Önemli değişkenleri yinele
this.elapsed = new Date().getTime() - this.start;
this.radius = 50 + (Math.sin(this.elapsed/this.searching)*10);
// Diğer efektlerin üzerine yazmak için arka planı doldur
ctx.fillStyle = 'black';
ctx.fillRect(0, 0, 320, 200);
// Ana yayı çiz
ctx.beginPath();
ctx.strokeStyle = `hsl(120, 100%, 50%)`;
ctx.lineWidth = 20;
ctx.arc(160, 100, this.radius, 0, 2 * Math.PI);
ctx.stroke();
// Ana yay doğru boyuttaysa yardımcı efektleri yardımcı dizisine ekle
if(this.radius > 59 && this.elapsed % 2 == 0){
this.effects.push(new lobbyHelper(160, 100, 69));
}
// Yardımcı efektleri oynat ve gerekirse kaldır
this.effects.forEach((e, i) => {
ctx.lineWidth = 1;
e.draw();
if (e.radius > 360) {
this.effects.splice(i, 1);
}
});
}
}
function lobbyHelper(x, y, radius){
this.x = x;
this.y = y;
this.radius = radius;
this.draw = function(){
// Küresel şeffaflık değerini ayarla. 1 opak, 0 görünmez.
ctx.globalAlpha = 1 - this.radius/360;
ctx.beginPath();
ctx.strokeStyle = `hsl(120, 100%, ${50 + (this.radius/7)}%)`;
ctx.arc(this.x, this.y, this.radius, 0, 2 * Math.PI);
ctx.stroke();
// Diğer efektlerinizin opak olması için küresel şeffaflığı 1'e geri ayarla
ctx.globalAlpha = 1;
this.radius++;
}
}
// Durum işleyicisini bildir (Snippet'lerde bulunur)
var stateMgr = new StateHandler();
// Yinelenen lobi animasyonlarını önlemek için küresel boolean
var lobbyAnim = false;
function update(){
// Animasyonu çizmek için Process fonksiyonunu çalıştır
stateMgr.Process();
}
function lobbyAnimationPlay(){
if(inLobby.value == 1 && yellowPlayButton.value == 1 && !lobbyAnim){
stateMgr.Push(new lobbyAnimation());
lobbyAnim = true;
}
};

Arama Yapılmıyor:

Aranıyor:

Callbacks eğitimimizin yanı sıra resmi SignalRGB geliştirici incelememiz de burada tamamlanıyor. Kılavuzumuza yapılacak bir sonraki ve son ekleme olan Lightscript Bakımı için takipte kalın!