猫でも分かる！🐈Unityでコライダーの大きさをオブジェクトのscale値の影響を受けないようにする方法！！

#### 前口上
unityでゲームを作っていて、演出としてゲーム上のオブジェクトのscale値をアニメーションとかで変えることってよくありますよね！(あるよね？)
でも、コライダーがついていると、そのオブジェクトのscale値の変動にあわせて、しっかりコライダーの大きさも変わってくれますよね～
すごく便利でありがたい機能ではあるんですけど、時としてこの機能が邪魔になってしまう事もゲームを作っていれば1回ぐらいはあるはず...
今回はそういう人に向けて、今回私がこの問題に当たった時に解決した方法を猫でも分かるように書いていきたいと思います！

# こんな挙動に困ってました😢
![ezgif.com-gif-maker.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/588962/0aeabcbf-adf6-08bb-cce9-381675b76a5b.gif)
この動画では、下の球をアニメーションでscale値を変更しているのですが、コライダーも一緒に変っているため、上に乗っかった球が一緒に動いているのが分かると思います

今回作っているゲームでは、コライダーの接触時に反射判定をしているため、何度もコライダーに当たるような挙動になりそうで困っていました...

# 試したこと①
真っ先に試したこととして、まず[sphrerColliderのリファレンス](https://docs.unity3d.com/jp/current/ScriptReference/SphereCollider.html)を確認しました
困ったときは解説記事を読む前に公式のリファレンスを読んで**自分で理解する力**を身に着けることも大事です!!

今回困っているのはcolliderの大きさなので、radiusの項目を読んでみると、
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/588962/95389374-788c-b345-08fc-952c38971811.png)
「ローカルサイズでのコライダーの半径」とありますね
つまり、radiusの値はどれだけ弄ってもローカルの値なので、そのオブジェクトのscale値によって変わってしまうのです🙀
もう少しわかりやすく解説すると、コライダーの大きさは、**scaleのxyzいずれかのうち最大の値 * radius** でコライダーの半径が求められます
``scale : x=2, y=1, z=1``
``radius: 0.5``
の場合
``` c#
transform.scale.x * radius = 1
=               2 * 0.5    = 1
```
となりますね！
そして、答えの「1」という値は半径なので、直径に直すと「2」となり、「scale.x」の値と同じになりますよね
だからsphireColliderのradiusはデフォルトで「0.5」になっているんですね～
私もこうやって改めて調べてみて初めて知ることができました🐈

### ①の結果を受けて...
はてさて知識を蓄えられたのはいいものの、肝心の問題は解決できませんでした
これでは、結局いくらinspector上でradiusの値を変えようが意味がないという事しかわかっていないですね😿

しかし逆に言えば、それ以外のアプローチを試すきっかけが出来たので、さっそく他の方法を考えてみました
先にinspector上での値操作はダメと書いたので、分かった方もいるかもしれませんが、動的に変化させるというのが正解な気がしますね！
私もそう思い、動的にradiusの値を変更する方法を2つ考えました

**1つめは、アニメーションで変更する方法です**
私自身がアニメーションを得意とするのもあり、真っ先に思い浮かびました
計算式はもう出ているので、それに当てはめてscale値が変わるのに合わせてradiusの値も変化させればうまくいくだろうと思いました

**2つめは、スクリプトから変更する方法です**
別に避けてたわけではないですが、なぜか最終手段だと思って最後まで考えないようにしていました
スクリプトを書けば解決するのは分かっていたんですけどね...

## 試したこと②
先に挙げたように、アニメーションを使えばできるだろうと私はかなり自信を持っていました
しかし、結果から言えば、この方法ではできなくもないが、かなり厳しいです🙀

アニメーションはオブジェクトの**inspectorにある情報のほとんどを動的に変化させることが可能**です
実際どのようにradiusの値を変化させようかと思ったときに、先の計算式を使うとすごく簡単です

``scale * raduis = コライダーの半径``
初期サイズがscale(1,1,1)の球で、コライダーの半径は常に初期サイズの半径と同じであってほしいので、「0.5」とします
求める値はradiusです
式変換をし、
``radius = コライダーの半径 / scale``
で求められますね

ということで、
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/588962/8c6545eb-d342-fe80-668b-bfc2561a31c6.png)

こんな感じでAnimationに入力していきます
ちなみに、unityエディター上の数値入力のフィールドでは四則演算、累乗、かっこを用いた計算ならフィールド内で出来ますよ！

さて、この計算式を使って、すべてのscaleアニメーションのピボットがある位置にradiusの値を入力していきました
各ピボットを見てみるとscaleが変わってもコライダーのサイズは変わってないです！
これは行った！！と思いました
![qiita_movie2.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/588962/7361fdc4-17c2-71eb-d44f-ffd34e583dc5.gif)

しかし、実際に動かしてみるとダメでした

![qiita_movie3.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/588962/bd73e8a5-4d26-f8e4-18a7-d048cd6d4f68.gif)



### なぜこうなったのか
原因を色々と考えたのですが、どう見てもピボットが打たれている場所以外の線形補完された部分で大きさに差異が出ていることには気付きました
しかし、なぜそうなるのかよく分からなくて明確な答えにはたどり着けていないので、この先は考察も含めた内容です

unityのアニメーションでは、ピボット間の数値の変化は直線的な補完ではなく、サインカーブのような補完がされます
サインカーブのような補完というのは、最初は数値の変動が少なく、中央に近づくにつれて変動が大きくなり、終わりにつれてまた変動が少なくなるということです(語彙力)
これは、アニメーションウィンドウで表示の仕方をカーブに変えるとわかると思います
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/588962/9aa7b4fd-a92e-236c-ef1b-93661ba56f89.png)

分かりやすいように、それぞれの値を大きく、ピボット間の時間を長くしてあります
これはピボットがある位置ではコライダーの大きさが変わっていないグラフです
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/588962/c7d71cea-7f8e-c498-bc68-41c8c40e2ded.png)

このように、各値がぐにゃ～ってなっているんですよね
おそらくこれが原因なのは確定なのですが、どうしてこれがダメなのかはよく分かりません(本末転倒)
**教えて！つよつよプログラマー！**

## 最終的な解決方法
結局、スクリプトを書いてやるしかないみたいです😺
ピボットを打った位置なら正常に動くのであれば、スクリプト上から毎フレームピボットでやったことと同じことをしてあげればいいだけの話です
いたって簡単なのに、なぜ最後までやらなかったのか...

``` c#:colliderScalling.cs
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class colliderScalling : MonoBehaviour
{
    [Header("このオブジェクトのSphereCollider"),SerializeField]
    SphereCollider col;
    [Header("絶対的なコライダーのサイズ(半径)"), SerializeField,Space(10)]
    float radiusAbsoluteSize;

    private void Start()
    {
        col = GetComponent<SphereCollider>();
    }

    private void Update()
    {
        //絶対サイズ / scale.x,y,zの最大値
        col.radius = radiusAbsoluteSize / Mathf.Max(this.transform.localScale.x,this.transform.localScale.y,this.transform.localScale.z);
    }
}
```

たったこれだけで実装できてしまいます
やっている事もさっきまでやってきた事をそのままスクリプトで毎フレーム計算しただけです
なんならこっちの方が球のサイズに左右されずに作れるので拡張性が高いですね！


## まとめ🐈

いかがだったでしょうか？
私が普段問題解決をする際の流れも込みでいろいろ書いてみました
あえてスクリプトを書こうとしなかったことで学んだことがあったので、自分的にはよかったかなって思います！
こういう普段何となく使っているツールの機能も、意外と調べたり、触ってみたりすると面白い動きをしていたり、逆に不便だと思ったりすることはよくあります
特に、Unityはかなり良くできたゲームエンジンですが、それ故に頼りすぎてしまう部分多いと思うので、ただ作るだけではなく、もう少し踏み込んでみるとより面白くなるのでお勧めですよ！！