[Modding] Scripting Fragen

Maus

Senior Member
Registriert
07.08.2002
Beiträge
8.850
Danke schon mal. Mit MYAREA hat es funktioniert und MoveBetweenAreas dann auch.

[Gelöst]
Jetzt eine philosophische Frage fast:
Kann ich mit GlobalLT (2 z.B.) eine Variable abfragen, die nicht existiert? Ich dachte immer, wenn man Global (=0) abfrägt, dann hat man diesen Fall, weil "does not exist" halt =0 ist. Und 0 ist kleiner als 2. Aber der Skriptblock läuft nicht...
Ich probier das mal aus. Wenn ich die Variable vorher erstmal auf 1 setze und dann den GlobatLT abfrage...

Und noch zu LeaveAreaLUA: mein Verdacht wäre, dass das nur mit cre funktioniert, die in der GAM eingetragen sind. Weil ich nur Beispiele damit gefunden habe... Müsste ich mal in NI durchjagen, mal schauen.

edit: das Problem war, dass das Skript wegen eines fehlenden Continue() vorher fest hing und nicht weiter lief bis zur Stelle, wo ich die GlobalLT abgefragt habe...
 
Zuletzt bearbeitet:

Maus

Senior Member
Registriert
07.08.2002
Beiträge
8.850
Mal eine schwierige Frage zur Performance: Wie programmiere ich effizient, so dass das Spiel nicht langsamer wird. Mir kommt es so vor, wie wenn Installationen mit vielen Mods mit fortschreitender Spieldauer immer langsamer laufen. Da ergeben sich dann ein paar Fragen:

a) Korrekt, dass man besser keine Skript in die BALDUR.BCS schreibt, weil die praktisch permanent läuft? Lieber in Area-Skripte Trigger setzen, etc.?

b) Wie ist das mit den Variablen? Packt BG die einfach in einen Speicher, der sich immer mehr füllt und wo die Skripte dann nachschauen, wenn eine Variable in einem Trigger sitzt? Möglichst sparsam mit Variablen umgehen ist klar und vor allem wieder verwenden mit anderen Werten, wenn man sie nicht mehr benötigt, würde ich mal sagen.

c) Gibt es hinsichtlich Performance einen Unterschied zwischen globalen und lokalen Variablen? Sind die lokalen vielleicht irgendwo weggepackt, bis z.B. das Skript oder die Area wieder geladen werden? Oder ist es ziemlich egal, ob eine Variable global oder lokal definiert wird, weil sie alle im selben Speicherbereich bereit gehalten werden?

Gibt sonst wichtige Dinge, was man nicht machen sollte? Nicht so viele Timer, weil die immer mitlaufen, lieber auf Ereignisse triggern oder ähnliches?
 

Taimon

Infinity Engineer
Registriert
25.11.2001
Beiträge
1.481
a) Korrekt, dass man besser keine Skript in die BALDUR.BCS schreibt, weil die praktisch permanent läuft? Lieber in Area-Skripte Trigger setzen, etc.?
Ja, so wenig wie möglich in die BALDUR.BCS packen.

b) Wie ist das mit den Variablen? Packt BG die einfach in einen Speicher, der sich immer mehr füllt und wo die Skripte dann nachschauen, wenn eine Variable in einem Trigger sitzt?
Die Variablen sind in unterschiedlichen Hashtables. Es gibt eine globale Tabelle, eine pro Area und eine pro Actor.

c) Gibt es hinsichtlich Performance einen Unterschied zwischen globalen und lokalen Variablen? Sind die lokalen vielleicht irgendwo weggepackt, bis z.B. das Skript oder die Area wieder geladen werden? Oder ist es ziemlich egal, ob eine Variable global oder lokal definiert wird, weil sie alle im selben Speicherbereich bereit gehalten werden?
Ich würde versuchen die globalen Variablen zu minimieren, aber generell ist ein Hashtable-Lookup eigentlich recht schnell. Der Zugriff auf die globale und die jeweilige Area-Hashtable könnte allerdings schneller sein als der Zugriff auf die Actor-Hashtable, da die Engine nicht erst das Actor-Objekt "akquirieren" muss. (Spielt wahrscheinlich kaum eine Rolle.)

Gibt sonst wichtige Dinge, was man nicht machen sollte? Nicht so viele Timer, weil die immer mitlaufen, lieber auf Ereignisse triggern oder ähnliches?
Wie an anderer Stelle schon erläutert: die meisten Timer sind auch nur Variablen und werden identisch behandelt.
Allgemeine Performance-Empfehlungen kann ich nicht geben, aber der generelle Tenor ist schon möglichst wenig global zu machen.
 

Maus

Senior Member
Registriert
07.08.2002
Beiträge
8.850
Da dann gleich die Nachfrage: Sind die Begleiter in der Gruppe auch Actors? Oder sind die automatisch geladen, weil ja eigentlich immer da?

In einem Lookup-Table muss man aber auch erstmal suchen, oder sind die prinzipiell sortiert? Ich kenne das eher so, dass man Sachen, die man schnell braucht, in Lookup-Tables packt anstatt sie zu berechnen oder über ne Funktion zu holen. Aber Lookup ist immer noch langsamer als sie gleich in den Arbeitsspeicher zu schreiben.
Daher hätte ich gesagt: je größer die Lookup-Table umso schlechter, da noch was reinzupacken. Und GLOBAL dürfte die größte sein ;) Richtiges Verständnis oder nur gefährliches Halbwissen?
 

Taimon

Infinity Engineer
Registriert
25.11.2001
Beiträge
1.481
Sind die Begleiter in der Gruppe auch Actors?
Ja. Mit Actors meine ich im Prinzip alles, was ein Skript ausführen kann. (Intern wird die Klasse "CGameAIBase" genannt.)
Dazu zählen alle CREs aber auch Türen, Traps, etc.

Oder sind die automatisch geladen, weil ja eigentlich immer da?
Geladen sind sie sicher, aber trotzdem muss vor einem Zugriff sichergestellt werden, dass niemand anderes gerade etwas an dem Objekt ändert. Deswegen gibt es eine Funktion, die den exklusiven Zugriff sicherstellt.

In einem Lookup-Table muss man aber auch erstmal suchen, oder sind die prinzipiell sortiert?
Hashtabellen funktionieren so, dass der Schlüssel (in diesem Fall der Variablenname) mittels einer Hashfunktion auf einen Zahlenwert abgebildet wird. Der Zahlenwert wird als Index in ein Feld genommen und an der Stelle steht dann der gesuchte Wert.
Rein theoretisch ist damit die Lookup-Zeit unabhängig von der Anzahl der Elemente. Praktisch gibt es aber irgendwann Kollisionen (selber Zahlenwert bei unterschiedlichen Schlüsseln), da man die Feldgröße auch nicht beliebig groß machen möchte.

Aber Lookup ist immer noch langsamer als sie gleich in den Arbeitsspeicher zu schreiben.
Ich denke, hier bringst du etwas durcheinander. Der Speicherort ist unabhängig von der Struktur.
Und heutzutage ist praktisch sowieso fast alles im RAM, was häufiger benötigt wird. Wir haben ja mittlerweile mehr als 640 KB zur Verfügung. :)

je größer die Lookup-Table umso schlechter, da noch was reinzupacken. Und GLOBAL dürfte die größte sein
Wie schon erwähnt ist die Lookup-Zeit theoretisch konstant, in der Praxis hat man aber 20-30% Kollisionen. Es hängt stark von der Güte der Hashfunktion und dem zur Verfügung stehenden Speicherplatz ab. Und wenn die Tabelle stetig wächst, muss man auch irgendwann das Feld, in dem die Werte gespeichert werden, vergrößern. (Dauert auch eine Weile.)
Mein Kommentar, dass der Zugriff auf die globale Tabelle schneller sein könnte, bezog sich nur darauf, dass das Spiel genau weiß, an welcher Speicheradresse die Tabelle steht. Bei den anderen muss man sich erst das Objekt holen. Der Zugriff innerhalb der Tabelle ist aufgrund der Kollisionen aber sicher langsamer.
 

Maus

Senior Member
Registriert
07.08.2002
Beiträge
8.850
Uh, das triggert jetzt bei mir Verständnisfragen...

Also die Hashfunktion macht aus dem dem Variablennamen einen numerischen Wert? Und die Tabelle hat nachher zwei Spalten (Hashwert und Variablenname) oder 3 Spalten (+Variablenwert)? Oder nur 2 Spalten (Hashwert und Variablenwert)?

Und wenn das Spiel einen Variablennamen hat, dann berechnet es den Hashwert davon über die Hashfunktion und schaut in der Tabelle nach? Und kann dann den Variablenwert dort finden?

Und Kollision würde heißen, dass zwei Variablennamen denselben Hashwert ergeben, korrekt? Ist die Hashfunktion bekannt, so dass man beim Auswählen von Variablennamen darauf Rücksicht nehmen könnte? Und wenn Kollisionen möglich sind, müsste weiter oben im Text die Tabelle ja schon immer Hashwert und Variablennamen enthalten, um die Richtigkeit der Zuordnung überprüfen zu können.

Und das macht deswegen Sinn, weil in der Tabelle die Hashwerte fest zuschrieben sind und nicht ein Pointer drüberlaufen muss, der prüft, wo der entsprechende Eintrag im Array steht? und dadurch wird die Zugriffszeit unabhängig von der Größe?
 

Taimon

Infinity Engineer
Registriert
25.11.2001
Beiträge
1.481
Ich fürchte, wir driften hier ganz schön ab. Einige Sachen sind sicher auch implementierungsabhängig.

Aber ich versuch's trotzdem mal:
So eine Hashtabelle wird mit einer bestimmten initialen Größe erzeugt. Diese bestimmt die Größe des Feldes (Array).
Typischerweise ist das ein Array aus Pointern (Verweise) auf Listen. (Es hat im Speicher also eigentlich nur eine Spalte. Wenn man den Index dazuzählen würde, wären es zwei.)
Die Listen wiederum verwalten alle Elemente für den jeweiligen Hashwert. Bei Kollisionen stehen also mehrere Elemente in der Liste. (Das Element ist das eigentliche Datum, hier wäre es der Variablenname und -wert.)
Initial sind die Listen alle leer. (Wahrscheinlich so umgesetzt, dass der Pointer NULL ist.)
Wenn man einen Wert zu der Hashtabelle hinzufügt, wird über die Hashfunktion ein Eintrag im Array selektiert. Dazu wird der Hashwert vom Schlüssel (Variablenname) gebildet.
Das hinzuzufügende Element wird dann einfach an die jeweilige Liste angehängt. Wenn es keine Kollisionen gibt, hat die Liste nur einen Wert.
Beim Lookup passiert quasi dasselbe, nur das der Wert von dem Element zurückgegeben wird. (Bei Kollisionen muss die Liste nach dem richtigen Variablennamen durchsucht werden.)

Ist die Hashfunktion bekannt, so dass man beim Auswählen von Variablennamen darauf Rücksicht nehmen könnte?
Kann man sicherlich recherchieren, aber ich würde darauf vertrauen, dass die meisten Implementierungen hier schon eine passende Funktion ausgewählt haben.
Also keine Rücksicht darauf nehmen.
 
Oben