Unity勉強中!あこがれだったプログラマーに今からなろう!

昔あこがれだったプログラマー。今からでもUnityを勉強してみようと思い立ち、チャレンジ開始! Unity勉強メモや、悪戦苦闘な日々の記録です。

3D脱出ゲーム:カバンからアイテムをドラッグして出せるようにしよう。

昨日の続き、アイテムを所定位置にセットできるようになったので、次はカバンからアイテムを取り出せるようにしよう。
基本的な仕組みは、実はもうできています。

フィールドにあるアイテムをドラッグする仕組みの、DragObjスクリプト
カバンからアイテムをドラッグするのも、同じイメージになるので、大筋のスクリプトは同じになります。

DragObjスクリプトの動きをざっくり説明すると、

①Drag開始した時にアイテムを縮小し、Canvasの手前に移動させる。
②Drag中は縮小したアイテムの位置をマウスの座標に移動し続ける。
③Drag終了時、アイテムから後方にRayを飛ばしてセットできる場所にいるか調べる。
 →セットできる位置にいれば、セットできる位置のメソッドにアイテムを引き渡す。
 →セットできる位置でなければ、アイテムをDrag開始位置に戻す。
④アイテムの大きさを元に戻す。

というものです。

今回やりたいのは、フィールドにあるアイテムではなく、カバンのアイテムをDragするというものです。
その違いは、カバンはあくまでSlotにImageを表示しているだけで、アイテムがそこにあるわけではないという事です。

なので、DragObjをベースに、機能の違いを変更した新しいスクリプト、DragObjUIスクリプトを作成する事にします。

DragObjからの変更点としては、
まず、Drag開始時点では動かすべきアイテムがないので、OnBeginDragでDrag用のアイテムを生成して、以降の処理はこの生成したアイテムを対象にするように変更します。

    public void OnBeginDrag(PointerEventData eventData)
    {
        if(!eventData.pointerDrag.GetComponent<Slot>().IsEmpty())//DragしたSlotにアイテムがあるか確認する。
        {
            Dragslot=eventData.pointerDrag.GetComponent<Slot>();//アイテムがあれば、Slotを格納。

            screenPosition=Input.mousePosition; //マウスポインタのスクリーン座標を取得
            var scrPos=new Vector3(screenPosition.x,screenPosition.y,2f);//1.5fはカメラからキャンバスまでの距離(2f)の手前にくる様に指定
            var worldPos=Camera.main.ScreenToWorldPoint(scrPos);

            GameManager.instance.OnDrag=true;

            //ドラッグ用のオブジェクトを生成
            this.item = eventData.pointerDrag.GetComponent<Slot>().GetItem();//Slotのスクリプトを利用してアイテムの情報を取得。
            PickUPObj=Instantiate(item.PickUPObj);//取得した情報からDrag用のObjectを生成。
            PickUPObj.transform.position=worldPos;//Drag用のObjectの座標をマウス座標に移動。
            PickUPObj.name="PickUPObj";        
            { ///transformの情報を取得
                ObjStartTransform = PickUPObj.transform;
                ObjStartPos = PickUPObj.transform.position;
                ObjStartRot = PickUPObj.transform.rotation.eulerAngles;
                ObjStartScale = PickUPObj.transform.localScale;
            }

            { //移動オブジェクトのスケールを変更  
                rectTransform=PickUPObj.GetComponent<RectTransform>();
                float currentHighest = rectTransform.sizeDelta.y;
                float scaleFactor=10/currentHighest;//移動オブジェクトの高さを10に指定
                
                PickUPObj.transform.localScale=new Vector3(scaleFactor,scaleFactor,scaleFactor);
            }
        }
    }

次に、Drag中の処理のOnDragもDragしたSlotではなく、生成したアイテムを対象にするように変更します。

    public void OnDrag(PointerEventData eventData)
    {

        if(!eventData.pointerDrag.GetComponent<Slot>().IsEmpty())
        {
            screenPosition=Input.mousePosition; //マウスポインタのスクリーン座標を取得
            var scrPos=new Vector3(screenPosition.x,screenPosition.y,2f);
            var worldPos=Camera.main.ScreenToWorldPoint(scrPos);

            PickUPObj.transform.position = worldPos;//生成したアイテムの座標をマウス座標に移動。
        }
    }

最後にDrag終了時点、ここは変更点がとても多くあるので注意が必要です。

Rayを飛ばす処理ですが、DragしているのはあくまでSlotなので、Rayの起点は生成したアイテムの位置=マウスの位置にする必要があります。
また、Rayに「セットできる位置」がヒットした場合、「セットできる位置」のメソッドに引き渡すのも、生成したアイテムに変更しなければなりません。
さらに、「セットできる位置」がヒットしなかった場合のアイテムを初期位置に戻す処理は、生成したアイテムなので、元に戻すのではなく、Destroyするようにします。

    public void OnEndDrag(PointerEventData eventData)
    {
       GameManager.instance.OnDrag=false;

        var scrPosition=Input.mousePosition; //マウスポインタのスクリーン座標を取得
        var scrPos=new Vector3(scrPosition.x,scrPosition.y,2f);//2fはカメラからキャン
        var worldPos=Camera.main.ScreenToWorldPoint(scrPos);

        //カバンの位置でない場合、Rayを飛ばしてオブジェクトが何かのセット位置に当たっているかを調べる。
        Vector3 direction = (worldPos - Camera.main.transform.position).normalized;
        Ray ray = new Ray(worldPos, direction);
        RaycastHit hit;
        int layerMask = 1 << LayerMask.NameToLayer("SetObjectStage");

        if (Physics.SphereCast(ray, 0.5f, out hit, 12, layerMask))
        {
            //当たっていれば、そのオブジェクトのSetPositionメソッドに、生成したPikuUPObjを引き渡す。
            hit.collider.gameObject.GetComponent<DropObj>().SetPosition(PickUPObj);
        }
        Invoke("CheckPosition",0.03f);
    }

    void CheckPosition()
    {
        if(!PickUPObj.GetComponent<DragObj>().IsDropSuccess)
        {
            Destroy(PickUPObj);
        }
        else
        {
            PickUPObj.transform.localScale = ObjStartScale;
            PickUPObj.transform.rotation = Quaternion.Euler(ObjStartRot);

            Dragslot.GetComponent<Slot>().SetItem(null);
            Dragslot.GetComponent<Slot>().HideBGPanel();
            ItemBox.instance.ResetSelectedItem();

        }
    }

これで変更は完了なので、このDragObjUIスクリプトをSlotにアタッチしてやれば完成です。
youtu.be

これで、アイテムをカバンに入れて、カバンから取り出してセットして、またカバンに戻すと一連の動きができるようになりました。

さて明日以降ですが、ちょっと脱出ゲームは休憩してUniTaskの実験をしようと思っています。
ゲームジャムで作ったゲーム「ColorChangePanic」で、敵Objectにはasync・awaitを使ったスクリプトをアタッチしているのですが
この敵Objectをオブジェクトプールで使い回すと、スクリプトの動きが思った通りにならなかったので、それの解決方法を考えてみようと思います。