tcg (トレーディング カード ゲーム) を作成していますが、プレイ中にカードのレイアウトを変更する方法を知りたいです。つまり、カードはキャンバス上で画面の中央に垂直方向と水平方向の両方に整列して配置され、カードを引く/破棄すると、カードがスペースを埋めてゲームで再び整列するようになります。どうやってやるの?何か案は?自分の番がいつ始まるかの解決策を考えたのですが(画面の中央から始めて、1ステップ×カードの枚数/2だけ後ろに下がって、次から次へとカードを出現させる)、わかりません。カードをすべて再度ロードせずにカードの 1 つを閉じるときに、カードの配置を変更する方法... 例の画像
1 に答える
最初の位置に使用したのと同じ方法を使用して、新しい位置を取得できるはずです。これで、各カードに と の 2 つの位置がoldPos
ありnewPos
ます。
カードはすでにインスタンス化されています。それらの位置は に保存されTransform.position
ます。あなたの目標は から に移動するoldPos
ことnewPos
です。最も簡単な方法は次のとおりです。
myCard.transform.position = newPos;
これにより、カードがすぐに新しい位置に移動します。ただし、オブジェクトをテレポートすることは一般的ではありません。これは、ユーザーに好印象を与えることがあまりないためです。より良い解決策は、オブジェクトをある位置から別の位置にスムーズに移動することです。
transform.Translate(new Vector3());
これを行うには、によって既存のオブジェクトの周りを移動できますVector3
。移動速度は によって決定されます。このメソッドTranslate()
は、position += movementDirection * movementAmount
期待どおりに機能しています。
フレーム上でオブジェクトを移動することを と呼びAnimation
ます。アニメーションには、動きをよりよく見せる(実際より速く見える、または自然に見える)テクニックがあります。数学の一般的な方法の 1 つは、線形補間または と呼ばれlerp
ます。lerp を使用すると、2 つの終点間の中間点を簡単に計算できます。計算した点に沿ってオブジェクトを配置すると、自然で見栄えがよくなります。私はこれがあなたが探しているものだと信じています。
========
編集:
これを実現する方法の例を次に示します。この例では、Card がフレームごとに同じ距離だけ移動していることに注意してください。lerp (イーズイン、イーズアウトなど) を使用すると、このアニメーションをさらに良くすることができます。
注意していただきたいもう 1 つの点は、私が行っているif (Vector2.Distance(nextPosition, transform.position) < 10)
のは ではなくif(oldPosition.equals(newPosition))
です。その理由は、andの代わりにandとして保存されることが多いため、equals()
比較するのは安全ではないからです。したがって、チェックする最善の方法は、それらが互いに「十分に近い」かどうかをテストすることです。floats
0.4999999
0.50001
0.5
0.5
floats
最後に、次のコードを改善すると、多くの異なる方法で改善される可能性があります。たとえば、次のとおりです。
Destroy()
Instantiate()
非常に遅い操作であり、これらの操作を常に実行することがわかっているため、使用する必要がありますObject Pooling
。- の動きは、 の
Card
ような優れたアニメーション技術によって改善される可能性がありますlerp
。 - 他にも保存方法があるかもしれません
List<Card> Cards
OnCardClick()
を使用しており、これについて知るFindObjectOfType<CardSpawner>().OnCardDeleted(this)
必要があります。これは と呼ばれ、悪として知られています。これが悪い理由を見つけることができる多くの議論があります。推奨される解決策は、使用することです ( Unity3d の方が適しています)。Card
CardSpawner
Tight Coupling
event
UnityEvent
CardSpawner.cs
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class CardSpawner : MonoBehaviour
{
[SerializeField] GameObject CardParent;
[SerializeField] GameObject CardPrefab;
Vector2 DefaultSpawnPosition = new Vector2(Screen.width / 2f, Screen.height / 10f);
List<Card> Cards = new List<Card>();
public void OnClickButton()
{
SpawnNewCard();
AssignNewPositions();
AnimateCards();
}
public void OnCardDeleted(Card removedCard)
{
Cards.Remove(removedCard);
AssignNewPositions();
AnimateCards();
}
void SpawnNewCard()
{
GameObject newCard = (GameObject)Instantiate(CardPrefab, DefaultSpawnPosition, new Quaternion(), CardParent.GetComponent<Transform>());
Cards.Add(newCard.GetComponent<Card>());
}
void AssignNewPositions()
{
int n = Cards.Count;
float widthPerCard = 100;
float widthEmptySpaceBetweenCards = widthPerCard * .2f;
float totalWidthAllCards = (widthPerCard * n) + (widthEmptySpaceBetweenCards * (n-1));
float halfWidthAllCards = totalWidthAllCards / 2f;
float centreX = Screen.width / 2f;
float leftX = centreX - halfWidthAllCards;
for (int i = 0; i < n; i++)
{
if (i == 0)
Cards[i].nextPosition = new Vector2(leftX + widthPerCard / 2f, Screen.height / 2f);
else
Cards[i].nextPosition = new Vector2(leftX + widthPerCard / 2f + ((widthPerCard + widthEmptySpaceBetweenCards) * i), Screen.height / 2f);
}
}
void AnimateCards()
{
foreach (Card card in Cards)
card.StartMoving();
}
}
Card.cs
using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Card : MonoBehaviour
{
public Vector2 oldPosition;
public Vector2 nextPosition;
bool IsMoving;
void Update ()
{
if (IsMoving)
{
int steps = 10;
Vector2 delta = (nextPosition - oldPosition) / steps;
transform.Translate(delta);
if (Vector2.Distance(nextPosition, transform.position) < 10)
IsMoving = false;
}
}
public void StartMoving()
{
IsMoving = true;
oldPosition = transform.position;
}
public void OnCardClick()
{
UnityEngine.Object.Destroy(this.gameObject);
Debug.Log("AfterDestroy");
FindObjectOfType<CardSpawner>().OnCardDeleted(this);
}
}