我正在创建一个 tcg(交易纸牌游戏),我想知道如何在玩游戏时更改纸牌的布局。我的意思是卡片将在画布上垂直和水平对齐到屏幕中心的行中展开,当我绘制/关闭卡片时,我希望卡片填充空间并在游戏中再次对齐。我怎样才能做到这一点?有任何想法吗?我想到了一个关于何时开始轮到你的解决方案(从屏幕中心开始,然后后退一步的长度 X 卡的数量 / 2,然后一张接一张地生成卡),但我不知道当您关闭其中一张卡片而不再次加载它们时如何更改卡片的对齐方式...... 例如图片
1 回答
使用与初始位置相同的方法,您应该能够获得新位置。现在每张卡片都有两个位置:oldPos
和newPos
。
你的卡片已经被实例化了。他们的位置存储在Transform.position
. 你的目标是从 移动oldPos
到newPos
。最简单的方法是:
myCard.transform.position = newPos;
这将立即将您的卡移动到新位置。但是,传送您的对象并不常见,因为它通常不会给用户带来良好的感觉。更好的解决方案是将对象从一个位置平滑地移动到另一个位置。
为此,您可以在现有对象周围移动transform.Translate(new Vector3());
,其中Vector3
将决定其移动速度。该方法Translate()
正在position += movementDirection * movementAmount
按照您的预期进行。
在帧上移动任何对象称为Animation
。有一些动画技术可以让动作看起来更好(看起来比实际更快,或者看起来更自然)。数学中的一种常用方法称为线性插值,或lerp
. 使用 lerp,您可以轻松计算两个端点之间的中间点,如果您将对象放置在您计算的点上,它会看起来自然而漂亮。我相信这就是您正在寻找的。
========
编辑:
这是一个如何实现的示例。请注意,在此示例中,卡片每帧移动相同的距离。使用 lerp(缓入、缓出等),您可以使这个动画变得更好。
我想让你注意的另一点是我正在做if (Vector2.Distance(nextPosition, transform.position) < 10)
,而不是if(oldPosition.equals(newPosition))
。原因是equals()
比较不安全,floats
因为它们通常存储为0.4999999
and0.50001
而不是0.5
and 0.5
。所以最好的检查方法floats
是测试它们是否“足够接近”。
最后,您可以改进以下代码,可能会以多种不同的方式进行改进。例如:
Destroy()
并且Instantiate()
是非常慢的操作,您应该使用Object Pooling
,因为您知道您将不断执行这些操作。- 的动作
Card
可以通过更好的动画技术来改进,例如lerp
. - 可能还有其他存储方式
List<Card> Cards
OnCardClick()
正在使用FindObjectOfType<CardSpawner>().OnCardDeleted(this)
,这需要Card
了解CardSpawner
。这叫Tight Coupling
作恶,俗称恶。有很多讨论你可以找到为什么这是不好的。推荐的解决方案是使用event
(UnityEvent
在 Unity3d 中更好)。
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();
}
}
卡片.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);
}
}