質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

ただいまの
回答率

88.23%

unityでクレーンゲームを作っているのですが・・・

受付中

回答 1

投稿 編集

  • 評価
  • クリップ 1
  • VIEW 2,474

binoyazyu

score 6

こんにちは。
unityで3Dのクレーンゲームを個人開発しようと思うのですが
どういった方法で動作させればいいのかわかりません。
とりあえず筐体を左右に移動はできました。が、

アームを開ける→筐体を下げる→アームを閉じる→筐体を上げる→初期位置まで戻りアームを開く

上記の動作をどういった方法で実現できますでしょうか。
初心者なのでわからないことだらけですが教えていただけると助かります。

C#で動かすべきなのでしょうか?
はたまたアニメーションで動かすべきなのでしょうか…?

イメージ説明

  • 気になる質問をクリップする

    クリップした質問は、後からいつでもマイページで確認できます。

    またクリップした質問に回答があった際、通知やメールを受け取ることができます。

    クリップを取り消します

  • 良い質問の評価を上げる

    以下のような質問は評価を上げましょう

    • 質問内容が明確
    • 自分も答えを知りたい
    • 質問者以外のユーザにも役立つ

    評価が高い質問は、TOPページの「注目」タブのフィードに表示されやすくなります。

    質問の評価を上げたことを取り消します

  • 評価を下げられる数の上限に達しました

    評価を下げることができません

    • 1日5回まで評価を下げられます
    • 1日に1ユーザに対して2回まで評価を下げられます

    質問の評価を下げる

    teratailでは下記のような質問を「具体的に困っていることがない質問」、「サイトポリシーに違反する質問」と定義し、推奨していません。

    • プログラミングに関係のない質問
    • やってほしいことだけを記載した丸投げの質問
    • 問題・課題が含まれていない質問
    • 意図的に内容が抹消された質問
    • 過去に投稿した質問と同じ内容の質問
    • 広告と受け取られるような投稿

    評価が下がると、TOPページの「アクティブ」「注目」タブのフィードに表示されにくくなります。

    質問の評価を下げたことを取り消します

    この機能は開放されていません

    評価を下げる条件を満たしてません

    評価を下げる理由を選択してください

    詳細な説明はこちら

    上記に当てはまらず、質問内容が明確になっていない質問には「情報の追加・修正依頼」機能からコメントをしてください。

    質問の評価を下げる機能の利用条件

    この機能を利用するためには、以下の事項を行う必要があります。

回答 1

+2

一案としましては、去年の話ですがC# - Unityでゲーム内の物理的負荷をトリガーにしたい|teratailのご質問に案を投稿した際には、一連の動きを表現するのに入れ子のコルーチンを使ってみました。
投稿したコードでは、ご要望を実現しようと試行錯誤した結果クローをアームにジョイントで繋いで開閉していますが、クローもKinematicにしてしまいMoveRotationで回転させる方が制御が簡単かもしれません。

追記

水平位置を元に戻すには、Craneがアタッチされたオブジェクトを元の位置に戻してやればよさそうですね。
Craneスクリプトに水平移動モーションを追加してみました。元々のコメントは削除し、今回変更があった部分にのみコメントを加えています。

using System.Collections;
using System.Linq;
using UnityEngine;

public class Crane : MonoBehaviour
{
    public Transform armTransform;
    public Joint LoadDetectorJoint;
    public HingeJoint[] ClawHinges;

    private bool playing;
    private float homePositionY; // フィールド名をhomePositionYに変更
    private Vector2 homePositionXZ; // 水平位置用のフィールドを追加

    private void Start()
    {
        this.homePositionY = armTransform.position.y; // フィールド名をhomePositionYに変更
        this.homePositionXZ = new Vector2(this.transform.position.x, this.transform.position.z); // アーム上昇後、水平位置をここへ移動する
    }

    private void Update()
    {
        if (!this.playing && Input.GetKeyDown(KeyCode.Space))
        {
            this.StartCoroutine(this.StartGame());
        }
    }

    private IEnumerator StartGame()
    {
        this.playing = true;

        Debug.Log("Start!");

        yield return this.StartCoroutine(this.MoveClawsMotion(60.0f));
        yield return new WaitForSeconds(1.0f);
        yield return this.StartCoroutine(this.MoveDownMotion());
        yield return new WaitForSeconds(1.0f);
        yield return this.StartCoroutine(this.MoveClawsMotion(0.0f));
        yield return new WaitForSeconds(1.0f);
        yield return this.StartCoroutine(this.MoveUpMotion());
        yield return new WaitForSeconds(1.0f);
        yield return this.StartCoroutine(this.ReturnHomeMotion()); // 水平位置を元に戻す
        yield return new WaitForSeconds(1.0f); // 少し待機
        yield return this.StartCoroutine(this.MoveClawsMotion(60.0f));
        yield return new WaitForSeconds(1.0f);
        yield return this.StartCoroutine(this.MoveClawsMotion(0.0f));

        this.playing = false;
    }

    private IEnumerator MoveDownMotion()
    {
        const int loadMovingAverageLength = 30;
        const float loadThreshold = 0.0f;
        const float acceleration = 0.5f;
        const float velocityMax = 1.0f;

        var loads = Enumerable.Repeat<float>(this.LoadDetectorJoint.currentForce.y, loadMovingAverageLength).ToArray();
        var loadSum = loads.Sum();
        var loadIndex = 0;

        var velocity = 0.0f;

        while (true)
        {
            var previousLoad = loads[loadIndex];
            var newLoad = this.LoadDetectorJoint.currentForce.y;

            loadSum += (newLoad - previousLoad);

            var averageLoad = loadSum / loadMovingAverageLength;

            Debug.LogFormat("Load:{0}", averageLoad);

            if (averageLoad >= loadThreshold)
            {
                Debug.Log("Stop!");
                break;
            }

            velocity = Mathf.Max(velocity - acceleration * Time.deltaTime, -velocityMax);
            this.armTransform.Translate(0.0f, velocity * Time.deltaTime, 0.0f, Space.World);
            loadIndex = (loadIndex + 1) % loadMovingAverageLength;

            yield return null;
        }
    }

    private IEnumerator MoveUpMotion()
    {
        const float acceleration = 0.5f;
        const float velocityMax = 1.0f;

        var velocity = 0.0f;

        while (this.armTransform.position.y < this.homePositionY) // フィールド名をhomePositionYに変更
        {
            velocity = Mathf.Min(velocity + acceleration * Time.deltaTime, velocityMax);
            this.armTransform.Translate(0.0f, velocity * Time.deltaTime, 0.0f, Space.World);

            yield return null;
        }

        // コメントで申し上げたように、アームのX、Zを0にしてしまわないように変更しました
        var armPosition = this.armTransform.position;
        armPosition.y = this.homePositionY; // フィールド名をhomePositionYに変更
        this.armTransform.position = armPosition;
    }

    // 元の位置に戻すためのモーションを追加
    // クレーンゲームらしくしようと思い、景品を持ち上げた位置から一直線に戻るのではなく、XとZを独立して動かしています
    private IEnumerator ReturnHomeMotion()
    {
        const float acceleration = 0.5f; // X軸またはZ軸の移動加速度
        const float velocityMax = 1.0f; // X軸またはZ軸の最大移動速度

        var velocity = 0.0f;
        var position = new Vector2(this.transform.position.x, this.transform.position.z); // 現在の水平位置

        // ホームポジションへの向きの符号を求める...移動方向のコントロールに使う
        var sign = this.homePositionXZ - position;
        sign.x = Mathf.Sign(sign.x);
        sign.y = Mathf.Sign(sign.y);

        while (true)
        {
            // ホームポジションへの相対位置を求め、十分近づいたら移動を終える
            var left = this.homePositionXZ - position;
            if (Mathf.Approximately(left.sqrMagnitude, 0.0f))
            {
                break;
            }

            velocity = Mathf.Min(velocity + acceleration * Time.deltaTime, velocityMax);
            var delta = velocity * Time.deltaTime; // XまたはZ方向の、このフレームでの移動量
            var nextPosition = position + sign * delta; // このフレームでの移動先

            // 目標地点を越えないように座標をクランプしてから、そこへ移動
            position.x = sign.x > 0.0f ? Mathf.Min(nextPosition.x, this.homePositionXZ.x) : Mathf.Max(nextPosition.x, this.homePositionXZ.x);
            position.y = sign.y > 0.0f ? Mathf.Min(nextPosition.y, this.homePositionXZ.y) : Mathf.Max(nextPosition.y, this.homePositionXZ.y);
            this.transform.position = new Vector3(position.x, this.homePositionY, position.y);

            yield return null;
        }

        this.transform.position = new Vector3(this.homePositionXZ.x, this.homePositionY, this.homePositionXZ.y);
    }

    private IEnumerator MoveClawsMotion(float angle, float timeout = 3.0f)
    {
        const float angularSpeed = 30.0f;

        var time = 0.0f;

        while (time < timeout)
        {
            var reached = true;
            var angleDeltaMax = angularSpeed * Time.deltaTime;

            foreach (var clawHinge in this.ClawHinges)
            {
                var spring = clawHinge.spring;

                spring.targetPosition = Mathf.MoveTowardsAngle(spring.targetPosition, angle, angleDeltaMax);
                clawHinge.spring = spring;
                reached &= Mathf.Approximately(spring.targetPosition, angle);
            }

            if (reached)
            {
                break;
            }

            time += Time.deltaTime;

            yield return null;
        }
    }
}

投稿

編集

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2018/11/16 18:33

    情報不足ですみません。Craneのほうにアタッチしました。

    キャンセル

  • 2018/11/17 09:40

    水平移動モーションについて追記しました。ご提示いただいたことから判断しますと、おそらくこれで水平位置が戻るかとは思いますが...

    キャンセル

  • 2018/11/17 11:06

    こんなに丁寧に対応していただき感謝の気持ちでいっぱいです!
    無事動作することができました。ありがとうございました。

    キャンセル

15分調べてもわからないことは、teratailで質問しよう!

  • ただいまの回答率 88.23%
  • 質問をまとめることで、思考を整理して素早く解決
  • テンプレート機能で、簡単に質問をまとめられる

関連した質問

同じタグがついた質問を見る