Sample project to showcase the use of UI Toolkit for Runtime based on the Unity Royale project.
您最多选择25个主题 主题必须以中文或者字母或数字开头,可以包含连字符 (-),并且长度不得超过35个字符
 
 
 
 

218 行
8.2 KiB

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Events;
using DG.Tweening;
using UnityEngine.UIElements;
using System;
namespace UnityRoyale
{
public class CardManager : MonoBehaviour
{
// Prefab properties
[SerializeField] private Camera mainCamera = default;
[SerializeField] private LayerMask playingFieldMask = default;
[SerializeField] private VisualTreeAsset visualTreeCard = default;
[SerializeField] private UIManager uiManager = default;
[SerializeField] private DeckData playersDeck = default;
[SerializeField] private MeshRenderer forbiddenAreaRenderer = default;
public UnityAction<CardData, Vector3, Placeable.Faction> OnCardUsed;
private CardElement[] cards;
private int cardCount = 3;
private bool cardIsActive = false; //when true, a card is being dragged over the play field
private GameObject previewHolder;
private readonly Vector3 InputCreationOffset = new Vector3(0f, 0f, 1f); //offsets the creation of units so that they are not under the player's finger
private void Awake()
{
previewHolder = new GameObject("PreviewHolder");
cards = new CardElement[3]; //3 is the length of the dashboard
}
public void LoadDeck()
{
DeckLoader newDeckLoaderComp = gameObject.AddComponent<DeckLoader>();
newDeckLoaderComp.OnDeckLoaded += DeckLoaded;
newDeckLoaderComp.LoadDeck(playersDeck);
}
private void DeckLoaded()
{
Debug.Log("Player's deck loaded");
//setup initial cards
StartCoroutine(AddCardToDeck(.1f));
for(int i=0; i< cardCount; i++)
{
StartCoroutine(PromoteCardFromDeck(i, .4f + i));
StartCoroutine(AddCardToDeck(.8f + i));
}
}
private VisualElement GetBackupContainer()
{
var root = uiManager.GetCardPanelRoot();
return root.Q<VisualElement>("backup");
}
private VisualElement GetActiveContainer()
{
var root = uiManager.GetCardPanelRoot();
return root.Q<VisualElement>("active");
}
//moves the preview card from the deck to the active card dashboard
private IEnumerator PromoteCardFromDeck(int cardId, float delay = 0f)
{
yield return new WaitForSecondsRealtime(delay);
var backupContainer = GetBackupContainer();
var backupCard = backupContainer.Q<CardElement>();
//setup listeners on Card events
backupCard.RegisterCallback<MouseDownEvent>(evt => CardTapped(evt, cardId));
backupCard.RegisterCallback<MouseUpEvent>(evt => CardReleased(evt, cardId));
backupCard.RegisterCallback<MouseMoveEvent>(evt => CardDragged(evt, cardId));
Vector2 screenPosition = backupCard.LocalToWorld(backupCard.transform.position);
var activePanel = GetActiveContainer();
activePanel.Add(backupCard);
backupCard.MoveTo(backupCard.WorldToLocal(screenPosition));
backupCard.MoveAndScaleIntoPosition(cardId, ComputeActiveCardPosition(cardId));
//store a reference to the Card component in the array
cards[cardId] = backupCard;
}
//adds a new card to the deck on the left, ready to be used
private IEnumerator AddCardToDeck(float delay = 0f) //TODO: pass in the CardData dynamically
{
yield return new WaitForSecondsRealtime(delay);
//create new card
var tree = visualTreeCard.CloneTree();
var card = tree.Q<CardElement>();
GetBackupContainer().Add(card);
card.Init(playersDeck.GetNextCardFromDeck());
card.Scale(0.1f);
card.AnimatedScale(0.7f, 0.2f);
card.MoveTo(new Vector2(10, 10));
}
private int draggedCardId = -1;
private Vector2 mouseDownPosition;
private void CardTapped(MouseDownEvent clickEvent, int cardId)
{
cards[cardId].SetAsLastSibling();
forbiddenAreaRenderer.enabled = true;
draggedCardId = cardId;
mouseDownPosition = clickEvent.mousePosition;
}
private void CardDragged(IMouseEvent dragEvent, int cardId)
{
if (cardId != draggedCardId)
return;
var dragAmount = dragEvent.mousePosition - mouseDownPosition;
cards[cardId].Translate(dragAmount);
mouseDownPosition = dragEvent.mousePosition;
//raycasting to check if the card is on the play field
RaycastHit hit;
Ray ray = mainCamera.ScreenPointToRay(Input.mousePosition);
bool planeHit = Physics.Raycast(ray, out hit, Mathf.Infinity, playingFieldMask);
if (planeHit)
{
if (!cardIsActive)
{
cardIsActive = true;
previewHolder.transform.position = hit.point;
cards[cardId].ChangeActiveState(true); //hide card
//retrieve arrays from the CardData
PlaceableData[] dataToSpawn = cards[cardId].cardData.placeablesData;
Vector3[] offsets = cards[cardId].cardData.relativeOffsets;
//spawn all the preview Placeables and parent them to the cardPreview
for (int i = 0; i < dataToSpawn.Length; i++)
{
Instantiate(dataToSpawn[i].associatedPrefab, hit.point + offsets[i] + InputCreationOffset,
Quaternion.identity, previewHolder.transform);
}
}
else
{
//temporary copy has been created, we move it along with the cursor
previewHolder.transform.position = hit.point;
}
}
else
{
if (cardIsActive)
{
cardIsActive = false;
cards[cardId].ChangeActiveState(false); //show card
ClearPreviewObjects();
}
}
}
private void CardReleased(MouseUpEvent mouseUpEvent, int cardId)
{
//raycasting to check if the card is on the play field
RaycastHit hit;
Ray ray = mainCamera.ScreenPointToRay(Input.mousePosition);
if (Physics.Raycast(ray, out hit, Mathf.Infinity, playingFieldMask))
{
//GameManager registers to OnCardUsed to spawn the actual Placeable
OnCardUsed?.Invoke(cards[cardId].cardData, hit.point + InputCreationOffset,
Placeable.Faction.Player);
ClearPreviewObjects();
cards[cardId].Delete(); //remove the card itself
StartCoroutine(PromoteCardFromDeck(cardId, .2f));
StartCoroutine(AddCardToDeck(.6f));
}
else
{
cards[cardId].AnimatedMoveTo(ComputeActiveCardPosition(cardId),.2f);
}
forbiddenAreaRenderer.enabled = false;
draggedCardId = -1;
}
private Vector2 ComputeActiveCardPosition(int cardId)
{
var activePanel = GetActiveContainer();
const float margin = 10f;
float offset = margin + cardId * (activePanel.layout.width - 2*margin) / cardCount;
return new Vector2(offset, 0);
}
//happens when the card is put down on the playing field, and while dragging (when moving out of the play field)
private void ClearPreviewObjects()
{
//destroy all the preview Placeables
for (int i = 0; i < previewHolder.transform.childCount; i++)
{
Destroy(previewHolder.transform.GetChild(i).gameObject);
}
}
}
}