Moin,
wie wir alle wissen, ist der Practice-Dummy ein wenig begrenzt. Schön, dass man mit ihm Aktionen aufnehmen und abspielen kann, aber für einige Sachen braucht man immer noch 'nen menschlichen Gegner. Hier kommt ein kleines Gimmick ins Spiel:
HoPi's Mixup-Freund
Die Idee ist, wie bei einem programmierbaren Controller, allerdings als Software-Lösung, den Dummy per Knopf-Druck ein Mixup ausführen zu lassen, dessen Follow-Up zufällig ausgewählt wird. Beispiele dafür: Rufus Dive Kick Follow Up oder Abels CoD.
Ihr braucht, um diese Sache zu benutzen, nur ein kleines Tool namens GlovePIE. Ursprünglich wurde dieses Tool geschrieben, um Eingaben eines Handschuhs (Essential Reality P5 Gaming Glove) einzulesen: im Skript, das man schreibt, steht, was bei einem Knopf-Druck passieren soll - vergleichbar mit einem Makro-Tool. Bekannt wurde das Programm, weil man damit die Wiimote am PC benutzen konnte, aber das gehört hier erstmal nicht hin
Ihr bekommt das Tool von der Seite des Programmieres: glovepie.org/
---
Um kurz in das Tool einzuweisen, ein kleines Beispiel-Skript:
Nicht wirklich nützlich, aber trotzdem: wenn dieses Skript gestartet wird, hält das Programm die Taste a auf der Tastatur für 100 Millisekunden gedrückt. Clevere Kerlchen ahnen jetzt schon, worauf das hinausläuft
Bevor ich auf die GlovePIE-Skripte eingehe, müssen die Einstellungen von SF4 evtl. angepasst werden. Steuergerät für Spieler 1 sollte die Tastatur sein (Optionen -> blah...), Spieler 2 bekommt im Trainingsmodus den Joystick. Wichtig ist außerdem, dass ihr für durchgängige 60fps sorgt - warum und was passieren könnte, wenn das nicht der Fall ist, kommt später.
GlovePIE bietet 'ne umfrangreiche Dokumentation, die man sich gerne mal anschauen darf. Ich gehe hier nur ein wenig auf die Skripte ein, die ich bisher geschrieben habe - zum Starten das Skript einfach laden und oben auf "Run" drücken.
Nehmen wir an, wir wollen, dass Ryu einen Hadoken macht. Keine unlösbare Aufgabe, oder? Kleine Anmerkung: 2 Schrägstriche deuten auf einen Kommentar hin, der nicht vom Programm ausgewertet wird.
Spoiler anzeigen
Alles anzeigen
Ziemlich einleuchtend: das Programm "drückt" die nötige Tastenkombination. Die Wartezeiten von 1ms zwischen Drücken und Loslassen einer Taste müssen sein, damit die Engine den Druck überhaupt akzeptiert. Die Abfrage, ob die Shift-Taste gedrückt wird, mache ich, damit Ryu nicht die ganze Zeit Hadokens spammt (bzw. die Motion eingibt).
Die Überlegungen, die ich nun hatte, waren folgende:
a) ich möchte eine Abkürzung für "warte 1 Frame", damit man sich nicht mit Millisekunden rumärgern muss; außerdem kann man so leichter "4 mal 1 Frame" warten
b) ich brauche eine Zufallszahl, von der ich abhängig machen kann, welche Taste gedrückt wird
Fall a ist nicht weiter wild: an den Anfang des Skripts setze ich eine Variable mit dem Namen "frame" und gebe ihr einen fixen Wert von 16,7ms (denn 60 Bilder pro Sekunde -> 16,67ms pro Bild). Fall b gestaltet sich auch einfach, da GlovePIE eine Funktion bietet, die bei Aufruf eine Zufallszahl zwischen 0 und 1 mit 2 Nachkommastellen ausspuckt.
Das erste Mixup zum Warmwerden: Blanka Ball in verschiedenen Stärken.
Spoiler anzeigen
Alles anzeigen
Wenn man das Hadoken-Beispiel verstanden hat, sollte das hier keine große Sache sein.
Ok, das war nun kein richtiges Mixup. Man könnte das Skript umschreiben und Blanka eine zufällige Zeit warten lassen, bis der Ball kommt, um dann mit 'nem Jab drauf reagieren zu können - das dürft ihr gerne ausprobieren
Schauen wir uns Rufus Dive-Kick Follow-Up an:
Spoiler anzeigen
Alles anzeigen
Je nach Zufallszahl wird ein anderes Followup ausgewählt. Kleine Besonderheit: wegen des englischen Tastaturlayouts muss man Z statt Y verwenden.
---
Soviel zur Theorie. In der Praxis klappt die Sache leider nicht ganz so, wie ich mir das vorgestellt habe - die Zeit, die GlovePIE benutzt, macht die Sache schwierig. Durch Schwankungen der Zeit (ich vermute, die wird von der CPU kommen) und durch leichte Schwankungen der FPS im Spiel (im meinem Fall selbst bei Einstellung "konstant" zwischen 59,8 und 60,4) ist es nicht 100% möglich, Sachen wie 1-Frame-Links umzusetzen. Diese funktionieren nur ab und zu. Was ich mir bisher ebenfalls nicht erklären konnte: die Frame-Länge von 16,67ms kommt in keinem meiner Skripte hin, ich musste einen Wert von 13,85ms nehmen. Den müsst ihr evtl. selbst in den Skripten anpassen.
Hier noch ein paar Skripte, die ich geschrieben habe (aber nicht aufgeräumt oder kommentiert habe):
Akuma Demon
Spoiler anzeigen
Alles anzeigen
Boxer: Jab xx Ultra
Spoiler anzeigen
Alles anzeigen
Abel CoD
Spoiler anzeigen
Alles anzeigen
Sagat: TU FADC Throw / TU / Block Mixup
Spoiler anzeigen
Alles anzeigen
wie wir alle wissen, ist der Practice-Dummy ein wenig begrenzt. Schön, dass man mit ihm Aktionen aufnehmen und abspielen kann, aber für einige Sachen braucht man immer noch 'nen menschlichen Gegner. Hier kommt ein kleines Gimmick ins Spiel:
HoPi's Mixup-Freund
Die Idee ist, wie bei einem programmierbaren Controller, allerdings als Software-Lösung, den Dummy per Knopf-Druck ein Mixup ausführen zu lassen, dessen Follow-Up zufällig ausgewählt wird. Beispiele dafür: Rufus Dive Kick Follow Up oder Abels CoD.
Ihr braucht, um diese Sache zu benutzen, nur ein kleines Tool namens GlovePIE. Ursprünglich wurde dieses Tool geschrieben, um Eingaben eines Handschuhs (Essential Reality P5 Gaming Glove) einzulesen: im Skript, das man schreibt, steht, was bei einem Knopf-Druck passieren soll - vergleichbar mit einem Makro-Tool. Bekannt wurde das Programm, weil man damit die Wiimote am PC benutzen konnte, aber das gehört hier erstmal nicht hin
Ihr bekommt das Tool von der Seite des Programmieres: glovepie.org/
---
Um kurz in das Tool einzuweisen, ein kleines Beispiel-Skript:
Nicht wirklich nützlich, aber trotzdem: wenn dieses Skript gestartet wird, hält das Programm die Taste a auf der Tastatur für 100 Millisekunden gedrückt. Clevere Kerlchen ahnen jetzt schon, worauf das hinausläuft
Bevor ich auf die GlovePIE-Skripte eingehe, müssen die Einstellungen von SF4 evtl. angepasst werden. Steuergerät für Spieler 1 sollte die Tastatur sein (Optionen -> blah...), Spieler 2 bekommt im Trainingsmodus den Joystick. Wichtig ist außerdem, dass ihr für durchgängige 60fps sorgt - warum und was passieren könnte, wenn das nicht der Fall ist, kommt später.
GlovePIE bietet 'ne umfrangreiche Dokumentation, die man sich gerne mal anschauen darf. Ich gehe hier nur ein wenig auf die Skripte ein, die ich bisher geschrieben habe - zum Starten das Skript einfach laden und oben auf "Run" drücken.
Nehmen wir an, wir wollen, dass Ryu einen Hadoken macht. Keine unlösbare Aufgabe, oder? Kleine Anmerkung: 2 Schrägstriche deuten auf einen Kommentar hin, der nicht vom Programm ausgewertet wird.
Quellcode
- // Ryu: Hadoken
- // Wenn die Shift-Taste auf der Tastatur getippt wird, dann...
- if keyboard.shift then begin
- // Warte 1 Sekunde
- wait 1000ms
- // Druecke auf der Tastatur nach unten
- keyboard.down = true
- wait 1ms
- // lasse wieder los
- keyboard.down = false
- wait 1ms
- // Druecke auf der Tastatur nach unten und vorne
- keyboard.right = true
- keyboard.down = true
- wait 1ms
- // lasse wieder los
- keyboard.right = false
- keyboard.down = false
- wait 1ms
- // Druecke auf der Tastatur nach vorne
- keyboard.right = true
- wait 1ms
- // lasse wieder los
- keyboard.right = false
- wait 1ms
- // Druecke Taste A (Standardbelegung: jab)
- keyboard.a = true
- wait 1ms
- keyboard.a = false
- // Warte 1 Sekunde
- wait 1000ms
- // fertig
- end if
Ziemlich einleuchtend: das Programm "drückt" die nötige Tastenkombination. Die Wartezeiten von 1ms zwischen Drücken und Loslassen einer Taste müssen sein, damit die Engine den Druck überhaupt akzeptiert. Die Abfrage, ob die Shift-Taste gedrückt wird, mache ich, damit Ryu nicht die ganze Zeit Hadokens spammt (bzw. die Motion eingibt).
Die Überlegungen, die ich nun hatte, waren folgende:
a) ich möchte eine Abkürzung für "warte 1 Frame", damit man sich nicht mit Millisekunden rumärgern muss; außerdem kann man so leichter "4 mal 1 Frame" warten
b) ich brauche eine Zufallszahl, von der ich abhängig machen kann, welche Taste gedrückt wird
Fall a ist nicht weiter wild: an den Anfang des Skripts setze ich eine Variable mit dem Namen "frame" und gebe ihr einen fixen Wert von 16,7ms (denn 60 Bilder pro Sekunde -> 16,67ms pro Bild). Fall b gestaltet sich auch einfach, da GlovePIE eine Funktion bietet, die bei Aufruf eine Zufallszahl zwischen 0 und 1 mit 2 Nachkommastellen ausspuckt.
Das erste Mixup zum Warmwerden: Blanka Ball in verschiedenen Stärken.
Quellcode
- /* Blanka: Ball Mixup, best from mid range */
- var.frame = 16.67ms;
- if keyboard.shift then begin
- var.zufall = random
- wait 1000ms
- // Charge anfangen
- keyboard.left = true
- keyboard.down = true
- // Warte 70 Frames
- wait 70 * var.frame
- // Charge aufloesen
- keyboard.left = false
- keyboard.down = false
- wait 1ms
- // Nach rechts druecken
- keyboard.right = true
- // Mixup
- if var.zufall < 0.25 then begin
- // LP
- keyboard.a = true
- else if var.zufall < 0.5 then begin
- // MP
- keyboard.s = true
- else if var.zufall < 0.75 then begin
- // HP
- keyboard.d = true
- else begin
- // d.h. wenn die Zufallszahl zwischen 0.75 und 1 liegt
- // EX
- keyboard.a = true
- keyboard.s = true
- end if
- wait 1ms
- // Alle Tasten loslassen
- keyboard.right = false
- keyboard.a = false
- keyboard.s = false
- keyboard.d = false
- wait 1000ms
- end if
Wenn man das Hadoken-Beispiel verstanden hat, sollte das hier keine große Sache sein.
Ok, das war nun kein richtiges Mixup. Man könnte das Skript umschreiben und Blanka eine zufällige Zeit warten lassen, bis der Ball kommt, um dann mit 'nem Jab drauf reagieren zu können - das dürft ihr gerne ausprobieren
Schauen wir uns Rufus Dive-Kick Follow-Up an:
Quellcode
- /* Rufus Dive Kick Mixup */
- var.frame = 16.67ms;
- if keyboard.shift then begin
- var.zufall = random
- wait 1000ms
- keyboard.down = true
- wait 1ms
- keyboard.right = true
- wait 1ms
- keyboard.down = false
- wait 1ms
- keyboard.z = true
- wait 1ms
- keyboard.right = false
- keyboard.z = false
- wait var.frame * 60
- if var.zufall < 0.33 then begin
- keyboard.z = true
- wait 1ms
- keyboard.z = false
- else if var.zufall < 0.66 then begin
- keyboard.x = true
- wait 1ms
- keyboard.x = false
- else begin
- keyboard.c = true
- wait 1ms
- keyboard.c = false
- end if
- wait 1000ms
- end if
Je nach Zufallszahl wird ein anderes Followup ausgewählt. Kleine Besonderheit: wegen des englischen Tastaturlayouts muss man Z statt Y verwenden.
---
Soviel zur Theorie. In der Praxis klappt die Sache leider nicht ganz so, wie ich mir das vorgestellt habe - die Zeit, die GlovePIE benutzt, macht die Sache schwierig. Durch Schwankungen der Zeit (ich vermute, die wird von der CPU kommen) und durch leichte Schwankungen der FPS im Spiel (im meinem Fall selbst bei Einstellung "konstant" zwischen 59,8 und 60,4) ist es nicht 100% möglich, Sachen wie 1-Frame-Links umzusetzen. Diese funktionieren nur ab und zu. Was ich mir bisher ebenfalls nicht erklären konnte: die Frame-Länge von 16,67ms kommt in keinem meiner Skripte hin, ich musste einen Wert von 13,85ms nehmen. Den müsst ihr evtl. selbst in den Skripten anpassen.
Hier noch ein paar Skripte, die ich geschrieben habe (aber nicht aufgeräumt oder kommentiert habe):
Akuma Demon
Quellcode
- if Keyboard.Shift = true then begin
- Keyboard.Right = true
- Keyboard.A = true
- Keyboard.C = true
- Wait 1ms
- Keyboard.A = false
- Keyboard.C = false
- Keyboard.Right = false
- Wait 1ms
- Keyboard.A = true
- Wait 1ms
- Keyboard.A = false
- Wait 1ms
- Keyboard.Left = true
- Wait 1ms
- Keyboard.Z = true
- Keyboard.D = true
- Wait 1ms
- Keyboard.Z = false
- Keyboard.D = false
- Keyboard.Left = false
- Wait 1000ms
- end if
Boxer: Jab xx Ultra
Quellcode
- /* Boxer: Jab xx Ultra; does only work from time to time */
- var.frame = 13.85ms; // 16.67ms;
- if keyboard.shift then begin
- var.zufall = random
- debug = "geht los: " + var.zufall
- wait 1000ms
- // charge down + back
- keyboard.down = true
- keyboard.left = true
- wait 70 * var.frame
- // let go of down to do the jab
- keyboard.down = false
- wait 1ms
- keyboard.a = true
- wait 1ms
- keyboard.a = false
- // wait for jab to finish
- // this is a one frame link! might not work all the time
- wait 160ms
- // ultra
- keyboard.left = false
- wait 1ms
- keyboard.right = true
- wait 1ms
- keyboard.right = false
- wait 1ms
- keyboard.left = true
- wait 1ms
- keyboard.left = false
- wait 1ms
- keyboard.right = true
- keyboard.z = true
- keyboard.x = true
- keyboard.c = true
- wait 1ms
- keyboard.right = false
- keyboard.z = false
- keyboard.x = false
- keyboard.c = false
- // wait a few moments for piano madness...
- wait var.frame * 120
- keyboard.z = true
- wait 1ms
- keyboard.z = false
- wait 1ms
- keyboard.x = true
- wait 1ms
- keyboard.x = false
- wait 1ms
- keyboard.c = true
- wait 1ms
- keyboard.c = false
- wait 1ms
- keyboard.z = true
- wait 1ms
- keyboard.z = false
- wait 1ms
- keyboard.x = true
- wait 1ms
- keyboard.x = false
- wait 1ms
- keyboard.c = true
- wait 1ms
- keyboard.c = false
- wait 1ms
- keyboard.z = true
- wait 1ms
- keyboard.z = false
- wait 1ms
- keyboard.x = true
- wait 1ms
- keyboard.x = false
- wait 1ms
- keyboard.c = true
- wait 1ms
- keyboard.c = false
- wait 1ms
- keyboard.z = true
- wait 1ms
- keyboard.z = false
- wait 1ms
- keyboard.x = true
- wait 1ms
- keyboard.x = false
- wait 1ms
- keyboard.c = true
- wait 1ms
- keyboard.c = false
- wait 1ms
- keyboard.z = true
- wait 1ms
- keyboard.z = false
- wait 1ms
- keyboard.x = true
- wait 1ms
- keyboard.x = false
- wait 1ms
- keyboard.c = true
- wait 1ms
- keyboard.c = false
- wait 1ms
- keyboard.z = true
- wait 1ms
- keyboard.z = false
- wait 1ms
- keyboard.x = true
- wait 1ms
- keyboard.x = false
- wait 1ms
- keyboard.c = true
- wait 1ms
- keyboard.c = false
- wait 1ms
- keyboard.z = true
- wait 1ms
- keyboard.z = false
- wait 1ms
- keyboard.x = true
- wait 1ms
- keyboard.x = false
- wait 1ms
- keyboard.c = true
- wait 1ms
- keyboard.c = false
- wait 1ms
- keyboard.z = true
- wait 1ms
- keyboard.z = false
- wait 1ms
- keyboard.x = true
- wait 1ms
- keyboard.x = false
- wait 1ms
- keyboard.c = true
- wait 1ms
- keyboard.c = false
- wait 1ms
- wait 1000ms
- debug = "fertig"
- end if
Abel CoD
Quellcode
- /* Abel: CoD Mixup */
- var.frame = 13.85ms; // 16.67ms;
- if keyboard.shift then begin
- var.zufall = random
- debug = "geht los: " + var.zufall
- wait 1000ms
- // qcf + lp
- keyboard.down = true
- wait 1ms
- keyboard.right = true
- wait 1ms
- keyboard.down = false
- wait 1ms
- keyboard.a = true
- wait 1ms
- keyboard.a = false
- keyboard.right = false
- // wait for 1st hit to retaliate
- wait var.frame * 30
- // Mixup: 2nd hit
- if var.zufall < 0.5 then begin
- keyboard.right = true
- keyboard.s = true
- wait 1ms
- keyboard.right = false
- keyboard.s = false
- wait var.frame * 35
- // Mixup: throw
- if var.zufall < 0.25 then begin
- keyboard.right = true
- keyboard.d = true
- wait 1ms
- keyboard.right = false
- keyboard.d = false
- else begin
- keyboard.right = true
- keyboard.c = true
- wait 1ms
- keyboard.right = false
- keyboard.c = false
- end if
- else begin
- keyboard.right = true
- keyboard.x = true
- wait 1ms
- keyboard.right = false
- keyboard.x = false
- wait var.frame * 40
- // Mixup: throw
- if var.zufall < 0.75 then begin
- keyboard.right = true
- keyboard.d = true
- wait 1ms
- keyboard.right = false
- keyboard.d = false
- else begin
- keyboard.right = true
- keyboard.c = true
- wait 1ms
- keyboard.right = false
- keyboard.c = false
- end if
- end if
- wait 1000ms
- debug = "fertig"
- end if
Sagat: TU FADC Throw / TU / Block Mixup
Quellcode
- /* Sagat: TU FADC Throw / TU / Block */
- var.frame = 13.85ms; // 16.67ms;
- if keyboard.shift then begin
- var.zufall = random
- debug = "geht los: " + var.zufall
- wait 1000ms
- // Kara
- keyboard.right = true
- keyboard.z = true
- wait 1ms
- keyboard.z = false
- keyboard.right = false
- wait 1ms
- // TU
- keyboard.right = true
- wait 1ms
- keyboard.right = false
- keyboard.down = true
- wait 1ms
- keyboard.right = true
- keyboard.down = true
- keyboard.a = true
- wait 1ms
- keyboard.down = false
- keyboard.right = false
- keyboard.a = false
- // Wait for...
- wait 15 * var.frame
- // FADC
- keyboard.s = true
- keyboard.x = true
- wait var.frame * 10
- keyboard.right = true
- wait 1ms
- keyboard.right = false
- wait 1ms
- keyboard.right = true
- wait 1ms
- keyboard.right = false
- keyboard.s = false
- keyboard.x = false
- // Mixup
- if var.zufall < 0.33 then begin
- // Wait for Dash to complete
- // time differs depending on mixup
- wait var.frame * 20
- // TU
- keyboard.right = true
- wait 1ms
- keyboard.right = false
- keyboard.down = true
- wait 1ms
- keyboard.right = true
- keyboard.down = true
- keyboard.a = true
- wait 1ms
- keyboard.down = false
- keyboard.right = false
- keyboard.a = false
- else if var.zufall < 0.66 then begin
- // Wait for Dash to complete
- // time differs depending on mixup
- wait var.frame * 29
- // Throw
- keyboard.a = true
- keyboard.z = true
- wait 1ms
- keyboard.a = false
- keyboard.z = false
- else begin
- // no wait needed, just hold block
- keyboard.left = true
- keyboard.down = true
- wait 1500ms
- keyboard.left = false
- keyboard.down = false
- end if
- wait 1000ms
- debug = "fertig"
- end if
Lattenrost ist keine Geschlechtskrankheit