ゲームで使える曲線に沿った移動

# はじめに
ゲームにおいて、敵の移動などにおいて、直線的な移動ではなくて、なめらかなカーブに沿った動きをしたいことがあります。曲線を描くのではなく、軌跡として扱います。以下の４種、

エルミート曲線
二次のベジェ曲線
三次のベジェ曲線(processing機能)
Catmull-Rom曲線(processing機能)
の実装について紹介します。

# エルミート曲線
こちらの式を使います。

https://en.wikipedia.org/wiki/Cubic_Hermite_spline

p0とp1は始点と終点を表し、
m0とm1は始点と終点における方向（ベクトル）です
変数tを0から1に変化させると、曲線の軌跡が得られます。

```java
PVector p0 = new PVector(  0, 200);
PVector p1 = new PVector(400, 200);
PVector m0 = new PVector(300, -300);
PVector m1 = new PVector(300, 300);

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

void draw() {
  background(255);
  for (float t=0; t<=1.0; t+=0.05) {
    PVector p = hermite(t);
    circle(p.x, p.y, 4);
    println(t);
  }
  
  PVector cha = hermite(frameCount%100*0.01);
  circle(cha.x, cha.y, 20);
}

PVector hermite(float t) {
  PVector r = new PVector(0, 0);
  r.x = (2f*t*t*t-3f*t*t+1f)*p0.x + (t*t*t-2f*t*t+t)*m0.x -(2f*t*t*t-3f*t*t)*p1.x + (t*t*t-t*t)*m1.x;
  r.y = (2f*t*t*t-3f*t*t+1f)*p0.y + (t*t*t-2f*t*t+t)*m0.y -(2f*t*t*t-3f*t*t)*p1.y + (t*t*t-t*t)*m1.y;
  return(r);
}
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/510257/40c7f686-7416-f5ea-5870-c0f752e0c7c5.png)


























# 二次のベジェ曲線
こちらのQuadratic Bézier curvesの式を使います。

https://en.wikipedia.org/wiki/Bézier_curve

p0とp1は始点と終点を表し、
c0は間の制御点です。
変数tを0から1に変化させると、曲線の軌跡が得られます。

```java
PVector p0 = new PVector(  0, 200);
PVector p1 = new PVector(400, 200);
PVector c0 = new PVector(200,   0);
//PVector m0 = new PVector(300, -300);
//PVector m1 = new PVector(300, 300);

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

void draw() {
  background(255);
  for (float t=0; t<=1.0; t+=0.05) {
    PVector p = bezier2(t);
    circle(p.x, p.y, 4);
    println(t);
  }
  
  PVector cha = bezier2(frameCount%100*0.01);
  circle(cha.x, cha.y, 20);
}

PVector bezier2(float t) {
  PVector r = new PVector(0, 0);
  r.x = (1f-t)*(1f-t)*p0.x + (2f*t-2f*t*t)*c0.x +t*t*p1.x;
  r.y = (1f-t)*(1f-t)*p0.y + (2f*t-2f*t*t)*c0.y +t*t*p1.y;
  return(r);
}
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/510257/dfd996c1-99c0-f5bb-634b-56b986108cf7.png)














# 三次のベジェ曲線(processing機能)
processingにbezierPointという便利な命令があります。

https://processing.org/reference/bezierPoint_.html

p0とp3は始点と終点を表し、
p1とp2は間の制御点です。
変数tを0から1に変化させると、曲線の軌跡が得られます。

```java
PVector p0 = new PVector(  0, 200);
PVector p1 = new PVector(100, 100);
PVector p2 = new PVector(300, 100);
PVector p3 = new PVector(400, 200);

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

void draw() {
  background(255);
  for (float t=0; t<=1.0; t+=0.05) {
    PVector p = bezier3(t);
    circle(p.x, p.y, 4);
    println(t);
  }
  
  PVector cha = bezier3(frameCount%100*0.01);
  circle(cha.x, cha.y, 20);
}

PVector bezier3(float t) {
  PVector r = new PVector(0, 0);
  r.x = bezierPoint(p0.x, p1.x, p2.x, p3.x, t);
  r.y = bezierPoint(p0.y, p1.y, p2.y, p3.y, t);
  return(r);
}

```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/510257/062f79cf-0afb-74dc-8457-016674e9cc18.png)














# Catmull-Rom曲線(processing機能)

https://processing.org/reference/curvePoint_.html

p1とp2は始点と終点を表し、
p0とp3は外側の制御点です。
変数tを0から1に変化させると、曲線の軌跡が得られます。

```java
PVector p0 = new PVector(-200, 800);
PVector p1 = new PVector(  0, 200);
PVector p2 = new PVector(400, 200);
PVector p3 = new PVector(600, 800);

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

void draw() {
  background(255);
  for (float t=0; t<=1.0; t+=0.05) {
    PVector p = curve(t);
    circle(p.x, p.y, 4);
    println(t);
  }
  
  PVector cha = curve(frameCount%100*0.01);
  circle(cha.x, cha.y, 20);
}

PVector curve(float t) {
  PVector r = new PVector(0, 0);
  r.x = curvePoint(p0.x, p1.x, p2.x, p3.x, t);
  r.y = curvePoint(p0.y, p1.y, p2.y, p3.y, t);
  return(r);
}
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/510257/a41a7957-0b9b-f7d5-55e1-aa41940219b4.png)








# 向き
向きも変えたいという場合は、
bezierTangent
curveTangent
命令を使うと楽と思います。