Frage zu LUA Script / virtuelle Controls und Unterschiede Steuerung mit Tastatur / Maus

  • Hallo zusammen


    in diesem Thread hier C&O H8 2-6-6-6 Dampflok (Allegheny) erhältlich (Freeware / in Überarbeitung) habe ich ja mitgeteilt, dass ich mich mit diesem Modell auch einmal mit dem Thema LUA intensiver beschäftigen möchte - was ich auch tatsächlich gemacht habe. Sogar recht erfolgreich für meine Verhältnisse :)

    Da es sich ja um eine Mallet Dampflok handelt, gibt es z.B. Zylinderhähne und Sander in dem Blueprint doppelt, einmal als Front_xxx und einmal als Rear_xxx. Ziel war, dass sich z.B. der im Core enthaltene "Sander" einschaltet, wenn ich einen oder auch beide der Nicht Standard Sander (eben Front oder Rear) betätige. Das habe ich im Script in der Tat hinbekommen, aber nur wenn ich dies über die Tastatur ansteuere. Über die Maus verändert sich zwar auch der jeweilige ControValue des jeweiligen Sanders (das sehe ich mit dem Raildriver & Joystick Interface), aber der Core Sander wird nicht aktiviert. Obwohl bei beiden Bedienvarianten der Wert bis auf "1" steigt, interessiert sich das Script nicht dafür, wenn dies durch die Maus verursacht wurde. Hat einer Idee warum dies so ist und ob es eine Limitation im Core ist, oder ich im Script etwas nicht so perfekt gelöst habe?


    Weiter unten mein Code. für den Sander und Zylinderhähne habe ich das über "function OnControlValueChange" vorgenommen (im bestehenden Script ergänzt). Gibt es da eventuell Verbesserungsvorschläge, bei CylinderCock habe ich die Abfrage einmal mit "or" verknüpft, was genauso gut zu funktionieren scheint.


    Dann noch eine Frage zu folgendem, ich habe die diversen Manometer auch über die Function Update mit Random Values angesteuert, so dass die Nadeln ein wenig zittern. Ist schon mal ganz schick, ich hätte aber gerne noch die Möglichkeit, dass die Anzeigewerte sich mit zunehmendem Wert des ControlValues auch erhöhen. Im Moment zittert es eben nur um einen Wert herum, zwischen 19 und 21 eben.

    if Call ("*:GetControlValue", "Stoking", 0) >= 0.05 then

    SSV = math.random(19,21)


    Gibt es eine Möglichkeit wie >0.05 and <0.2 ist der Wert 19,21, zwischen 0.2 und 0.5 24,26 usw.?

    Habe dazu zwar etwas gefunden (https://stackoverflow.com/ques…-less-than-another-number), aber wenn ich dies eintrage funktioniert das ganze Script nicht mehr :(


    Danke für jeden Input und viele Grüsse


    Thorsten

  • Hallo Thorsten,


    vielleicht nicht die eleganteste Methode, aber so könnte es gehen (habe das aber jetzt nicht geprüft):

    Viele Grüße

    Heiko

  • Merci Heiko Mistral für Deinen Input, der Ansatz war schon mal goldrichtig. Habe 4 Bereiche gesetzt, 0.05 - 0.2, 0.2 - 0.5, 0.5 - 0.75, 075 -1

    Es hat dann insofern funktioniert, aber erst nachdem der Wert über 0.75 ging. Beim Verringern standen die Zeiger fest, unter 0.05 gingen die Zeiger auf 0 zurück.

    Hab es dann so hingebracht, also zwischen jedem Bereich SetControlValue. Funktoniert wie gewünscht, wenn vielleicht auch etwas sperrig. Naja, bin erst am Anfang der Scriptkunst :)


  • Warum machst du das nicht einfach mit einer Sinus Funktion und nutzt den Wert aus Stoking als Multiplikator und die SimulationTime als x-Geber. Wäre dann ein 4 Zeiler statt 25 und der Zeiger würde schön weich hin und her eiern je nachdem wie stark der Stoking Wert ist.


    gStoking = Call("GetControlValue","Stoking",0);

    gSimulationTime = Call("GestSimulationTime");

    Call("SetControlValue","StokerSteam Gauge",0,(math.sin(simulationTime)*gStoking*0.8));

    Call("SetControlValue","StokerJetsPSI",0,(math.sin(simulationTime)*gStoking*1.0));


    Der jeweils letzt wert ersetzt die Unterschiede aus deinen Randoms. Komm nicht in Versuchung auch da Random einzusetzen. Das kostet. Und immer schön dran denken, man nutzt nur "GetControlValue" und nicht "*:GetControlValue", selbes gilt für alle anderen Calls. Kein "*:" benutzen. Das kostet immens Performance und nutzt gar nichts.

  • Moin Moin,


    sind SSV und SJP übergeordnet außerhalb der Funktion definiert? Wenn nicht, ist da schon mal eine Fehlerquelle. Denn ganz zu Anfang, beim ersten Durchlauf des Skripts, sind die ControlValues meines Wissens nach alle erst bei 0. Du müsstest dann darauf achten, dass du auch den Fall abfängst, dass wenn gStoking gleich 0 ist, um SSV und SJP zu definieren, damit es dann einen Wert hat bzw definiert ist, wenn es die Zeile hier erreicht Call("*:SetControlValue", "StokerSteam Gauge", 0, SSV);. Packst du das mit in die If-Abfrage schließt sich das natürlich aus, dass die beiden Variabeln nicht definiert sind, sieht aber sehr redundant (doppelt) aus, was in der Programmierung vermieden werden sollte. Aber wenn jetzt alles funktioniert, dann ist das schon mal super. Variabeln, die im weiteren Verlauf eine Verwendung finden, sollten immer ganz oben, außerhalb einer Funktion (oder in der Initialisierungsfunktion) definiert werden. Nimm am besten den Vorschlag von Maik G.


    Zum anderen Problem noch mal die Nachfrage: Musst du, um den Front_Sander zu aktivieren, die Maus gedrückt halten? Ist es so, dass wen du die Maus los lässt, der Front_Sander auch wieder aus geht? Bei der Maus ist das alles etwas holprig. Dummerweise werden die ControlValues, wenn man mit der Maus arbeitet, erst verändert, wenn die linke Taste los gelassen wird. Schiebe solche Sachen, die empfindlich über die Maus geändert werden können, immer in die Update-Funktion, da nur die OnControlValueChange besagtes Problem hat.

  • Maik Goltz und questo besten Dank, das mit der Sinusfunktion hört sich sehr gut an, und auch der Tip mit ohne * ist sicher viel Wert - werde aber erst Morgen oder Am Wochenende dazu kommen das zu testen.

    Die Mausbedienung teste ich dann auch aus, wie gesagt die Werte der beiden virtuellen Sander verändern sich schon, habe dabei aber die Maustaste gedrückt ( ich nutze die Maus eher nie, denke da an eben an Mausfans :) )

  • Controls, welche nicht in ihrer jeweiligen Endstellung verbleiben (also Taster in Taststellung), lösen kein OnControlValueChange Event bei Mausbedienung aus. Deswegen passiert da nix. Du musst in der Update Funktion einfach die Taster abfragen und wenn auf bestimmten Wert dann an die OnControlValueChange delegieren mit den gewünschten Werten. Aber Achtung, es kommt zum continuous Fire wenn man da nicht noch eine Prüfung einbaut, ob der Schalter schon betätig wurde (Entprellung).

  • Hallo Maik Goltz, also ich habe mich einmal daran versucht, und war zumindest zum Teil erfolgreich.

    Die Animation ist in der Tat um Längen besser - toll. Aber die Anzeigen steigen nun, und fallen dann wieder auf 0 zurück für ca. 2- 3 Sekunden, sie pendeln nicht um einen Wert herum etwas auf und ab.

    Statt (math.sin(simulationTime) habe ich (math.sin(gsimulationTime) genommen, gsimulationTime ist ja die Variable die ich zuvor setze, oder?

    Nur mit den vier Zeilen, also ohne if Schleife habe ich das nicht zum Laufen bekommen - ich habe das Script ja auch nur erweitert und hatte dann mit dem Ersetzen des Codes wohl irgendwo eine End Anweisung zu viel / zu wenig - da müsste ich, wenn die vier Zeilen alleine genügen sollten, noch einmal über die Bücher.


    So sieht es jetzt aus:

    ---Stoker gauges

    gStoker = Call ("GetControlValue", "Stoking", 0) -> hab hier gStoker genommen, gsStoking hat aber genauso funktioniert - wird im Script aber schon an anderer Stelle verwendet.

    gSimulationTime = Call("GetSimulationTime");

    if gStoker >= 0.05 then

    Call("SetControlValue","StokerSteam Gauge",0,(math.sin(gSimulationTime)*gStoker*8.8)); -> Die Controlvalues gehen von 0-40, daher Werte erhöht

    Call("SetControlValue","StokerJetsPSI",0,(math.sin(gSimulationTime)*gStoker*25.0)); -> gSimulationTime statt SimulationTime

    else

    Call("SetControlValue", "StokerSteam Gauge", 0, 0);

    Call("SetControlValue", "StokerJetsPSI", 0, 0);

    end


    ---Feedwater gauge

    gFeedwater = Call ("GetControlValue", "ExhaustInjectorSteamOnOff", 0);

    gSimulationTime = Call("GetSimulationTime");

    if gFeedwater > 0.1 then

    Call("SetControlValue", "FeedwaterPressurePSI", 0,(math.sin(gSimulationTime)*gFeedwater*18.3));

    else

    Call("SetControlValue", "FeedwaterPressurePSI", 0, 0);

    end


    Auf jeden Fall schon mal Danke für Deine Hinweise! Ich versuche mal weiter, es gibt noch eine Anzeige für den Speisewasserdruck - mal sehen ob ich das hinbringe :)

    Edit : OK, habe die Anzeige für Speisewasserdruck nun hin bekommen, war ja fast nur copy & paste - bleibt aber ebenso eine Zeit auf 0, kann ja eher nur aus dem Wert gSimulationTime kommen, das der eine Zeit auf 0 bleibt? Wie setzt sich diese Simulationtime zusammen / wie verändert sich der Wert?Der sollte ja, wenn Du sagst der "eiert dann", immer etwas variabel sein.


    Viele Grüsse


    Thorsten

  • Hallo nochmals


    ich habe anstatt dem Wert "Simulationtime" es nun einmal mit dem Parameter "Time" versucht, das ist wie ich es verstehe die Deltazeit zwischen den Frames. Damit funktioniert die zitternde Bewegung der Zeiger, aber wie bei meinen vorherigen Versuchen deutlich unruhiger, der Zeiger fällt aber nicht auf 0 zurück wie bei der Nutzung von Simulationtime.

    Kann mir jemand da auf die Sprünge helfen, warum Simulationtime scheinbar immer wieder auf 0 zurückkehrt und wie sich dieser Wert errechnet? Die DevDocs geben da auch nicht wirklich sinnvolle Informationen. Dort steht nur "Integer of the simulation time in seconds", was ich so verstehe dass dieser Wert mit längerer Laufzeit des Szenarios doch eher immer steigen sollte. Ich würde da eher erwarten dass der Zeiger irgendwann auf dem höchsten Wert des Controls stehen bliebe.


    Viele Grüsse


    Thorsten


    Edit - ok wenn ich recht überlege kommt das wohl von der Sinusfunktion (math.sin)? Der Graph der SInusfunktion ist ja auf- und absteigend, würde erklären warum der Wert immer gegen Null oder evtl. darunter geht. Frage mich nur warum der Sinus von dem Time Parameter dies dann nicht tut?

  • SimulationTime ist eine Sekunden basierte Zahl (float) die einfach ansteigt. Die Sinusfunktion wird so erst mal nur angetrieben um immer zwischen positiv und negativ hin und her zu pendeln, wobei 0 die Mitte darstellt. Die Weite des Ausschlags der Sinuskurve musst du natürlich noch einsteuern. Da werden dann noch ein paar Multiplikatoren und Additionen nötig sein. Du musst quasi die Amplitude über 0 nach oben "verschieben". Such mal im Netz nach Sinusfunktion, Da gibts hier und da ein paar selbst laufende Animationen wo man erkennen kann, was das tut, sogar auch Werte eingeben kann. Mit der Delta-Frametime kommst du da nicht weit. Die ist ja im zweifel immer gleich, wenn die Framerate stabil ist, oder halt größer wenn die Framerate sinkt und umgedreht kleiner wenn die Framerate steigt. Es ist aber keine fortlaufend steigende Zahl. Die brauchst du aber für Sinus.

    Einmal editiert, zuletzt von Maik Goltz () aus folgendem Grund: ein k eingefügt ^^

  • Verstanden und umgesetzt Maik Goltz - sehr sehr cool! Vor allem top dass Du mir nicht ein fertiges Script hinlegst, so lerne ich wenigstens ordentlich was dabei!

    Noch eine Frage - diese Entprellung von der Du zuvor geschrieben hast, ist das so etwas in der Art?


    if gRH ~= gPrevRH then


    Danke nochmals und Gruss


    Thorsten

  • Vor allem top dass Du mir nicht ein fertiges Script hinlegst, so lerne ich wenigstens ordentlich was dabei!

    So ist das auch gedacht :)


    Zur Entprellung. Da sich der Wert des Schalters bei Bewegung die ganze Zeit ändert (im Zweifel von 0 nach 1 oder eben von min nach max und umgedreht), solltest du eben nur auf den Wert prüfen den du auswerten willst und gleichzeitig festsetzen, ob der Wert bereits erreicht wurde. Da ein Taster immer in seine Ausgangstellung zurückfällt, ist das recht einfach.


  • Hallo Maik Goltz


    sorry, habe noch keine Rückmeldung und Dank gegeben zu Deinem vorigen Beitrag - bin aus beruflichen Gründen nicht gross dazu gekommen mich weiter am Script zu versuchen. Hab aber noch recht viel mit den Nadelanimationen verbracht, schneller, langsamer, kleinerer und grösserer Ausschlag - bin jetzt mit dem Ergebnis absolut zufrieden.


    Wenn ich Deinen Code oben recht verstehe:


    1. Kann ich aus der Update(Time) Funktion sozusagen einen ControlValueChange veranlassen? Das war mir nicht so bekannt dass diese Funktionen untereinander "kommunizieren" können.

    2. Wird in Update(Time) ständig der Wert von "Schaltername" abgerufen, im OnControlValueChange passiert aber erst etwas wenn der Wert 1 (bzw. was ich definiere) ist? Ansonsten würde jeder Zwischenwert während der Bewegung wohl eine Änderung des ControlValue bewirken, was in diesem Teil weniger Auswirkung auf die Performance hat.


    Ich werde das einmal testen und mich dazu zurückmelden - mal sehen ob ich es hinbekomme.


    Gruss


    Thorsten

  • Hallo Maik Goltz


    also ich würde sagen ich konnte das erfolgreich so umsetzen, hatte aber ein Problem mit der Abarbeitung der weiteren Anweisungen im Script als ich meinen bisherigenCode für die Sander durch Deinen Code ersetzt habe. Alles was danach folgte, wurde scheinbar nicht mehr angefasst.

    Ich dachte natürlich an ein "END" zu viel oder so, der Editor Markup zeigte aber nichts derartiges, manuell überprüft fiel mir da auch nichts auf.


    Ich habe dann den Code an das Ende des Scripts verfrachtet, wo es nun funktioniert. Sieht nun so aus, das letzte End schliesst die Update function:


    So weit so gut - habe da noch eine Frage zu "internen" Funktionen oder ControlValues. Es gibt ja diverse Dampflok mit snychronisiertem Emitter für die Zylinderhähne. Ich frage mich die ganze Zeit wie das wohl erstellt ist? Für die Synchroniserung des Sound kann man vermutlich einen Standard Exhaust Proxy nehmen, aber für die Emitter? Muss man da selbst komplizierte Berechnungen mit Radumfang und zurückgelegte Wegstrecke anstellen (was ja wohl auch nicht tatsächlich auf Position des Kolbens Rücksicht nimmt), oder gibt es "core interne" Werte die man anzapfen kann? Z.B. für Position des Kolbens?


    Danke und Gruss


    Thorsten

  • Hallo zusammen


    inzwischen bin ich mit dem Script für die Allegheny recht weit gekommen und konnte auch so einiges selbst aus den gemachten Erfahrungen ableiten. Was ich aber derzeit nicht korrekt hinbekomme, ist eine Steuerung der Zylinderhahn Emitter, die ich gerne einzeln ansteuern möchte, also kein dauerhafter Dampfausstoss, sondern zeitversetzt und mit zunehmender Geschwindigkeit auch natürlich kürzere Öffnungszeiten.


    Ich habe mir nach dem Tip von Maik Goltz gedacht, dass ich diese Sinusfunktion hier gebrauchen könnte, und mir einen ControlValue für Drehzahl der Räder erstellt, sowie Controlvalues für linken und rechten "Kolben", und natürlich Controlvalues für jeden Zylinderhahn.

    Die Idee war, mit steigender Drehzahl die Sinuskurve entsprechend in der Frequenz zu beeinflussen, habe in den Quellen wo ich mich ein wenig schlau gemacht habe gelesen, dass die wohl als Parameter "b" bezeichnet wird. Der Ansatz ist sicher vielleicht nicht extrem genau bzw. es erfordert einiges testen bis man die Öffnungszeiten angenähert hat - könnte aber damit leben wenn dies eben nur genähert ist.

    Im Grunde war ich mit der Lösung sogar erfolgreich, beim Start bleiben die Hähne länger auf, die Zeiten verkürzen sich auch mit steigendem Tempo.


    Was mich aber absolut wundert ist, dass bei einer Bremsung, sobald wohl eine Verögerung eintritt, die Öffnunszeiten extrem schnell erfolgen, viel schneller als bei erhöhen des Tempos. Dies bleibt auch bis zum Stillstand so, beim Wiederanfahren bleibt dies merkwürdigerweise auch so - ich habe mit den Parametern etwas hin und her probiert, und zumindest dieses Phänomen deutlich vermindert. Ich frage mich halt ob da irgendwelche von Acceleration / Tractive Effort mit einbezogen werden, obwohl ich in der Rechnung davon keinen dieser Werte mit einbeziehe - als variables Element habe ich nur die Drehzahl bzw. auch einmal die Simulationtime genutzt.


    Hat jemand noch eine andere Idee wie ich die Hähne synchronisieren könnte, also z.B. über Zeit / Weg oder sonstiges? Oder habe ich da einen Denkfehler generell in meinem Ansatz? Oder schlau gedacht und nur schlecht gemacht?

    Unten mein erster so weit funktionierender Code, ich habe die eigentliche Berechnung für gCockLHval und gCockRHval noch einige Male überarbeitet, meine einige KLammern passen hier nicht bzw. fehlen, stelle die aktuelle Variante später auch noch ein.

    Dort ist Simulationtime nicht mehr enthalten, der Wertebereich wurde verändert usw. - letztlich bleibt aber das Verhalten beim Bremsen.


    Erledigt - den notwendigen Input fand ich hier:

    https://www.trainsimdev.com/forum/viewtopic.php?f=30&t=282#



    Danke für jeden Input und viele Grüsse


    Thorsten