読者です 読者をやめる 読者になる 読者になる

つらねの日記

プログラムの進捗やゲームをプレイした感想などを書き連ねる日記。

艦と刀を平行するにあたり

前置き

最近艦これと刀剣乱舞を並列してプレイすることが多い。
どちらも出撃させた後は眺める系統のゲームで待ち時間というものが発生しうるためだ。たとえば、陣形選択から開戦までの間の時間は眺めることも祈ることもできず、完全に無駄な時間といえる。それゆえそういった時間を惜しむべくこれら二つを同時並行でプレイしているわけである。
f:id:turane_gaku:20150306211617p:plain
その際、一つのブラウザにタブを二つ表示してプレイするのだが、タブを切り替えるためには画面中央部でプレイしているマウスを画面上部に持っていき、正しく狙い定めてクリックするか、キーボードでCTRL+PGDOWN or PGUPを打つかである。しかしながらこれらは無論面倒くさくあり、特に前者の場合×を間違えて押してしまい、タブをクローズしてしまう危険性がある。(ボス前でこの誤動作を行ってしまった際の哀しみは尋常ならざる)


従って比較的容易にかつ安全高速にタブを切り替える方法が必要となった。
それゆえ作成したのが以下のプログラムである。

MousePageDown.pde
import java.awt.MouseInfo;
import java.awt.Robot;
import java.awt.event.KeyEvent;

Robot r;

final int BORDER=900;

void setup() {
  try {
    r=new Robot();
  }
  catch(java.awt.AWTException e) {
  }
}

boolean checked=false;

void draw() {
  background(checked?#ff0000:#0000ff);

  if (checked) {
    if (MouseInfo.getPointerInfo().getLocation().x>BORDER) {
      checked=false;
    }
  } else {
    if (MouseInfo.getPointerInfo().getLocation().x<=BORDER) {
      checked=true;
      r.keyPress(CONTROL);
      r.keyPress(KeyEvent.VK_PAGE_DOWN);
      r.keyRelease(KeyEvent.VK_PAGE_DOWN);
      r.keyRelease(CONTROL);
    }
  }
}

MouseInfo.getPointerInfo().getLocation()でマウスの絶対座標をとり、その値を一定値以上右に送った後に左に戻した際にRobotを用いてCTRL+PGDOWNを押したことにするというものである。
画面色はそのフラグを満たしているかどうかをみるために一応色を変えるようにはした。ただし、大体の場合後ろの方に行ってしまうし見ないので実質需要はなく不要だった。

使いにくさを感じた

一時期これは利用したが問題があった。これを裏で起動しながら画面の左でゲーム、右で情報検索をというプレイをしていると、残念ながら意図しないタブ移動が発生するのである。
勿論ブラウザに直接キーイベントやらタブ移動のイベントを送信することができれば容易にその問題は解決できるのであろうが、わからなかった。

逆移動を実装するのも手段の一つではあったがどうせならということで別のものを作ることにした。

PS3コントローラーによるタブ切り替え

processingのライブラリにproControllというものがある。
コントローラーの扱いを簡易化するためのライブラリである。このライブラリを用いてPS3コントロラーでそれこそゲームをするがごとく、艦これと刀剣乱舞をプレイすることにした。このライブラリを導入するにあたって注意する点は64bit版の場合は、jinput-dx8_64.dllを別に持ってきてそれをjinput-dx8.dllリネームする必要があるという点である。(ちょっと前は普通に動いたのだがこれを書こうとしたら動かなくなっていて多少困惑した。)
このライブラリにおいて注目すべき点はフォーカスが外れていてもデバイスの情報を取得することができるという点だ。したがって位置座標と同様にして裏で動かして使うことができる。ちなみにキーボードやマウスもデバイスとして取得することができる。

そこで作成したのが以下のプログラムである。

GameByPS3Controller.pde
import java.awt.*;
import java.awt.event.*;
import java.awt.event.KeyEvent;  //processingのKeyEventの使用を避ける
import procontroll.*;

ControllIO controll;
PS ps;
Robot r;

void setup() {
  size(400, 400);

  controll = ControllIO.getInstance(this);

  ps = new PS(controll.getDevice("Controller (XBOX 360 For Windows)"));
  try {
    r=new Robot();
  }  
  catch(AWTException e) {
  }
}

void draw() {
  if (frame.isVisible())frame.setVisible(false);

  background(0);
  fill(255);

  if (frameCount>10) {
    if (ps.getStickL().mag()>ps.BORDER) {
      PVector d=ps.getStickL();
      Point p=MouseInfo.getPointerInfo().getLocation();
      if (ps.is(ps.R)) d.mult(2);
      p.x+=sq(d.x)*5*(d.x>0?1:-1);
      p.y+=sq(d.y)*5*(d.y>0?1:-1);
      r.mouseMove((int)p.x, (int)p.y);
    }
    if (ps.getStickR().mag()>ps.BORDER) {
      PVector d=ps.getStickR();
      if (frameCount%10==0)
        r.mouseWheel(round(d.y*3));
    }
  }
}

PApplet This() {
  return this;
}

public void pressA() {
  r.mousePress(InputEvent.BUTTON1_MASK);
}
public void releaseA() {
  r.mouseRelease(InputEvent.BUTTON1_MASK);
}
public void pressX() {
  r.mousePress(InputEvent.BUTTON3_MASK);
}
public void releaseX() {
  r.mouseRelease(InputEvent.BUTTON3_MASK);
}
public void pressL() {
  r.keyPress(CONTROL);
  r.keyPress(KeyEvent.VK_PAGE_UP);
}
public void releaseL() {
  r.keyRelease(KeyEvent.VK_PAGE_UP);
  r.keyRelease(CONTROL);
}

public void pressWay(float x, float y) {
  if (ps.getWay()%2==1)return;
  r.keyPress(LEFT+ps.getWay()/2%4);
}
public void releaseWay(float x, float y) {
  if (ps.getWay()%2==1)return;
  r.keyRelease(LEFT+ps.getWay()/2%4);
}

class PS {
  ControllSlider[] slider;
  ControllButton[] button;
  PVector stickL, stickR;
  public final float BORDER=0.15;
  public final int B=0;
  public final int R=5;
  public final int W=10;

  public PS(ControllDevice device) {
    stickL=new PVector();
    stickR=new PVector();

    slider=new ControllSlider[device.getNumberOfSliders()];
    for (int i=0; i<slider.length; i++) slider[i]=device.getSlider(i);
    button=new ControllButton[device.getNumberOfButtons()];
    for (int i=0; i<button.length; i++) button[i]=device.getButton(i);
    button[1].plug(This(), "pressA", ControllIO.ON_PRESS);
    button[1].plug(This(), "releaseA", ControllIO.ON_RELEASE);
    button[3].plug(This(), "pressX", ControllIO.ON_PRESS);
    button[3].plug(This(), "releaseX", ControllIO.ON_RELEASE);
    button[4].plug(This(), "pressL", ControllIO.ON_PRESS);
    button[4].plug(This(), "releaseL", ControllIO.ON_RELEASE);
    button[10].plug(This(), "pressWay", ControllIO.ON_PRESS);
    button[10].plug(This(), "releaseWay", ControllIO.ON_PRESS);
  }

  public int getWay() {
    return button[10].pressed()?(int)button[10].getValue():0;
  }
  public boolean is(int n) {
    return button[n].pressed();
  }

  public PVector getStickL() {
    stickL.set(slider[1].getValue(), slider[0].getValue());
    return stickL;
  }
  public PVector getStickR() {
    stickR.set(slider[3].getValue(), slider[2].getValue());
    return stickR;
  }
}

自分の環境では左スティックでマウスを移動し、L1でタブ切り替えを行えるようにした。

感想とか展望とか

惜しむらくはALT+TABを使うために何やらいろいろと面倒な設定を行う必要があるらしく実装に至れなかったことだ。また、このライブラリが中で行っていることをしっかりと理解すれば無駄な部分をそぎ落とし、Java側でわざわざPAppletのimportなどを行わずに済むのだが、面倒だったのであきらめた。
このプログラムを今はいくつかの改良を施し、Javaに書き換えて利用している。なかなかに快適である。早く餅を集め狐を狩りたいものであるが、どちらも思うようにいかないのが現実である。