Ersteinmal die Aufteilung in die einzelnen Aufgabenteile:
Ein Skript muss bearbeitet werden können.
Ein Skript muss einem Gameobject zugewiesen werden können.
Ein zugewiesenes Skript muss ausgeführt werden können.
Das Bearbeiten ist fast Ausschließlich eine Sache des Interfaces und sollte nicht weiter problematisch sein.
Das Zuweisen sollte relativ unkompliziert sein. Ich persönlich würde so vorgehen, dass der entsprechenden Komponente der Einheit das Skript als String zugewiesen wird und die Komponente dann ihre Magie wirken lässt.
Komplexer ist da die Ausführung:
Die bereits erwähnte Komponente müsste dann wohl ein paar Dinge übernehmen:
Vorbereitung des Skriptcodes (z. B. Kompilieren, sofern notwendig) und ggf. das Melden von Fehlern (Wahlweise kann dieses auch schon an einer vorherigen Stelle oder durch eine andere Komponente durchgeführt werden.)
Aufrufen der im Skript definierten Funktionen/Methoden.
Einschränken der erreichbaren Funktionalität.
Die letzten beiden Dinge kann man auch mit "Bereitstellen der API" zusammenfassen. Wichtig für die Ausführung ist: was soll wann aufgerufen werden, was wird zur Verfügung gestellt und was wird als Rückgabe erwartet. (Das ist großteils dann deine Aufgabe, da du das Schreiben der KI zur Verfügung stellen willst, was unabhängig von der Ausführung von Skripten ist, die der Benutzer/Spieler geschrieben hat.)
Damit es nicht zu Sicherheitsproblemen kommen kann, sollte der Code nicht einfach alles einbinden können, was Mono so hergibt (keine Netzwerkkommunikation, kein Zugriff aufs Dateisystem, kein Absetzen von Systembefehlen, kein Ansprechen des Speakers auf dem Mainboard (dürfte in Mono meines Wissens ohnehin nicht gehen, aber mein Wissen kann auch veraltet sein...) etc.)
Damit neben der Malware auch Cheating unterbunden wird, sollte kein freier Zugriff auf die Szene, die GameObjects oder die Komponenten zugelassen werden.
Wichtig ist auch noch, dass der Code zu einem fest definierten Zeitpunkt initialisiert und freigegeben wird und auch, was der Code dabei machen darf/kann. In der Zeit dazwischen sollte es dem Spieler evtl. lieber nicht möglich sein, den Code zu ändern, da das nur zu Problemen führt (Syntaxfehler, Verlust des Zustands durch das erneute initialisieren, ...).
Und du musst an jeder Stelle, von der aus du Benutzercode aufrufst, mit Exceptions seitens des Benutzercodes rechnen (Division durch 0, NullReferenceException, Typfehler, ...). Wenn der Code einen Wert ermitteln sollte, muss dein Code an dieser Stelle einen Standardwert für einen Fehlerfall oder ein anderes Verhalten ("nichts tun") ausführen können.
Grundsätzlich dürfte es egal sein, ob Lua, Python, JavaScript (damit meine ich kein UnityScript
) oder vielleicht sogar C# angeboten wird. Für die erstgenannten braucht man lediglich einen Interpreter für Mono (IronPython, IronLua und IronJS), für C# müsste man es schaffen, dieses vorher mit Mono (worin die Unity-Komponenten ausgeführt werden) zu kompilieren, aber das sollte durchaus möglich sein.
Wenn du es bereits geschafft hast, Lua-Code auszuführen, dann kannst du auf gleichem Wege auch Benutzerdefinierten Code ausführen. Irgendwie hast du ja einen String mit Lua-Code irgendwo, mit dem du irgendwas machst, und mit der Eingabe des Spielers muss genau das nochmal gemacht werden.)
Das sind die Dinge, die mir auf Anhieb eingefallen sind. Es kann durchaus sein, dass es noch weitere Dinge gibt, auf die ich hier noch nicht hingewiesen habe.