[Processing]紙飛行機を飛ばそう

# 追記
諸事情によりJavaではなくJavascriptになりました

# こんな
![PaperPlane2.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/554244/f542e936-0b34-9736-688c-662d9b4da18f.gif)

<iframe src="https://openprocessing.org/sketch/1629018/embed/" width="400" height="400"></iframe>


# 操作方法
| 操作       |       キー |
|:----------------:|:-----------------:|
| 視線移動             |              方向キー |
|加速|A|
|減速|Z|
| ズーム           |            左クリック |
|視点切り替え|右クリック|
|紙飛行機|1|
|お楽しみ|2|

# プログラム
```PaperPlane.js


//画角
let fov = 3
//視点切り替え
let cameraView = true
//紙飛行機クラス
let pp
// カメラ視点
let camera

let cloudPosArray = []

function setup() {
	createCanvas(600, 600, WEBGL)
	background(100)
	
	pp = new PaperPlane()
	camera = createCamera()
	
	let length = 100
	for(let i=0; i<length;i++){
		cloudPosArray.push(new createVector(random(0,TWO_PI), random(-5000,500), random(2000,3000)));
	}
}

function draw() {
  background(50,50,100)
	
	//モードで視点切り替え
	
  if(cameraView)
	{
		camera.setPosition(150, -500, 1000)
		camera.lookAt(pp.pos.x, pp.pos.y, pp.pos.z)
		let dis = pp.pos.copy().sub(150, -500, 1000).mag()
  	perspective(PI/max(2,min(10,dis/100)), width/height,10,10000)
  }
  else
	{
		camera.setPosition(pp.pos.x, pp.pos.y, pp.pos.z)
		camera.lookAt(pp.nextPos.x, pp.nextPos.y, pp.nextPos.z + (pp.speed === 0 ? 1 : 0))
  	perspective(PI/3, width/height,10,10000)
  }
	
  push()
    rotateX(PI/2)
		lights()
  pop()
	
	
	let inputDire = createVector()
	
	if(keyIsPressed){
		switch(key){
			case "a":
				pp.SpeedUp()
				break
			case "z":
				pp.SpeedDown()
				break
			case "ArrowUp":
				inputDire.y += 1
				break
			case "ArrowRight":
				inputDire.x += 1
				break
			case "ArrowDown":
				inputDire.y -= 1
				break
			case "ArrowLeft":
				inputDire.x -= 1
				break
			case "r":
				pp.Reset()
				break
			case "1":
				pp.SetMode(1)
				break
			case "2":
				pp.SetMode(2)
				break
		}
	}
	
	pp.Control(inputDire)
	
	pp.Move()
	pp.Draw()
	
	drawEnv()
	
}

function drawEnv() {
	drawFlor()
	drawCloud()
	
	//床
	function drawFlor(){
		fill(0,150,0)
		strokeWeight(1)
		for(let i= -5;i<= 5;i++){
			for(let j= -10;j<=10;j++){
				push()
				translate(i*300,0,j*300)
				box(180,10,180)
				pop()
			}
		}
	}
	
	function drawCloud(){
		//雲
		fill(255)
		strokeWeight(1)
		for(let i=0; i<cloudPosArray.length;i++){
			push()
			let pos = cloudPosArray[i]
			rotateY(pos.x)
			translate(0,pos.y,pos.z)
			box(200,50,100)
			pop()
		}
	}
}

function ClampPI(rotate){
	// 半回転したら逆からの回転値を返す
	// 例：[3.15 => -3.13 ] = sign(-x) * (3.14 - (abs(x)-3.14)) =sign(-x) * (3.14 -abs(x) + 3.14)) =sign(-x) * (2*PI-abs(x))
	if(abs(rotate) >= PI) return Sign(-rotate) * (2 * PI - abs(rotate))
	return rotate
}

function Sign(a){
  if(a > 0) return 1
  else if(a < 0) return -1
  else return 0
}

function mousePressed(){
  //画角切り替え
  if(mouseButton === LEFT) {
    if(fov == 36) fov = 3;
    else fov = 36;
  }
  //視点切り替え
  if(mouseButton === RIGHT){
    cameraView = !cameraView;
  }
}

// コンテキストメニューを無効化する
document.oncontextmenu = (e) => {
    e.preventDefault();
}
```

```paperPlane.js
class PaperPlane{
	
	constructor(){
		this.Reset()
	}
	
  Reset(){
    this.pos = createVector(0, -300 , -1000)
		this.nextPos = this.pos.copy()
		this.direction = createVector(0,0,0.01)
		this.rotate = createVector()
		this.speed = this.wy = this.wr = 0
    this.wx = 30
		this.wingR = PI/90//翼のはばたき速度
    this.mode = 1
  }
	
	SpeedUp(){
		this.speed += 0.05
	}
	SpeedDown(){
		if(this.speed > 0.1) this.speed -= 0.05
		else this.speed = 0
	}
	
	SetMode(num){
		switch(num){
			case 1:
			case 2:
				this.mode = num
				break
			default:
				console.log("対応してないモードです")
				break
		}
	}
	
	Control(inputVec){
		if(inputVec.magSq() < 1){
			//水平との誤差が1度なら(2*PI/360=PI/180)機体を水平とする
			if(abs(this.rotate.x) < PI/180) this.rotate.x = 0
			else this.rotate.x -= Sign(this.rotate.x) * PI / 360
			
      if(abs(this.rotate.z) < PI/180) this.rotate.z = 0
      else this.rotate.z -= Sign(this.rotate.z) * PI / 360
		}else{
			// 入力と回転がずれてるのでxにyで減算する
			this.rotate.x -= inputVec.y * PI / 180
			this.rotate.y -= inputVec.x * PI / 180	
			// 傾きを与える
			if(abs(this.rotate.z) < PI/3) this.rotate.z -= Sign(inputVec.x) * PI/360
			
			// PIを超えた回転を修正
			this.rotate.x = ClampPI(this.rotate.x)
			this.rotate.y = ClampPI(this.rotate.y)
			this.rotate.z = ClampPI(this.rotate.z)
		}
		
		this.direction.x = sin(this.rotate.y)
		this.direction.y = sin(this.rotate.x)
		this.direction.z = cos(this.rotate.x) * cos(this.rotate.y)
	}
	
	Move(){
    //位置移動
		let dire = this.direction.copy().normalize()
    this.pos = this.pos.add(dire.mult(this.speed)) 
		
    this.nextPos = this.pos.copy().add(dire.mult(max(this.speed,1))) 

	}
	
	Draw(){
		
    push()
      translate(this.pos)
      rotateX(this.rotate.x * Sign(cos(this.rotate.y)) * -1)
      rotateY(this.rotate.y)
      rotateZ(this.rotate.z)
			strokeWeight(0.5)
      switch(this.mode){
        case 1: this.paperPlane()
					break
        case 2: this.paperClane()
					break
      }
    pop()
		
  }

  paperPlane(){
    beginShape(TRIANGLE_FAN)
      vertex(0,0,0)
      vertex(-30,5,-50)
      vertex(-5,0,-50)
      vertex(0,20,-50)
      vertex(5,0,-50)
      vertex(30,5,-50)
    endShape()
  }

  //おりづる
  paperClane(){

    //ツバサバサバサ
    this.wr += this.wingR * this.speed * 0.5
    if(abs(this.wr) > radians(30)){
      this.wingR *= -1
      this.wr += this.wingR
    }

    this.wx = 40 * cos(this.wr)
    this.wy = 40 * sin(this.wr)

    //頭
    beginShape(TRIANGLE_FAN)
      vertex(0,0,10)
      vertex(-2,-7,5)
      vertex(0,-5,2)
      vertex(2,-7,5)
    endShape()

    //首
    beginShape(QUAD_STRIP)
      vertex(-2,-7,5)
      vertex(-3,10,-2)
      vertex(0,-5,2)
      vertex(0,7,-4)
      vertex(2,-7,5)
      vertex(3,10,-2)
    endShape()

    //左右胴体
    for(let i=-1;i<=1;i+=2){
    beginShape(QUAD_STRIP)
      vertex(0,17,-7)
      vertex(0,7,-4)
      vertex(3*i,17,-5)
      vertex(3*i,10,-2)
      vertex(2*i,17,-13)
      vertex(4*i,10,-13)
      vertex(3*i,17,-21)
      vertex(3*i,10,-23)
      vertex(0,17,-20)
      vertex(0,7,-22)
    endShape()
    }

    //背中のとんがり
    beginShape(TRIANGLE_FAN)
      vertex(0,6,-13)
      vertex(0,7,-4)
      vertex(3,10,-2)
      vertex(4,10,-13)
      vertex(3,10,-23)
      vertex(0,7,-22)
      vertex(-3,10,-23)
      vertex(-4,10,-13)
      vertex(-3,10,-2)
      vertex(0,7,-4)
    endShape()

    //下側
    beginShape(TRIANGLE_FAN)
      vertex(0,17,-13)
      vertex(0,17,-7)
      vertex(3,17,-5)
      vertex(2,17,-13)
      vertex(3,17,-21)
      vertex(0,17,-20)
      vertex(-3,17,-21)
      vertex(-2,17,-13)
      vertex(-3,17,-5)
      vertex(0,17,-7)
    endShape()

    //尾
    beginShape(TRIANGLE_FAN)
      vertex(0,-10,-30)
      vertex(3,10,-23)
      vertex(0,7,-22)
      vertex(-3,10,-22)
    endShape()

    //翼
    for(let i=-1;i<=1;i+=2){
      push()
        translate(0,10,0)
        beginShape(TRIANGLE_FAN)
          vertex(this.wx*i,this.wy,-13)
          vertex(3*i,0,-2)
          vertex(4*i,0,-13)
          vertex(3*i,0,-23)
        endShape()
      pop()
    }

  }
}
```