Javascript'te dallanma diyaloğunu nasıl uygularım?


11

JavaScript'te çok temel bir görsel roman türü oyun yapıyorum . Yeni başlayan biriyim, bu yüzden bunu sadece eğlence ve öğrenme için yapıyorum ve kötü planlama nedeniyle diyalogdaki bir şubeye gittiğinizde biraz sorunla karşılaştım.

Şu anda oyunun senaryosunu bir dizi değişkeninde tutuyorum ve her sahneyi "# ~" gibi bir etiketle daha küçük dizilere ayırıyorum, böylece oyun senaryosu şöyle görünüyor:

var script = "Hello World!#~How are you today?"
var gameText = script.split("#~");
//gameText[0]= Hello World!

Bu doğrusal şeyler için harika çalışıyor, ancak diyalog ağacındaki bir dalı nasıl ele almalıyım? Bu yöntem çok karmaşık görünüyor, çünkü her yolun tam olarak ne kadar uzun olduğunu bilmek zorundayım ve daha sonra herhangi bir şeyi değiştirmem gerekirse baş ağrısı olurdu.

Bunu nasıl daha basit bir şekilde yapabilirim? Oyunun Web Run Time ile çalışmasını istediğim için vanilya JavaScript'e bağlı kalmaya çalışıyorum.


Bu video size bazı fikirler verebilir: youtube.com/watch?v=XM2t5H7kY6Y
JCM

Son zamanlarda Düğüm kullanarak bunun için bir şey geliştirmek zorunda kaldı ve çok temel bir metin dosyası yapısını tercih etti. Ortaya çıkan kodu ve metin biçimini şu adreste görebilirsiniz: github.com/scottbw/dialoguejs Kod GPL'dir , kullanmaktan çekinmeyin. Düğüm olmayan bir JS oyununa uyum sağlamak zor olmayacağından eminim - "fs.load ()" bölümünü Ajax ile değiştirin.
Scott Wilson

Check out Ink , tarafından geliştirilen dallanma hikaye kodlama dili, Inkle Studio . Çeşitli programlı Mürekkep entegrasyonları (Java, Javascript, C #) ve birçok üçüncü taraf kaynağı vardır . Mürekkep birçok ticari oyunda da kullanılmıştır. Son olarak, dallanma diyaloglarınızı kontrol edip 'çalabilen' bir masaüstü editörü Inky var.
Büyük Zengin

Yanıtlar:


8

Philipp'in cevabı zaten doğru yönü gösteriyor. Sadece veri yapısının gereksiz yere ayrıntılı olduğunu düşünüyorum. Daha kısa metinlerin yazılması ve okunması daha kolay olacaktır.

Kısa metinler algoritmayı biraz daha karmaşık hale getirse bile, bu yapmaya değer, çünkü algoritmayı sadece bir kez yazıyorsunuz, ancak zamanınızın çoğu hikayeyi yazmaya ve sürdürmeye harcanacak. Bu nedenle, en çok zaman harcadığınız kısmı kolaylaştırmak için optimize edin.

var story = [
  { m: "Hi!" },
  { m: "This is my new game." },
  { question: "Do you like it?", answers: [
    { m: "yes", next: "like_yes" },
    { m: "no", next: "like_no" },
  ] },
  { label: "like_yes", m: "I am happy you like my game!", next: "like_end" },
  { label: "like_no", m: "You made me sad!", next: "like_end" },
  { label: "like_end" },
  { m: "OK, let's change the topic" }
];

Bu tasarım için bazı açıklamalar:

Hikayenin tamamı bir dizide yazılmıştır. Sayı sağlamanız gerekmez, bunlar otomatik olarak dizi sözdizimi tarafından sağlanır: ilk öğenin dizini 0, diğerinin dizini 1 vb.

Çoğu durumda, aşağıdaki adımın numarasını yazmak gerekli değildir. Metin satırlarının çoğunun dal olmadığını varsayıyorum . "Sonraki adım aşağıdaki öğedir" varsayılan varsayımını yapalım ve yalnızca aksi durumda notlar alalım.

Atlamalar için sayı değil etiket kullanın . Daha sonra, birkaç satır ekler veya kaldırırsanız, hikayenin mantığı korunur ve sayıları ayarlamanız gerekmez.

Netlik ve kısalık arasında makul bir uzlaşma bulun. Örneğin, "mesaj" yerine "m" yazmanızı öneririm, çünkü bu şimdiye kadarki en sık kullanılan komut olacaktır, bu yüzden kısa yapmak metni daha okunaklı hale getirecektir. Ancak, geri kalan anahtar kelimeleri kısaltmaya gerek yoktur. (Ancak, istediğiniz gibi yapın. Önemli olan , sizin için en okunaklı hale getirmektir . Alternatif olarak, geçerli anahtar kelimeler olarak "m" ve "ileti" yi de destekleyebilirsiniz.)

Oyunun algoritması şöyle olmalıdır:

function execute_game() {
  var current_line = 0;
  while (current_line < story.length) {
    var current_step = story[current_line];
    if (undefined !== current_step.m) {

      display_message(current_step.m);
      if (undefined !== current_step.next) {
        current_line = find_label(current_step.next);
      } else {
        current_line = current_line + 1;
      }

    } else if (undefined !== current_step.question) {

      // display the question: current_step.question
      // display the answers: current_step.answers
      // choose an answer
      // and change current_line accordingly

    }
  }
}

Bu arada, bu fikirler Ren'Py'den ilham aldı , bu tam olarak istediğiniz şey değil (JavaScript değil, web değil), ancak yine de size harika fikirler verebilir.


Derinlemesine açıklama için teşekkür ederim, dizilerin sizin ve Philipp'in gösterdiği şekilde çalışabileceğinin farkında değildim, sadece Dizeleri veya sayıları tutabileceklerini düşündüm.
Sessiz Haritacı

1
Çözümünüzü uygulamaya çalışıyorum ve bazı yerlerde ({ label: "like_yes"; m: "I am happy you like my game!"; next: "like_end" },)bir 'değil'; 'olması gerektiğini düşünüyorum, ancak oldukça iyi çalışıyor . Ayrıca kıvırcık parantezlerde tam olarak ne denir? Bu dizi içindeki bir nesne mi? bunun nasıl kullanılacağı hakkında daha fazla bilgi isteseydim, ne ararım?
The Silent Cartographer

Evet, {...}bir nesnedir. JavaScript'te nesne, PHP'deki diziye veya Java'da Haritaya benzer bir anahtar / değer ilişkilendirmeli dizisidir (bu örnekte kullanılmayan bazı ekstra işlevlerle). Daha fazla bilgi için JavaScript ve ECMAScript hakkındaki Wikipedia makalelerine ve oradan bağlantılı belgelere, özellikle resmi ECMAScript belgelerine bakın.
Viliam Búr

1
btw burada önerdiği veri yapısının temelde JSON olduğunu unutmayın. Şahsen JSON'a kadar gitmenizi tavsiye ederim (çoğunlukla kıvırcık parantez ve bir "ağaç" ekleyin: her şeyin etrafında; {"ağaç": [vb]} gibi) ve sonra diyalog ağaçlarınızı harici dosyalarda saklayabilirsiniz. Verilerinizi oyununuzun yüklediği harici dosyalara koymak çok daha esnek ve modülerdir (bu nedenle bu yaklaşım en iyi yöntemdir).
jhocking

5

Bir dizi iletişim etkinliği oluşturmanızı öneririm. Her olay, NPC'nin söylediği metni ve bir dizi olası oyuncu yanıtı içeren bir nesnedir; bu da yanıt metnine sahip nesneler ve bu yanıtı izleyen olayın endeksidir.

var event = []; // create empty array

// create event objects and store them in the array
event[0] = { text: "Hello, how are you?",
             options: [    { response: "Bad", next: 1 },
                           { response: "Good", next: 2 }
                      ]
           };
event[1] = { text: "Why, what's wrong?",
             options: [    { response: "My dog ran away", next: 3},
                           { response: "I broke up with my girlfriend", next: 4}
                      ]
           };
event[2] = { text: "That's nice",

...

2

Farklı bir yaklaşım kullanmalısınız. JavaScript dizileri ve nesneleri destekler, bu yüzden neden giriş başına bir tane kullanmıyorsunuz, tüm bölünmeyi kaydediyor ve gerçek metni düzenlemeyi / okumayı kolaylaştırıyor?

İsterseniz, # 1gam için birkaç saat boyunca yaptığım bazı prototiplere göz atabilirsiniz . Kaynak GPLv3 altında kullanmak için ücretsiz (GPL'ye yapışmazsanız, sadece ilham için kullanıyorsanız, ama oyununuz bittiğinde bana bildirin.). Sadece harika bir yazı ya da bunun gibi bir şey beklemeyin. ;)

Kodun nasıl çalıştığı hakkında kısa bir açıklama yapmak için, CSS animasyon şeylerini ve bunun gibi şeyleri göz ardı ederek:

  • var data aslında tüm hikayeyi olası tüm seçimlerle birlikte içerir.
  • Her "konum" (veya sayfa / giriş) bir kimlikle tanımlanır. Listedeki ilk kimlik start, ikincisidir cwaitvb.
  • Her konum iki zorunlu öğe içerir: Altyazı ve gerçek metin. Kararlar için linkler basit bir biçimlendirme biçiminde yazılır [target location:display text].
  • Tüm "sihir" içeride gerçekleşiyor navigate(): Bu işlev İşaretleme bağlantılarını tıklatıyor. Biraz daha uzun, çünkü orada çıkmaz sokaklar için de statik metinler kullanıyorum. Önemli olan ilk iki çağrı replace().
  • İsteğe bağlı son girişler, karıştırılacak yeni arka plan renklerini tanımlar ve oyunun genel ruh halini destekler.
  • Bunun yerine bu renkleri tanımlamak siz de başka yerlere işaret eden bağlantılar ekleyebilir (bu not edilir değil benim kod tarafından ele, aynı Bunu göstermek için sadece bazı fikir):

    'start': ['Waking up', 'You wake...', 'cwait:yell for help', 'cwait: wait a bit', 'clook: look around']

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.