Waffenrad in Doom 1993

Schöne Grüße.



Viele von uns beziehen sich sehr auf Videospiele der alten Schule, die um die Jahrhundertwende veröffentlicht wurden. Sie haben eine ausgezeichnete Atmosphäre, hektische Dynamik und viele originelle Lösungen, die nach Jahrzehnten nicht veraltet sind. Heutzutage hat sich die Vision der Spieloberfläche jedoch etwas geändert - lineare Korridore haben verwirrende Ebenen ersetzt, Regeneration hat Erste-Hilfe-Kits ersetzt, und anstelle einer langen Reihe von Tasten 0-9 zur Auswahl eines Arsenals kam zuerst das Mausrad und dann das virtuelle Rad. Es wird heute über ihn diskutiert.



Bild



Historische Zusammenfassung



Zuvor wurde während des Aufkommens des Shooter-Genres als solches die Frage der Maussteuerung nicht aufgeworfen - nur die Tastatur wurde zur Steuerung des Protagonisten verwendet. Darüber hinaus gab es auch kein einziges Verwaltungsformat - WASD wurde etwas später zum Standard. Weitere Informationen zu alten Gaming-Tastaturlayouts finden Sie hier .



Dementsprechend wurde in den Spielen, in denen die Möglichkeit zur Auswahl der Ausrüstung implementiert wurde (Doom, Wolfenstein, Quake usw.), dies zu diesem Zeitpunkt auf die einzig intuitive Weise implementiert - unter Verwendung der Zifferntasten auf der Tastatur. Und für viele Jahre war diese Methode die einzige.

In den späten 90ern wurde es dann möglich, Waffen mit dem Mausrad zu wechseln.



Wir konnten keine eindeutigen Informationen zu diesem Thema finden, aber in CS 1.6 wurde diese Funktion über die Konsole aktiviert. Möglicherweise gab es jedoch schon einmal solche Präzedenzfälle. In diesem Fall geben Sie dies bitte in den Kommentaren oder in der PM an. Aber in der in unserer Zeit üblichen Form des Waffenrads wurde es nur mit Crysis und seinem Anzugmenü verwendet. Obwohl Versuche, etwas Ähnliches zu tun, in HL2 begannen, ging das „Rad“ erst Ende der 00er Jahre an die Massen, und jetzt ist es Mainstream ...



Dies ist jedoch nur eine historische Zusammenfassung, die nur als Geschichte von Interesse ist. Im Rahmen dieses Artikels wird es keine langen Diskussionen über die Gründe für die Popularität einer bestimmten Lösung geben. sowie Geschmack darüber, welcher Selektor besser ist. Nur weil der Prozess der Anpassung des guten alten Schicksals an die Auswahl der Werkzeuge mit der Maus unten beschrieben wird.



Ziele setzen



Um WW zu implementieren, müssen Sie die Mausbewegungen irgendwie abfangen, ihre Bewegung verfolgen, während die Auswahltaste gedrückt wird, und nach dem Loslassen den Klick auf die Schaltfläche emulieren, die dem ausgewählten Sektor entspricht.



Dafür habe ich die Java-Sprache verwendet, insbesondere das Abfangen der Schlüssel erfolgt über die jnativehook-Bibliothek, und das Drücken ist auf awt.Robot zurückzuführen. Die Verarbeitung empfangener Hooks ist nicht schwierig und erfolgt daher manuell.



Implementierung



Zuvor wurden Klassen entwickelt, die Koordinatenpaare definieren, um den Verschiebungsvektor zu bestimmen.



Mit der Shift-Klasse können Sie insbesondere einen zweidimensionalen Vektor speichern sowie dessen Länge bestimmen, und mit der NormalizedShift-Klasse, mit der unter anderem ein normalisierter Vektor gespeichert werden soll, können Sie den Winkel zwischen dem abgefangenen Vektor und dem Vektor bestimmen (1,0).



Spoiler Überschrift
class Shift{
    int xShift;
    int yShift;

    public int getxShift() {
        return xShift;
    }

    public int getyShift() {
        return yShift;
    }

    public void setxShift(int xShift) {
        this.xShift = xShift;
    }

    public void setyShift(int yShift) {
        this.yShift = yShift;
    }
    double getLenght(){
        return Math.sqrt(xShift*xShift+yShift*yShift);
    }

}
class NormalisedShift{
  double normalizedXShift;
  double normalizedYShift;
  double angle;
  NormalisedShift (Shift shift){
      if (shift.getLenght()>0)
      {
          normalizedXShift = -shift.getxShift()/shift.getLenght();
        normalizedYShift = -shift.getyShift()/shift.getLenght();
      }
      else
      {
          normalizedXShift = 0;
          normalizedYShift = 0;
      }
  }
  void calcAngle(){
      angle = Math.acos(normalizedXShift);
  }

  double getAngle(){
      calcAngle();
      return (normalizedYShift<0?angle*360/2/Math.PI:360-angle*360/2/Math.PI);
    };
};




Sie sind nicht von besonderem Interesse, und nur die Zeilen 73 bis 74, die den Vektor normalisieren, erfordern einen Kommentar. Unter anderem wird der Vektor gespiegelt. Der Bezugsrahmen ändert sich - Tatsache ist, dass Vektoren aus Sicht der Software und aus Sicht der bekannten Mathematik traditionell unterschiedlich gerichtet sind. Aus diesem Grund haben die Vektoren der Shift-Klasse ihren Ursprung oben links und die NormalizedShift-Klasse unten links.



Um die Arbeit des Programms zu implementieren, wurde die Wheel-Klasse implementiert, die die Schnittstellen NativeMouseMotionListener und NativeKeyListener implementiert. Der Code befindet sich unter dem Spoiler.



Spoiler Überschrift
public class Wheel  implements NativeMouseMotionListener, NativeKeyListener {

    final int KEYCODE = 15;
    Shift prev = new Shift();
    Shift current = new Shift();
    ButtomMatcher mathcer = new ButtomMatcher();


    boolean wasPressed = false;

    @Override
    public void nativeMouseMoved(NativeMouseEvent nativeMouseEvent) {
        current.setxShift(nativeMouseEvent.getX());
        current.setyShift(nativeMouseEvent.getY());

    }
    @Override
    public void nativeMouseDragged(NativeMouseEvent nativeMouseEvent) {

    }
    @Override
    public void nativeKeyTyped(NativeKeyEvent nativeKeyEvent) {

    }

    @Override
    public void nativeKeyPressed(NativeKeyEvent nativeKeyEvent) {
        if (nativeKeyEvent.getKeyCode()==KEYCODE){
            if (!wasPressed)
            {
                prev.setxShift(current.getxShift());
                prev.setyShift(current.getyShift());
            }
            wasPressed = true;

        }
    }

    @Override
    public void nativeKeyReleased(NativeKeyEvent nativeKeyEvent) {
        if (nativeKeyEvent.getKeyCode() == KEYCODE){
            Shift shift = new Shift();
            shift.setxShift(prev.getxShift() - current.getxShift());
            shift.setyShift(prev.getyShift() - current.getyShift());
            NormalisedShift normalisedShift = new NormalisedShift(shift);
            mathcer.pressKey(mathcer.getCodeByAngle(normalisedShift.getAngle()));
            wasPressed = false;
        }
    }




Lassen Sie uns herausfinden, was hier los ist.



Die Variable KEYCODE speichert den Code des Schlüssels, mit dem der Selektor aufgerufen wird. Normalerweise ist dies TAB, aber bei Bedarf kann es im Code geändert oder - idealerweise - aus der Konfigurationsdatei abgerufen werden.



prev speichert die Position des Mauszeigers, die sich zum Zeitpunkt des Aufrufs des Selektors befand. Die aktuelle Position des Cursors bleibt aktuell. Dementsprechend werden beim Loslassen der Auswahltaste die Vektoren subtrahiert und die Verschiebung des Cursors während der Zeit, in der die Auswahltaste gedrückt gehalten wird, in die Verschiebungsvariable geschrieben.



Dann wird in Zeile 140 der Vektor normalisiert, d.h. reduziert, um sich zu bilden, wenn seine Länge nahe eins ist. Danach wird der normalisierte Vektor an den Matcher übertragen, der eine Entsprechung zwischen dem Code der zu drückenden Taste und dem Drehwinkel des Vektors herstellt. Aus Gründen der Lesbarkeit wird der Winkel in Grad umgerechnet und ist auch in einem vollständigen Einheitskreis ausgerichtet (acos funktioniert nur mit Winkeln bis zu 180 Grad).



Die ButtonMatcher-Klasse definiert die Entsprechung zwischen dem Winkel und dem ausgewählten Schlüsselcode.



Spoiler Überschrift
class ButtomMatcher{

    Robot robot;
    final int numberOfButtons = 6;
    int buttonSection = 360/numberOfButtons;
    int baseShift = 90-buttonSection/2;
    ArrayList<Integer> codes = new ArrayList<>();
    void matchButtons(){
        for (int i =49; i<55; i++)
            codes.add(i);

    }
    int getCodeByAngle(double angle){
        angle= (angle+360-baseShift)%360;
        int section = (int) angle/buttonSection;
        System.out.println(codes.get(section));
        return codes.get(section);
    }
    ButtomMatcher() {
        matchButtons();
        try
        {
            robot = new Robot();
        }
        catch (AWTException e) {
            e.printStackTrace();
        }
    }
    void pressKey(int keyPress)
    {

        robot.keyPress(keyPress);
        robot.keyRelease(keyPress);
    }
}




Darüber hinaus bestimmt die Variable numberOfButtons die Anzahl der Sektoren und die entsprechenden Schaltflächen, baseShift legt den Drehwinkel fest (insbesondere sorgt es für Symmetrie um die vertikale Achse und dreht das Rad um 90 Grad, sodass sich die Nahkampfwaffe oben befindet), und das Code-Array speichert Codes Tasten - falls die Tasten geändert werden und die Codes nicht aufeinander folgen. In einer fortgeschritteneren Version wäre es möglich, sie aus der Konfigurationsdatei abzurufen, aber mit dem Standardtastaturlayout ist die aktuelle Version durchaus realisierbar.



Fazit



Im Rahmen dieses Artikels wurde die Möglichkeit beschrieben, die Benutzeroberfläche klassischer Schützen an moderne Standards anzupassen. Natürlich fügen wir hier keine Erste-Hilfe-Kits oder Linearität hinzu - es gibt viele Mods dafür, aber oft liegt in solchen Details eine freundliche und bequeme Oberfläche. Der Autor erkennt, dass er wahrscheinlich nicht den optimalsten Weg beschrieben hat, um das gewünschte Ergebnis zu erzielen, und erwartet in den Kommentaren auch ein Bild mit einem Laib und einem Oberleitungsbus, aber es war dennoch eine interessante Erfahrung, die vielleicht einige Spieler zum Entdecken ermutigen wird Die wunderbare Welt von Java.



Konstruktive Kritik ist willkommen.



Quellcode



All Articles