Construction Set Morrowind-Scriptkurs - Lektion 4

Killfetzer

Super-Moderator
Teammitglied
4. Bewegung von Objekten - Teil 1

Heute beschäftigen wir uns zum ersten Mal mit der Bewegung von Objekten. Dies ist natürlich ein riesiges Thema, deswegen gibt es heute nur den ersten Teil.

Man kann mit der Bewegung von Gegenständen eine größere Interaktivität in Modifikationen bringen. Statt einer Teleporttür kann man auch ein sich bewegendes Fallgitter oder statt einer Treppe einen Lift benutzen. Um genau diese Dinge wird es heute gehen.


Tatsächlicher Ablauf von Scripten

Zunächst einmal müssen wir ein wenig Theorie über Scripte in Morrowind machen. Für die bisherigen Scripte war es nicht wichtig, wie genau Morrowind Scripte behandelt, jetzt wird dies mit eine entscheidende Bedeutung bekommen.

Morrowind lässt jedes Script, dass sich auf irgendeinem Gegenstand in der aktuellen Zelle befindet in jedem Frame (fps = Frames per Second, also (Halb-)Bilder pro Sekunde) einmal durchlaufen. Auf den meisten Rechnern wird also jedes Script einige Dutzend Male pro Sekunde ausgeführt!

Was ziehen wir daraus für Schlussfolgerungen? Zum Ersten muss ein Script nicht erst aktiviert werden, es läuft automatisch, sobald man die Zelle des entsprechenden Objekts betritt. Das ist normalerweise sehr praktisch, wie wir gleich sehen werden. Zum Zweiten muss man aber auch sicherstellen, dass Aktionen nur einmal ausgeführt werden und nicht in jedem Frame erneut beziehungsweise das genaue Gegenteil, nämlich eine Funktion jeden Frame aufzurufen.


Bewegung von Gegenständen

Nur Gegenstände erscheinen und verschwinden zu lassen, wird natürlich schnell langweilig, man möchte sie auch bewegen. Wie funktioniert Bewegung in Morrowind? Genau genommen gibt es keine echte Bewegung. Objekte werden in jedem Frame an eine leicht veränderte Position gesetzt. Da das menschliche Auge aber nur etwa 20 Bilder pro Sekunde sehen kann (und wir hoffentlich eine viel höhere Framerate haben), nehmen wir diese vielen kleinen Sprünge als kontinuierliche Bewegung wahr.

Da Morrowind seine Scripte sowieso jeden Frame ausführt, nutzen wir dies bei Bewegungen, wir lassen die Bewegungsfunktion einfach in jedem Durchlauf des Scripts eine kleine Bewegung machen. Allerdings gibt es auch immer Situationen, in denen man keine Bewegung haben möchte. Zum Beispiel pausieren die normalen Animationen, wenn man sich im Inventar befindet, Scripte nicht. Also müssen wir dieses Pausieren per Hand erzeugen.

MenuMode

Dazu gibt es die äußerst praktische Funktion MenuMode. Diese liefert immer dann eine 1, wenn irgendein Menü geöffnet ist. Ob dies nun das Spielmenü (ESC), das Inventar, ein Dialog oder eine Messagebox ist, macht keinen Unterschied. Mit folgender Konstruktion kann man die Ausführung eines Scripts unterbrechen, wenn ein Menü geöffnet wird:

Code:
if ( MenuMode == 1 )
  return
endif

Dies ist einer der wichtigsten Codebrocken in Morrowind Script. In vielen Scripten ist dies der erste Block, der nach der Variablendeklaration steht.

Move
MoveWorld


Die Bewegung in Morrowind findet in einem dreidimensionalen, kartesischen Koordinatensystem statt (ja, das ist Mathe 😉). Die Achsen dieses Koordinatensystems werden mit X, Y und Z bezeichnet. X läuft dabei von West nach Ost (in Außenzellen), Y von Süd nach Nord (in Außenzellen) und Z von unten nach oben.
Außerdem unterscheidet Morrowind zwischen einem absoluten und einem relativen Koordinatensystem. Das absolute Koordinatensystem ist das der Welt mit den Eigenschaften, die oben beschrieben sind. Für jedes Objekt existiert aber auch ein eigenes relatives Koordinatensystem. In diesem sind die Achsen entsprechend der Drehung des Objekts in der Welt mitgedreht.

Scriptkurs_Koordinatensysteme1.gif


Vergleich des absoulten Welt-Koordinatensystems (schwarz) und des relativen Koordinatensystems eines Gegenstands (rot).
Das relative Koordinatensystem mit X' und Y' ist aus einer Drehung des Gegenstandes um die Z-Achse um 45° entstanden.

Wenn das Objekt nicht gedreht ist (also alle Winkel gleich 0 sind), sind das absolute und das relative Koordinatensystem gleich.

Die beiden Funktionen Move und MoveWorld werden so aufgerufen:

Code:
Move, axis, value
MoveWorld, axis, value

---

Move, Z, 100
MoveWorld, X, -200

Diese Funktionen bewegen das aufrufende Objekt mit der in value angegebenen Geschwindigkeit bezüglich der in axis angegebenen Achse. Für axis werden die Buchstaben X, Y und Z akzeptiert. value verarbeitet float-Werte, es können aber auch beliebige andere Werte angegeben werden. Die Werte werden als Editoreinheiten pro Sekunde interpretiert. Tatsächlich wird bei jedem Aufruf nur der entsprechende Bruchteil der Bewegung ausgeführt, die der Zeit seit dem letzten Frame entspricht. Um eine kontinuierliche Bewegung zu erreichen, muss die Funktion also jeden Frame aufgerufen werden. value akzeptiert (leider) keine Variablen!
Die Funktionen haben keine Auswirkungen auf NPCs, Kreaturen oder den Spieler.

Move bewegt das Objekt entlang der relativen Achse. Diese Funktion kann zum Beispiel dafür benutzt werden, dass sich ein Pfeil immer in die Richtung seiner Spitze bewegt, egal in welche Richtung er gedreht ist.

MoveWorld bewegt das Objekt entlang der absoluten Achse. Diese Funktion wird weitaus häufiger benutzt werden und sorgt zum Beispiel dafür, dass sich ein Objekt immer nach oben bewegt, unabhängig davon wie es gedreht ist.

Im obigen Beispiel ist das Objekt um 45° um die Z-Achse gedreht worden. Ein Move, X, 100 würde das Objekt entlang der rotmarkierten X'-Achse bewegen. Die Funktion MoveWorld, X, 100 hingegen bewegt das Objekt entlang der schwarzen X-Achse.

Um ein Gefühl für die Geschwindigkeit zu bekommen, muss man wissen, dass eine Editoreinheit 1,42 cm entspricht. Also entsprechen etwa 20 Editoreinheiten pro Sekunde einem km/h. Geschwindigkeiten über 400 Editoreinheiten pro Sekunde (20 km/h) sollten vermieden werden, weil sie meistens abgehackt aussehen, da die Abstände zwischen den einzelnen Sprungpositionen zu groß werden.

Nun können wir also eine Bewegung starten, wir können sie sogar pausieren, wenn ein Menü geöffnet wird, aber so wird das Objekt eine endlose Bewegung ausführen und irgendwann im Nichts verschwinden. Wie können wir die Bewegung also aufhalten?

Dazu stehen uns grundsätzlich zwei Optionen zur Verfügung. Entweder stoppen wir die Bewegung, wenn das Objekt einen bestimmten Punkt erreicht hat oder wir lassen die Bewegung nur eine bestimmte Zeit lang laufen.

GetPos

Mit dieser Funktion kann man die Position des aufrufenden Objekts bezüglich der angegebenen Achse abfragen:

Code:
float xpos

set xpos to GetPos, X

Statt dem X kann man natürlich auch wieder Y und Z abfragen. Der Rückgabewert der Funktion ist eine float-Größe (also eine Dezimalkomma-Zahl). Über eine if-Abfrage kann man nun prüfen, ob das Objekt bereits an seinem Zielpunkt angelangt ist (natürlich funktioniert hier auch ein while, aber das will ich heute nicht auch noch einführen). Hier ein Beispielscript:

Code:
begin kf_sk_bewegung_bsp1_script

short state
float zpos

if ( MenuMode == 1 )
  return
endif

if ( state == 0 )
  if ( OnActivate == 1 )
    MessageBox, "Nach oben!"

    set zpos to GetPos, Z       ;berechne eine obere Schranke für die Bewegung
    set zpos to zpos + 300

    set state to 1
  endif
elseif ( state == 1 )
  if ( GetPos, Z <= zpos )
    MoveWorld, Z, 100
  else
    set state to 2          ;sorgt dafür, dass man nur einmal nach oben fahren kann
  endif
endif

end

Statt der Konstruktion mit zpos könnte man natürlich auch direkt die Koordinaten aus dem CS einsetzen, die man sich als Zielpunkte ausgesucht hat. Diese Formulierung ist aber universell anwendbar, da ich ja nicht weiß, wo in der Spielwelt ihr das Script ausprobieren wollt.

GetSecondsPassed

Die zweite Möglichkeit, um Bewegung zu begrenzen, liegt in der Zeitmessung. Bisher kennen wir dafür die globale Variable GameHour, hiermit ist es aber nicht möglich wirklich kurze Zeitspannen genau zu messen. Dafür gibt es die Funktion GetSecondsPassed. Diese liefert die vergangenen Sekunden(-bruchteile) seit dem letzten Frame zurück. Dies stellt die einzige Scriptmöglichkeit dar, kurze Zeitspannen zu messen. Hier noch einmal ein Script mit der gleichen Funktion wie oben, diesmal mit GetSecondsPassed implementiert:

Code:
begin kf_sk_bewegung_bsp2_script

short state
float timer

if ( MenuMode == 1 )
  return
endif

if ( state == 0 )
  if ( OnActivate == 1 )
    MessageBox, "Nach oben!"
    set state to 1
  endif
elseif ( state == 1 )
  set timer to timer + GetSecondsPassed
  MoveWorld, Z, 100
  if ( timer >= 3 )    ;3 Sekunden mal 100 Einheiten pro Sekunde = 300 Einheiten nach oben
    set state to 2          ;sorgt dafür, dass man nur einmal nach oben fahren kann
  endif
endif

end


Aufgaben

  1. Probiert die Beispielscripte aus und versteht, wie sie funktionieren.
  2. Erstellt einen einfachen Aufzug, der ständig hoch und runter fährt und an jedem Ende jeweils kurz anhält.
  3. Probiert, was passiert, wenn ihr die MenuMode-Abfrage weglasst und auf dem Aufzug das Inventar öffnet.
  4. Warum muss timer im zweiten Beispielscript eine float-Variable sein?
  5. Erstellt einen Activator, der euch beim Aktivieren die aktuelle Framerate (fps) ausgibt.
  6. Erstellt einen Aufzug, der anfangs still steht. Wenn er aktiviert wird, bewegt sich der Aufzug nach oben, wartet dort einen Moment und fährt wieder runter.
  7. Erstellt einen Aufzug, der nach Aktivieren nach oben beziehungsweise unten fährt und dort bleibt, bis er erneut aktiviert wird. Danach fährt er wieder in seine andere Position und wartet dort.

Lösungen


-
Im nächsten Teil beschäftigen wir uns mit der Rotation von Gegenständen. Dies ist quasi nur eine Wiederholung der Bewegungsfunktionen, da sie prinzipiell identisch funktionieren. Außerdem werden wir Gegenstände aus der Ferne bewegen beziehungsweise die Bewegung eines Objekts von einem anderen steuern lassen.
 
Zuletzt bearbeitet:
  • Like
Reaktionen: Mercarryn
Zu deinen Beispielskripten:
Ich verstehe die Funktionen im Moment so, dass sie das Objekt in jedem Frame um den angegebenen Wert verschieben. Ist das nicht ein bisschen unbequem, da die Framerate auf jedem Rechner zu anderen Zeiten unterschiedlich ist? In meiner Theorie müsste ich den Wert also noch mit "getSecondsPassed" multiplizieren, um das Objekt um den angegebenen Wert in einer Sekunde zu bewegen.

Der Timer muss eine Float sein, weil er sonst bei
Code:
set timer to (timer + getSecondsPassed)
immer gleich bleiben würde (es sei denn die Framerate ist so gering, dass Morrowind nichtmal ein Bild pro Sekunde schafft :roll: ).

Code:
Begin welche_framerate

float fps

if onActivate
 set fps to (1 / getSecondsPassed)
 messagebox "Die aktuelle Framerate beträgt %.0f FPs", fps
endif

end

Der Rest folgt, wenn mein Einwand geklärt ist...
 
Zu deinen Beispielskripten:
Ich verstehe die Funktionen im Moment so, dass sie das Objekt in jedem Frame um den angegebenen Wert verschieben. Ist das nicht ein bisschen unbequem, da die Framerate auf jedem Rechner zu anderen Zeiten unterschiedlich ist? In meiner Theorie müsste ich den Wert also noch mit "getSecondsPassed" multiplizieren, um das Objekt um den angegebenen Wert in einer Sekunde zu bewegen.

Gut mitgedacht 🙂
Aber nein, dass macht die Move-Funktion schon alleine. Du gibst Geschwindigkeit pro Sekunde an. Die Umrechnung wie viel das Objekt dann in jedem Frame bewegt werden muss, stellt die Funktion schon alleine an. Irgendwas muss die ja auch noch tun 😉

Deine Lösungen passen, auch wenn dein Script äußerst unschön formatiert ist. Dass es läuft, ist in dem Fall wohl eher zufallsabhängig, wenn ich mir überlege, wie kleinlich Morrowind Script sonst mit fehlenden Klammern ist. 😛
 
Ach, das steht ja sogar in dem Text... Hab ich wohl überlesen.
Dann folgen mal die anderen Aufgaben:

Code:
Begin Lift_Loop

short break
short state
float timer

if MenuMode
 return
endif

set timer to (timer + getSecondsPassed)
if break
 if timer >= 3   [COLOR="Red"]; 3 Sekunde Pause[/COLOR]
  set break to 0
  set timer to 0
 else
  return
 endif
endif

if state
 moveWorld z, -50
 if timer >= 10  [COLOR="Red"]; 10 Sekunden Abwärtsbewegung[/COLOR]
  set break to 1
  set state to 0
  set timer to 0
 endif
else
 moveWorld z, 50
 if timer >= 10  [COLOR="Red"]; 10 Sekunden Aufwärtsbewegung[/COLOR]
  set break to 1
  set state to 1
  set timer to 0
 endif
endif

end

Code:
Begin Lift_Activate_Once

short activated
short state
short break
float timer

if MenuMode
 return
endif

if activated
 set timer to (timer + getSecondsPassed)
 if break
  if timer >= 3
   set break to 0
   set timer to 0
  else
   return
  endif
 endif

 if state
  moveWorld z, -50
  if timer >= 10
   set state to 0
   set activated to 0
   set timer to 0
  endif
 else
  moveWorld z, 50
  if timer >= 10
   set state to 1
   set break to 1
   set timer to 0
  endif
 endif

else
 if onActivate
  set activated to 1
 endif
endif

end

Code:
Begin Lift_Activate_Twice

short activated
short state
float timer

if MenuMode
 return
endif

if activated
 set timer to (timer + getSecondsPassed)
 if state
  moveWorld z, -50
  if timer >= 10
   set activated to 0
   set state to 0
   set timer to 0
  endif
 else
  moveWorld z, 50
  if timer >= 10
   set activated to 0
   set state to 1
   set timer to 0
  endif
 endif
else
 if onActivate
  set activated to 1
 endif
endif

end
 
Ich hab mal aus dem Kopf gecoded... hoffentlich hab ich in der Eile nichts übersehen... :-D
Code:
Begin Lift_AutoUpDown
short State
float Timer

IF ( MenuMode != 0 ) ;im Menu nichts machen
  return
EndIF

set Timer to (Timer + GetSecondsPassed )

IF ( State < 0 )
  IF ( Timer < 3 )
    return
  EndIF
  set State to ( State * State )
  set Timer to 0
EndIF

IF ( State == 4 )
  moveWorld z, -50
Else
  moveWorld z, 50
EndIF

IF Timer >= 10)
  set Timer to 0
  IF State == 4 )
    set State to -1
  Else
    set State to -2
  EndIF
EndIF

end
Antwort:
Weil das Schreiben in eine Short/Long Variable den Dezimalteil automatisch abschneidet. Der Timer würde sich damit nicht verändern, es sei denn die fps wäre so unterirdisch, dass GetSecondsPassed ganzzahlige Werte liefert...
Code:
Begin Lift_UpDownOnPush
short State
float Timer

IF ( MenuMode != 0 ) ;im Menu nichts machen
  return
ElseIF ( State == 0 )
  IF ( OnActivate == 0 )
    return  
  EndIF
  set State to 1
EndIF

set Timer to (Timer + GetSecondsPassed )

IF       ( State == 1 )
  MoveWorld Z 50
ElseIF ( State == 2 )
  IF ( Timer >= 3 )
    set Timer to 10
  EndIF
ElseIF ( State == 3 )
  MoveWorld Z -50
EndIF

IF ( Timer >= 10 )
  set Timer to 0
  set State to ( State + 1 )
  IF ( State == 4 )
    set State to 0
    SetAtStart
  EndIF
EndIF

end
Code:
Begin Lift_MoveOnPush
short State
short Direction
float Timer

IF ( MenuMode != 0 ) ;im Menu nichts machen
  return
ElseIF ( State == 0 )
  IF ( OnActivate == 0 )
    return  
  EndIF
  set State to 1
EndIF

set Timer to (Timer + GetSecondsPassed )

IF ( Direction == -1 )
  MoveWorld Z -50
Else
  MoveWorld Z 50
EndIF

IF ( Timer >= 10 )
  set State to 0
  set Timer to 0
  IF ( Direction == -1 )
   set Direction to 1
  Else
   set Direction to -1
  EndIF
EndIF

end
 
Zuletzt bearbeitet:
Oh man, die Aufgaben sind ja höllisch schwer. Ich bin dran, also noch nicht auflösen oder gar die fünfte Lektion starten.

a. Ich will das in meinem Script über die genauen Koordinaten machen, wie muss der GetPos-Befehl dann aussehen? Ich stelle mir das nämlich leichter vor, denn dann muss ich nur die Koordinaten eingeben, anstatt einzuschätzen, wieviele Einheiten, also cm der Weg von Punkt A zu Punkt B wohl sind.
b. Eine Verständnisfrage: Im ersten Beispielscript geht das Objekt so lange um 100 Einheiten nach oben, bis die 300 erreicht ist, oder? Also 3 mal, also drei Frames, oder?
c. Ich habe erstmal einen Aufzug ohne Timer gebaut (ja, ich bin noch bei dem ersten Aufzugsversuch), der als Grundgerüst für die Varianten der anderen Aufgaben dienen soll. Aber das Ding wackelt nur munter vor sich hin. Was mache ich falsch?
Code:
Begin Teri_L4_Aufzug1_Script

short status
float unten
float oben

if ( MenuMode == 1 )
  return
endif

if ( status == 0 )
    set oben to GetPos, Z
    set oben to oben + 300
    set unten to GetPos, Z
    set unten to unten - 300
    set status to 1
elseif ( status == 1 )
    if ( GetPos, Z <= oben )
        MoveWorld, Z, 100
    endif
    set status to 2
elseif ( status == 2 )
    if ( GetPos, Z >= unten )
        MoveWorld, Z, -100
    endif
    set status to 1
endif

End Teri_L4_Aufzug1_Script
 
Oh man, die Aufgaben sind ja höllisch schwer. Ich bin dran, also noch nicht auflösen oder gar die fünfte Lektion starten.

Keine Angst, ich weiß, dass es ab dieser Lektion einiges schwerer wird 😉

a. Ich will das in meinem Script über die genauen Koordinaten machen, wie muss der GetPos-Befehl dann aussehen? Ich stelle mir das nämlich leichter vor, denn dann muss ich nur die Koordinaten eingeben, anstatt einzuschätzen, wieviele Einheiten, also cm der Weg von Punkt A zu Punkt B wohl sind.

Das mit den Zentimetern ist doch nur eine Orientierung für euch, dass ihr es mit Größen aus der realen Welt vergleichen könnt. Im CS rechnet ihr natürlich in Editoreinheiten. Die könnt ihr ja immer in der Statusleiste am unteren Rand des Fensters sehen.

Deine Frage mit dem GetPos verstehe ich nicht ganz, aber wenn du keine Variable verwenden willst, würde es (zum Beispiel) so aussehen:

if ( GetPos, Z < 12345 )

b. Eine Verständnisfrage: Im ersten Beispielscript geht das Objekt so lange um 100 Einheiten nach oben, bis die 300 erreicht ist, oder? Also 3 mal, also drei Frames, oder?

Nein. Die 100 bezeichnet die Editoreinheiten pro Sekunde und nicht pro Frame. In einer Sekunde gibt es in Morrowind normalerweise so zwischen 20 und 80 Frames (je nach Rechner). Sagen wir einfach mal 25 Frames pro Sekunde. Dann wird das Objekt mit 100 Einheiten pro Sekunde = 100 Einheiten pro Sekunde geteilt durch 25 Frames pro Sekunde = 4 Einheiten pro Frame bewegt.

c. Ich habe erstmal einen Aufzug ohne Timer gebaut (ja, ich bin noch bei dem ersten Aufzugsversuch), der als Grundgerüst für die Varianten der anderen Aufgaben dienen soll. Aber das Ding wackelt nur munter vor sich hin. Was mache ich falsch?
Code:
Begin Teri_L4_Aufzug1_Script

short status
float unten
float oben

if ( MenuMode == 1 )
  return
endif

if ( status == 0 )
    set oben to GetPos, Z
    set oben to oben + 300
    set unten to GetPos, Z
    set unten to unten - 300
    set status to 1
elseif ( status == 1 )
    if ( GetPos, Z <= oben )
        MoveWorld, Z, 100
    endif
    set status to 2
elseif ( status == 2 )
    if ( GetPos, Z >= unten )
        MoveWorld, Z, -100
    endif
    set status to 1
endif

End Teri_L4_Aufzug1_Script

Dein Script sieht schon ganz gut aus. Du hast nur einen kleinen Fehler gemacht. Du erreichst die Zeile set status to 2 bereits im ersten Durchlauf und nicht erst, wenn die Position überschritten ist. Also bewegst du den Aufzug ein Stück nach oben, schaltest dann auf die Abwärtsbewegung um, bewegst ein winziges Stück nach unten und schaltest dort direkt wieder auf die Aufwärtsbewegung um.

Diesen staus-Wechsel musst du mit einem else in deinen if-Block mit dem Move einbringen.
 
  • Like
Reaktionen: Teridan
Das hat mir schon weitergeholfen, durch das eingebaute Else funktioniert es jetzt ziemlich gut. Der Aufzug versinkt zwar kurz im Boden, aber da muss ich an den Werten noch etwas feinjustieren. 😉

Da ich wie gesagt auf die Variablen verzichten wollte, habe ich ein neues Probelm:

Code:
Begin Teri_L4_Aufzug1_Script

short status

if ( MenuMode == 1 )
  return
endif

set status to 1

if ( status == 1 )
    if ( GetPos, Z <= 473.000 )
        MoveWorld, Z, 100
    else
        set status to 2
    endif
elseif ( status == 2 )
    if ( GetPos, Z >= 109.000 )
        MoveWorld, Z, -100
    else
        set status to 1
    endif
endif

End Teri_L4_Aufzug1_Script

Der Aufzug geht nun nach oben und hält auch genau an der Stelle, an der er halten soll. Allerdings kommt er nicht wieder runter. :huh:
 
Zuletzt bearbeitet:
Ich würde sagen, das liegt daran, dass du direkt nach der MenuMode-Abfrage "status" auf 1 setzt. Bei der entsprechenden "if"-Schleife ist "status" also nicht 2. Du könntest also entweder eine zusätzliche Variable einfügen, die dafür sorgt, dass "status" am Anfang nur einmal auf 1 gesetzt wird oder du schreibst die Abfragen dahinter anders (mit "status == 0" und "status == 1").
 
Da ich wie gesagt auf die Variablen verzichten wollte, habe ich ein neues Probelm:

Code:
Begin Teri_L4_Aufzug1_Script

short status

if ( MenuMode == 1 )
  return
endif

set status to 1

if ( status == 1 )
    if ( GetPos, Z <= 473.000 )
        MoveWorld, Z, 100
    else
        set status to 2
    endif
elseif ( status == 2 )
    if ( GetPos, Z >= 109.000 )
        MoveWorld, Z, -100
    else
        set status to 1
    endif
endif

End Teri_L4_Aufzug1_Script

Der Aufzug geht nun nach oben und hält auch genau an der Stelle, an der er halten soll. Allerdings kommt er nicht wieder runter. :huh:

Ich hab lange überlegen müssen, bis ich den Fehler gefunden habe. Gehen wir das Script doch mal durch:

  • Das Hochfahren klappt ja, also springen wir direkt auf den Punkt an dem das GetPos, Z < 473 nicht mehr erfüllt ist. In dem Fall geht das Script in den else-Zweig und setzt status auf 2. Danach überspringt das Script den Rest und geht wieder an den Anfang.
  • Das Menü ist nicht offen, also wird über diesen Punkt hinweggesprungen. Danach wird der status auf 1 gesetzt.
  • Das Script kommt zur Abfrage state == 1. Die ist ja inzwischen wieder wahr, das Script geht also wieder in die Aufwärtsabfrage und setzt dort den status wieder auf 2.
  • Und wieder von vorne...

Du musst also dieses set status to 1 entfernen. entweder fragst du in deinem Script die Werte 0 und 1 für Status ab (statt 1 und 2) oder du baust noch ein OnActivate um diese Zeile drumrum. Dann kannst du den Aufzug manuell starten.
 
Es funktioniert!

So muss sich Frankenstein gefühlt haben, als die Finger seines Monsters angefangen haben zu zucken. :lol: Aber wenigstens habe ich euch auch noch zum grübeln gebracht. 😛

Ok, jetzt mache ich mich mal an die eigentlichen Aufgaben. :blink:
 
Ich werd mich dieses Wochenende an diese Lektion setzen, vorher habe ich nicht so genau Zeit, mich in Ruhe damit beschäftigen zu können. Lektion 3 hab ich ja letztens durchgenommen.
 
Ich mache auch noch mit. Wie Mercarryn bin auch ich nicht so schnell. Außerdem ist volle Berufstätigkeit zwar sehr gut, einem zeitintensiven Hobby jedoch eher abträglich. 😉

Wenn ihr weitermachen wollt, dann hole ich Lektionen halt nach wie dPüm.
 
Meine auch... Zwar keine Vorabi Klausuren, aber was anderes. Ich vermute ich werde die nächste Zeit zu gar nichts kommen, was Scripten angeht... Lektion 2 muss ich auch noch nachholen... aber das bleibt doch offen. Also macht ruhig weiter, ich humpel hinterher.
 
Wie versprochen, hab ich mich heute mal mit dieser Lektion näher beschäftigt:

Sag mal Killfetzer, an deinem ersten Script bekomme ich die Fehlermeldung " script kf_sk_bewegung_bsp1 You need to enter a value in line 21? Compiled script not saved", wenn ich es jetzt mal so rüberkopiere
Da ist doch aber ein Wert angegeben?
PHP:
begin merc_aufzug

short state
float timer

if ( MenuMode == 1 )
  return
endif

set timer to timer + GetSecondsPassed
if    (State==0)
    if    (timer>=3)
    MoveWorld Z, 200
        if    (timer>=5)                            ;5-3 Sekunden ergibt 2 Sekunden Bewegungszeit
        set timer to 0
        set state to 1
        endif
    endif
endif

if    (state==1)
    if    (timer>=3)
    MoveWorld Z, -200
        if    (timer>=5)                            ;5-3 Sekunden ergibt 2 Sekunden Bewegungszeit
        set timer to 0
        set state to 0
        endif
    endif
endif
        

end
Ich hatte es mir schon gedacht, dass zwar im Menü-Modus die Bewegung einfriert, da aber das Script im Hintergrund dann doch läuft, wird dann die Position des Activators innerhalb der verstrichenen Zeit sofort aktualisiert.
Ich nehme an, der timer muss als float-Variable angelegt werden, da die Zeit fliessend ist und kein fester Wert festgelegt werden kann, da man nicht genau weiss, WANN der Spieler das Objekt aktiviert.

Die weiteren Aufgaben folgen noch.
 
Ein Wert ist angegeben, aber die Achse fehlt.

Ja klar, warum ist mir das nicht selber aufgefallen?:headwall:



PHP:
begin merc_aufzug_hin_zurueck

short state
float zpos

if ( MenuMode == 1 )
  return
endif

if ( state == 0 )
  if ( OnActivate == 1 )

    set zpos to GetPos, Z
    set zpos to zpos + 300

    set state to 1
  endif
elseif ( state == 1 )
  if ( GetPos, Z <= zpos )
    MoveWorld,  Z, 100
  else
    set state to 2
  endif
endif

if ( state == 2 )
  if ( OnActivate == 1 )

    set zpos to GetPos, Z
    set zpos to zpos - 300

    set state to 3
  endif
elseif ( state == 3 )
  if ( GetPos, Z >= zpos )
    MoveWorld,  Z, -100
  else
    set state to 0
  endif
endif


end
Wenn man sich das erste Beispielscript von Killfetzer. anschaut und mit etwas Logik den Ablauf umkehrt, hat man in groben Zügen die Lösung dafür. Wenn ich allerdings nicht aufpasse, dass die Werte und Operatoren nicht angepasst sind, muss ich mich nicht wundern, wenn der Lift dem Begriff "Himmelfahrtskommando" ne ganz neue Bedeutung gibt :lol:

Bei Aufgabe 5 hab ich noch nicht mal nen Ansatz und an Aufgabe 6 beisse ich mir so richtig die Zähne aus, obwohl ich zumindest bei letzterer der Lösung nahe sein sollte.