[Processing]キャラクターを移動させるためのクラスがほしい

#移動するためのクラスほしいことあるじゃん？
- 別に向き変えなくていい
- 座標で移動位置指定ができる
- 相対座標で移動位置指定ができる

#こんな
![キャラクター移動クラス.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/554244/580910ef-c560-b0bd-5078-885a7378f502.gif)



















#作りはこんな感じ
##まずフィールド
必要最低限を用意

```Charactor.java
  public PVector pos;//現在位置
  private PVector startPos;//線形補間開始地点
  private PVector target;//目標地点
  private float amt;//線形補間の割合0～1
  private float amtAdd;//線形補間の割合の加算量
  private PShape shape;//表示するモデル
```

##コンストラクタ
モデルと初期位置を指定できますが、モデルの指示がない場合は、箱を表示するようにしてあります。
さらに位置の指定は、ベクトルでもx,y,zでも指定できるようになっておりますお客様

```Charactor.java
  public Chara(PShape pshape, PVector position) {
    //モデル表示がなしなら箱を表示
    if (pshape == null) {
      pshape = createShape(BOX, 10);
    }
    shape = pshape;
    pos = position;
    amt = 0;
    amtAdd = 0;
  }
  public Chara(PShape pshape, float x, float y, float z) {
    this(pshape, new PVector(x, y, z));
  }
  public Chara(PVector position) {
    this(null, position);
  }
  public Chara(float x, float y, float z) {
    this(null, x, y, z);
  }
```

#位置指定
種類は2つ

- この座標に移動する　
    - 絶対座標 → moveAbs
- 現在位置から一定量移動する
    - 相対座標 → moveRel

第1引数は移動先の位置
第2引数は移動にかける時間を代入します

```Charactor.java
  //相対座標で移動位置指定
  public void moveRel(PVector pos, float second) {
    moveAbs(pos.add(this.pos),second);
  }
  //相対座標xyzで移動位置指定
  public void moveRel(float x, float y, float z, float second) {
    moveAbs(new PVector(x, y, z).add(pos), second);
  }

  //絶対座標で移動位置指定
  public void moveAbs(PVector pos, float second) {
    startPos = this.pos;
    target = pos;
    amtAdd = 1f/ second / frameRate;
  }
  //絶対座標xyzで移動位置指定
  public void moveAbs(float x, float y, float z, float second) {
    moveAbs(new PVector(x, y, z), second);
  }
```

#使ってみる
キーボード入力で0～3を入力すると適当にメソッドを試すようになってます
このプログラムと後述の`Chara`クラスを使います

```Main.java
Chara[] charas = new Chara[4];
String code = "don't pressed";

void setup() {
  size(1000, 800, P3D);
  randomSeed(millis());
  for (int i=0; i<charas.length; i++) {
    charas[i] = new Chara(i*20, 0, 0);
  }
}

void draw() {
  background(100, 100, 200);
  translate(width/2, height/2, 500);
  for (int i=0; i<charas.length; i++) {
    charas[i].update();
  }
  text("code:" + code,0,0);
}

void keyPressed() {
  switch(key){
    case '0':
    for (int i=0; i<charas.length; i++) {
      charas[i].moveAbs(PVector.random3D().mult(20), 5);
    }
    break;
    case '1':
    for (int i=1; i<=charas.length; i++) {
      charas[i-1].moveAbs(-20*i, -10*i, -10*i, 3);
    }
    break;
    case '2':
    for (int i=0; i<charas.length; i++) {
      charas[i].moveRel(-10,-30,20, 1);
    }
    break;
    case '3':
    float second = random(1,5);
    for (int i=0; i<charas.length; i++) {
      charas[i].moveRel(PVector.random3D().mult(10*second), second);
    }
    break;
  }
  code = key + "";
}

```

##移動させるためのクラス全貌


```Charactor.java
public class Chara {
  public PVector pos;//現在位置
  private PVector startPos;//線形補間開始地点
  private PVector target;//目標地点
  private float amt;//線形補間の割合0～1
  private float amtAdd;//線形補間の割合の加算量
  private PShape shape;//表示するモデル

/***********************コンストラクタ******************************/
  public Chara(PShape pshape, PVector position) {
    //モデル表示がなしなら箱を表示
    if (pshape == null) {
      pshape = createShape(BOX, 10);
    }
    shape = pshape;
    pos = position;
    amt = 0;
    amtAdd = 0;
  }
  public Chara(PShape pshape, float x, float y, float z) {
    this(pshape, new PVector(x, y, z));
  }
  public Chara(PVector position) {
    this(null, position);
  }
  public Chara(float x, float y, float z) {
    this(null, x, y, z);
  }
/***********************コンストラクタ******************************/

  //表示と移動はこのメソッドを呼ぶ
  public boolean update() {
    boolean m = move();
    pushMatrix();
    translate(pos.x, pos.y, pos.z);
    shape(shape);
    popMatrix();
    return m;
  }

  //移動計算
  private boolean move() {
    if (target != null) {
      amt += amtAdd;//線形補間の割合を加算して1に近づける
      pos = new PVector(startPos.x,startPos.y,startPos.z).lerp(target, amt);//開始位置と目標地点で線形補間

      if (amt == 0 || amt >= 1) {//割合が1を超えたら移動しないように
        amt = 0;
        pos = target;
        startPos = pos;
        target = null;
      }
    }
    return target == null;//移動完了ならtrue 移動中ならfalseを返す
  }

  //相対座標で移動位置指定
  public void moveRel(PVector pos, float second) {
    moveAbs(pos.add(this.pos),second);
  }
  //相対座標xyzで移動位置指定
  public void moveRel(float x, float y, float z, float second) {
    moveAbs(new PVector(x, y, z).add(pos), second);
  }

  //絶対座標で移動位置指定
  public void moveAbs(PVector pos, float second) {
    startPos = this.pos;
    target = pos;
    amtAdd = 1f/ second / frameRate;
  }
  //絶対座標xyzで移動位置指定
  public void moveAbs(float x, float y, float z, float second) {
    moveAbs(new PVector(x, y, z), second);
  }
}

/*
参考資料
「PVector \ Language (API) \ Processing 3+」
https://processing.org/reference/PVector.html

「【Processing】ベクトルを扱ってみよう！」
https://dekfractal.com/2019/02/02/%E3%80%90processing%E3%80%91%E3%83%99%E3%82%AF%E3%83%88%E3%83%AB%E3%82%92%E6%89%B1%E3%81%A3%E3%81%A6%E3%81%BF%E3%82%88%E3%81%86%EF%BC%81/
*/
```
#タイムライン方式
順番に行動を起こさせるならこんな感じでどうよ

```Main.java

//タイムライン方式
Chara[][] chara=new Chara[4][4];
int mode = 0;

void setup() {
  size(800, 800, P3D);
  for (int i=0; i<chara.length; i++) {
    for (int j=0; j<chara[i].length; j++) {
      chara[i][j] = new Chara( 0, 0, 0);
    }
  }
}
void draw() {
  background(0);
  lights();
  translate(width/2, height/2, 300);
  boolean change = true;

  for (int i=0; i<chara.length; i++) {
    for (int j=0; j<chara[i].length; j++) {
      change &= chara[i][j].update(); //全部が移動し終えるとchangeがtrueになる
    }
  }

  if (change)switcher();
}

//移動先を切り替え
void switcher() {
  switch (mode) {
  case 0:
    mode0();
    break;
  case 1:
    mode1();
    break;
  case 2:
    mode2();
    break;
  case 3:
    mode3();
    break;
  case 4:
    mode4();
    break;
  }
  mode++;
}

void mode0() {
  float f=50, o=100, time = 5;

  chara[0][0].moveAbs( new PVector(-o, -o), time);
  chara[0][1].moveAbs( new PVector(-f, -o), time);
  chara[0][2].moveAbs( new PVector(-o, -f), time);
  chara[0][3].moveAbs( new PVector(-f, -f), time);

  chara[1][0].moveAbs( new PVector(o, -o), time);
  chara[1][1].moveAbs( new PVector(f, -o), time);
  chara[1][2].moveAbs( new PVector(o, -f), time);
  chara[1][3].moveAbs( new PVector(f, -f), time);

  chara[2][0].moveAbs( new PVector(-o, o), time);
  chara[2][1].moveAbs( new PVector(-f, o), time);
  chara[2][2].moveAbs( new PVector(-o, f), time);
  chara[2][3].moveAbs( new PVector(-f, f), time);

  chara[3][0].moveAbs( new PVector(o, o), time);
  chara[3][1].moveAbs( new PVector(f, o), time);
  chara[3][2].moveAbs( new PVector(o, f), time);
  chara[3][3].moveAbs( new PVector(f, f), time);
}
void mode1() {
  float fif=50, oneh=100, onefif=150, time = 5;

  chara[0][0].moveAbs( new PVector(fif, 0), time);
  chara[0][1].moveAbs( new PVector(oneh, fif), time);
  chara[0][2].moveAbs( new PVector(oneh, -fif), time);
  chara[0][3].moveAbs( new PVector(onefif, 0), time);

  chara[1][0].moveAbs( new PVector(0, fif), time);
  chara[1][1].moveAbs( new PVector(-fif, oneh), time);
  chara[1][2].moveAbs( new PVector(fif, oneh), time);
  chara[1][3].moveAbs( new PVector(0, onefif), time);

  chara[2][0].moveAbs( new PVector(0, -fif), time);
  chara[2][1].moveAbs( new PVector(fif, -oneh), time);
  chara[2][2].moveAbs( new PVector(-fif, -oneh), time);
  chara[2][3].moveAbs( new PVector(0, -onefif), time);

  chara[3][0].moveAbs( new PVector(-fif, 0), time);
  chara[3][1].moveAbs( new PVector(-oneh, -fif), time);
  chara[3][2].moveAbs( new PVector(-oneh, fif), time);
  chara[3][3].moveAbs( new PVector(-onefif, 0), time);
}

void mode2() {
  for (int i=0; i<chara.length; i++) {
    for (int j=0; j<chara[i].length; j++) {
      chara[i][j].moveRel(50, 0, 0, i+j+1);
    }
  }
}

void mode3() {
  for (int i=0; i<chara.length; i++) {
    for (int j=0; j<chara[i].length; j++) {
      chara[i][j].moveRel(0, 50, 0, 3);
    }
  }
}

void mode4() {
  for (int i=0; i<chara.length; i++) {
    for (int j=0; j<chara[i].length; j++) {
      chara[i][j].moveRel(-50, -50, 0, 3);
    }
  }
}
```

##こんな
![Charactorクラス.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/554244/e9146554-6fb9-efe9-89dc-33f3b6f00691.gif)
ちゃんと全部のアニメーションが終わるまで、次のアニメーションに移行しませんね

#参考資料
- [PVector \ Language (API) \ Processing 3+](https://processing.org/reference/PVector.html)
- [【Processing】ベクトルを扱ってみよう！](https://dekfractal.com/2019/02/02/%E3%80%90processing%E3%80%91%E3%83%99%E3%82%AF%E3%83%88%E3%83%AB%E3%82%92%E6%89%B1%E3%81%A3%E3%81%A6%E3%81%BF%E3%82%88%E3%81%86%EF%BC%81)