Povray - Open Source --- MA_Screenplay


Veröffentlicht: 29.12.2024
Status: beta

Die Makrosammlung Screenplay erleichtert das Erstellen komplexer Animationen.
Ohne sie muss viel mit clock und clock_on gearbeitet werden - mit ihr können einen Vielzahl kurzer, unterschiedlicher Szenen übersichtlich zu einem langen Film zusammengeschnitten werden. Wie der Name schon sagt wird eine Art Drehbuch definiert, in dem genau vorgegeben wird, welches Objekt wann und wie erscheinen soll.

Beispiel-Dateien

Das Paket enthält nebst dem eigentlichen Include-File mehrere Beispieldateien.

ma_screenplay_example2_tutorial.pov ist als Tutorial aufgebaut, in dem ich bei jedem Schritt geschrieben habe, wann und was und warum. Innerhalb werden geometrische Objekte wie Kugeln, Rechtecke, Kegel und anderes durch die Gegend gescheucht, um die Erklärungen halbwegs übersichtlich zu gestalten.

ma_screenplay_example7_tussi.pov lässt richtig die Post abgehen, ich schicke meine tatenhungrige Raumsonde Tussi auf die Reise. Einmal von der Erde in den Weltraum, zum Mond und einer Landung auf jenem. Es wird mit komplexen Problemchen "aus dem Alltag" gearbeitet, anhand derer die Möglichkeiten von Screenplay deutlich werden. Auch in der Datei sind unzählige Kommentare eingebaut, die dort wahrscheinlich besser aufgehoben sind, als in dieser Doku.

Aufruf per Povray
Um sämtliche Bilder für eine Animation herzustellen bietet sich eine INI-Datei an. Das macht mir jedoch zuviel Arbeit, daher ziehe ich die kurze Variante über den Konsolen-Aufruf und entsprechende Parameter vor.

Beispiele:
povray +Ima_screenplay_example2_tutorial.pov +Oanim2/anim0- +w1200 +h900 -GR +A +KFI0 +KFF10000
povray +Ima_screenplay_example0_empty.pov +Oanim2/anim0- +w800 +h600 -GR -A +KFI0 +KFF10000

Siehe dazu http://www.povray.org/documentation/3.7.0/r3_2.html#r3_2.

Die hinteren Parameter +KFI0 +KFF10000 sorgen dafür, das das Skript mehr oder weniger endlos läuft.
Werden per Terminal mehr Bilder angefordert als die Angaben für Laufzeit und Frames-per-Second erlauben, bricht Screenplay mit einem simulierten Fehler ab. Seine Aufgabe hat es bis dahin erledigt, der Fehler ist entsprechend markiert und hat nichts Böses zu bedeuten.

Grundsätzlich sind im Kopf der Beispieldateien mehrere Terminal-Zeilen hinterlegt, die zum Erzeugen von Einzelbildern, aller Bilder, Film oder Anim-Gifs genutzt werden können.

Mini-Tutorial

Container

Trotz der genannten Beispiele inside soll es an ein paar Worten zum Einstieg nicht fehlen. Für alle hier gezeigten Code-Schnipsel kann folgender Container verwendet werden, der ebenfalls als Datei im Zip enthalten ist:
ma_screenplay_example0_empty.pov

#version 3.7;

#include "ma_helpers.inc"
#include "ma_screenplay.inc"

#declare LigVector          = <0,10,0>*100;
#declare CamLookAt          = <0,3,0>;
#declare CamLocation        = <1 , 5 ,-15>;
#declare CamAngle           = 60;

#declare SCR_FPS            = 10;
#declare SCR_SecondStart    = 0;
#declare SCR_SecondEnd      = 10;
#declare SCR_SecondFreeze   = 5;

#macro SCR_Init ()
    SCR_RegisterScene ("first", 10)
#end

#macro SCR_Timeline ( mSimulation )


	/* Fill in the example-lines here instead of this comment*/

	
	//Do some other stuff
	MA_Testarea (5)
	sky_sphere { pigment { color Gray05 } }
#end 

SCR_Start()
MA_CameraAndLight_Set()

Beispiel 1 - ganz simpel

Fangen wir Stück für Stück an und gucken, welche Arbeiten Screenplay erledigt.
Unten der entscheidenden Code, der an Stelle des Kommentars oben eingefügt werden sollte.

//Say what scene you define
SCR_SetScene("first")

//Define the screenplay
#local mName		= "sphere";
#local mProp		= "translate-y";
#local mValueStart	= 0;
#local mValueEnd	= 8;
#local mSecond0		= 0.0;
#local mSecond1		= 0.0;
#local mSecond2		= 10;
#local mSecond3		= 10;
SCR_SetNumber (mName, mProp, mValueStart, mValueEnd, "", mSecond0, mSecond1, mSecond2, mSecond3, 1)

//Handle the actor(s)
#local mTransY = SCR_GetNumber ("sphere", "", "translate-y",	0);
sphere {
	<0,0.5,0> 0.5
	texture { pigment { color rgb <0,1,0> } }
	translate <0,mTransY, 0>
}

Es wird nicht mehr gemacht als einer Kugel neue Werte für translate zuzuordnen. Der Film läuft 10 Sekunden, der Y-Wert von Translate wird während der 10 Sekunden linear hochgezählt. Ganz gleich wieviele Frames-per-Seconds eingestellt sind, das Makro SCR_SetNumber() erledigt die Berechnung und SCR_GetNumber() holt den Wert später wieder zum Vorschein.

Bei größeren Animation würden im oberen Block erst sämtliche Zuweisungen per Set kommen, ohne Rücksicht darauf, wie nun die Objekte erzeugt werden, aussehen und zustandekommen. Weiter unten käme die Erzeugung der Objekte mit Unterstützung von Get, alle in einen Rutsch.

Selbstreden braucht niemand mit derart vielen Variablen zu arbeiten, sämtliche Werte können den Makros direkt übergeben werden. Allerdings kann das schnell unübersichtlich werden.

Das hier wäre die Kurzschreibweise vom Beispiel oben.

SCR_SetScene("first")

SCR_SetNumber ("sphere", "translate-y", 0, 8, "", 0, 0, 10, 10, 1)

sphere {
	<0,0.5,0> 0.5
	texture { pigment { color rgb <0,1,0> } }
	translate <0, SCR_GetNumber ("sphere", "", "translate-y", 0), 0>
}

Was mir in dem Fall die Arbeit ein wenig erleichtert, um SCR_SetVector() und entsprechend SCR_GetVector() vorzustellen. Auf die Weise lässt sich die Murmel über alle drei Achsen bewegen. In unserem Bespiel bewegt sie sich von links-unten <-5,0,0> nach rechts-oben <+5,8,0>.

SCR_SetScene("first")

SCR_SetVector	("sphere", "translate", <-5,0,0>, <+5,8,0>, "", 0, 0, 10, 10, 1)

sphere {
	<0,0.5,0> 0.5
	texture {	pigment { color rgb <0,1,0> }		}
	translate SCR_GetVector ("sphere", "", "translate",	<0,0,0>)
}

Second-0 bis Second-3

Die Berechnung findet zwischen Second1 und Second2 statt. Nur dort werden die Werte von ValueStart bis ValueEnd hochgerechnet. Allerdings hat sich bei einigen Gelegenheiten gezeigt, das Werte für Zeitspannen vorher und nachher sinnvoll sind. Da Povray keine optionalen Parameter zulässt, hätte ich für diese Fälle neue Makros bzw. Makro-Header schreiben müssen, was unnötig kompliziert geworden wäre.

Somit wird zwischen Second0 und Second1 der Wert von ValueStart verwendet.
Welcher Wert zwischen Second2 und Second3 verwendet wird, hängt vom allerletzten Parameter EndState ab. Ist der = 0 (grüne Kugel) wird ValueStart wieder aktiv, ansonsten ValueEnd bis zum bitteren Ende zurückgegeben (blaue Box).

Vereinfachtes Beispiel:
#local mSecond0 = 0;
#local mSecond1 = 2;
#local mSecond2 = 8;
#local mSecond3 = 10;
SCR_SetVector	("sphere", "translate-y",  0, 8, "", mSecond0, mSecond1, mSecond2, mSecond3, 0)
SCR_SetVector	("box",    "translate-y",  0, 8, "", mSecond0, mSecond1, mSecond2, mSecond3, 1)
SCR_SetVector	("both",   "translate-x", -5, 5, "", 0, 0, 10,10, 1)

Der letzte Parameter von SCR_Get*() ist eine Default-Angabe, die zurückgegeben wird wenn kein berechneter Wert für die Variable aufzufinden ist. Eignet sich unter anderm gut, um herauszufinden warum sich irgendwas so gar nicht bewegen will.

#declare mFloat  = SCR_GetFloat  ("test", "", "translate-x", 0);
#declare mVector = SCR_GetVector ("test", "", "translate", <10,2,-3>);

Steigung bzw. Slope
Der bisher freigelassene String in der Mitte der SCR_Set*()-Makros gibt den Typen der Steigung an, mit der die Berechnung durchgeführt wird. Ohne Parameter wird linear gerechnet, bei 10 Schritten von 0 bis 10 somit in 1-er Schrittchen.

Angegeben wird dafür ein Komma- oder Semikolon-separierter Text, bestehend aus dem Typen der Steigung, der Wiederholungsrate und dem Startpunkt innerhalb der Steigung. Beispiel: "cosinus;2;1".

Genauere Informationen dazu siehe Doku zu MA_GetSlope() bzw. MA_GetSlope_ByString(), die in meinem Allzweck-Werkzeug MA_Helpers enthalten sind.

#declare mFloat  = SCR_GetFloat  ("test", "linear", "translate-x", 0);
#declare mVector = SCR_GetVector ("test", "cosinus++;1,0", "translate", <10,2,-3>);

Datentypen
Standard

Wie die genau zu verwenden sind und wie sie sich unterscheiden, wird in der Tutorial-Povray-Datei an lebenden Objekten erklärt.

Die String-Makros entscheiden sich vom Rest, da ein String nicht berechnet werden kann, sondern entweder da ist oder nicht. Eventuell wird sich an den Makro-Parametern in Zukunft etwas ändern, bisher hatte ich nicht genügend Einsatzmöglichkeiten, um die sinnigsten Parameter herauszufinden. Die drei möglichen Values werden zwischen den vier Seconds zurückgegeben.

SCR_SetNumber ("wall", "height",   mValueStart, mValueEnd, mSlope, mS0, mS1, mS2, mS3, 1)
SCR_SetFloat  ("wall", "height",   mValueStart, mValueEnd, mSlope, mS0, mS1, mS2, mS3, 1) //same as SetNumber
SCR_SetVector ("bird", "position", mValueStart, mValueEnd, mSlope, mS0, mS1, mS2, mS3, 1)
SCR_SetString ("text", "message ", mValue1, mValue2, mValue3,      mS0, mS1, mS2, mS3)

#local mHeight  = SCR_GetFloat  ("wall", "", "height"   , 0);
#local mPos     = SCR_GetVector ("bird", "", "position" , <1,1,1>);
#local mText    = SCR_GetString ("text", "", "message"  , "nothing to say");

Spline
Ein Spline muss vorher definiert werden und dem Makro als ein Parameter Value-Start übergeben werden. Der resultierende Wert wird mit GetVector() ermittelt, denn genau das will man schließlich zurückbekommen. Ein Makro GetSpline() gibt es nicht.
#local mSpline = spline { // linear_spline | quadratic_spline | cubic_spline | natural_spline
    quadratic_spline
    -0.25, <-5,+1,-3>
    0.00, <+5,+1,-5>
    0.25, <+5,+1,+5>
    0.50, <-5,+4,+4>
    0.55, <-4,+2,+3>
    0.75, <-5,+1,-3>
    1.00, <+5,+1,-5>
    1.25, <+5,+1,+5>
}
SCR_SetSpline ("flightpath", "translate", mValueStart, mSpline, mSlope, mS0, mS1, mS2, mS3, 1)

ShowBigRocket ( SCR_GetVector ("flightpath", "", "translate",	<0,0,0> ) )

Mixed

Im Mixed-Typ können unzählige Parameter in einem String untergebracht werden. Für viele Dinge können Vektoren zweckentfremdet werden, bei mehr als drei bzw. fünf Dimensionen wird es in denen langsam eng. Mir fällt in dem Zusammenhang als erstes ein Roboterarm ein, der Fuß und Kopf drehen und dazu Gelenke zwischen den Armen neigen kann. Da kommen gerne ein paar mehr Angaben zusammen.

/* translate (3 params), rotation foot, tilt-1, tilt-2, tilt-3, rotation head */
#local mMixed1 = "  0,  0,  -20,  0,  0,  0,  0,  0";	
#local mMixed2 = "  0,  0,  100, 90, 20,180, 90, 45";
SCR_SetMixed ("robotic-arm", "all", mMixed1, mMixed2, mSlope, mS0, mS1, mS2, mS3, 1)

#local mTrans   = SCR_GetMixed_Vector ("robotic-arm", "", "all", 0, 2, <0,0,0>); //first three parameter
#local mRotate1 = SCR_GetMixed_Number ("robotic-arm", "", "all", 3, 0);
#local mTilt1   = SCR_GetMixed_Number ("robotic-arm", "", "all", 4, 0);
#local mTilt1   = SCR_GetMixed_Number ("robotic-arm", "", "all", 5, 0);
#local mTilt1   = SCR_GetMixed_Number ("robotic-arm", "", "all", 6, 0);
#local mRotate2 = SCR_GetMixed_Number ("robotic-arm", "", "all", 7, 0);

ShowMyAmazingRoboticArm ( mTrans, mRotate1, mTilt1, mTilt2, mTilt3, mRotate2 )

Marker
Als Spezial-Typ gibt es noch SetMarker und GetMarker, mit denen unkompliziert eine Bereich in der Zeitleiste markiert werden kann, ohne das etwas berechnet werden soll. Ist die Anwesenheit eines Sternenhimmels zwischen Sekunde 10 und 30 erforderlich, würden die entsprechenden Zeilen wie folgt aussehen:

SCR_SetMarker("stars", 10, 30)

#if (SCR_GetMarker("stars"))
    ShowStars()
#end

Der ominöse zweite Parameter

Der zweite Parameter der Get-Makros wurde in den Beispielen immer freigelassen.
#local mHeight  = SCR_GetFloat  ("wall", "", "height"   , 0);

Er ist dafür gedacht, um Objekte, die innerhalb von Objekten erzeugt werden, simpel mit Namen zu versehen. Als Beispiel dient das Raumschiff Tussi, das intern unter anderem einen Hauptantrieb erzeugt und sich an die Unterseite heftet.
Der Aufruf der Tussi ist:

#local mTussi = TU_GetTussi ("tussi", "", mTussiParams, mTussiCenterType)

Makro-Kopf und erste Zeile in ihr sehen wiefolgt aus (fürs Beispiel auf mehrere Zeilen verteilt):
#macro TU_GetTussi (mParentName, mNewName, xParams, mCenterType)
    #ifndef( MA_Screenplay_Temp ) /* checks if screenplay is included */
        #local mMyName = ""; /* default if Screenplay isnt available */
    #else
        #local mMyName = SCR_GetName(mParentName, mNewName);
    #end
    ...

Der Name mMyName wird aus den beiden angelieferten Namen gebildet, mit "/" separiert.
In dem Fall ist mMyName = "tussi".
Weiter unten wird der Hauptantrieb erzeugt.

#local mEngine = TU_MainEngine ( mMyName, "engine", mParams)

Der MainEngine hat wiederum einen Makro-Kopf und dieselbe erste Zeile:
#macro TU_MainEngine ( mParentName, mNewName, mParams)
    #ifndef( MA_Screenplay_Temp ) #local mMyName = ""; #else #local mMyName = SCR_GetName(mParentName, mNewName); #end

Sein mMyName ist nun "tussi/engine" und könnte somit wieder an Sub-Objekte weitergegeben werden.
In dem Fall kann die Tussi ihren Namen einfach separat übergeben, der zweite Teil ist der Name der des Antriebs.

Okay ... zugegeben, ganz kapiert habe ich meine Beschreibung beim Korrekturlesen selbst nicht, ich empfehle einen Blick in das Tussi-Tutorial.

Das Ganze erspart im Prinzip nur eine Reihe von Concats und mag bei lediglich zwei Objekt-Ebenen übertrieben wirken. Da ich es jedoch mit Sub- und Sub-Sub-Sub-Objekten zu tun habe, übergebe ich lieber einen leeren Parameter mehr als ständig zu concatten.
Denken wir zum Beispiel an meine geplante ISS. Der Entfernungsmesser an einem Teleskop des Columbus-Moduls könnte heißen, "iss/columbus/tele3/laser" und würde mit diesem Namen in den Set- und Get-Makros verwendet werden.

Ein Beispiel das meine Denkweise hoffentlich verdeutlicht ist die dritte Szene im Tutorial, genannt "Magic-Object".

Interne Handhabung
Intern werden relevante, die Zeitleiste betreffende Daten in einem großen Array gespeichert, das sich in der Variable SCR_Data befindet. Das hat im Normalfall viele Zeilen, aber nur drei Spalten.

Als da wären:
- Name des Objekts       "sphere"
- Name der Eigenschaft   "translate"
- der Wert               "<0,75.333, -197.4545>"

Bei jedem aufgerufenem Set-Makro wird als erstes geprüft, ob der aktuell zu rendernde Frame überhaupt zwischen Second0 und Second3 liegt. Tut er das nicht, wird die Arbeit sofort beendet und das Set-Kommando ignoriert.

Beispiel: Es soll ein Frame in Sekunde 35 gerendert werden und folgender Aufruf kommt rein:

SCR_SetVector	("sphere", "translate", <-5,0,0>, <+5,8,0>, "", 10, 10, 20, 20, 1)

Die Anweisung spielt zwischen Sekunde 10 und 20. Sekunde 35 ist gerade gefragt, die Anweisung somit nicht relevant und braucht nicht länger beachtet zu werden. Für diese Kombination aus Name und Eigenschaft (sphere und translate) wird der in SCR_Get*() angegebenen Default zurückgegeben. Tatsächlich werden besonders in größeren Animationen die allermeisten Set-Anweisungen ignoriert, es finden kaum Berechnungen statt und nur wenig landet im großen Array.

Wir sind noch immer in Sekunde 35 und Folgendes tut sich:
SCR_SetVector	("cylinder", "rotate", <0,0,0>, <180,0,60>, "linear", 30, 32, 38, 40, 1)

Das Set-Makro stellt nicht nur fest, das die Anweisung bearbeitet werden muss, sondern das wir uns zwischen 32 und 38 Sekunden befinden. Entsprechend müssen die Werte (hier <0,0,0> und <180,0,60>) linear über einen Zeitraum von 6 Sekunden (38-32) zu einem Endresultat verrechnet werden.

Nur dieses Endresultat landet im Array SCR_Data, zusammen mit Name "cylinder" und Eigenschaft "rotate".

Nun befinden wir uns im unteren Teil, wo die Helden des Films fragen, was sie zu tun haben.

SCR_GetVector ("sphere", "", "translate",	<0,0,0>)
SCR_GetVector ("cylinder", "", "rotate",	<0,0,0>)

Die Kugel (sphere) landet auf <0,0,0>, bekommt den Default-Wert zurückgeliefert.
Der Zylinder dagegen bekommt einen berechneten Wert geliefert, in dem Fall <90,0,30>.

Hier der Inhalt des Arrays, den mein Tussi-Beispiel in Sekunde 45 produziert.

----------------------------------------------------
Comment: SCR_Data
Rows   : 200
Cols   : 3
----------------------------------------------------
 stars, INTERNAL-marker,                    1.000000
   lig,          vector, 0.0000,0.0000,-1000000.0000
   sun, INTERNAL-marker,                    1.000000
 tussi,       translate,        0.7007,0.0000,0.0000
 tussi,          rotate,    0.0000,-45.0000,-90.0000
  moon,          rotate,       0.0000,-0.0201,0.0000
   cam,        location,       0.1752,5.0000,50.0000
   cam,          lookat,        0.7007,0.0000,0.0000
----------------------------------------------------

Anders sieht es da schon in Sekunde 16 aus

----------------------------------------------------------
Comment: SCR_Data
Rows   : 200
Cols   : 3
----------------------------------------------------------
          stars, INTERNAL-marker,                 1.000000
   tussi/engine,          active,                 1.000000
 tussi/booster1,          active,                 1.000000
 tussi/booster2,          active,                 1.000000
 tussi/booster3,          active,                 1.000000
 tussi/booster4,          active,                 1.000000
          tussi,       translate,   0.0000,389.3215,0.0000
            cam,        location,   1.0000,5.0000,-25.0000
            cam,          lookat,   0.0000,390.2069,0.0000
            cam,           angle,                60.000000
            cam,        location, 0.0000,379.2881,-50.0000
        bluesky,        transmit,                 0.801603
     tussi/leg1,       stretched,                 0.000000
     tussi/leg2,       stretched,                 0.000000
     tussi/leg3,       stretched,                 0.000000
     tussi/leg4,       stretched,                 0.000000
 tussi/booster1,      separation,                 5.030060
 tussi/booster2,      separation,                 8.036072
 tussi/booster3,      separation,                 5.030060
 tussi/booster4,      separation,                 8.036072
----------------------------------------------------------
 

Trotz an die hundert Set-Aufrufen im Tussi-Beispiel sieht es im Array halbwegs übersichtlich aus.
Kann jedoch auch ganz anders aussehen. Je mehr Objekte anwesend sind und je mehr sich tut, umso voller wird das Array.

Szenen

Alle Szenen des Films werden in einem separaten Makro definiert.

#macro SCR_Init ()
    SCR_RegisterScene ("first",  20)
    SCR_RegisterScene ("slopes", 15)
    SCR_RegisterScene ("magic",  30)
#end
asdf

Der erste Parameter definiert den Namen der Szene, der zweite die Dauer in Sekunden. Auf diese Weise sind alle weiteren Screenplay-Makros darüber informiert, welche Szenen es gibt und wann sie im Film vorkommen.

Oben im Drehbuch-Teil wird vor den ganzen Angaben der Zeitleiste gesagt, zu welcher Szene die Angabe gehört. Das hat unter anderem den Vorteil, das die Sekunden innerhalb einer Szene immer bei 0 anfangen und nicht fortlaufend aufaddiert werden müssen. Das ist besonders dann eine nette Sache, wenn sich die Dauer einer Szene während der Entwicklung ändert, da nicht in jeder Szene wieder alles geändert werden muss.

    SCR_SetScene  ("first")
    SCR_SetNumber ("sphere", "translate", <0,0,0>, <3,0,20>, "cosinus++", 0, 0, 20, 20, 1) 
    SCR_SetNumber ("box", "rotate", <0,0,0>, <180,0,360>,    "linear"   , 0, 0, 10, 20, 1) 
    ...
    
    SCR_SetScene  ("second")
    SCR_SetNumber ("car", "move", <0,0,0>, <3,0,20>, "bounce+", 0, 0, 15, 15, 1) 
    ...
    

Screenplay starten

Fast am Ende der Povray-Datei muss Screenplay letztendlich gestartet werden.

/* Execute Screenplay */
SCR_Start()

Diese Zeile erweckt die Vorarbeiten zum Leben. Sofern alles ohne Fehler kodiert wurde.

Aufteilung der Anweisungen

Die Erstellung einer längeren Animation muss man sich tatsächlich wie in Hollywood vorstellen. Es gibt einen Drehbuchautor, der die Geschichte schreibt, und auf der anderen Seite die Schauspieler, die diesen Anweisungen folgen.

Daher sollte man sich grundsätzlich zwei strikt voneinander abgetrennte Bereiche angewöhnen.

Oben das Drehbuch, die Zeitleiste - der virtuelle Teil
Es geht nur um die Definitionen, wann was getan werden soll. Um nichts anderes! Wie die späteren Objekte erzeugt werden und was da noch für ein Aufwand hintersteckt, ist im oberen Teil völlig egal. Oben werden nur Anweisungen gegeben. Frei nach dem Motto: Wie verschicke ich eine Giraffe? Einpacken, abholen lassen, fertig.
Im Prinzip kann diesen oberen Teil jemand schreiben, der von Povray nur begrenzt Ahnung hat.

Unten die Objekte bzw. die Schauspieler, die Helden des Films - die praktische Umsetzung
Alle Objekte folgen blind dem weit im voraus erstellten Drehbuch und kümmern sich nur darum, in der richtigen Szene gut auszusehen. Wann sie wo sein müssen ist hinter vielen ominösen SCR_Get*()-Makros versteckt, gewissermaßen die Zurufe der Regie oder Autoren. Mit der ganzen Berechnung haben die unten nichts zu tun, die sollen sich auf Befehl (von oben!) irgendwo hinstellen und aussehen wie gewünscht, wohin ist denen völlig wurscht und so gehört sich das. Außerdem interessiert dort die Reihenfolge nicht, wie was erzeugt wird. Vielmehr kommt es auf Wiederverwendbarkeit der Objekte an, Code sparen und aufeinander abstimmen.
Und ihr könnt ihr euch dann bewusst werden, das Giraffe und per-Post doch was komplizierter ist.
Wie im wahren Arbeitsleben: "Martin, mach mal kurz die Kleinigkeit, geht doch fix, oder?"

---

Beides miteinander vermischen, da findet man schnell nichts mehr wieder.
Geht, interessiert das Makro nicht, ist trotzdem keine gute Idee.
Könnt es gerne ausprobieren, wünsche viel Erfolg.

Das wars erstmal
Diese Doku könnte noch seitenlang weitergehen, jedoch fehlt mir dafür die Zeit.
Wer neugierig ist, sollte einen Blick ins Zip und die darin enthaltenen Beispieldateien werfen.

Download

ma_screenplay.7z => 180 kB 7-Zip - Version 1.0 vom 29. Dezember 2024

Kommentare

Keine Kommentare bisher.
* Die Angabe von E-Mail und Webseite ist optional.
Die E-Mail wird nicht mit dem Kommentartext angezeigt sondern nur intern gespeichert, sollte lediglich bei Kontaktwunsch angegeben werden.
der deutschen Seite
der englischen Seite
beiden Seiten