Seite 1 von 1

Scripting mit AngelScript

Verfasst: 16.03.2019, 22:52
von Jonathan
Moin,

ich möchte gerne eine Scriptsprache einbauen (C++ Game). Momentan ist mein Favorit Angel Script, und zwar aus folgenden Gründen:

- klein und kompakt. Ich will keinen Python-Interpreter mitliefern
- Kompatibilität zu C++ per Design
- Statische Typen. Wer mich kennt, weiß, dass ich da großen wert drauf lege
- wird noch aktiv gepflegt
- wird zumindest in einigen bekannten Spielen offenbar erfolgreich genutzt
- soweit ich das sehen kann: Keine Abhängigkeiten. Man schmeißt einfach den Code in sein Projekt und kann loslegen, kein CMake-Quatsch oder sonstiges.

Ich bin gerade dabei mir die Bibliothek anzuschauen, Meinungen und Warnung sind deshalb willkommen. Speziell würde mich Feedback von Schrompf interessieren, Splatter ist nämlich eines der Spiele auf der offiziellen Liste.

Re: Scripting mit AngelScript

Verfasst: 16.03.2019, 23:13
von Krishty
Bis Schrompf Zeit findet, habe ich was aus dem Archiv herausgesucht:
Allgemeine Diskussion über Skriptsprachen 2012: viewtopic.php?p=32214#p32214
Schrompfs AngelScript-Meinungen 2011: viewtopic.php?p=23242#p23242

Re: Scripting mit AngelScript

Verfasst: 17.03.2019, 09:24
von Alexander Kornrumpf
Jonathan hat geschrieben: 16.03.2019, 22:52 Moin,

ich möchte gerne eine Scriptsprache einbauen (C++ Game).
Warum eigentlich?

Re: Scripting mit AngelScript

Verfasst: 17.03.2019, 11:14
von Jonathan
Alexander Kornrumpf hat geschrieben: 17.03.2019, 09:24 Warum eigentlich?
Im Wesentlichen um ein Triggersystem umzusetzen. Schalter A öffnet Tür B. Das kann man natürlich auch einfacher haben, aber ich dachte mir, man kommt vermutlich schnell an den Punkt, wo "n Bedingungen logisch Verknüpft lösen m Aktionen aus" nicht mehr unbedingt ausreichen. Und dann kann man es gleich auch richtig machen und beliebige Skripte erlauben.

Letztendlich soll es ein Puzzle-Spiel aus der Egoperspektive werden. Daher auch meine Ego-Controller Posts im [Anti]-Jammer-Thread.

Re: Scripting mit AngelScript

Verfasst: 20.03.2019, 15:07
von Schrompf
Ich benutze AngelScript auch für mein aktuelles Projekt noch. Aber ich kann nicht wirklich hilfreiches Feedback beitragen, außer: es geht. Ich habe nämlich seit Jahrzehnten keine Alternative mehr ausprobiert. Höchstens Unity und C#, und das ist Meilen besser als Angelscript, aber halt wegen des fertigen Toolings dafür.

Ich mag den pragmatischen Ansatz bei Angelscript: du kannst inzwischen alle Referenzzähler und den ganzen Scheiß abschalten und kriegst eine statisch typisierte Sprache, die komplett unintrusive all Deine C++-Typen abbilden kann. Meine ganzen Mathe-Typen und so kann ich mit allen Operatoren als ALL_FLOATS oder sowas anmelden und kann damit im Script rechnen wie im C++. Alle anderen Typen werden eh vom Spiel erstellt und verwaltet, im Skript sind das alles reine Referenzen. Ich kann damit prima arbeiten. Ich wünsche mir aber natürlich viele Sachen - allen voran ne einfache Code Completion oder Compile Time Validation. Es ist ein bissl nervend, den Skriptcode anzupassen und dann erst beim Starten ne Exception zu fangen und im Log nachgucken zu müssen, was genau denn am Skript jetzt scheitert.

Skripting ist bei mir außerdem noch ein Bereich der hässlichen Hacks. Ich schreibe vor jeder Skriptausführung die Laufzeitdetails wie Spielwelt-Zeiger, Arbeitskontext (Client, Lokaler Client oder Server) und sowas in ne globale SkriptKontext-Struktur, damit alle Funktionen im Skript kontextfrei und bequem arbeiten können. Wenn dann aus dem Skript noch ein Skript getriggert wird, bau ich über ne statische Variable nen schmutzigen Stack des Kontextes auf. Definitiv kein Lehrbeispiel für Architektur, aber es funktioniert.

Re: Scripting mit AngelScript

Verfasst: 21.03.2019, 15:50
von Thoran
Ich hab vor einigen Jahren auch AngelScript für meine Game-Engine auf Ogre3D-Basis verwendet. Mir war das damals wesentlich lieber als Lua, mit dessen Tabellen-Konzept ich nicht klar kam. In meiner Engine konnte man für ein beliebiges Objekt im Spiel ein Skript definieren, dass das Verhalten dieses Objektes steuerte. Das Verhalten wurde dabei über eine State-Machine abgebildet. Damit ich nicht so viel Aufwand mit dem Skripten hatte, habe ich mir dafür einen grafischen Skript-Editor gebaut, mit dem ich die StateMachines zusammen klicken konnte. Danach musste man noch etwas manuell nacharbeiten, um zum Beispiel Logik für die Transitionen zwischen den States zu realisieren (das ging nicht über mein Tool). Damals war ich mit Abgelscript sehr zufrieden, auch wenn die Skripte in meinem Fall recht lang werden konnten. Das wäre aber wahrscheinlich mit anderen Skriptsprachen auch passiert und lag eher in der Natur meiner State-Machine.
Zwischenzeitlich habe ich die Engine jedoch eingestellt und bin bei UE4 gelandet, was mit Blueprints den Weg des Skriptings geht, den ich immer haben wollte.

Re: Scripting mit AngelScript

Verfasst: 23.03.2019, 11:42
von Jonathan
Vielen Dank fürs Feedback. Ich habe ein paar Tage mit AS rumgespielt und habe jetzt auch erste Erfahrungen.

Was mir als aller erstes auffiel: Man ist das alt. Keine Namespaces, man muss überall chars* statt strings übergeben (ok, dieser Punkt ist strittig, nicht alle benutzen std::string), und, als ich für ein allererstes Beispiel ein Lambda binden wollte ging das nicht und ich habe auch im Internet keine Lösung dafür gefunden. Ich hätte auf ein Interface vergleichbar mit std::function gehofft, wo man sehr elegant globale Funktionen, Methoden, Lambdas und irgendwelche Ergebnisse von std::bind übergeben kann. Geht scheinbar nicht. Oh und wenn man Parameter an Skripte übergeben will, muss man sie von Hand in der richtigen Reihenfolge auf den Stack leben? Das duftet ja nach Assembler... Aber ich bin mir sicher, wenn ich mich mal 20 Minuten in Ruhe hinsetze kann man sich etwas sehr schönes mit variadic-Templates schreiben und dann ist die Sache erledigt. Hätte nur halt auch so schon drin sein können...

Die Dokumentation ist so mittelgut. Es gibt durchaus längere Texte die Konzepte erklären und nicht nur die standard Interfacekommentare, aber viele davon könnten ein klein wenig ausführlicher sein. Man muss auch öfters mal hin und her springen, um Operatoren zu überladen muss man wissen, wie diese in der Skriptsprache implementiert sind und wie man sie registriert, beides steht an vollkommen unterschiedlichen Stellen. Und als Nischenprodukt gibt es auch nur mäßig viele Treffen bei google&co. Ich glaube eigentlich fehlen mir nur ein paar mehr Beispiele die typische Use-Cases abdecken und das etwas weiter erklären. Bisher habe ich zwar immer noch alles gefunden was ich gesucht habe, es hätte aber auch mit weniger Suchen gehen können.

Nun zum Guten: Die Integration lief erstaunlich reibungslos. Es gibt keine fiesen Abhängigkeiten, man kann einfach so loslegen. Besonders viel Code braucht man auch nicht, ich habe angefangen einen kleinen Scriptmanager zu schreiben der alles verwaltet und der hat bis jetzt 150 Zeilen. Und man kann letztendlich doch überraschend nett eigene Klassen einbinden: Die Unterscheidung zwischen value und reference Typen klingt erst komisch, ist dann aber sinnvoll. Meine Entities müssen beim erzeugen auf komplexe Art mit dem Scene-Manager verbunden werden, das will ich gar nicht dynamisch in einem Skript machen. Aber dank reference-Typ braucht man nur das sichtbar zu machen, was man wirklich benötigt. Auf der anderen Seite habe ich angefangen, glm::vec3 anzubinden, es ist extrem nett in Skripten mit 3d Vectoren rechnen zu können (inklusiv Operator-Überladung).

Und die Debug-Ausgaben sind überraschend Hilfreich. Immer wenn ich etwas falsch verwendet habe konnte ich anhand der Meldung den Fehler finden. Man kann durchaus einiges falsch machen (z.B. muss man die Calling-Convention explizit angeben), aber man bekommt diese Fehler gefixt, und das ist auch gut.


Ansich hat es Schrompf schon ziemlich gut zusammen gefasst: "Es geht". Man bekommt ohne viel Aufwand Skripte in sein Projekt, aber alles wirkt etwas rustikal und nicht unbedingt super poliert oder super benutzerfreundlich. Ich bin immer noch am experimentieren, habe aber mittlerweile vermutlich jedes Feature einmal getestet, das ich benutzen möchte. Insgesamt ist meine Meinung bisher ganz positiv, ich glaube AngelScript ist das, wonach ich gesucht habe.

Re: Scripting mit AngelScript

Verfasst: 25.03.2019, 09:19
von Schrompf
Und es wird noch gepflegt. Wenn Du mal wirklich nicht weiter kommst (und den Overhead für eine Frage an echte Menschen in Kauf nimmst), kannst Du auf gamedev.net im eigenen Unterforum ne Frage stellen und der orginale AngelScript-Autor antwortet. Sehr hilfreich.

Re: Scripting mit AngelScript

Verfasst: 20.06.2019, 00:00
von Jonathan
Noch eine Frage an die Compiler-Experten. Ich versuche, AngelScript in einer 64bit Anwendung zu verwenden (mit VS2015) und bekomme Compilerfehler. Die Dokumentation sagt dazu:

"The MSVC compiler doesn't support inline assembler for the x86 64bit CPU family. To support this platform a separate assembler file has been created: as_callfunc_x64_msvc_asm.asm. To compile this file it is necessary to configure a custom build command with the following: ml64.exe /c /nologo /Fo$(OutDir)\as_callfunc_x64_msvc_asm.obj /W3 /Zi /Ta $(InputDir)\$(InputFileName)"

Der Befehl hat so direkt bei mir nicht funktioniert (unter anderem weil meine VC Version scheinbar kein InputDir Makro hat? Es steht zumindest nicht in der Liste). Ich habe den Befehl jetzt etwas angepasst, er funktioniert aber immer noch nicht:

ml64.exe /Fo$(OutDir)\as_callfunc_x64_msvc_asm.obj /c /W3 /Zi /Ta ../../Source/AngelScript/as_callfunc_x64_msvc_asm.asm

In der ml64 Dokumentation heißt es unter anderem, dass /c ggf. nach /Fo kommen soll, im Beispiel sieht es so aus, als würde man mit /c Dateinamen angeben, aber hinter /Ta wohl auch. Ich bin ein bisschen verwirrt, was der Befehl eigentlich genau machen soll (außer halt asm->obj zu kompilieren).

Es werden keine Fehler angezeigt, aber auch keine Ausgabedatei erstellt. Zumindest der Eingabedateiname scheint richtig (wenn man ihn ändert gibt es Fehlermeldungen) und der Ausgabepfad scheint in der Makro-Preview auch richtig zu sein. In der Konsole steht nur "Assembling: ../../Source/AngelScript/as_callfunc_x64_msvc_asm.asm", aber es wird keine .obj erstellt (auch nicht in anderen Ordnern). Im späteren Build-Prozess nörgelt der Linker dann, weil er die .obj Datei nicht finden kann. Was jetzt?

Re: Scripting mit AngelScript

Verfasst: 20.06.2019, 00:09
von Krishty
Falls du normalerweise in der IDE arbeitest:

1. Rechtsklick aufs Projekt -> Build Dependencies -> Build Customizations -> Häkchen bei masm
2. as_callfunc_x64_msvc_asm.asm dem Projekt hinzufügen
3. Kompilieren und Linken sollte nun genau wie C++ funktionieren

Die Befehlszeile rühre ich für sowas normalerweise mit der Kneifzange nicht an.

Re: Scripting mit AngelScript

Verfasst: 20.06.2019, 10:06
von Jonathan
Äh ja. Wäre ich jetzt nicht drauf gekommen, klappt aber und ist viel viel einfacher. Geil, danke!

Re: Scripting mit AngelScript

Verfasst: 11.10.2021, 18:25
von Jonathan
Nun, bisher musste ich diese Schritte immer von Hand wiederholen, wenn CMake die Projekte neu erstellt hat. Ich war jetzt endlich mal genervt genug, das ganze vernünftig umzusetzen. Anstatt irgendwie die Commando-Zeile zu definieren, gibt es eine viel einfachere Lösung:

Code: Alles auswählen


file(GLOB files
	"*.S"
	#"*.asm"
	"*.cpp"
	"*.h"
	"scriptbuilder/*.cpp"
	"scriptbuilder/*.h"
	"scriptstdstring/*.cpp"
	"scriptstdstring/*.h"
	"scriptmath/*.cpp"
	"scriptmath/*.h"
	"as_callfunc_x64_msvc_asm.asm"
)

ENABLE_LANGUAGE(ASM_MASM)

ADD_LIBRARY(AngelScript STATIC
	${files}
)
(d.h. einfach Assembler aktivieren und die Datei dem Projekt hinzufügen)

Re: Scripting mit AngelScript

Verfasst: 04.11.2021, 21:58
von Jonathan
Jonathan hat geschrieben: 23.03.2019, 11:42 Was mir als aller erstes auffiel: Man ist das alt. Keine Namespaces, man muss überall chars* statt strings übergeben (ok, dieser Punkt ist strittig, nicht alle benutzen std::string), und, als ich für ein allererstes Beispiel ein Lambda binden wollte ging das nicht und ich habe auch im Internet keine Lösung dafür gefunden. Ich hätte auf ein Interface vergleichbar mit std::function gehofft, wo man sehr elegant globale Funktionen, Methoden, Lambdas und irgendwelche Ergebnisse von std::bind übergeben kann. Geht scheinbar nicht. Oh und wenn man Parameter an Skripte übergeben will, muss man sie von Hand in der richtigen Reihenfolge auf den Stack leben? Das duftet ja nach Assembler... Aber ich bin mir sicher, wenn ich mich mal 20 Minuten in Ruhe hinsetze kann man sich etwas sehr schönes mit variadic-Templates schreiben und dann ist die Sache erledigt. Hätte nur halt auch so schon drin sein können...

Zum Thema "Man kann keine std::function oder Lambdas binden": https://www.gamedev.net/forums/topic/65 ... dfunction/

Sprich: AngelScript verzichtet Konsequent auf alles was mit C++11 oder später zu tun hat, und wird das daher niemals können.

Aber: Man kann halbwegs OK pointer auf std::functions binden. Bloß muss man dann die Funktionsobjekte irgendwo speichern. Beispiel:

Code: Alles auswählen

void ScriptSystem::RegisterAdditionalInterface()
{
	static std::function<int(void)> GetNumPlayerWeapons = [this]() -> int
	{
		auto W = m_Game.GetPlayerController()->GetWeapons();
		int n = 0;
		for(auto& w : W)
		{
			if(w.Available)
				n++;
		}
		return n;
	};

	m_Engine->RegisterGlobalFunction("int GetNumPlayerWeapons()", asMETHOD(decltype(GetNumPlayerWeapons), operator()), asCALL_THISCALL_ASGLOBAL, &GetNumPlayerWeapons);
Kurze Erklärung: AngelScript kann nicht direkt Lambdas binden, aber sehr wohl Methoden von Klassen. Hier wird dann also die Methode operator() des Lambda-Objektes gebunden, die natürlich den entsprechenden Lambda-Code ausführt. Dazu muss man aber zusätzlich einen Zeiger auf das Objekt übergeben, denn das Lambda hat ja auch einen Zustand (die Referenz auf das Game-Objekt, bzw. den this-Zeiger des ScriptSystems). Daher ist das lokal deklarierte Lambda hier eine statische Variable, damit sie nicht zerstört wird (und damit die Game-Referenz undefiniert ist), sobald die Register-Funktion fertig ist. Das geht fürs erste, ist aber natürlich unschön, da mein ScriptSystem jetzt funktional ein Singleton ist (man kann nur eine funktionierende Instanz davon haben, die statische Variable ist für alle ScriptSystem Objekte gleich).

Was ich wohl bräuchte wäre ein ScriptSystem::FunctionStorage Member, vermutlich vom Typ vector<unique_ptr<std::function>>. Aber soweit ich das sehe, gibt es keine generische function, die Parameter sind teil des Typs.
Ich hatte jetzt damit experimentiert, einen vector von unique_ptrs auf eine leere Struktur zu haben, für jedes Lambda wird dann eine neue Struktur davon abgeleitet, die ein std::function des passenden Typs enthält, die auf ein passendes Lambda gesetzt wird. Ich hab es nicht so weit gebracht, dass diese Lösung tatsächlich kompiliert, weil ich vorher schon viel zu viel Boilerplate-Code hatte. Hat jemand eine Idee, wie man das schöner lösen könnte? Ich brauche ja bloß irgendwie einen persistenten Zeiger auf ein lokal erstelltes Lambda, dass dann zu einem wohldefinierten Zeitpunkt auch wieder gelöscht wird...

Re: Scripting mit AngelScript

Verfasst: 05.11.2021, 20:33
von Jonathan
Habs.

Zunächst: Ein generischer std::function Typ ist schwierig, da irgendwie fraglich ist, was man mit einer Funktion tun will, von der man nicht weiß, wie man sie aufrufen soll. In meinem Fall will ich ein Objekt aber auch einfach nur speichern und später freigeben, selber benutzen tue ich es nie. Wie ich lernte, funktioniert ein unique_ptr<void> ohne Custom-Deleter nicht, da nicht entschieden werden kann, welcher Destruktor aufgerufen werden soll. Ein shared_ptr<void> klappt aber, da der shared_ptr ohnehin einen Block an Zusatzinformationen (z.B. Referencecounter) speichert, und da wird wohl gleich auch irgendwas bezüglich Destruktor gespeichert. Egal, was man will ist ein vector<shared_ptr<void>>, um dort seine Lambdas drin zu speichern.

Man nehme also:

Code: Alles auswählen


class ScriptSystem
{
	// anderer kram ...
	
	asIScriptEngine* m_Engine;
	template<typename t> void RegisterLambda(std::string decl, t&& function);
	std::vector<std::shared_ptr<void>> m_FunctionStorage;
	
	int m_ScriptSystemState; // dummy variable, used in lambda example
};


template<typename t> void ScriptSystem::RegisterLambda(std::string decl, t&& function)
{
	auto p = make_shared<t>(function);
	m_Engine->RegisterGlobalFunction(decl.c_str(), asMETHOD(t, operator()), asCALL_THISCALL_ASGLOBAL, p.get());
	m_FunctionStorage.push_back(p);
}

void ScriptSystemPyramid::RegisterAdditionalInterface()
{
	// a lambda with some state
	RegisterLambda("int ScriptSystemState()", [this]() -> int
		{
			return m_ScriptSystemState;
		});
}
Tjo, und so geht das dann.

Re: Scripting mit AngelScript

Verfasst: 14.11.2021, 19:53
von Jonathan
Ich hab mir nochmal Container-Support angesehen. Ihr wisst schon, sowas wie vector<int> oder map<string, Entity*>. Um es kurz zu machen, der Support ist scheiße.

Also ganz ehrlich, es ist doch ein absolutes Grundfeature, dass man auch mal 5 Objekte anstatt eines einzelnen zurück geben will. Gerade für Trigger-Systeme (ganz klassisches Beispiel für den Einsatz von Skriptsprachen!) will man ja Funktionen wie GetAllEntitiesInArea() oder so haben. Idealerweise würde man da einen std::vector oder so an die Skriptsprache weiterreichen.

Das einzige was es in AngelScript gibt, ist eine CScriptArray-Klasse für generische Arrays dynamischer Größe. Hat man also einen vector, muss man den zunächst in ein Array-Objekt umkopieren und dann an das Skript geben. Was für große Container ein ziemlicher unnötiger Aufwand ist. Oder man benutzt auch intern für alles CScriptArrays, was aber aufgrund des furchtbaren Interfaces auch eine richtig schlechte Idee ist.
Diese Klasse macht einfach durch und durch keinen Spaß: Zunächsteinmal habe ich weit über eine Stunde gebraucht um ein triviales Beispiel (Array mit 3 Zahlen an Skript übergeben) zum Laufen zu bringen, weil die Dokumentation viel zu kurz ist. Sorry, dafür hat es einfach mal ein komplettes, lauffähiges Beispiel zu geben, das man dann ausbauen kann. Stattdessen muss ich endlos im Internet rumsuchen und am Ende durch planloses rumprobieren eine lauffähige Version finden.
Dann ist das Interface echt zum Kotzen, das ist so ein C-Style generischer Container, der void-Zeiger auf einzelne Elemente speichert (die einzelnen Elemente hintereinander im Speicher haben? Vergiss es! Lieber schön für jeden Integer eine weitere Indirektion einbauen!). So ziemlich das einzige was man damit machen will ist eine Funktion zu schreiben, die einen normalen vector in ein CScriptArray kopiert und dieses an ein Skript übergeben kann, damit man wirklich absolut nichts anderes von dem Interface anfassen muss.
Und zu guter Letzt macht die Klasse noch irgendein Memory-Management aus dem letzten Jahrtausend. Es gibt irgendwelche dubiosen Referenz-Counter, man erstellt alle Arrays über die Engine und bekommt bloß einen Zeiger zurück, und mir ist absolut unklar, ob und wie viele Memory-Leaks ich gerade erzeuge. Weil RAII ja viel zu einfach gewesen wäre.

Es gibt auch ein 5-Jahre altes Addon, was angeblich STL-Container mit AngelScript kompatibel macht. Aber das bietet auch bloß neue Klassen an, die sich verhalten wie STL-Klassen und halt ein klein wenig einfacher an AS übergeben werden können. Keine Ahnung, ob die noch mit der aktuellen oder zukünftigen Version kompatibel sind, das riecht einfach nach so einer Situation wo man bis ans Ende aller Tage nie wieder eine aktuellen AS Version verwenden kann, ohne dass dieses Addon kaputt geht.

Ich meine, es ist ja eigentlich toll, dass AS noch gepflegt wird. Aber das es sich sowas von dermaßen gegen absolut jedes Feature sträubt, was C++ irgendwie erträglich macht, ist meiner Meinung nach Entwickeln für eine Zielgruppe, die es nicht geben sollte. Ich bin jedenfalls richtig genervt, dass ich für so einen uatsch dermaßen viel Zeit verschwenden musste...

Re: Scripting mit AngelScript

Verfasst: 14.11.2021, 21:53
von joggel
Das einzige was es in AngelScript gibt, ist eine CScriptArray-Klasse für generische Arrays dynamischer Größe. Hat man also einen vector, muss man den zunächst in ein Array-Objekt umkopieren und dann an das Skript geben.
Ich unterstützte ja in meinem Tool auch Scripting (sehr sehr primitiv im Moment). Und da muss ich das ebenfalls so machen. Also alles was auf C/C++(Host)-Seite so existiert muss ich in ein Python-Äquivalent kopieren. Ich benutze Boost.Python und da funktioniert das anscheinend nur so. Außnahmen sind solche triviale Typen wie int, float, char und bool(?)

Ich glaube, das ist auch normal. Ich kenne mich zwar nicht sonderlich mit dem Thema "Scripting in eine Hostanwendung einbetten" aus; aber wie soll es denn auch anders funktionieren? Ich weiß nicht wie das intern läuft, gerade was Speichermanagement betrifft... Vlt arbeitet der Interpreter dann in einem anderen Speicherbereich, und kann nicht auf Daten der Hostanwendung zugreifen und andersherum.

Wie gesagt: ich kenne mich da nicht aus. Wäre aber interessiert an Informationen diesbezüglich :)

Naja... was ich sagen wollte: Ich muss diesen Umweg auch gehen, wenn ich Daten an meinen Pythoninterpreter übergeben will..

[Edit]
Ich schaue mir gerade mal etwas Code von Boost.Python an.
Okay... Python ist schon mal was ganz anderes als C.
Insofern kann ich das schon nachvollziehen das ich diesen Weg gehen muss...

Re: Scripting mit AngelScript

Verfasst: 15.11.2021, 07:36
von Schrompf
AngelScript hat ursprünglich wohl versucht, eine echte Skriptsprache zu sein und Speicher-Management aus der Skriptseite rauszuhalten. Daher gibt's da nur Value-Types und RefCounted Types (die mit @ im Skriptcode verwaltet werden). Heutzutage würde ich wahrscheinlich eine Factory für std::span registrieren bzw. nen eigenen ValueType dafür erfinden. In anderen Skriptsprachen dürfte das ganz genauso ausfallen, fürchte ich.

Bei Splatter hab ich mich damals darauf verlassen, dass ich meine Skripte immer single-threaded ausführe, und habe RangeQueries in nem internen State gebunkert, anstatt sie über Rückgabewerte abzubilden. Also FindeAlleObjekte*() und dann for( int a = 0; a < AnzahlGefundeneObjekte(); ++a ) und so weiter. Unsicher, aber bequem.

Re: Scripting mit AngelScript

Verfasst: 15.11.2021, 08:02
von Alexander Kornrumpf
Jonathan hat geschrieben: 17.03.2019, 11:14
Alexander Kornrumpf hat geschrieben: 17.03.2019, 09:24 Warum eigentlich?
Im Wesentlichen um ein Triggersystem umzusetzen. Schalter A öffnet Tür B. Das kann man natürlich auch einfacher haben, aber ich dachte mir, man kommt vermutlich schnell an den Punkt, wo "n Bedingungen logisch Verknüpft lösen m Aktionen aus" nicht mehr unbedingt ausreichen. Und dann kann man es gleich auch richtig machen und beliebige Skripte erlauben.

Letztendlich soll es ein Puzzle-Spiel aus der Egoperspektive werden. Daher auch meine Ego-Controller Posts im [Anti]-Jammer-Thread.
Der Hintergrund meiner Frage damals war eigentlich: Unter der Annahme, dass es sich um ein Hobbyprojekt mit entsprechender Teamgröße handelt, lohnt sich der Aufwand?

Ich weiß nicht warum Programmierer so verliebt in den Gedanken von Scriptsprachen und Domain Specific Languages sind. Normalerweise hast du bereits mindestens eine ausgereifte general purpose Sprache in deinem Projekt, die man einfach verwenden könnte *zwinkersmiley*. Der Traum, dass Leute aus der Domain programmieren könnten, wenn man ihnen nur eine spezifische Sprache zur Verfügung stellen würde wird schnell zum Support-Albtraum. Und im Hobbybereich musst du dich obendrein eben noch fragen, wer das sein soll, diese Leute aus der Domäne.

Aber das war 2019 (wow), jetzt ist 2021 und mich würde natürlich interessieren: Stehst du mit AngelScript irgendweie besser dar, als wenn du dasselbe System in der Projektsprache (ich nehme an C++) gebaut hättest?

Re: Scripting mit AngelScript

Verfasst: 15.11.2021, 08:24
von Schrompf
Hot Reloading ist schon cool. Und bei Splatter hat auch mein Grafiker manches Mal die Skripte angepasst.

Wenn ich jetzt alleine was anfange, wäre aber evtl. Spiellogik in ner DLL auch ne feine Erfindung. Irgendwas mit ner einzelnen Funktion extern "C" int doScript( "some_named_id", WorldState* state) und Du kannst Dir das komplette Binding ersparen.

Hängt halt ein bissl davon ab, was man eigentlich erreichen will. Sobald andere Personen beteiligt sind, lohnt sich das Rumärgern mit Skriptsprachen, finde ich. Man muss nur seinen "Ordentlich"-Drang bei Skriptsprachen im Zaum halten, sonst baut man sich wirklich adelig für "schöne" Lösungen, die am Ende niemand außer einem selbst würdigt.

Re: Scripting mit AngelScript

Verfasst: 15.11.2021, 22:11
von Krishty
Alexander Kornrumpf hat geschrieben: 15.11.2021, 08:02Stehst du mit AngelScript irgendweie besser dar, als wenn du dasselbe System in der Projektsprache (ich nehme an C++) gebaut hättest?
Danke für die Frage, die spricht mir aus der Seele …

Kurz my 5 ct: Ich bin übers Modding zur Programmierung gekommen, also kann die Einschätzung verzerrt sein, aber: Ich finde, dass Modding-Möglichkeiten Spiele unheimlich aufwerten. Community Building wird dadurch viel, viel einfacher und der Wiederspielwert viel höher.

Wenn die Spiel-Logik in einer C-DLL liegt, gibt es de facto kein Modding. Selbst wenn sich jemand die Mühe macht, C/C++ zu lernen und das Projekt neu zu kompilieren (falls es denn Open Source ist) und dann z. B. eine neue Figur oder Waffe oder Fahrzeug einbaut: Sobald ein weiterer Modder eine weitere Figur/Waffe/Fahrzeug einbaut, hast du zwei DLLs, kannst aber nur eine davon laden.

Skriptsprachen kann hingegen jeder lernen (meist durch Rumprobieren); die Ansprüche an den Modder sind weitaus niedriger (und damit auch die Hürden), und man kann verschiedene Mods einfach zusammenführen (jedenfalls viel einfacher als EXEs). Wenn dann jemand eine Bunnyhop-Mod mit einem neuen Granatwerfer kombinieren will, ist das ein Kinderspiel.

So jedenfalls mein mentales Modell, das leider auf kaum Eigenerfahrung als Spiele-Entwickler beruht.

Aktuell packe ich Adapter für die Skriptsprache in eine minimale C++-DLL. Wenn dann alle nach Lua und Python fürs Modding schreien, füge ich halt noch einen zweiten Adapter für Lua und einen dritten für Python hinzu. Damit habe ich das Low-Level-Zeug weiter in meiner vertrauten Umgebung und die Modder und Mitarbeitenden haben ihre vertraute Umgebung.

Re: Scripting mit AngelScript

Verfasst: 15.11.2021, 23:17
von Jonathan
Zu den technischen Fragen muss ich mir erst nochmal Gedanken machen, aber jetzt schonmal hierzu:
Alexander Kornrumpf hat geschrieben: 15.11.2021, 08:02 Aber das war 2019 (wow), jetzt ist 2021 und mich würde natürlich interessieren: Stehst du mit AngelScript irgendweie besser dar, als wenn du dasselbe System in der Projektsprache (ich nehme an C++) gebaut hättest?
Also, ich denke schon.
Beispielsweise bastel ich gerade am Zombiespiel weiter. Da gibt es jetzt ja Waffen, die irgendwo auf der Karte liegen und es kommen mit der Zeit immer mehr Gegner. Aber zum Testen will man halt vielleicht mal direkt mit Waffen starten. Oder mal ganz viele Gegner haben. Oder weniger. Ich könnte jetzt eine config-Datei schreiben, in der ich irgendwie angebe, welche Waffen man zu Beginn hat und wie viele Zombies kommen sollen. Das muss ich dann in C++ einlesen und irgendwie verarbeiten und gucken, welche Parameter es gibt und was die bedeuten. Oder ich mach es per Skript. Aktuell z.B. so:

Code: Alles auswählen

float g_spawn_counter = 0;

void ZombieSpawn(float time)
{
	float level_time = GetLevelTime();
	
	g_spawn_counter -= time;
	if(g_spawn_counter <= 0)
	{
		if(level_time < 30)
			SpawnRandomZombies(2);
		else if(level_time < 60)
			SpawnRandomZombies(4);
		else
			SpawnRandomZombies(10);
			
		g_spawn_counter = 10.f;
	}
}

bool StartLevel(float time)
{
	EgoController@ player = GetPlayerController();
	player.AddWeapon("Shotgun");
	player.AddAmmo("Shotgun", 240);
	
	SpawnRandomZombies(20);
	
	return true;
}
Die erste Funktion wird in jedem Frame aufgerufen, die zweite nur einmal am Anfang. Will ich die Waffen selber suchen müssen, wird die Zeile auskommentiert. Brauche ich mehr Munition, kann ich den Wert ändern. Will ich zu Beginn schon viele Zombies haben, rufe ich halt die Spawn-Funktion auf, ansonsten passe ich halt die obere an. Natürlich könnte ich das alles fest in C++ programmieren. Aber ich will ja z.b. nicht beliebig viele globale Variablen haben, d.h. den Spawn-Counter würde ich vermutlich in die Game.hpp packen und sobald ich das tue, kompiliert es wieder erstmal 30 Sekunden. Also alleine um Dinge schnell mal ohne neu zu kompilieren ändern zu können, lohnt das irgendwie schon.
Und die Idee ist halt, dass man damit später dann nicht nur debuggen kann, sondern eben interessantere Level bauen kann. Und da ist eine Skriptsprache irgendwie schon wesentlich geiler, als ein handgebautes Triggersystem. Die würde man ja auch irgendwie in eine Textdatei packen (oder in eine binäre Level-Datei und dann einen grafischen Trigger-Editor schreiben, ugh), die man irgendwie parsen muss. Und halt so triviale Dinge wie irgendwas 5 mal machen oder mal zwei Zahlen zusammen rechnen, oder ein Item an dem Punkt spawnen lassen, der am weitesten vom Spieler entfernt ist, oder oder oder. Gefühlt hat man sobald man simple Skripte hat schon so viel mehr Möglichkeiten. Und naja, alles ans Laufen zu kriegen war jetzt zwar ein paar Tage Arbeit. Aber wenn erstmal alles etabliert ist, hat man dann doch recht schnell neue Funktionen angebunden.

Also insgesamt würde ich schon sagen, dass ein eigenes System auf jeden Fall mehr Arbeit gewesen wäre, als AS einzubinden und sicherlich weniger Features und Komfort bedeutet hätte. Aber es ist gut möglich, dass man es performanter hinbekommen hätte. Aber ich lagere auch wirklich nur ein paar Dinge ins Skript aus, ich würde z.B. nicht die KI von 50 Gegnern alle einzeln durch Skripte implementieren. Und letztendlich hat man ja auch schnell wiederkehrende Code-Teile aus dem Skript in C++ ausgelagert, um sie schneller zu machen.

Re: Scripting mit AngelScript

Verfasst: 16.11.2021, 08:21
von Alexander Kornrumpf
Krishty hat geschrieben: 15.11.2021, 22:11
Alexander Kornrumpf hat geschrieben: 15.11.2021, 08:02Stehst du mit AngelScript irgendweie besser dar, als wenn du dasselbe System in der Projektsprache (ich nehme an C++) gebaut hättest?
Wenn die Spiel-Logik in einer C-DLL liegt, gibt es de facto kein Modding. Selbst wenn sich jemand die Mühe macht, C/C++ zu lernen und das Projekt neu zu kompilieren (falls es denn Open Source ist) und dann z. B. eine neue Figur oder Waffe oder Fahrzeug einbaut: Sobald ein weiterer Modder eine weitere Figur/Waffe/Fahrzeug einbaut, hast du zwei DLLs, kannst aber nur eine davon laden.
Jonathan hat geschrieben: 15.11.2021, 23:17 d.h. den Spawn-Counter würde ich vermutlich in die Game.hpp packen und sobald ich das tue, kompiliert es wieder erstmal 30 Sekunden. Also alleine um Dinge schnell mal ohne neu zu kompilieren ändern zu können, lohnt das irgendwie schon.
Schrompf hat geschrieben: 15.11.2021, 08:24 Hot Reloading ist schon cool. Und bei Splatter hat auch mein Grafiker manches Mal die Skripte angepasst.
Ich habe die Zitate mal auf das verkürzt, was ich für das Pattern halte: "es lohnt sich, aber nur, weil die host Sprache C++ ist".

Zu Jonathan, ohne jetzt explizit alles zu zitieren: Mein Vorschlag für einen Vergleich wäre gewesen, hypothetisch genau das System, das du in AngelScript baust in C++ nachzubauen. Also so dass du für jedes Skript halt stattdessen 1:1 den äquivalenten C++ code hast. Mir ist unklar wie das mehr Aufwand sein können soll und ich finde es interessant, dass dein erster Reflex anscheinened ist darüber nachzudenken, was du in C++ anders gemacht hättest.

Zu Krishty: Ich verstehe das mit dem Modding, würde aber behaupten, dass es mit den begrenzten Resourcen eines Hobbys leichter ist den Sourcecode offenzulegen (drei klicks) als sich auch noch um Modability zu kümmern. Sage ich als jemand von dem kein einziges Hobbyprojekt online ist :)

Re: Scripting mit AngelScript

Verfasst: 16.11.2021, 08:38
von Alexander Kornrumpf
Krishty hat geschrieben: 15.11.2021, 22:11 Selbst wenn sich jemand die Mühe macht, C/C++ zu lernen

[...]

Skriptsprachen kann hingegen jeder lernen (meist durch Rumprobieren);
Das erfordert vielleicht doch noch eine separate Antwort, wenn auch orthogonal zum eigentlichen Thema. Es gehlt mir nicht um irgendeine Art von Elitismus aber ich bin überzeugt dass eben gerade _nicht_ jeder Programmieren, sagen wir bewusst _will_. Ich bin sehr sicher, dass es Leute gibt, die "sogar" beruflich Software entwickeln, aber ihren Weg nicht aus einer nassen Papiertüte heraus programmieren könnten, wenn ihr Leben davon abhinge. [Deren Strategie ist vermutlich auch "meist durch Rumprobieren"].

Ich würde vermuten, dass die Modder crowd, oder der Teil davon den du für dein Spiel gerne hättest, wenn er sich denn für dein Spiel interessieren würde, allein durch die signalisierte Bereitschaft Zeit zu investieren, näher an "kann C++ lernen" ist, als "jeder" daran ist Skriptsprachen lernen zu können.
Alexander Kornrumpf hat geschrieben: 15.11.2021, 08:02 Der Traum, dass Leute aus der Domain programmieren könnten, wenn man ihnen nur eine spezifische Sprache zur Verfügung stellen würde wird schnell zum Support-Albtraum.
Was ich hier ausdrücken wollte ist, dass es in gewisser Weise ehrenwert ist, dass Programmierer andere Menschen empowern wollen auch programmieren zu können, aber reale andere Menschen das in aller Regel nicht genügend wollen als dass es wirklich einen Sinn hätte. Also versuchen die Programmierer, die Hürden so weit zu senken, bis es passt, und das halte ich für ein aussichtsloses Unterfangen.

Re: Scripting mit AngelScript

Verfasst: 16.11.2021, 11:17
von Chromanoid
Ich glaube es geht nicht ungedingt um programmieren sondern eher um rumfrickeln. Das habe ich früher ein bisschen mit Clonk gemacht und schon ziemlich cool gefunden. Es hat mich bestärkt darin programmieren können zu wollen. Und das Spiel hat dann auch mehr Spaß gemacht, weil man sozusagen hinter die Kulissen schauen darf.

Aber noch mal zur Sinnhaftigkeit für einen selbst. Hättest Du das nicht genauso auch in C++ in eine Level1 Klasse gießen können? Also das was Alexander auch auch fragt.

Re: Scripting mit AngelScript

Verfasst: 16.11.2021, 11:30
von Jonathan
Alexander Kornrumpf hat geschrieben: 16.11.2021, 08:21 Zu Jonathan, ohne jetzt explizit alles zu zitieren: Mein Vorschlag für einen Vergleich wäre gewesen, hypothetisch genau das System, das du in AngelScript baust in C++ nachzubauen. Also so dass du für jedes Skript halt stattdessen 1:1 den äquivalenten C++ code hast. Mir ist unklar wie das mehr Aufwand sein können soll und ich finde es interessant, dass dein erster Reflex anscheinened ist darüber nachzudenken, was du in C++ anders gemacht hättest.
Alle Skripte komplett in C++ zu schreiben wäre schon kürzer und schneller gewesen. Also rein vom Schreiben her. Auch, weil das Skript-Interface ja ein wenig abgespeckt ist (und irgendwo auch sein soll), so dass man manche Dinge vielleicht direkt in C++ mit leicht weniger Aufwand lösen könnte. Man könnte vielleicht noch anführen, dass Skripte irgendwie nett Level-Logik von Spiellogik trennen, würde man alles direkt in C++ implementieren wäre es vermutlich verwurschtelter, außer man ist sehr diszipliniert (rigide Interfaces schaffen eben auch Struktur).

Aber letztendlich ist es der selbe Grund, weshalb ich z.B. die Fensterauflösung in eine config-Datei ausgelagert habe und nicht einfach in einer globalen Variable im Code habe. Das ist auch mehr Aufwand. Aber ich kann sie ändern, ohne irgendwas neu kompilieren zu müssen. Und ich kann das Spiel an andere weiter geben und die können auch die Auflösung in der config-Datei ändern.
Es ist ja nicht nur so, dass C++ nur langsam kompiliert. Wenn ich mein Spiel auf einem anderen Rechner kompilieren will, muss ich zuvor locker einen ganzen Tag lang gigabyteweise an Kram installieren und konfigurieren. AngelSkript macht meine exe vielleicht ein Megabyte größer (und im Vergleich zu allen Assets ist die Größe der Binaries wirklich schnurzpiepegal) und alles läuft direkt und bei jedem.
Und warum ich den config-Datei Vergleich gut finde: Es geht ja nicht darum, dass jeder gleich neue Logik schreibt. In den Skripten sind ja auch viele Konstakten (e.g. alle 10 Sekunden kommen neue Zombies), die man mit minimalem Vorwissen super schnell anpassen kann. Sowas würde man ja normalerweise eben auch in irgendwelche config-Dateien auslagern, aber solange die Skripte klein und übersichtlich sind, ist das da ja gut aufgehoben.

Ein Kumpel von mir macht seit einiger Zeit alles nur noch in C, weil das sau schnell kompiliert. Da braucht man dann auch keine config-Files für Kleinkram mehr, wenn es eh innerhalb von 1-3 Sekunden startet. Aber ich mache halt C++, und das ist halt langsam.
Zu dll's: Ja, könnte man gewiss auch benutzen, dann könnte man auch Code zur Laufzeit nachladen (auch ein ganz großer Vorteil!) und mit dem richtigen Interface wären die gewiss auch nicht so aufwändig auf anderen Rechnern zu kompilieren (weniger Abhängigkeiten, etc.). Aber ganz ehrlich, ob das alles schneller eingerichtet ist, als eine Skriptsprache ist auch irgendwie fraglich.

Re: Scripting mit AngelScript

Verfasst: 16.11.2021, 12:47
von Schrompf
Gute Zusammenfassung:

Für Dich allein: C++ ist schneller gemacht, Du ersparst Dir die Skriptanbindung. Hot Reloading geht über DLLs
Für Andere: Scripting ermöglicht es überhaupt erst.

Re: Scripting mit AngelScript

Verfasst: 16.11.2021, 14:33
von NytroX
Also meine Erfahrung ist eigentlich eine andere.
Ich bin zwar "erst" seit ein paar Jahrzehnten als Programmierer dabei, aber ich habe viele Menschen kennengelernt die sehr lernwillig waren was programmieren betrifft.
Entweder kann man die Sachen wirklich konfigurieren (JSON oder sowas), oder es ist komplexer. Sobald man diese Komplexität erreicht hat, sind einfach gute Tools unschlagbar, d.h. ich würde jederzeit sowas wie Go oder C# jeder anderen Script-Sprache vorziehen.

Selbst jemand der noch nie programmiert hat ist interessiert daran, wie er sein Problem gelöst bekommt. Und ich habe schon einige Menschen von "echten" Programmiersprachen überzeugen können.
Oft fängt es mit bash oder Excel(VB) an.
"Warum bin ich den ganzen Tag am debuggen, programmieren ist scheiße"
=> "Weil deine 'Programmiersprache' keine richtige ist und kein Typsystem hat. Normalerweise merkt die IDE SOFORT, wo dein Fehler ist."
"Zeig mal... [einige Zeit später ] ... Kann ich irgendwie dieses Go in Excel benutzen?"
=> "Nicht direkt, aber das bearbeiten der Excel-Datei geht mit dieser Libray so..."
"Es funktioniert! Wenn ich das vorher gewusst hätte, wäre ich letzten Monat schon fertig gewesen..."

Oder HTML:
=>"Wenn ich was falsch mache, warum sehen ich keinen Fehler?"

Oder 0ad:
=> Lasst uns mal eine Scriptsprache einbauen. Ach sch*t, das Game ist so langsam dass es unspielbar ist.

Das waren alles echte Beispiele aus meiner Erfahrung. Hätten die Leute es gleich richtig "gelernt", hätten sie viele Probleme weniger gehabt.
Man muss kein Meister-Programmierer sein oder werden wollen, aber Scripts haben mir und anderen bisher eigentlich gar nicht geholfen und erzeugen die schon genannte "Support-Hölle" für alle Beteiligten.

Das ist aber nur meine Welt, YMMV.

PS:
Aber man muss auch ganz klar sagen, dass die saubere Trennung zwischen Script und Basis-Game die API sauber hält.
Man macht das halt sauber, weil es nicht anders geht.
Das geht aber auch beim Programmiersprachen-Wechsel, z.B. C++ für Basis-Game, Go für "Scripts".
Go ist durch die Goroutines top geeignet, weil es viele Dinge löst, weshalb man Scripts haben will, wie etwa die Unabhängigkeit zum Render-/Game-Loop und den GC.