こんにちは。
unityで3Dのクレーンゲームを個人開発しようと思うのですが
どういった方法で動作させればいいのかわかりません。
とりあえず筐体を左右に移動はできました。が、
アームを開ける→筐体を下げる→アームを閉じる→筐体を上げる→初期位置まで戻りアームを開く
上記の動作をどういった方法で実現できますでしょうか。
初心者なのでわからないことだらけですが教えていただけると助かります。
C#で動かすべきなのでしょうか?
はたまたアニメーションで動かすべきなのでしょうか…?
-
気になる質問をクリップする
クリップした質問は、後からいつでもマイページで確認できます。
またクリップした質問に回答があった際、通知やメールを受け取ることができます。
クリップを取り消します
-
良い質問の評価を上げる
以下のような質問は評価を上げましょう
- 質問内容が明確
- 自分も答えを知りたい
- 質問者以外のユーザにも役立つ
評価が高い質問は、TOPページの「注目」タブのフィードに表示されやすくなります。
質問の評価を上げたことを取り消します
-
評価を下げられる数の上限に達しました
評価を下げることができません
- 1日5回まで評価を下げられます
- 1日に1ユーザに対して2回まで評価を下げられます
質問の評価を下げる
teratailでは下記のような質問を「具体的に困っていることがない質問」、「サイトポリシーに違反する質問」と定義し、推奨していません。
- プログラミングに関係のない質問
- やってほしいことだけを記載した丸投げの質問
- 問題・課題が含まれていない質問
- 意図的に内容が抹消された質問
- 過去に投稿した質問と同じ内容の質問
- 広告と受け取られるような投稿
評価が下がると、TOPページの「アクティブ」「注目」タブのフィードに表示されにくくなります。
質問の評価を下げたことを取り消します
この機能は開放されていません
評価を下げる条件を満たしてません
質問の評価を下げる機能の利用条件
この機能を利用するためには、以下の事項を行う必要があります。
- 質問回答など一定の行動
-
メールアドレスの認証
メールアドレスの認証
-
質問評価に関するヘルプページの閲覧
質問評価に関するヘルプページの閲覧
+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;
}
}
}
投稿
-
回答の評価を上げる
以下のような回答は評価を上げましょう
- 正しい回答
- わかりやすい回答
- ためになる回答
評価が高い回答ほどページの上位に表示されます。
-
回答の評価を下げる
下記のような回答は推奨されていません。
- 間違っている回答
- 質問の回答になっていない投稿
- スパムや攻撃的な表現を用いた投稿
評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。
15分調べてもわからないことは、teratailで質問しよう!
- ただいまの回答率 88.23%
- 質問をまとめることで、思考を整理して素早く解決
- テンプレート機能で、簡単に質問をまとめられる
2018/11/12 16:37
クローは2本なのですがその場合はどのように対応したらよいのでしょうか。
まだまだ未熟でわからないことだらけなので教えていただけると助かります。
2018/11/12 19:25
実際のところ、あの回答のコードはクローの本数に依存しないよう作ったつもりでして、インスペクタ上の「Claw Hinges」の「Size」を変えれば2本でも3本でも対応可能なはずで、どのようなアドバイスを付け加えるべきか悩ましいです...
これ以上のアドバイスとなりますと、ご質問者さんのクレーンの設計をより詳しくご説明いただきたいところです。
現状ではこのようにクレーンを作った、クレーンのこの部分にはこういうスクリプトをアタッチしている...といった情報を追記いただけますとありがたいです。
2018/11/12 23:09 編集
助言助かりました。クローの本数に関する問題は解決しました。
さらなる問題が起きているので画像をとりあえず追加しました。
ほぼ他のスクリプトはアタッチしていません。
今は試験的な感じでUFOキャッチャーを作っているのでとりあえず動けばそれでよし、という状態です。
なにか追加してほしい情報ありましたらコメントお願いします。
手伝ってもらってすみません:;
本当に助かります。
2018/11/13 08:25 編集
左右に開閉したいので、おそらく「claw right」は(0, 0, 1)、「claw left」は(0, 0, -1)だろうと予想されますが、とりあえず「Axis」を色々な向きにしてみて正しい向きに開閉するかチェックしてみてください。
また、アームを下に動かした時に、クローが上方にフワッと回転してしまっていることから想像すると、ジョイントの力が足りないのかもしれません。
「Use Spring」の下の「Spring」項目の左の三角をクリックして展開すると、その下にさらに「Spring」という項目が現れるかと思いますが、これを数千~数万に上げて試してみてはいかがでしょう。
もしバネのようにビョンビョンと動いて困るようでしたら、さらに「Spring」の下の「Damper」も上げてみてください。「Spring」が目標角度へ引きつける力であるのに対して、「Damper」はそれを妨げる向きに働く抵抗力です。これを上げるとバネ運動の減衰が早くなり、弾みにくい重いバネになるかと思います。
2018/11/13 18:32
おかげさまで解決することができました。
また問題が発生しているのですが、もし解決策をご存知でしたらご教授していただきますと助かります。
問題:クレーンが上がり終えた瞬間に高速で{X 0} {Z 0}の座標まで飛ばされてしまいます。
スクリプトの設定でしたら申し訳ないです:;
※GIFを追加しておきました。
2018/11/14 05:16
あれを作った際には、クレーンの水平移動は重要な問題ではなかったため省略し、初期位置からの上下移動だけしか考慮していませんでした。
MoveUpMotionの一番最後に、
this.armTransform.position = new Vector3(0.0f, this.homePosition, 0.0f);
とありますが、ここでYをホームポジションに戻すのと同時に、XとZも0に戻してしまっています。
ここを、
var armPosition = this.armTransform.position;
armPosition.y = this.homePosition;
this.armTransform.position = armPosition;
に変更するとどうなるでしょうか?
また念のため申し上げますと、あのコードの一連のモーションでは、アームの上昇が終わった後はクローを開くモーションへ進んでしまいますので、「アームを上げる」「少し待機」の後、「クローを開く」の前に、水平位置を初期位置まで移動させるモーションを加えてやる必要があるかと思います。
2018/11/15 00:05
水平移動をするためにどのようなモーションが必要となるのでしょうか。
2018/11/15 06:43
最初の左右移動と矛盾しないように水平移動しないと正しく元の位置に戻らないかもしれない、という点が気になってしまいまして...
2018/11/15 17:32
これが右(X軸)です
↓
using UnityEngine;
using System.Collections;
public class right : MonoBehaviour
{
bool rightmove;
// Use this for initialization
void Start()
{
}
// Update is called once per frame
void Update()
{
if (rightmove == true)
{
transform.position += new Vector3(1 * Time.deltaTime, 0, 0);
}
}
public void rightButtonDown()
{
rightmove = true;
}
public void rightButtonUp()
{
rightmove = false;
}
}
これが縦(Z軸)
↓
using UnityEngine;
using System.Collections;
public class forward : MonoBehaviour
{
bool forwardmove;
// Use this for initialization
void Start()
{
}
// Update is called once per frame
void Update()
{
if (forwardmove == true)
{
transform.position += new Vector3(0, 0, 1 * Time.deltaTime);
}
}
public void forwardButtonDown()
{
forwardmove = true;
}
public void forwardButtonUp()
{
forwardmove = false;
}
}
2018/11/15 17:34
他に必要な情報がありましたらコメントお願いします。
2018/11/16 05:01
以前ご提示いただいた図では、たしか「Crane」という名前のキューブにCraneスクリプトがアタッチされていたかと思いますが、rightとforwardもこのオブジェクトでしょうか?それともアーム部分...「hontai」の方にアタッチされているでしょうか。
2018/11/16 18:33
2018/11/17 09:40
2018/11/17 11:06
無事動作することができました。ありがとうございました。