Du bist nicht angemeldet.

Stilllegung des Forums
Das Forum wurde am 05.06.2023 nach über 20 Jahren stillgelegt (weitere Informationen und ein kleiner Rückblick).
Registrierungen, Anmeldungen und Postings sind nicht mehr möglich. Öffentliche Inhalte sind weiterhin zugänglich.
Das Team von spieleprogrammierer.de bedankt sich bei der Community für die vielen schönen Jahre.
Wenn du eine deutschsprachige Spieleentwickler-Community suchst, schau doch mal im Discord und auf ZFX vorbei!

Werbeanzeige

1

24.11.2014, 17:43

Technisches Konzept für "Kampf-Loop" in einem Browser-Game (node.js)

Hallo zusammen,

mein Name ist Daniel, ich bin auch bekannt als Mangokid und bin gerade dabei mein erstes Browser-Game zu programmieren.
Bisher bin ich ganz gut voran gekommen, bin jetzt aber mit dem Gesicht zuerst volle Lotte gegen die Glastür gerannt und stecke ein bisschen fest - ich hoffe einer von euch kann mir den entscheidenden Denkanstoß geben! :dash:

Kurz zu meinem Projekt:
Es soll ein MMO "Idle Action RPG" werden.
Klingt zunächst widersprüchlich, ist es aber gar nicht. Es soll im späteren Verlauf darum gehen, effiziente "Builds", also Kombinationen aus Skills und Ausrüstung zu finden, die in der Lage sind den Endgame-Content zu erledigen (so wie in "echten" ARPGs, nur, dass man eben nicht selber spielen muss). :crazy:

Kurz und grob der theoretische Spielablauf erklärt:
- Du erstellst dir einen Charakter, z.B. einen Krieger
- du schickst diesen Charakter los in die böse Welt und er begegnet einem Monster
- Der Encounter startet und dein Charakter spielt in einem Loop seine Fertigkeiten ab (vice versa das Monster ebenfalls ... später irgendwann mal ... vielleicht ;))
- das Monster stirbt, dropt Ausrüstung und gibt dir Erfahrung. Dadurch wird dein Held stärker ...
- das nächste Monster erscheint ... etcpp.

Soweit so gut - jetzt zu meinem Problem:
Ich möchte, dass das mal ein MMO wird und möchte auch direkt ein POC abliefern.

Das ganze läuft auf sails.js (also auf einem node.js-Backend) - die ganzen Aktionen und Responses kommen über Websockets zum Client bzw. Server ... das läuft alles schon soweit ganz gut. :thinking:
Mein Problem ist der Loop, der im Hintergrund laufen soll (selbst wenn der User nicht eingeloggt ist) und die Datenbankstruktur generell (was speicher ich wann wo und wie verknüpfe ich das Ganze?!).

Konkret:
- Wie starte ich "sauber" einen Loop auf einem Webserver, der einem User zugewiesen ist?
- Wie und wo speichere ich das Monster-Objekt, gegen das der Charakter gerade kämpft? (Session fällt weg, Client fällt weg, in der Datenbank liegen Daten und keine Objekte ... ich stehe auf dem Schlauch ?( ) ... beim Schreiben kommt mir gerade die Idee, dass das Objekt einfach in dem Loop gespeichert bleibt - duh. Das führt mich wieder zu Punkt 1.

Gibt es zu solchen Punkten gute Lektüre, die ihr mir empfehlen könnt?
Ich kann diese Punkte leider nicht einmal gut thematisch Einordnen ... generelle Architektur vielleicht?
Bin ich vielleicht auch mit meinem MVC/Web-Ansatz komplett auf dem Holzweg?

Wie gesagt, das ist mein erstes "Spiele"-Projekt und ich bin Web-Entwickler.
Dieses Konstrukt ist aus meiner Leidenschaft für ARPGs und Web-Entwicklung auf node-Basis entstanden - wenn ihr mich jetzt für komplett bekloppt erklärt, kann ich das auch gut verstehen - ich hoffe aber auf gute Tipps und Ratschläge! ;)

Danke für eure Zeit und falls ihr mehr Infos braucht, dann lasst es mich bitte wissen!

Gruß,
Mangokid

2

24.11.2014, 18:37

Ich kenne sails.js jetzt nicht so gut aber grundlegend ist Node genau die richtige Loesung fuer was du machen moechtest.

Zitat


- Wie und wo speichere ich das Monster-Objekt, gegen das der Charakter gerade kämpft? (Session fällt weg, Client fällt weg, in der Datenbank liegen Daten und keine Objekte ... ich stehe auf dem Schlauch ?( ) ... beim Schreiben kommt mir gerade die Idee, dass das Objekt einfach in dem Loop


Auf dem Server. Du musst das nichtmal in ne DB schreiben. Das ist ja das schoene an Node (wenn das spiel groesser wird und du mehr als einen server laufen hast musst du etwas vorsichtig sein. Les dich dann mal in node cluster ein. Brauchste aber erstmal eher nicht).

Wenn du nicht weisst was ich meine schreib dir nen super einfachen node http server der auf einem pfad irgendeinen parameter nimmt. Weis das einer globalen variable zu und las dir das auf nem anderem pfad ausgeben. Du wirst sehen das du den wert von der ersten Verbindung wieder bekommst. Vollkommen egal welcher client fragt.

Davon abgesehen: Was benutzt du denn als DB? Wenn du einfach MongoDB nutzt dann speicherst du das "einfach" als Objekt (JSON, was in JS ja nen objekt ist).

Nun zu deiner loop: Hast du immer, automatisch. Quasi.

Ich gebe dir mal ein ganz einfaches Beispiel:

Quellcode

1
2
3
4
5
6
7
8
9
10
var http = require('http');

var test = 1;

http.createServer(function(req, res) {
  res.writeHead(200);
  res.end(test.toString());
}).listen(3000);

setInterval(function() {test++;}, 1000);


Starte das script, geh in deinem Browser zu localhost:3000

Es zaehlt einfach jede sekunde um eins hoch, egal ob ein client connected ist oder nicht oder was auch immer. So lange der server laeuft zaehlt es eins hoch/sekunde.

Versteh mich nicht falsch: Du brauchst hoechstwahrscheinlich nicht "setInterval" fuer was auch immer du machen moechtest, ich hoffe nur es macht etwas deutlicher wie das ganze funktioniert.

Wie auch immer, was machst du als Web-Entwickler? Wenn du von php kommst und jetzt zum ersten mal mit Node arbeitest wirst du feststellen das es da einige krasse unterschiede gibt. So vom Konzept her.

Macht das irgendwie Sinn fuer dich?

Tobiking

1x Rätselkönig

  • Private Nachricht senden

3

24.11.2014, 19:01

Bei einem Projekt von mir habe ich eine ähnliche Situation. Ich habe es getrennt in einen Dienst, der in Echtzeit Daten sammelt/generiert und einen Webserver der diese anzeigt. Dazwischen läuft Redis. Das ganze läuft praktisch so wie hier (ganz unten) beschrieben.

Wenn ein Benutzer sich einloggt, trägt der Webserver die ID bei Redis in eine Queue ein. Der Dienst holt sich die ID und emitted dann die Echtzeitdaten. Diese gehen automatisch über Redis und dem Webserver an den Benutzer.

4

24.11.2014, 20:01

Hi TheBenji,

zunächst erst mal vielen Dank für deine Antwort!

Zitat

Ich kenne sails.js jetzt nicht so gut aber grundlegend ist Node genau die richtige Loesung fuer was du machen moechtest.
Das klingt schon mal gut, dann bin ich immerhin nicht total auf dem Holzweg, wie's aussieht!:thumbup:
Sails.js ist ein MVC Framework, das auf express.js aufsetzt und unter anderem einen DB Adapter (Waterline) direkt mit eingebaut hat. Ich schreibe also nur meine Models, ob die Daten dann in einer MongoDB, SQL oder wo auch immer landen wird dann per Konfiguration geklärt. socket.io ist auch gleich mit drin und die MVC Struktur kenne ich aus ZendPHP. Das Framework steckt an sich noch in den Kinderschuhen, bietet aber bisher alles was ich brauche um mein Spiel schnell an den start zu bringen.

Zitat

Davon abgesehen: Was benutzt du denn als DB? Wenn du einfach MongoDB nutzt dann speicherst du das "einfach" als Objekt (JSON, was in JS ja nen objekt ist).

Hmm... also mein "Monster" ist ja ein Objekt aus meiner Monster Klasse -> pseudo Code mäßig:

Quellcode

1
2
3
4
var wolf = new Monster();
wolf.getCurrentHealth(); //100
hero.attack(wolf);
wolf.getCurrentHealth(); //90


Ich könnte mir jetzt natürlich einen getter schreiben um alle relevanten Daten für das Monster mit validem JSON in eine DB zu schreiben - wenn ich dann aber wieder Zugriff auf alle Funktionen und so haben will, dann müsste ich das erst wieder initialisieren. Wahrscheinlich hast du deswegen auch die Anführungszeichen ans "einfach" gesetzt! ;)

Egal ... also das ist geklärt in meinem Kopf: Das Monster existiert so lange wie der/die/das Loop läuft, es stirbt, es gibt die Belohnungen, der Loop startet wieder von vorne.

Zitat

Nun zu deiner loop: Hast du immer, automatisch. Quasi.
...

Ok ... also dass ich eine Loop auf dem Server mit setInterval starten kann, das war mir schon klar.
Mein Problem lag eher darin, dass ich mir partout nicht vorstellen konnte wie ich so eine Loop dynamisch pro User / pro Charakter erstellen kann.
Ich glaube ich habe aber jetzt eine Lösung. Ein bisschen drüber quatschen, auch wenn's über die Tastatur ist, hilft mir meistens schon enorm! :D

Mein Plan ist jetzt:
Ich lege alle Loops mit der User/Charakter-ID in ein Objekt eines Services, so habe ich sie an einer gesammelten Stelle und behalte den Zugriff ... das war eigentlich mein größtes gedankliches Problem.
Dort können sie dann fröhlich vor sich hin rödeln bis sie jemand stoppt! ;)
Müsste gehen, oder? Probiere ich gleich mal aus!

Kann zu sowas jemand eine Performance Abschätzung abgeben? Was passiert, wenn hier mal irgendwann 1000 Schleifen laufen, die jeweils alle ~10 Sekunden was in die DB schreiben?
Ebenfalls würde jeder dieser Loops am Ende an einen "privaten", also user-spezifischen Websocket-"Raum" eine Nachricht raushauen.
Websockets sind für mich auch Neuland ... wenn die größtenteils ins Nirvana gehen, macht das irgendwas aus?

Zitat

Wie auch immer, was machst du als Web-Entwickler? Wenn du von php kommst und jetzt zum ersten mal mit Node arbeitest wirst du feststellen das es da einige krasse unterschiede gibt. So vom Konzept her.
Ja - "Backendseitig" arbeite ich vor allem mit PHP, im speziellen mit Zend. Da aber alle großen Unternehmen gerade so auf cq5 abgehen bleibt Java, .jsf/.jsp und so weiter auch nicht aus.
Richtig zuhause fühle ich mich aber nur an vorderster Front, also bei clientseitigem Javascript.
Deswegen macht mir node auch so viel Freude! Wir hatten bisher noch kein Kunden-Projekt mit node und mir macht's gerade richtig Spaß mich an diesem Projekt mal so richtig auszutoben, mit allen schicken Technologien die ich die letzten Jahre nicht benutzen durfte!:thumbsup:

Danke jedenfalls für deine Tipps - ich glaube ich stehe jetzt nicht mehr vor der Wand und kann erst mal weiter machen.
Wenn ich mal einen benutzbaren Prototyp oder weitere Fragen habe, dann melde ich mich auf jeden Fall wieder. Wie schon gesagt, einfach das Aufschreiben und Ausformulieren meiner Gedanken hilft mir schon enorm, hehe.

Falls sonst noch jemand Ideen oder Bedenken oder irgendwas für mich hat, dann gerne her damit! Ich schaue ab Heute auf jeden Fall mal öfters hier vorbei!

Gruß!

Dieser Beitrag wurde bereits 1 mal editiert, zuletzt von »Mangokid« (24.11.2014, 20:08)


5

24.11.2014, 20:06

Bei einem Projekt von mir habe ich eine ähnliche Situation. Ich habe es getrennt in einen Dienst, der in Echtzeit Daten sammelt/generiert und einen Webserver der diese anzeigt. Dazwischen läuft Redis. Das ganze läuft praktisch so wie hier (ganz unten) beschrieben.

Wenn ein Benutzer sich einloggt, trägt der Webserver die ID bei Redis in eine Queue ein. Der Dienst holt sich die ID und emitted dann die Echtzeitdaten. Diese gehen automatisch über Redis und dem Webserver an den Benutzer.


Auch interessant! Einen zusätzlichen Dienst wollte ich aber eigentlich erst mal vermeiden (auch wenn ich mir vorstellen kann, dass das wahrscheinlich die einzige saubere Lösung ist).
Für den Prototyp muss das erst mal so gehen ... für später muss ich mir wohl noch einen separaten Node-Service basteln. Wenn der dann wegfetzt bleibt wenigstens noch der Webserver am start und kann sagen "hier läuft was nicht!".
Das sollte ich mir merken! Danke! :)

6

24.11.2014, 20:31

Wenn Du wirklich auf Skalierbarkeit aus bist, versuch den Spielzustand lieber möglichst "lazy" zu evaluieren, d.h. alles wird erst dann berechnet wenn jemand oder etwas nachschaut. Das bedeutet allerdings auch, dass Dein System sich merken muss, wann es ein bestimmtes Objekt spätestens wieder anschauen muss. Im Grunde läuft das auf Complex Event Processing (CEP) und viel Vorausberechnung hinaus, ist also nicht unbedingt trivial zu implementieren. Je simpler Deine Spielsysteme desto besser.

7

24.11.2014, 20:55

Wenn Du wirklich auf Skalierbarkeit aus bist, versuch den Spielzustand lieber möglichst "lazy" zu evaluieren, d.h. alles wird erst dann berechnet wenn jemand oder etwas nachschaut. Das bedeutet allerdings auch, dass Dein System sich merken muss, wann es ein bestimmtes Objekt spätestens wieder anschauen muss. Im Grunde läuft das auf Complex Event Processing (CEP) und viel Vorausberechnung hinaus, ist also nicht unbedingt trivial zu implementieren. Je simpler Deine Spielsysteme desto besser.
Erst mal überhaupt nicht! Ich will erst mal einen funktionierenden Prototypen haben. Wenn mein Server dann bei 3 Nutzern in die Knie geht mach ich mir dazu konkret Gedanken! ;)
Aber vielen Dank für die Stichwörter, das gucke ich mir mal genauer an.

Trotzdem mal kurz drüber nachgedacht ... ganz zu Anfang ist mir sowas auch im Kopf herumgespukt.
Hieße das, grob formuliert, ich halte nur den initial-zustand zu Beginn des "Loops" fest und berechne dann anhand der "Dauer" was in der Zwischenzeit passiert ist?!
"Spätestens nachschauen" heißt dann vermutlich, wenn ein User mal ein halbes Jahr nicht nachschaut, dann ist es vermutlich nicht so gut die Operationen aus dem Loop für ein halbes Jahr nachzuberechnen ... richtig?

Auf jeden Fall interessant ... aber Zukunftsmusik! :D

8

24.11.2014, 21:34

Erst mal überhaupt nicht! Ich will erst mal einen funktionierenden Prototypen haben.
Find ich super :)
Du solltest auf jeden Fall überlegen statt auf Sekunden auf diskreten Runden aufzubauen. Eine Runde dauert dann eine bestimmte Zeit und man kann die Zeit, die eine Runde brauchen soll, leichter verändern. Was dann dem Benutzer angezeigt wird, ist davon ja nicht abhängig. Wenn dann jede Runde irgendwann so viel Rechenzeit braucht, dass man das nicht mehr in der ursprünglich angedachten Zeit schafft, kann man die Rundendauer einfach entsprechend anpassen. Klar kann man die Runden auch Spielsekunden o.Ä. nennen, aber ich glaube im Code ist eine andere Bezeichnung einfacher zu deuten. Bei Eve Online nennen die das Anpassen der Zeit "time dilation" und das wird in Spielregionen mit überbeanspruchten Servern gemacht.

Schorsch

Supermoderator

Beiträge: 5 145

Wohnort: Wickede

Beruf: Softwareentwickler

  • Private Nachricht senden

9

24.11.2014, 21:48

Auch dafür gibt es ja verschiedene Ansätze. Je nachdem musst du ja vielleicht sogar nicht mal jeden Kampf richtig simulieren ala, Spieler führt AttackeX aus, Monster kontert mit AttackeY und so weiter. Vielleicht reicht es ja schon wenn du eine Funktion hast die abhängig von deinen Parametern und von ein wenig Zufäll das Ergebnis für beliebige Kämpfe bestimmt. Hier denkst du dir irgendein Prinzip aus nach welchem entschieden wird ob der Spieler gewinnt oder verliert und was er gegebenenfalls als Belohnung erhält. Sobald der Spieler sich jetzt einloggt oder von einem anderen Spieler angegriffen wird oder irgendetwas anderes passiert wobei du den Spieler im aktuellen Zustand brauchst berechnest du diesen eben und speicherst ihn ab. Wenn dein Spieler zum Beispiel automatisch kämpfen geht so kannst du verschiedene Funktionen für verschiedene Zeiträume anbieten. Ist das letzte Update einen Tag her wird die eine Variante benutzt, nach einem halben Jahr vielleicht eine andere. Dadurch kannst du dir ne Menge Rechenzeit sparen. Weiterhin ist der Vorteil dass du die eigentlichen Aktion wie einen einzelnen Kampf gar nicht implementieren musst sondern nur eine Funktion die abhängig vom aktuellen Zustand vom Spieler seinen neuen Zustand bestimmt.
Stell dir einfach mal vor du möchtest ein Tamagotchi entwickeln wobei das Spiel nicht rund um die Uhr laufen muss. Auch wenn man das Spiel nicht laufend hat soll sich das "Tier" ja weiter entwickeln und machen was es eben tut. Im Fall von dem Browsergame ist das eigentlich nichts anderes.
„Es ist doch so. Zwei und zwei macht irgendwas, und vier und vier macht irgendwas. Leider nicht dasselbe, dann wär's leicht.
Das ist aber auch schon höhere Mathematik.“

10

25.11.2014, 08:15

Danke für die Tipps! Da sind auch wieder ein paar gute Denkanstöße dabei.

Zitat

Weiterhin ist der Vorteil dass du die eigentlichen Aktion wie einen einzelnen Kampf gar nicht implementieren musst sondern nur eine Funktion die abhängig vom aktuellen Zustand vom Spieler seinen neuen Zustand bestimmt.
Also ich will auf jeden Fall, dass der User seinem Charakter "live" beim kämpfen zuschauen kann. Also ich denke einen "Live"-Kampf-Loop muss ich schon basteln.
Vor allem möchte ich, dass es einen Modus gibt, wo du deinen Charakter einfach "farmen" schicken kannst und dann alles automatisch läuft - zusätzlich soll es aber einen Modus geben, wo du durch aktive Teilnahme den Prozess verbessern kannst - in etwa so wie bei einem "Cookie-Clicker".
Die automatische Variante wird zunächst mit Attacken / Zaubern stattfinden die einen globalen Cooldown von mindestens 0.5 Sekunden haben. Zusätzlich kämpft ein Charakter gegen maximal 3 Monster - die Berechnungen sind im Moment noch der totale Witz.

Aber das driftet jetzt zu weit von der eigentlichen Fragestellung ab - die ist für mich erst mal geklärt!

Das angedachte Game-Design / Spielprinzip stelle ich vielleicht zu gegebener Stunde dann in einem nächsten Thread vor. Dann habe ich vielleicht auch ein paar Erfahrungen zum Live-Loop gesammelt und wir können da weiter diskutieren, wenn ihr lust habt! :D

Fazit zu den initialen Fragen:
1. Wie manage ich verschiedene Loops für einzelne User/Charaktere?
- Plan: Einen Loop-Manager/Service erstellen. In einem Objekt werden die verschiedenen Loops mit einer User-ID gespeichert und können so wieder ausgelesen und gestoppt / beeinflusst werden. Wenn es hier weitere Fragen/Probleme gibt melde ich mich noch mal.

2. Wo wird das Monster/Gegner-Objekt gespeichert?
- Das Objekt muss nicht persistiert werden, es kann also einfach als Variable in dem Loop existieren. Ist der Loop fertig wird es "weggeschmissen" und ein neues Monster/Gegner wird generiert. Easy. :rolleyes:

Ich danke nochmals für eure Hilfe!

Werbeanzeige