逆引き風！Three.jsでゲームを作る時に役立ちそうなTips

#まずはじめに

###お前は誰だ
　Jey-enと申します。業務でC#を、JavaScriptは業務と趣味で合わせて1年という、素人と知ったかぶりの狭間を生きております。

####こんなん作った
Three.jsでゲームっぽいのが作れるかなと思い、[このくらいまでやってみました](http://www001.upp.so-net.ne.jp/adrs2002/teststage.html)。

大体ね、「３DぐらふぃっくがWebで出来るようになった！おっしゃゲーム作ったろ！」なんて、脳筋すぎると思いませんかね:fist_tone3:

そのゲームっぽいものを作りながら思いついた、使えそうな小技をお伝えできればと思います。
他の方がやっているように、一つのテーマで完結まで行くという形ではなく、タイトルにもあるように【逆引き辞典風】な何かです。
#対象としている人：

**Three.jsを使って、【何かモデルを表示する】とかできて、次は何をしようかと考えてる人**
**よせばいいのにUn●tyじゃなくてThree.jsでゲームっぽいのを作ろうとしてる人**
**せめて「Scene」に「カメラ」と「モデル」を追加すれば表示できる、という所までわかってる人**

* * *

というように、多くの人に必要かとか、そういうことは一切考えずに「独断と偏見」のみでTipsを分類しました。
全部読もうと思わず、気になるところをつまみ食いをするのが一番いいかと思います。見出し一覧からGoです。全部知っていたらBackボタンGoです。
それでは、ごゆるりとお付き合いくださいませ。


# Lv１：ゲーム以外のアプリ・サイト等でも（多分）使えそうなもの＆初歩的なもの



##☆キャラに、カメラの方向を向いてほしい（簡易版）
「あの子を振り向かせたい」、プログラムなら確実に出来ます。Three.jsなら2次元じゃなくて３次元だぞ！って言っていいですか。ダメですか。そうですか。

手順としましては、
1・視点の位置から、あの子（被写体）に対して向かうベクトルを用意する
2・それを単位ベクトルにする（normalize)
3・魔法の呪文を唱える（atan2)
4・キャラの向きにセットする(rotation.y)

```js
// charaMesh が、向かせたいキャラのメッシュオブジェクトとします
var tmpV = new THREE.Vector3();
tmpV.copy(camera.position);
tmpV.sub(charaMesh.position);     //1.向かうベクトルの用意
tmpV.normalize();                 //2.単位ベクトル化

var angle = Math.atan2(tmpV.x, tmpV.z);    //3.魔法の呪文
charaMesh.rotation.y = angle;               //4.向きに適用
```

　Vector3.angleTo が使えるかな～と思ってたのですが、　angleTo　は、値が「角度の絶対値」しか返ってこないんですよね。ダメでした。アレは罠です。向かせたいなら、Math.atan2　を使いましょう。
ここで検出できる向きは、「Y軸」の向きです。コレを応用すると、「進行方向にキャラを向かせる」とかも実現できるようになります。簡単かつ出来ることが多いのがこいつです。



##☆地形の上に立たせたい（メッシュからの高さ検出）
ド定番ですね。これを１つずつ必要な要素を切り出すと、
1.「立たせたい（上に置きたいもの）の、はるか上空のポイント（Vector3）を用意する
2.「1」から、真下に向かう「線」を用意する（Raycaster)
3.その「線」と、「地面となるもの」の、ピンポイントの衝突点を探す(Raycaster.intersectObject)

という流れになります。

XとZは元の位置を利用し、Yだけ「はるか上空」に置くのは、「指定した一方向に対してしか、衝突を調べてくれない」からです。ここに元位置のYをそのままセットしてしまうと、「自分が今いる場所から、下の場所の高さ」しか正確な位置が調べられず、階段で言うと「降りる」しかできなくなります。それは困るため、「下向きに調べれば全部分かる」ようにするため、Yだけ大きい位置にしてあります。

```js
 // basePos というVector3型に、「立たせたいもの」のPosが入ってるとします

var TopOverPos = new THREE.Vector3(basePos.x, 65535, basePos.z); //1.はるか上空のポイントを用意
var downVect = new THREE.Vector3(0,-1,0);     //下向きのベクトルのみが入ったVector3を用意

var ray = new THREE.Raycaster(TopOverPos, downVect.normalize());  //2.真下に向かう線がコレ

var maxY = 0;  //衝突対象が複数あった場合、一番「高い」ものを取得するようにします
var objs = ray.intersectObjects(scene.children, true);   //衝突点検出！
for (var i = 0; i < objs.length; i++) {
    if(maxY <  objs[i].point.y)
    maxY = objs[i].point.y;
}

//return maxY;  //当然、この maxY  に、検出できた高さが入ってきます。

basePos.y = maxY;
```

なお、上のサンプルは「scene全部にあるもの片っ端から高さを検出する」ものとなっています。計算は「メッシュの頂点量」に比例して時間がかかりますので、sceneの中に人物用のメッシュとかあったら当然ソコからも調べて、まぁソレは重くなります。
「コレが地面！」と、決めたものがある場合は、

```js
var objs = ray.intersectObject(zimen_mesh);
```

というように、メッシュ単品からも取得できます。こちらのほうがより実践的で、現実的でしょう。

<!--
なお、この　intersectObjects　にはちょっとだけ【罠】があり、自分はその罠にまんまと引っかかりました。
気になる人は、[こちらの記事をご参照ください](http://qiita.com/adrs2002/items/dac5a03f90cc8d10d26a)（自演）
-->


##☆３D上のポイントを画面に出したときの２D位置を知りたい（スクリーン座標）
ゲームを良くする人であれば、「キャラのすぐ上や下に表示されてるHP」とかが、ピンとくる感じでしょうか。
画面は３D、でもHPは２Dでさくっと表示したい。そういうときに「３D上のとある位置は、２D表示にするとドコに来るのか、が知りたくなります。
コレは、しばしば【スクリーン座標】という呼ばれ方をするので、３Dプログラムをするなら覚えておくと幸せかもしれません。
そのスクリーン座標をThree.jsで得るためには、こんな感じになります。

```js
 // basePos というVector3型に、「２Dにしたい位置」のPosが入ってるとします

var screenV = new THREE.Vector3();  //入れものを用意
screenV.copy(screenV);             //値を複製。こうしないと、値を乗っ取ってしまうので。
screenV.project(camera);           //この project というメソッドで、あっさり取得できます。だがしかし！

//この値は、 -1 ～ +1 で返ってくる（左＆下が-1、中心がゼロ、右＆上が+1）ので、画面サイズに直すにはもう一工夫

if(screenV.z < 1.0)  //1.0を超える＝カメラの裏側にある、ということ！
{
 var scr_pos_x, scr_pos_y;
 scr_pos_x = screenSize.Width * 0.5 + (screenV.x * 0.5 * screenSize.Width);
 scr_pos_y = screenSize.Height * 0.5 + (screenV.y * -0.5 * screenSize.Height);
}

```

イメージとしてはこんな感じ
![screenPos.png](https://qiita-image-store.s3.amazonaws.com/0/133895/85623570-a6cd-e942-8f33-63f1a456f789.png)




##☆逆に、クリックした位置を３D座標に変えたい
これはもう、[非常にわかりやすいものを他の人が書いてくださってるので、お任せします](http://qiita.com/edo_m18/items/5aff5c5e4f421ddd97dc)。
上の「Project」の逆、unproject　と、最初に書いた Raycaster の合わせ技ですね。


##☆キャラを動かした時の「キャラの向きの前方」の位置を知りたい（ローカル座標のワールド座標化：非ボーン）
これはもう、[非常にわかりやすいものを他の人が書いてくださってるので、お任せします](http://qiita.com/edo_m18/items/f74236c6b1bd26e838a1)。
リンク先のVector4型「forward」が、いわゆるローカル座標（キャラを中心とした座標）になります。

##☆オススメ！Canvas重ねのススメ

もうThree.js関係ないんじゃね？と言われそうな勢いですが。
ゲームを作る時、体力ゲージとか得点とか、敵のマーカーとか、いわゆる「２D要素」を表示したいことは多々あります。
このとき、一昔前であればDirectDrawとか、２Dを弄るための命令はありました（何年前の話だ）。
んじゃあ、Three.jsではどうしたら良いのでしょう？

Three.jsが乗っかっているHTMLには、ご存知「Canvas」の「2dContext」という、２D要素をガリッガリに書きまくれる優れたコントロールがあります。そうです。こいつに書いてしまえばいいのです。
どうするかと言うと、「Three.jsの３D表示を行っている要素に対し、全く同じ位置＆全く同じサイズのCanvas要素を用意して重ね、重ねたヤツに対して描画する」のです！

コレ、あっさり気づく人は気づくでしょうけど、気づかない人はずっと気づかないくらいの盲点的要素な気が。
Canvasは透明＆透過が許されるので、下にあるものを表示したまま、円とか四角とか自由に書けます！
コレは便利ですし、管理も楽です。いやーイイですよコレ。

重なりはZ-indexで指定します。z-indexが大きいほど、手前に来ます。

```html
<!-- 3D描画領域のためのdiv要素を配置 -->
<div id="canvas3d" style="position:fixed; top:0px; left:0px; width:100%; height:100%; z-index: 1; background-color: #000000;"></div>
<!-- その真上に、2D描画領域のためのdiv要素を配置 -->
<canvas id="canvas2d" style="position:fixed; top:0px; left:0px; width:100%; height:100%; z-index: 100; background-color:rgba(0,0,0,0);"></canvas>

```

なお、コレをやると、多くのThree.jsのサンプルで適用されている「【ページ】にThree.jsレンダー用のdivエレメントを追加(container)　して、それに対してレンダーを適用(renderer.domElement)」が、動かなくなります。そのために、
【特定されたdivを、Three.jsの表示結果出力用に指定する】という処理が必要になります。まぁごく簡単な変更ですが

```js
var canvas3D = document.getElementById('canvas3d');
canvas3D.appendChild(renderer.domElement);
```

そして、上に重なっている[ canvas2d ]のcontext2Dに対して、好き放題書きます。ですが、良くCanvasのサンプルなどでおいてある、「まず黒く塗りつぶします」的なことはやったらダメです（当然、Three.jsの３D結果が見えなくなります）

```js
//conteText2Dは、canvas2dのcontextです。
conteText2D.clearRect(0, 0, conteText2D.canvas.width, conteText2D.canvas.height);  //２D描画結果のクリア。これはよくあるので、そのまま
//conteText2D.fillRect(0, 0, conteText2D.canvas.width, conteText2D.canvas.height);   これはやりません！

```

あとは、conteText2Dに対して、好き放題にfillやstrokeするだけです。

# Lv2：使いみちは多いんだけど、初歩的とは言いづらいもの（初級者以上）

##☆キャラの手に、持たせたい
ここでいう「キャラ」とは、一通りボーンが組まれており、かつアニメーションさせて動くようなキャラクターです。
アニメーションさせるということは、位置が動きまくります。当たり前です。手の位置なんて、上半身とか腕の上げ角度とか、それはもういろんな要素に引きずられて動きまくります。
じゃあその「動きまくる」ものに対して、持たせる＝位置を合わせるには、どうすればよいのでしょう？

1.「持たせる場所」を、しっかり特定する。　
　これは、いわゆる「ボーン」を特定させます。３Dモデルを作った人がキチンとした方＆きちんとした人が作ったインポーターなら、【 mesh.skeleton.bones[]　】　にボーンが、そしてbonesの中のBoneオブジェクトのname に、特定できそうな名前で入っています。
たとえば例のMMDミクであれば、mesh.skeleton.bones[54]　が、右手の手首のボーンです。
2.そのBone型に対して、 add する
 このBone型（Three.Bone型）に対し、なんとSceneと同じように add が出来るのです。
そう、その add で持たせたいオブジェクト（mesh等）を追加するだけで、そのボーン位置にぴったりと表示するようにしてくれます。
 　
　んが、ここで少しだけ問題点。
![negi_err.png](https://qiita-image-store.s3.amazonaws.com/0/133895/94d7f0ce-ef90-2b2b-e3f1-6b99e88f321c.png)

　これでそのまま「add」するだけだと、いわゆる「ぴったりすぎる」のですね（角度もアレですが）。手首のボーン位置なので、ぴったりそのまま、手首にくっついてきます。めり込んでます。これじゃダメです。リ○カっぽくてアウトです。そのため、「手首から少しずらし、手の平の位置で持たせる」のが望ましいですね。

```js
 
//キャラモデルがmesh。　コレは、ボーン組のmesh=SkinnedMesh であることが大前提。
//手に持たせたいものを emonoMesh とします
//なお、コレは毎フレームやる必要はなく、1回だけ行えばいい。

mesh.skeleton.bones[54].add(emonoMesh ) ;	//この 54 番ボーンが、手首のボーン。ここに対して add する

var transMX =  new THREE.Matrix4();
var position = ( new THREE.Vector3()).set(-0.9,-1.1,0); //これが、手首から見た、手の平の位置。
var scale = ( new THREE.Vector3()).set(1,2,1);		//縦長にする
var quaternion  = (new THREE.Quaternion()).setFromAxisAngle((new THREE.Vector3().set(1,0,0)), Math.PI / 2);	//角度をｘ軸からみて90度回転

transMX.compose( position, quaternion, scale );  //各要素をMatrixとして確定させます。
emonoMesh.applyMatrix(transMX);      //【持たせた】物体に対して、移動を適用させます。
```
この移動と変形と回転を適用させると、手首にしっくりくるようになります
![oknegi.png](https://qiita-image-store.s3.amazonaws.com/0/133895/3ec6bdc8-7666-2454-12d7-7da2cd35cbcb.png)

この「applyMatrix」で、【持たせた】方の物体のMatrixを変更します。Matrixなので、当然角度や大きさも変更自由です。ちょうどよい大きさを見つけましょう。


##☆キャラの「指先」の座標を知りたい（ボーンを使ったローカル座標のワールド化）
ここでいう「キャラ」とは、一通りボーンが組まれており、かつアニメーションさせて動くようなキャラクターです。
アニメーションさせるということは、位置が動きまくります。当たり前です。手の位置なんて、上半身とか腕の上げ角度とか、それはもういろんな要素に引きずられて動きまくります。
じゃあその「動きまくる」ものの、【動いた後の位置】を知るためには、どうすればよいのでしょう？

ん？上の「ボーンに持たせる」と、何が違うのかって？
こちらは、「その時点での位置を知ること」を目的としています。ずっとくっついてくるのではなく、その場所に残るエフェクトなどが設置できるようになり、
![enbu.png](https://qiita-image-store.s3.amazonaws.com/0/133895/37745a7a-da15-6927-f722-ab09ba9bfb47.png)

こんな感じのことができるようになります。イイでしょ？ってことでやり方です

```js
var tmpV = new THREE.Vector3();              //入れ物を用意
var localAddPos = new THREE.Vector3();        //取得したいボーンからの、ローカルに進んだ位置として作成
localAddPos.set(-0.1,-0,1);                      //指先になるように、進めてみた

tmpV.copy( mesh.skeleton.bones[60].localToWorld(localAddPos));  //60番は右人差し指の第三関節のボーン
```

このObject3D.localToWorld　というメソッドで、ボーン座標+ボーンのローカル座標に値を加えたものを、ワールド座標にできます。（Object3Dクラスが持っているメソッドなので、別にBoneじゃなくても使えます）
コレは指先とか銃口とか、ゲーム用途には使いまくりなメソッドです。覚えておきましょう。

[サンプル的なものはこちら](http://www001.upp.so-net.ne.jp/adrs2002/webgl_loader_mmd_audio_edit.html)

#Lv3：ゲーム作る気じゃなければ必要なさそうなもの（習熟度ばらばら）

##☆物体と物体が当たっているかどうかを知りたい

いわゆる「アタリ判定」というものを調べるのに良く使われるのは、 BoundingSphereというものです。
コレは、「そのメッシュ（モデル）の全頂点が入りきるように作られた、ちょうどいい大きさの球体」です。BoundingSphereは、何もしなければ普通にメッシュを読んだときに自動的に作られ、移動すればBoundingSphereもくっついて移動します。イメージとしては、ふぉーすふぃーるどです（雑）
　一番最初に書いた「メッシュからの厳密なアタリ判定」よりも、実質的には「2点間の距離」の計測で完結するBoundingSphere同士の判定の方が、圧倒的に高速です。キャラクターにも「玉」となるオブジェクトにも、このBoundingSphereは作られますので、BoundingSphere同士で衝突判定を行うのは、てっとり早い方法でしょう。
BoundingSphere同士の衝突判定は、 intersectsSphereで得ることができます。


```js
//ここは、モデル読み込み後、一回だけ行う
if(charaMesh.geometry.BoundingSphere == null){ charaMesh.geometry.computeBoundingSphere();} //なかったら作る。
//少しだけ小さめにする
charaMesh.geometry.BoundingSpher.radius *= 0.8;
```

```js
//ここは毎フレーム行う
 if(charaMesh.geometry.BoundingSphere.intersectsSphere(tamaMesh.geometry.BoundingSphere)
{
  console.log('死んだ！');
}
```


　小さくしている理由は、この方法、試してみれば分かるのですが、そのままの大きさではゲームに使うには「予想以上にアタリ判定が大きく思う」ことでしょう。頭の先から足の先まで、それが奥行きも全部すっぽりなので、見えてない所まで当たるように思えます。
そのため、気持ち小さくして、見た目との違和感を減らしています。

ところでこの geometry.BoundingSphere は、「視野判定」にも使われます。視野判定とは、「カメラから見える範囲に、そのキャラクターが入っているかどうか」の判定です。
ここの判定で外れると、そのキャラクターは描画のためにGPUにセットする対象から外されます。カメラに写らないのに、描画するための計算をするのは無駄だよね、ということです。
そのため、小さくしすぎると、いわば「キャラクターがカメラから見切れるか見切れないか」の隅っこぎりぎりにいるときに描画対象から外れ、パっと消えてしまうという感じになります。中心はカメラから外れてるけど、手だけは見える、ということもなくなってしまいます。
コレを避けるために、アタリ判定の球体とgeometry.BoundingSphere は、別で持たせる方がよい場合が多いです。BoundingSphereの元となっている　Sphere　オブジェクトは、中心点(Vector3)と半径（float)の２つの引数だけで作れますので、さくっと作ってしまうのが良いでしょう。

```
//初期化＆作成（1回だけ行えばよいもの
if(charaMesh.geometry.BoundingSphere == null){ charaMesh.geometry.computeBoundingSphere();} //

//アタリ判定球を別途定義
var HitBall = new Sphere(charaMesh.geometry.BoundingSphere.center, charaMesh.geometry.BoundingSphere.radius);
HitBall.radius *= 0.5;   //けっこー小さめに
//判定球をモデルオブジェクトの「子」として追加
charaMesh.add(HitBall);
charaMesh.HitBall = HitBall;  //アクセスを容易にしとく
```

```js
//ここは毎フレーム行う
 if(charaMesh.HitBall.intersectsSphere(tamaMesh.geometry.BoundingSphere)
{
  console.log('死んだ！');
}
```


##☆キャラが「本当に」カメラから見えているか見えていないかを知る、良い？方法
　上のトピックで「視野判定」なるものが出ましたが、ココはちょっと違う話。
「視野判定」は、【カメラの見える範囲内】にそのキャラクターがいるかどうかの判定でしたが、実際に「見えている」となると、ちょっと話が変わってくるということにお気づきでしょうか。
　そう、「壁」や「地面の裏側」の話です。
スクリーン座標のトピックの時に書きましたが、「壁の裏側に隠れているのに、HPとか情報が表示されててバレバレ」なのは、ゲームによってはがっかりしてしまうことでしょう。コレを防ぐにはどうするか、のアプローチです。

アプローチの一例を挙げると、
1.カメラから、敵の中心点までの距離を測定
2.カメラ位置から敵の位置までのベクトルを取得し、正規化
3.カメラ位置から敵の位置まで、地面を対象とした「Raycaster」を飛ばし、衝突点を検出。
4.「３」で取得した検出点までの距離が、「１」で取得した距離より近ければ、「壁」のほうが敵の手前にある！ということ

という形になります。
そのとーり、既に紹介済みの「Raycaster.intersectObject」で、なんとかなってしまいます。
ココではソースの紹介は省略します。挑戦してみましょう。


##☆「空」を表示させる方法（SkyBoxの表現のために）
Three.jsのサンプルには、[非常にキレイな空のサンプル](https://threejs.org/examples/#webgl_shaders_sky)がありますが、残念ながら雲一つ無い空しか出来ません。
ゲームでは、「Skybox」や「Skydome」と呼ばれる、めっっちゃくちゃ大きい球体や立方体の【内側】に、空のテクスチャを貼り付けて表現したりします。

さてさて。また「視野判定」の話になりますが、カメラから外れている物体を表示しないのはあたりまえですが、「カメラの正面にいるけど、表示する必要がない」ものがある、ということを、考えたことはあるでしょうか。
･･そう、「遠くにあって、めっちゃ小さいっていうか見えない」ものは、表示する必要＝計算をさせる必要ってありませんよね。
コレ実は、　PerspectiveCamera　の宣言時の引数時にパラメータの設定があり、「カメラからこの距離以上のものは描画しない」という設定があったのです。　PerspectiveCamera　の3番目と4番目の引数です。この設定は、よく「クリップ」と呼ばれます。3番目は「近すぎる場合に表示しない（ニアクリップ）」、4番目は「遠すぎる場合に表示しない（ファークリップ）」となります。

```
//    1　がニアクリップ、　2500 がファークリップ
camera = new THREE.PerspectiveCamera(45, window.innerWidth / window.innerHeight, 1, 2500);
```

前項で「BoundingShpereによる視野判定」がありましたが、アレは「オブジェクト全部が」表示されなくなりますが、この「クリップ」に関しては、メッシュのポリゴン1枚単位（というか１頂点毎）で表示する/しないの判定が行われます。
![facrip.gif](https://qiita-image-store.s3.amazonaws.com/0/133895/3e5c2c4e-24a5-a245-8a64-3057ae076bd6.gif)

こんな感じで、なんか「遠くの方の山がメリメリ欠けてから成形されていくように見える」のは、そのせいですね。メッシュは地面として1つなのですが、カメラからの遠さ判定が１頂点ずつ行われているため、「高い」場所は、近づかないと見えてこないとかいうことになります
･･さて。
SkyBoxの話に戻りますが。「サイズをめっちゃ大きくすればよい」という話をしましたが、そのカリング設定のおかげで、大きくしすぎると、今度は「あ、こいつ遠すぎるから表示しなくていいな」という判断がされてしまいます。
空は、山より遠くにあります。では、山より遠くにある空を表示するには、どうしたらよいでしょう。

いろいろ調べた結果、「シーンとカメラを2つ用意する」とする方法しか、なさそうです。でも、それで出来るんです。
ポイントは
1.カメラとシーンを２つずつ用意
2.背景用のカメラには、ファークリップはめちゃくちゃ大きい数をセット
3.レンダラーの「自動クリア」を無効にする
の3点です。

```
//宣言部分
camera = new THREE.PerspectiveCamera(45, window.innerWidth / window.innerHeight, 1, 2500);    //これがメイン用
camera_back = new THREE.PerspectiveCamera(45, window.innerWidth / window.innerHeight, 1, 250000);  //これが背景用

//シーンも２つ
scene = new THREE.Scene();
scene_back = new THREE.Scene();

//レンダラーは１つ
renderer = new THREE.WebGLRenderer();
renderer.autoClear = false;    //【重要！】自動クリアを無効にする！

//「遠くでも見える」ものは、scene_backにaddしていく
scene_back.add(sky_mesh);

//遠くの場合は表示しなくてもいいもの（ファークリップが効いてほしいもの）は、普通のsceneに
scene_back.add(zimen_mesh);
scene.add(hito_mesh);
scene.add(teki_mesh);
```

そして毎フレーム走る描画が下のような感じに。

```
renderer.clear(true, true, true);          //クリアは明示的に行う。
renderer.render(scene_back, camera_back);  //後ろに来るもの＝背景用のモノを先に描画
renderer.render(scene, camera);
```

「自動クリアを無効」にしないと、renderer.render　が実行された時点で自動的にそれまでの描画結果が破棄されます。つまり、2回目のrenderが走った時点で、1回目の結果が消えてしまいます。そうさせないための自動クリア無効です。
当然ながら、視点の移動や回転などは、両方のカメラに対して行う必要があるので、面倒は2倍になります。うまいことメソッドを用意して逃げましょう。


#まとめに

100％独断で、なるべく出番が多そうなモノをピックアップしたつもりですが、いかがだったでしょうか。
バラバラに散らばった知識が、のちのち１つに繋がり、何かが出来上がる。そんな体験の一つの中に、ここでの知識があったら幸いです。

あと、最後にコレだけ伝えて、お別れの言葉とさせていただきます。




**３Dゲーム作りたかったら、おとなしくU●ityのほうがいいよ！**