まるぼ実験場

今年専門を卒業したけど就活サボってしまいました(関係ない職種でフリーター生活中)。どうすんだよこれ。

Arduinoでアーケードスティック(LS-32-01)をPCに繋いで簡易アケコン的に遊ぶ

最近アーケード格ゲーを触って、ちょっとコマンド練習してみたいなーと思ったのだが、
練習用にアケコンを買うにも高いし。今ストリートファイター6需要でどうも品薄っぽいし。てかまず部屋に置き場所ないし。

格ゲーやるなら家庭用とかでええやん、ってなるかもだけど、自分の場合ただ格ゲーをガチりたいわけではなく、
ふらっと友達や家族とゲーセンに行ったときに一緒に遊べるゲームを増やしたいなー程度で練習しておきたいというだけなのである。
なので正直今更アケコン買うほどか?と思うところもあるし、家庭用なら別にパッドでもいいと思ってる(というか多分パッドやキーボード操作のが上手い……)
ということでゲーセン版への拘りがあるので、どうしてもアーケード仕様のスティックには慣れる必要がある。スティック操作さえ練習できれば……。
いや、昔なら現地でお金積んでみんな上手くなっていったんだろうけども……。

そこで閃いたのだった、アーケード仕様のスティックだけならパーツ屋行けば単品で買えるやん。
このスティックをPCに接続し、ボタン操作はPCのキーボード操作でカバーすれば省スペースローコストにスティック操作を練習できるのでは?
今の時代のトレンドは自作アケコンだとも聞いたことがある!

買ったもの

セイミツ工業 LS-32-01

スティック本体は個人のお好みのものを。機種によって端子の種類や取り付け可能ベースが変わったりするのでその辺は注意。
筆者はまだあんまり分からないが、機種毎にクリック感の違いとかあるようでなかなかの沼を感じる。
よく行くゲーセンのと同じ機種にしても良かったかもしれない。格ゲーなら三和電子製がお好みな人のが多いのかな?

レバーボール

付いてなかったので。

5Pハーネス

↑のLS-32-01はコネクタ接続タイプだったので購入。

薄めのMDF合板、ベース用ネジ

そのうち外側を作るため。

マイコンに接続


本体にハーネスを繋ぎ、橙の線をArduinoのGND、
他の線を適当なデジタル入力に接続してシリアルモニタで入力をチェックした。
ハーネスが右上として、入力の方向と出力の内容を以下のソースコードの出力によって確認した。

const int PIN_A = 2; //緑
const int PIN_B = 3; //黒 
const int PIN_C = 4; //赤
const int PIN_D = 5; //黄

void setup() {
  // put your setup code here, to run once:
  pinMode(PIN_A, INPUT_PULLUP);
  pinMode(PIN_B, INPUT_PULLUP);
  pinMode(PIN_C, INPUT_PULLUP);
  pinMode(PIN_D, INPUT_PULLUP);

  Serial.begin(9600);
}

void loop() {
  // put your main code here, to run repeatedly:
  int val_a,val_b,val_c,val_d;

  val_a = digitalRead(PIN_A);
  val_b = digitalRead(PIN_B);
  val_c = digitalRead(PIN_C);
  val_d = digitalRead(PIN_D);

  Serial.print(val_a);
  Serial.print(val_b);
  Serial.print(val_c);
  Serial.print(val_d);
  Serial.println();

  delay(500);
}

結果まとめ

格ゲーライクにテンキー表記。

7:1010 8:1011 9:1001
4:1110 N:1111 6:1101
1:0110 2:0111 3:0101

……なんとなくビットで処理してみたくなる文字列が出てきましたね。
各方向の入力が特定できたので、それをパターンわけしてキーボードとして入力すればできそうだ。

ソースコード

#include <Keyboard.h>

//セイミツ工業LS-32-01使用、ハーネスを右上とする
const int PIN_A = 2; //緑
const int PIN_B = 3; //黒 
const int PIN_C = 4; //赤
const int PIN_D = 5; //黄

const int INPUT_1 = 6;  //0110
const int INPUT_2 = 7;  //0111
const int INPUT_3 = 5;  //0101
const int INPUT_4 = 14; //1110
const int INPUT_5 = 15; //1111
const int INPUT_6 = 13; //1101
const int INPUT_7 = 10; //1010
const int INPUT_8 = 11; //1011
const int INPUT_9 = 9;  //1001

boolean state_down, state_up, state_left, state_right;

void setup() {
  // put your setup code here, to run once:
  pinMode(PIN_A, INPUT_PULLUP);
  pinMode(PIN_B, INPUT_PULLUP);
  pinMode(PIN_C, INPUT_PULLUP);
  pinMode(PIN_D, INPUT_PULLUP);

  Keyboard.begin();
}

void loop() {
  // put your main code here, to run repeatedly:
  int val_a,val_b,val_c,val_d;

  val_a = digitalRead(PIN_A);
  val_b = digitalRead(PIN_B);
  val_c = digitalRead(PIN_C);
  val_d = digitalRead(PIN_D);

  int input;
  input = val_a << 3;
  input = input | (val_b << 2);
  input = input | (val_c << 1);
  input = input | val_d;
  
  switch(input){
    case INPUT_1:
      state_up = false;
      state_down = true;
      state_left = true;
      state_right = false;
      break;
    case INPUT_2:
      state_up = false;
      state_down = true;
      state_left = false;
      state_right = false;
      break;
    case INPUT_3:
      state_up = false;
      state_down = true;
      state_left = false;
      state_right = true;
      break;
    case INPUT_4:
      state_up = false;
      state_down = false;
      state_left = true;
      state_right = false;
      break;
    case INPUT_5:
      state_up = false;
      state_down = false;
      state_left = false;
      state_right = false;
      break;
    case INPUT_6:
      state_up = false;
      state_down = false;
      state_left = false;
      state_right = true;
      break;
    case INPUT_7:
      state_up = true;
      state_down = false;
      state_left = true;
      state_right = false;
      break;
    case INPUT_8:
      state_up = true;
      state_down = false;
      state_left = false;
      state_right = false;
      break;
    case INPUT_9:
      state_up = true;
      state_down = false;
      state_left = false;
      state_right = true;
      break;
  }
  /*
  state_up ? Keyboard.press(KEY_UP_ARROW) : Keyboard.release(KEY_UP_ARROW);
  state_down ? Keyboard.press(KEY_DOWN_ARROW) : Keyboard.release(KEY_DOWN_ARROW);
  state_left ? Keyboard.press(KEY_LEFT_ARROW) : Keyboard.release(KEY_LEFT_ARROW);
  state_right ? Keyboard.press(KEY_RIGHT_ARROW) : Keyboard.release(KEY_RIGHT_ARROW);
  */
  
  state_up ? Keyboard.press('w') : Keyboard.release('w');
  state_down ? Keyboard.press('s') : Keyboard.release('s');
  state_left ? Keyboard.press('a') : Keyboard.release('a');
  state_right ? Keyboard.press('d') : Keyboard.release('d');
  
  delay(1);
}

アケコンのスティックの入力はボタンとは異なり、倒された時には押されっぱなしになる必要があり、
入力ナシになるのがレバーニュートラル(真ん中)の状態のみである。
なので基本はKeyboard.pressを使用する必要があるが、入力の方向が変わったときにはKeyboard.releaseを入力しなければならない。
パターン分けするのは大変そうだったので、4方向分の入力情報を持たせて最後に纏めて処理させる事にした。

ここであることに気づいた

ところで先述の表をもう一度見て欲しい。

7:1010 8:1011 9:1001
4:1110 N:1111 6:1101
1:0110 2:0111 3:0101

……よく見ると法則が見えてこないだろうか。

ニュートラル時は固定で全て1、
スティックが、
下方向に入力されているときは1桁目が0、
上方向に入力されているときは2桁目が0、
右方向に入力されているときは3桁目が0、
左方向に入力されているときは4桁目が0、
斜め方向の場合、両方向の積を取った値になっていた、ということに気がついた。
これならソースコードをさらにシンプルにできる。

ソースコード・改

#include <Keyboard.h>

//セイミツ工業LS-32-01使用、ハーネスを右上とする
const int PIN_A = 2; //緑
const int PIN_B = 3; //黒 
const int PIN_C = 4; //赤
const int PIN_D = 5; //黄

void setup() {
  // put your setup code here, to run once:
  pinMode(PIN_A, INPUT_PULLUP);
  pinMode(PIN_B, INPUT_PULLUP);
  pinMode(PIN_C, INPUT_PULLUP);
  pinMode(PIN_D, INPUT_PULLUP);

  Keyboard.begin();
}

void loop() {
  // put your main code here, to run repeatedly:
  int val_a,val_b,val_c,val_d;

  val_a = digitalRead(PIN_A);
  val_b = digitalRead(PIN_B);
  val_c = digitalRead(PIN_C);
  val_d = digitalRead(PIN_D);
  
   /*
  val_b == 0 ? Keyboard.press(KEY_UP_ARROW) : Keyboard.release(KEY_UP_ARROW);
  val_a == 0 ? Keyboard.press(KEY_DOWN_ARROW) : Keyboard.release(KEY_DOWN_ARROW);
  val_d == 0 ? Keyboard.press(KEY_LEFT_ARROW) : Keyboard.release(KEY_LEFT_ARROW);
  val_c == 0 ? Keyboard.press(KEY_RIGHT_ARROW) : Keyboard.release(KEY_RIGHT_ARROW);
  */
  
  val_b == 0 ? Keyboard.press('w') : Keyboard.release('w');
  val_a == 0 ? Keyboard.press('s') : Keyboard.release('s');
  val_d == 0 ? Keyboard.press('a') : Keyboard.release('a');
  val_c == 0 ? Keyboard.press('d') : Keyboard.release('d');
  
  delay(1);
}

入力を判定していたところがごっそりと消えてしまった(笑)サンプルコード並のシンプルさ。
いやまぁ、最終的にやったことってdigitalReadしてキーボード入力にパスしただけ……。

いちおうPC版メルブラの初心者ミッションをこのスティック繋いでやってみて、だいたい達成出来たから方向キーの入力に関しては大丈夫だろう、たぶん。キーボードと入力が混信せずにちゃんと入力されてそう。(ただし筆者の腕前はお察しなのでそう言い切るのは不安である)

MELTY BLOOD: TYPE LUMINA - PS4

MELTY BLOOD: TYPE LUMINA - PS4

  • ディライトワークス
Amazon

これでスティック部分のプログラムはOKかなってところで。
てかここにボタンを足したらまんまアケコンができてしまいそうよねw

あとは操作中にスティックが動かないように外側の箱もつくらないとかなー。

※続き
marubodiary.hateblo.jp