Unityでゲーム内の物理的負荷をトリガーにしたい
解決済
回答 1
投稿
- 評価
- クリップ 1
- VIEW 2,480
前提・実現したいこと
Unityでクレーンゲームのシュミレーターを作成しています。
アームユニットを下降させる際にrigidbody(Is Kinematic)を用いているのですが、
アームユニットが物体に衝突しても、トリガーを設定していないのでそのまま下降していき非現実的な挙動となってしまいます。(y座標の最低高度は指定してあります。)
現実のクレーンゲームだと紐を巻き取ったりして制御しているそうです。(負荷がかかると紐がたるんで下降がストップする)
どのようなアルゴリズムを用いれば良いでしょうか?
まだプログラムに関するリテラシーが浅いので質問させていただきました。
試したこと
_★_
◯◯◯
◯◆◯
◯_◯
◯◯◯
★がトリガー(Is trigger)
◯がrigidbody(Use Gravity)を適用した物体(繋がっています)
◆がユニット
上記で◯が★に接触した場合に下降を停止するようにしていますが、ほかの方法の方が良いのではという勘です。
-
気になる質問をクリップする
クリップした質問は、後からいつでもマイページで確認できます。
またクリップした質問に回答があった際、通知やメールを受け取ることができます。
クリップを取り消します
-
良い質問の評価を上げる
以下のような質問は評価を上げましょう
- 質問内容が明確
- 自分も答えを知りたい
- 質問者以外のユーザにも役立つ
評価が高い質問は、TOPページの「注目」タブのフィードに表示されやすくなります。
質問の評価を上げたことを取り消します
-
評価を下げられる数の上限に達しました
評価を下げることができません
- 1日5回まで評価を下げられます
- 1日に1ユーザに対して2回まで評価を下げられます
質問の評価を下げる
teratailでは下記のような質問を「具体的に困っていることがない質問」、「サイトポリシーに違反する質問」と定義し、推奨していません。
- プログラミングに関係のない質問
- やってほしいことだけを記載した丸投げの質問
- 問題・課題が含まれていない質問
- 意図的に内容が抹消された質問
- 過去に投稿した質問と同じ内容の質問
- 広告と受け取られるような投稿
評価が下がると、TOPページの「アクティブ」「注目」タブのフィードに表示されにくくなります。
質問の評価を下げたことを取り消します
この機能は開放されていません
評価を下げる条件を満たしてません
質問の評価を下げる機能の利用条件
この機能を利用するためには、以下の事項を行う必要があります。
- 質問回答など一定の行動
-
メールアドレスの認証
メールアドレスの認証
-
質問評価に関するヘルプページの閲覧
質問評価に関するヘルプページの閲覧
checkベストアンサー
0
申し訳ないです、せっかく図で説明していただきましたが、いまいち状況が把握しきれず、勝手な解釈での回答です(クレーンが景品に下降していく際、何も対策しないとクレーンが景品の山にめり込んでいってしまうので、個々の景品の上面にクレーン停止用トリガー領域を設置する方法をとったが、これ以外に案はないだろうか...ということでしょうか?)。
トリガー方式も悪くないと思うのですが、違ったアプローチもご所望でしたら、一案としてクレーンをキネマティックなRigidbodyと非キネマティックなRigidbodyを組み合わせた構造にして、両者をFixedJointなど適当なジョイントで繋ぎ、そのジョイントのcurrentForceを監視して負荷のかかり方を検出する...というのを思いつきましたが、いかがでしょう。
クレーンが空中にあるときは、非キネマティック部分にかかる重力によってジョイントに下向きの負荷がかかるが、景品の上に非キネマティック部分が乗っかると、景品からの抗力で下方向の負荷が消失するはず...という目論見で実験してみました。
下図のような構造のクレーンを作って、
クレーンに下記スクリプトをアタッチしました。
using System.Collections;
using System.Linq;
using UnityEngine;
public class Crane : MonoBehaviour
{
public Transform armTransform; // アームのTransform...アームはキネマティックとし、Transformを操作して移動させる
public Joint LoadDetectorJoint; // アームと円盤(非キネマティック)を繋ぐジョイント...ここにかかる負荷を見てアームの衝突を検知する(今回はFixedJointを使用しました)
public HingeJoint[] ClawHinges; // クローと円盤を繋ぐヒンジ...Use Springをオンにし、適当なSpring(大きいほど握力が強まる)を設定しておく
private bool playing;
private float homePosition;
private void Start()
{
this.homePosition = armTransform.position.y; // アームを上げるとき、この高さまで戻す
}
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.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; // 負荷を積算するフレーム数(試しに「1」などとしてみると、アーム降下開始時の加速によって負荷が消失し、すぐにアームが止まってしまうと思います)
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; // currentForceのY成分を負荷の指標とする
loadSum += (newLoad - previousLoad);
var averageLoad = loadSum / loadMovingAverageLength; // 過去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.homePosition)
{
velocity = Mathf.Min(velocity + acceleration * Time.deltaTime, velocityMax);
this.armTransform.Translate(0.0f, velocity * Time.deltaTime, 0.0f, Space.World);
yield return null;
}
this.armTransform.position = new Vector3(0.0f, this.homePosition, 0.0f);
}
private IEnumerator MoveClawsMotion(float angle, float timeout = 3.0f)
{
const float angularSpeed = 30.0f; // 毎秒30°で開閉
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;
}
}
}
円盤が景品に乗っかっている状態以外でも、アームの加速時など一時的に負荷がなくなる場合があるので、一定フレームの間の平均の負荷でアーム停止の判定をしています。
アームの下降に伴う負荷の変化は下図のようになりました(あくまでも一例で、クレーンの各部位や景品の質量設定などの要因でクレーンの挙動は変わってくると思います)。
[追記]
補足として、今回はcurrentForceのyをそのまま使っていますが、ジョイントでRigidbody同士を接続した場合、currentForceは接続相手の座標系に従うみたいですので、今回のように重力の負荷を調べる場合は、適宜ワールド座標系に直す必要があるかもしれません。
投稿
-
回答の評価を上げる
以下のような回答は評価を上げましょう
- 正しい回答
- わかりやすい回答
- ためになる回答
評価が高い回答ほどページの上位に表示されます。
-
回答の評価を下げる
下記のような回答は推奨されていません。
- 間違っている回答
- 質問の回答になっていない投稿
- スパムや攻撃的な表現を用いた投稿
評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。
15分調べてもわからないことは、teratailで質問しよう!
- ただいまの回答率 88.22%
- 質問をまとめることで、思考を整理して素早く解決
- テンプレート機能で、簡単に質問をまとめられる
質問への追記・修正、ベストアンサー選択の依頼
sakura_hana
2017/11/27 14:13
現状はどうなっていて(トリガーを設定していないのか?停止するようにしているのか?)、どのような問題を懸念しているのか(または問題が発生しているのか)、質問内から読み取れません。ヒエラルキーツリーやスクリプトを提示して説明してください。