magicleap入門-チュートリアルでレイキャストができない

# magicleapのチュートリアルでレイキャストができない
とある理由でmagicleapを触り始めたのでその時の困ったことをメモします．
※C#すらろくに触ったことないレベルのメモ書きです

# これは何をやっているのか
このチュートリアルは"レイキャスト"をmagicleapで扱うためのものです．レイキャストとは光線を飛ばしてそれがぶつかった先の情報を取得するものです．今回はその先にキューブを生成するようにつくっているみたいです．

# 困ったこと
[2.2 Unityでのレイキャスト](https://developer.magicleap.com/ja-jp/learn/guides/raycast-in-unity)をやっているとエラーが出てきた．その中でも，エラーメッセージ通りに修正しても消えなかったのはこれ．
> error CS0117: `GetWorldRays' does not contain a definition for `MLRaycast'

このエラーの原因はGetWorldRaysという命令が存在しないことのようだ．結局はこのチュートリアルはSDK0.22用に作られたもので，今使っているSDKは0.24でした．

# 解決していく
現在使っているレイキャスト用のサンプルスクリプトが公開されていました．[Raycast Snippet - Unity](https://developer.magicleap.com/en-us/learn/guides/raycast-snippet-unity)というv0.24用に書かれた記事を見つけたのでこれを自分なりに解釈して進めていく．

## 大まかな流れ
基本は0.22用のチュートリアルと同じ．

1. レイキャスト用の空のゲームオブジェクトを作る
2. C#スクリプトを作る
3. スクリプトをゲームオブジェクトに登録する


今回は3番についてつまずいたのでそれについて書いていく．


## 具体的なコード
※参照したページをコピペしただけです

```C#:MLRaycast.cs
using System.Collections;
using UnityEngine;
using UnityEngine.XR.MagicLeap;

public class MLRaycast : MonoBehaviour 
{

    public Transform camTransform;
    public GameObject prefab;

    private MLRaycast.QueryParams _raycastParams = new MLRaycast.QueryParams();

    void Start() 
    {
        // Start raycasting.
        MLRaycast.Start();
    }

    private void OnDestroy() 
    {
        // Stop raycasting.
        MLRaycast.Stop();
    }
    
    void Update()
    {
        // Update the orientation data in the raycast parameters.
        _raycastParams.Position = camTransform.position;
        _raycastParams.Direction = camTransform.forward;
        _raycastParams.UpVector = camTransform.up;

        // Make a raycast request using the raycast parameters 
        MLRaycast.Raycast(_raycastParams, HandleOnReceiveRaycast);
    }

    private IEnumerator NormalMarker(Vector3 point, Vector3 normal) 
    {
        // Rotate the prefab to match given normal.
        Quaternion rotation = Quaternion.FromToRotation(Vector3.up, normal);
        // Instantiate the prefab at the given point.
        GameObject go = Instantiate(prefab, point, rotation);
        // Wait 2 seconds then destroy the prefab.
        yield return new WaitForSeconds(2);
        Destroy(go);
    }
    
    void HandleOnReceiveRaycast( MLRaycast.ResultState state, 
                                UnityEngine.Vector3 point, Vector3 normal, 
                                float confidence) 
    {
        if (state ==  MLRaycast.ResultState.HitObserved) 
        {
            StartCoroutine(NormalMarker(point, normal));
        }
    }
}
```
このように内容を置き換えます．この内容を読み解いていきます．
## ざっくりとした理解
```C#
using System.Collections;
using UnityEngine;
using UnityEngine.XR.MagicLeap;
```
このあたりで各関数を呼び出す準備をしています．`System`はC#の基本系，`UnityEngine`はunityの基本系`MagicLeap`はマジックリープ固有の命令が収納されているようです．

```C#
 public Transform camTransform;
 public GameObject prefab;

 private MLRaycast.QueryParams _raycastParams = new MLRaycast.QueryParams();
```
各オブジェクトの準備です．`camTransform`はこのあとレイキャストに使うパラメータを引き出す先を登録しておきます．この場合はカメラなのでヘッドセットの動きに合わせて光線が飛びます．`prefab`にはレイキャストで作成されるオブジェクトを登録します．`_raycastParams`はレイキャストに使うパラメータを収納します．

## 細いメモ類
前提として [連載C#入門第3回クラスとオブジェクト](https://www.atmarkit.co.jp/fdotnet/csharp_abc/csharp_abc_003/csharp_abc02.html)を読んでおくとスムーズかもしれません．簡単に言うとクラスは命令の設計図，オブジェクトはその設計図から作った命令自体だと思って読んでいます．

### `using UnityEngine.XR.MagicLeap;`
magicleap名前空間を追加するみたいです．名前空間というのはクラス（命令文のようなもの？）をまとめたくくりです．フォルダのように名前空間の中にさらに小さな名前空間がある場合もあるみたいです．これがあることによって同じ名前のクラスもどの名前空間の中にいるかで区別できるようになります．

つまりは，この宣言をすることによってmagicleap関係の命令文を使えるようになります．

※ちなみに`System.Collections`はunityで標準的な名前区間（Cでいうstdioヘッダーのようなもの？），`UnityEngine`はunity関係の名前空間です．

### `public class RaycastSnippet : MonoBehavior`
RaycastSnippetというクラスを宣言しました．`public`は名前空間の宣言に関係なくいつでも使える言葉です．これを使うことでこのコードの外からもこのクラスを使うことができるようになります．つまりは，unityエディタからRaycastSnippetが使えるようになります．

`:MonoBehavior`はこのクラスの属性を追加しています．`MonoBehavior`はunityで作ると自動でつけてくれる属性で物体の動きをきっかけとする属性のようです．

### `public Transfrom camTransfrom;`
`public`を使って外からも使える形で，camTranfromというクラスをTranfromというカタチで宣言しています．Transfromはgameobjectを動かすためのクラスで，今回はカメラを移動させるために用います．これによりgameobjectの位置情報を使う準備ができました．

### `public GameObject prefab;`
`public`を使って外からも使える形で，スクリプトからgemeobjectを扱えるようになります．レイキャストによるキューブの生成に使うようです．

### `private MLRaycast.QueryParams _raycastParams = new MLRaycast.QueryParams();`
`private`は`public`と同じように名前空間の宣言に関係なくいつでも使える言葉です．`public`とは反対にこのスクリプトの外からアクセスできないようになるります．後で使用するMLRaycast.Raycastというクラスに送るRayの情報を入れるためのクラスです．`new`を付けることによって宣言と同時にオブジェクト化しています．


### `void Start()`
Startメゾットです．メゾットとは複数のクラスが1つの塊になったものです．特にStartは特別な名前で，このスクリプトが実行されたときに1回だけ実行されます．`MLRaycast.Start();`によりレイキャストが始まります．

### `private void OnDestroy()`
これもStartメゾッドと同様に特定のタイミングで呼び出されるメゾッドです．これはgameobjectが削除した時に呼び出されるメゾッドです．今回は，`MLRaycast.Stop();`によりレイキャストが終了されるように設定されています．

### `void Update()`
これもStartメゾッドと同様に特定のタイミングで呼び出されるメゾッドです．Updateメゾッドはunityで画面が更新されるたびに実行されるメゾッドです．今回は毎回カメラの移動と入力の確認をしているみたいです．

### `_raycastParams.〇〇 = camTransform.〇〇;`
前もって用意しておいた_raycastPatams.〇〇にcamTransfrom.〇〇を代入しています．つまりはmaincameraの状態を常にMLRaycastのパラメータとして受け取っています．これによりmaincameraと光線が同じように移動します．実際にMLRaycast.QueryParamsやTransfromがどのような構成・機能を持っているかは[unityスクリプトカンファレンス](https://docs.unity3d.com/ja/current/ScriptReference/index.html)などで逐次調べる必要がありそうです．

### `MLRaycast.Raycast(_raycastParams, HandleOnReceiveRaycast);`
レイキャストを実際に動かすクラスです．引数に光線のパラメータと物体とぶつかったときの動作を受け取ります．

### `private IEnumerator NormalMarker(Vector3 point, Vector3 normal) `
光線が物体にぶつかったときにキューブを生成するためのクラスです．


# 参考にしたもの

1. [連載C#入門第3回クラスとオブジェクト](https://www.atmarkit.co.jp/fdotnet/csharp_abc/csharp_abc_003/csharp_abc02.html)
2. [[超初心者向け]やっと納得、Unityを初めて触ると出てくるC#の何だあれの答え](https://qiita.com/JunShimura/items/3c2e23bb77cc9085bfda)
3. [レイキャストまとめ](https://nopitech.com/2018/07/05/post-725/)
4. [【Unity】transform.positionを変更してオブジェクトを移動させよう!](https://www.sejuku.net/blog/51251)
5. [【MagicLeap】検出した平面をRayで取得してオブジェクトを垂直に配置する](https://gaprot.jp/2020/05/18/magicleap-ray-detect-plane/)
6. [OnDisableやOnDestroyをエディタを止めた時には実行しないようにする【Unity】](https://kan-kikuchi.hatenablog.com/entry/OnApplicationQuit)