Doom 1993のウェポンホイール

こんにちは。



私たちの多くは、世紀の変わり目にリリースされた昔ながらのビデオゲームに熱く関わっています。彼らは素晴らしい雰囲気、必死のダイナミクス、そして何十年も経っても時代遅れにならない多くのオリジナルのソリューションを持っています。ただし、今日では、ゲームインターフェイスのビジョンが多少変更されました。線形の廊下が混乱するレベルに置き換わり、再生は救急箱に取って代わりました。武器0-9の長い列の代わりに武器を選択し、最初にマウスホイール、次に仮想ホイールになりました。議論されるのは今日の彼についてです。



画像



歴史的要約



以前は、このようなシューティングゲームのジャンルが出現したときには、マウスコントロールの問題は提起されませんでした。主人公のコントロールにはキーボードのみが使用されました。さらに、単一の管理形式もありませんでした。WASDは少し後に標準になりました。古いゲーミングキーボードレイアウトの詳細については、こちらをご覧ください



したがって、機器を選択する機能が実装されたゲーム(Doom、Wolfenstein、Quakeなど)では、キーボードの数字キーを使用して、その時点で直感的な方法でのみ実装されました。そして長年、この方法が唯一の方法でした。

その後、90年代後半に、マウスホイールで武器を変更することが可能になりました。



このトピックに関する明確な情報を見つけることはできませんでしたが、CS 1.6では、この機能はコンソールから有効になりました。ただし、そのような前例が以前に存在した可能性があります。この場合は、コメントまたはPMでこれを示してください。しかし、私たちの時代に馴染みのある形式では、武器の輪はクライシスとそのスーツメニューでのみ使用されるようになりました。 ...



ただし、これは単なる歴史的要約であり、歴史としてのみ関心があります。この記事の枠組みの中で、このソリューションまたはそのソリューションが人気を博している理由についての長い議論はありません。また、どのセレクターが優れているかについても味わってください。古き良きドゥームをマウスを使ったツールの選択に適応させるプロセスを以下に説明します。



目標を設定する



WWを実装するには、マウスの動きを何らかの方法でインターセプトし、セレクターキーを押している間、その動きを追跡し、離したときに、選択したセクターに対応するボタンのクリックをエミュレートする必要があります。



このため、私はJava言語を使用しました。特に、キーのインターセプトはjnativehookライブラリを使用して実行され、プレスはawt.Robotを使用して実行されます。受信したフックの処理は難しくないため、手動で行います。



実装



以前は、変位ベクトルを決定するための座標のペアを指定するクラスが開発されていました。



特に、Shiftクラスを使用すると、2次元のベクトルを格納し、その長さを決定できます。特に、正規化ベクトルを格納するように設計されたNormalisedShiftクラスを使用すると、インターセプトされたベクトルとベクトル(1,0)の間の角度を決定できます。



スポイラーヘッダー
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);
    };
};




それらは特に興味深いものではなく、ベクトルを正規化する73〜74行目のみにコメントが必要です。とりわけ、ベクトルが反転します。参照の枠組みが変わる-事実は、ソフトウェアの観点から、そして親しみのある数学の観点から、ベクトルは伝統的に異なる方向を向いているということです。そのため、Shiftクラスのベクトルの左上に原点があり、NormalizedShiftクラスの左下に原点があります。



プログラムの作業を実装するために、NativeMouseMotionListenerおよびNativeKeyListenerインターフェースを実装するWheelクラスが実装されました。コードはネタバレの下にあります。



スポイラーヘッダー
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;
        }
    }




ここで何が起こっているのかを理解しましょう。



KEYCODE変数には、セレクターの呼び出しに使用されるキーのコードが格納されます。通常、これはTABですが、必要に応じてコードで変更するか、理想的には構成ファイルから取得できます。



prevは、セレクターが呼び出されたときのマウスカーソルの位置を格納します。カーソルの現在位置は現在のまま維持されます。したがって、セレクターキーが離されると、ベクトルが減算され、セレクターキーが押されている間、カーソルのシフトがシフト変数に書き込まれます。



次に、ライン140で、ベクトルが正規化されます。長さが1に近づくと、フォームに縮小されます。その後、正規化されたベクトルがマッチャーに転送され、マッチャーが押されるキーのコードとベクトルの回転角度の対応を設定します。読みやすさの理由から、角度は度に変換され、完全な単位円に向けられます(acosは最大180度の角度でのみ機能します)。



ButtonMatcherクラスは、角度と選択されたキーコード間の対応を定義します。



スポイラーヘッダー
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);
    }
}




さらに、変数numberOfButtonsはセクターの数と対応するボタンを決定し、baseShiftは回転角度を設定します(特に、垂直軸について対称性を提供し、近接武器が上になるようにホイールを90度回転させます)、およびコード配列はコードを格納しますキー-ボタンが変更され、コードが連続しない場合。より洗練されたバージョンでは、構成ファイルからそれらをプルアップすることが可能ですが、キーの標準的な配置により、現在のバージョンは非常に実行可能です。



結論



この記事の枠組みの中で、クラシックなシューティングゲームのインターフェースを現代の標準に合わせてカスタマイズする可能性について説明しました。もちろん、ここでは救急キットも直線性も追加していません。これには多くの変更がありますが、多くの場合、フレンドリーで便利なインターフェースは細部にあります。著者は、彼がおそらく望ましい結果を達成するための最も最適な方法を説明しなかったことに気づき、コメントにパンとトロリーバスのある写真を期待していますが、それでも、おそらく一部のゲーマーに発見を促す興味深い経験でしたJavaの素晴らしい世界。



建設的な批判が奨励されています。



ソースコード



All Articles