[Processing]当たり判定のあるゲームを作る（作った）

#Processingでエアーホッケーを作りました
![エアーホッケー.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/554244/07d63c11-dc67-310d-ac3e-c3b7c73cff13.gif)
当たり判定の計算は負荷軽減とか考えず、毎フレーム行ってます（てへぺろ）
~~当時負荷軽減という概念を考えなかったから許して~~
右のプレイヤーはマウスで
左のプレイヤーはWASDで動きます
どなたかの参考になれば...

#プログラム
拡張子本当は.pdeですけど、javaにした方が色ついて見やすいかなって

```Air_Hocckey.java


//プレイヤーの得点
int RightPlayerPoints = 0;
int LeftPlayerPoints = 0;

//ゴールライン
int Goal_Line = 150;

//ゴールメッセージポジション
int Goal_Text_PosX ;
  
//アニメーション画像
PImage[] LefPlayers = new PImage[4];
PImage[] RigPlayers = new PImage[4];

//ゴールライン切ったかどうか
boolean goal = false;

//ボールクラス配列で管理
Ball[] balls = new Ball[6];

enum Play_Mode{
    Mouse , Key
}
  
void setup(){
  size(1800,800);
  frameRate(30);
  ellipseMode(RADIUS);
  imageMode(CENTER);
  randomSeed(millis());
  
  //Ball(位置座標 , 角度(ラジアン) , (int)半径 , 移動速度)
  for(int i = 0;i<4;i++){
      balls[i] = new Ball(random((i+2)* width/6 , width * (i+2)/6f),height/6 * (i+2) 
                , random(-1f,1f)*PI , 40 ,random(3f,6f) , loadImage("Neon"+i+".png"));
  }
  
  //左プレイヤーのアニメーション読み込み
  for(int i = 0;i<4;i++){
    LefPlayers[i] = loadImage("LefPlayer"+i+".png");
  }
  
  //右プレイヤーのアニメーション読み込み
  for(int i = 0;i<4;i++){
    RigPlayers[i] = loadImage("RigPlayer"+i+".png");
  }
  
  //Mallet(float x , float y , float rad , int size , float speed , Play_Mode mode)
  //左側のプレイヤー　キーボード操作
  balls[4] = new Mallet( 60 , height/2f , 0 , 50 ,4 ,Play_Mode.Key , LefPlayers[0]);
  
  //右側のプレイヤー　マウス操作
  balls[5] = new Mallet(width - 60 , height/2 , 1 , 50 , 4 , Play_Mode.Mouse , RigPlayers[0]);
  
  Goal_Text_PosX = width;
  goal = false;
}

void Start(){
  
  //パックの位置をランダムで配置
  for(int i =0;i<4;i++){
    balls[i].Set_Pos((int)random((i+1)*width/6,width*5/6),(int)random((i+1)*height/6,height*5/6));
  }
  
  //変化のあった値のリセット
  Goal_Text_PosX = width;
  goal = false;
}

void draw(){
  background(0);
  
  //ゴールライン
  fill(0);
  strokeWeight(10);
  stroke(255);
  ellipse(0,height/2,50,Goal_Line);
  ellipse(width,height/2,50,Goal_Line);
  
  //センターライン
  line(width/2,0,width/2,height);
  noStroke();
  
  for(int i=0; i < balls.length; i++){
    
    for(int j=0;j<balls.length;j++){
      
      if(i==j){ 
        //自身を参照しないように continue
        continue;
      }
      if(Collision(balls[i],balls[j])){
        balls[i].Collision(balls[j]);
        balls[j].Collision(balls[i]);
      }
    }
    
    //ゴールした場合動かない
    if(!goal){
      balls[i].Move();
      
    }
    
    //画像表示
    image(balls[i]);
    
  } // balls[i] for() end }
    
  //スコア表示
  textSize(50);
  fill(0,255,0);
  text(LeftPlayerPoints,width *1/4 , 70);
  text(RightPlayerPoints,width *3/4 , 70);
  
  if(goal){
    textSize(200);
    
    //ゴールメッセージ
    if(Goal_Text_PosX > -5000){
      fill(255,255,0);
      text("GOOOOOOOOOOOOOOOOOOOOOOOOOOOOL!!!!!!!!!!",Goal_Text_PosX + 8,height/2);
      fill(255,0,0);
      text("GOOOOOOOOOOOOOOOOOOOOOOOOOOOOL!!!!!!!!!!",Goal_Text_PosX,height/2);
      Goal_Text_PosX -= 50;
    }
    else{
      //ゴールメッセージ表示後、パックの位置を再配置
      Start();
    }
    
  }
    
} // draw() end }


//ボールクラス
class Ball{
  
  //direction x y は、radian があれば求まるため削除
  
  //位置座標
  protected Vector pos;
  
  //ベクトルの角度
  protected float radian;
  
  //半径
  protected int radius;
  
  //移動速度
  protected float speed;
  
  //画像
  protected PImage image;
  
  //ゴール in 判定
  protected boolean goal_In = false;
  
  //コンストラクタ
  Ball(float x , float y , float rad , int size , float speed , PImage ima){
    this.pos = new Vector(x,y);
    radian = rad;
    radius = size;
    this.speed = speed;
    image = ima;
  }
  
  //エアホッケーのパック　(パック同士跳ね返る)
  public void Move(){
    
    //画面右端or左端　X向き反転
    // Pi * 1/6  →  PI * 5/6
    if(pos.x < radius || pos.x > width - radius ){
      
      if(!goal_In){
        
        //ゴール範囲外
        //画面右端or左端　X向き反転
        // Pi * 1/6  →  PI * 5/6
        if(pos.y < height/2 - Goal_Line + radius || pos.y > height/2 + Goal_Line - radius){
          radian = PI - radian;
        }
        else{ // ゴール範囲内
          goal_In = true;
        }
         
      } // if(!goal_In) end }
      else{
        
          //高さ調整
          if(pos.y > height/2 + Goal_Line - radius || pos.y < height/2 - Goal_Line + radius){
            radian *= -1;
          }
        
          //左のゴールに入り切ったら
          if(pos.x < -radius * 1.5f){
              RightPlayerPoints++;
          }
          else if(pos.x > width + radius * 1.5f)
          {
            //右のゴールに入り切ったら
            LeftPlayerPoints++;
          }
          //入り切ったらゴール
          goal = pos.x < -radius * 1.5f || pos.x > width + radius * 1.5f;
          goal_In = !goal;
      } //if("goal_In) else end }
      
    } // xの範囲 else end }
      
  
      //画面上or下　Y向き反転
      //PI * 1/6  →  PI * (- 1/6)
      if(pos.y < radius || pos.y > height - radius){
        radian *= -1;
      }
     
      //角度からそれぞれの移動距離を得て、速度を考慮する
      pos.x += cos(radian) * speed;
      pos.y += sin(radian) * speed;
      
  } // Move() end }
  
  //引数のボールとの角度を求め、その逆方向に radian を設定
  // → ボール同士で跳ね返る
  public void Collision(Ball ball){
    
    //引き算の関係を逆にすることで、逆向きの角度を得る
    float x = pos.x - ball.Get_Pos().x;
    float y = pos.y - ball.Get_Pos().y;
    
    radian = atan2(y,x);
  }
  
  //セッターズ&ゲッターズ
  public void Set_Radian(float rad){ radian = rad; }
  public void Set_Speed(float spe) { speed = spe; }
  public void Set_Pos(int x , int y){ pos.x = x; pos.y = y; }
  public PImage Get_Image(){ return image; }
  public float Get_Radian(){ return radian; }
  public float Get_Speed(){ return speed; }
  public int Get_Radius(){ return radius; }
  public Vector Get_Pos(){ return pos; }
}

//エアホッケーのプレイヤー
class Mallet extends Ball {
  
  //マレットの移動範囲
  Vector xRange;
  
  //操作モード
  Play_Mode mode;
  
  Mallet(float x , float y , float rad , int size , float speed , Play_Mode mode , PImage ima){
    super(x,y,rad,size,speed,ima);
    this.mode = mode;
    
    //操作モードによって移動範囲を制限
    if(mode == Play_Mode.Mouse){
      xRange = new Vector(width/2 + radius, width - radius);
    }
    else{
      xRange = new Vector(radius , width/2 - radius);
    }
  }
  
  //for文で回すためMove()をオーバーライドしてプレイヤー仕様にする
  public void Move(){
    float x = 0;
    float y = 0;
    int alfa = 0;
    int f = (frameCount/10) % 2;
    
    //マウス操作
    if(mode == Play_Mode.Mouse){
        
      x = mouseX - pos.x;
      y = mouseY - pos.y;
     
      //マウスと近いなら移動しない
      if(abs(x) < speed && abs(y) < speed){
        return;
      }
      
    }//キーボード操作
    else // mode == Play_Mode.Key
    { 
      if(keyPressed){
        if(key == 'a' || key == 'A'){
          x = -1;
        }else if(key == 'd' || key == 'D'){
          x = 1;
        }
        
        if(key == 'w' || key == 'W'){
          y = -1;
        }else if(key == 's' || key == 'S'){
          y = 1;
        }
        
      }else{ // なにもキーボードが押されてないなら動かない
        return;
      }
      
    } // Play_Mode end }
    
    //それぞれの x,y 関係から角度を得る
    radian = atan2(y,x);
    
    //角度からそれぞれの移動距離を得て、速度を考慮する
    if( (pos.x > xRange.x || x > 0 ) && (pos.x < xRange.y || x < 0) ){
      pos.x += cos(radian) * speed;
    }
    if( (pos.y > radius || y > 0) && (pos.y < height - radius || y < 0) ){
      pos.y += sin(radian) * speed;
    }
    
    //左移動時は、配列の後半の画像を参照させるための alfa
    if(x < 0){
          alfa = 2;
    }
    
    if(mode == Play_Mode.Mouse){
      image = RigPlayers[f + alfa];
    }
    else{
      image = LefPlayers[f + alfa];
    }
    
  } // Move() end }
  
} // class Mallet end }

//2次元ベクトルクラス
class Vector{
  
  //使いやすさ考慮のため public 
  public float x;
  public float y;
  
  Vector(float x , float y){
    this.x = x;
    this.y = y;
  }
  
  Vector(){
    this(0,0);
  }
  
}

//当たっていたら true を返す
boolean Collision(Ball b1,Ball b2){
  return ( dist(b1.Get_Pos().x,b1.Get_Pos().y,b2.Get_Pos().x,b2.Get_Pos().y) < b1.Get_Radius() + b2.Get_Radius() );
}


//ボールクラスから画像を表示するメソッド
void image(Ball ball){
  image(ball.Get_Image(),ball.Get_Pos().x,ball.Get_Pos().y,180,180);
}
```

#使用画像
##左のプレイヤー
LefPlayer0.png~LefPlayer3.png
![LefPlayer0.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/554244/beb5d536-aaf1-c372-c7c9-c0ae345fb614.png)
![LefPlayer1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/554244/8d622058-2107-0843-3308-560238daa713.png)
![LefPlayer2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/554244/a1c7b6db-7a77-9b25-ec4b-0ec72fa8ebd4.png)
![LefPlayer3.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/554244/4261534f-7c61-c855-9f63-4dfb20ae76c6.png)

##右のプレイヤー
RigPlayer0.png~RigPlayer3.png
![RigPlayer0.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/554244/4d73b2d3-0a94-b29b-012a-6f2631da459c.png)
![RigPlayer1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/554244/92354b05-e3bf-c1c8-2890-ef60546fae31.png)
![RigPlayer2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/554244/416761ad-5b92-daed-31c5-a6c80f505132.png)
![RigPlayer3.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/554244/c2cb0e49-7bec-5cc5-2999-5eaafd44531c.png)

##マレット
Neon0.png~Neon3.png
![Neon0.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/554244/00f48889-17fc-8a6c-18ed-8afc01f4aa8a.png)
![Neon1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/554244/6f546e59-04c6-1a29-303a-efc1fa7a7269.png)
![Neon2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/554244/63088458-7cb0-7906-6b97-e35b060ddd6f.png)
![Neon3.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/554244/4ced885c-8f7b-b982-c7e7-b45481c49e94.png)