1. Getting Started
3. Setting Up A New URP Project











拖动资源到项目中

2. Layouts
1. Laying Out Our Scene




右键, 创建3d地板

camera的位置



card frame 拖动到项目目录






4个小卡片x分别是-1.5, -3, 1.5, 3



新建empty object
2. Making A Card

新建empty object

card model文件拖入项目










文字居中






关掉3D icon



创建第2个, 后面加outline


3. Finishing The Card Layout


从上到下auto size的最大值22, 20, 20


拖动图片到source image属性, 点击preserve aspect可以按比例缩放


新建image, 点击关闭maskable, add component添加mask



调整mask image的大小



这个随着canvas的创建而出现, 但是我们不需要, 所以删掉
3. Cards
1. Creating Our First Script

创建script文件夹

右键创建c#脚本

创建的时候, 名称和Hierarchy的项目名称对上, 否则创建完再改名, 脚本的class名称还是默认的


脚本拖动到card项目目录

在这配置双击脚本打开的ide
2. Making The Card Script Work


定义的属性在unity中显示了


在unity中绑定变量

启动时设置text的值

运行

看到text确实被改变了

选择max的那个, 运行时就会全屏

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using TMPro;
public class Card : MonoBehaviour
{
public int currentHealth;
public int attackPower;
public int manaCost;
public TMP_Text healthText, attackText, costText;
// Start is called before the first frame update
void Start()
{
healthText.text = currentHealth.ToString();
attackText.text = attackPower.ToString();
costText.text = manaCost.ToString();
}
// Update is called once per frame
void Update()
{
}
}
3. Creating A Scriptable Object







textArea注释

unity中该选项发生变化

创建专门存放card的文件夹


using System.Collections;
using System.Collections.Generic;
using UnityEngine;
// 添加一个菜单创建选项, 文件名New Card, 菜单项名Card
[CreateAssetMenu(fileName = "New Card", menuName = "Card", order = 1)]
public class CardScriptableObject : ScriptableObject
{
public string cardName;
[TextArea]
public string actionDescription, cardLore;
public int currentHealth;
public int attackPower;
public int manaCost;
public Sprite characterSprite, bgSprite;
}
现在我们存储了数据到自定义的对象, 接下来就是读取并写入card
4. Loading Data From The Scriptable Object
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using TMPro;
using UnityEngine.UI;
public class Card : MonoBehaviour
{
public CardScriptableObject cardSO;
public int currentHealth;
public int attackPower;
public int manaCost;
public TMP_Text healthText, attackText, costText;
public TMP_Text nameText, actionDescriptionText, loreText;
public Image characterArt, bgArt;
// Start is called before the first frame update
void Start()
{
setupCard();
}
public void setupCard()
{
currentHealth = cardSO.currentHealth;
attackPower = cardSO.attackPower;
manaCost = cardSO.manaCost;
healthText.text = currentHealth.ToString();
attackText.text = attackPower.ToString();
costText.text = manaCost.ToString();
nameText.text = cardSO.cardName;
actionDescriptionText.text = cardSO.actionDescription;
loreText.text = cardSO.cardLore;
characterArt.sprite = cardSO.characterSprite;
bgArt.sprite = cardSO.bgSprite;
}
// Update is called once per frame
void Update()
{
}
}

添加属性

绑定card object



创建多个card和cardObject, 然后一一绑定, 使用cardScriptObject比使用多个card内存占用来得少
4. Holding Hands
1. Setting Up Our Hand


empty object

拖动handControler脚本到handController项目对象

empty object


两个小球用来定位

把小球删除


using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class HandController : MonoBehaviour
{
public Card[] heldCards;
public Transform minPos, maxPos;
// Start is called before the first frame update
void Start()
{
SetCardPositionsInHand();
}
// Update is called once per frame
void Update()
{
}
public void SetCardPositionsInHand()
{
}
}
2. Setting Card Positions In Hand

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class HandController : MonoBehaviour
{
public List<Card> heldCards = new List<Card>();
public Transform minPos, maxPos;
public List<Vector3> cardPositions = new List<Vector3>();
// Start is called before the first frame update
void Start()
{
SetCardPositionsInHand();
}
// Update is called once per frame
void Update()
{
}
public void SetCardPositionsInHand()
{
cardPositions.Clear();
Vector3 distanceBetweenPoints = Vector3.zero;
if (heldCards.Count > 1)
{
distanceBetweenPoints = (maxPos.position - minPos.position) / (heldCards.Count - 1);
}
for (int i = 0; i < heldCards.Count; i++)
{
cardPositions.Add(minPos.position + (distanceBetweenPoints * i));
heldCards[i].transform.position = cardPositions[i];
}
}
}

3. Preventing Overlapping Cards


选择hand controller, 右上角锁住面板, 左侧选中卡片, 拖动到held cards属性

修改一下minPos的rotation(倾斜)

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class HandController : MonoBehaviour
{
// ...
public void SetCardPositionsInHand()
{
// ...
for (int i = 0; i < heldCards.Count; i++)
{
cardPositions.Add(minPos.position + (distanceBetweenPoints * i));
heldCards[i].transform.position = cardPositions[i];
// 卡片倾斜, 防止叠在一起穿模
heldCards[i].transform.rotation = minPos.rotation;
}
}
}
4. Moving Cards Smoothly



debug模式可以看到private属性

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using TMPro;
using Unity.Mathematics;
using UnityEngine.UI;
public class Card : MonoBehaviour
{
///
private Vector3 _targetPoint;
private Quaternion _targetRot;
public float moveSpeed;
public float rotateSpeed;
// Start is called before the first frame update
void Start()
{
setupCard();
}
public void setupCard()
{
// ...
moveSpeed = 5f;
rotateSpeed = 540f;
}
// Update is called once per frame
private void Update()
{
transform.position = Vector3.Lerp(transform.position, _targetPoint, moveSpeed * Time.deltaTime);
transform.rotation =
Quaternion.RotateTowards(transform.rotation, _targetRot, rotateSpeed * Time.deltaTime);
}
public void MoveToPoint(Vector3 pointToMoveTo, Quaternion rotToMatch)
{
_targetPoint = pointToMoveTo;
_targetRot = rotToMatch;
}
}
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class HandController : MonoBehaviour
{
///
public void SetCardPositionsInHand()
{
///
for (int i = 0; i < heldCards.Count; i++)
{
cardPositions.Add(minPos.position + (distanceBetweenPoints * i));
// heldCards[i].transform.position = cardPositions[i];
// 卡片倾斜, 防止叠在一起穿模
// heldCards[i].transform.rotation = minPos.rotation;
// 移动卡片
heldCards[i].MoveToPoint(cardPositions[i], minPos.rotation);
}
}
}
5. Raising Cards While Hovering

3d->spherep->向上移动一点->add component->rigidbody


小球和card碰撞测试

using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using TMPro;
using Unity.Mathematics;
using UnityEngine.UI;
public class Card : MonoBehaviour
{
///
public bool inHand;
public int handPosition;
private HandController theHC;
// Start is called before the first frame update
void Start()
{
setupCard();
theHC = FindObjectOfType<HandController>();
}
private void OnMouseOver()
{
if (inHand)
{
// 该卡的位置向y(上)移动
MoveToPoint(
theHC.cardPositions[handPosition] + new Vector3(0f, 1f, .5f),
quaternion.identity
);
}
}
private void OnMouseExit()
{
if (inHand)
{
// 该卡的位置向y(上)移动
MoveToPoint(
theHC.cardPositions[handPosition],
theHC.minPos.rotation
);
}
}
}
using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class HandController : MonoBehaviour
{
///
public void SetCardPositionsInHand()
{
///
for (int i = 0; i < heldCards.Count; i++)
{
cardPositions.Add(minPos.position + (distanceBetweenPoints * i));
// heldCards[i].transform.position = cardPositions[i];
// 卡片倾斜, 防止叠在一起穿模
// heldCards[i].transform.rotation = minPos.rotation;
// 移动卡片
heldCards[i].MoveToPoint(cardPositions[i], minPos.rotation);
heldCards[i].inHand = true;
heldCards[i].handPosition = i;
}
}
}
5. Pick & Place
1. Selecting Cards







using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using TMPro;
using Unity.Mathematics;
using UnityEngine.UI;
public class Card : MonoBehaviour
{
///
public bool inHand; // 是否在手上
public int handPosition;
private HandController theHC;
private bool _isSelected;
// 碰撞
private Collider _theCol;
public LayerMask whatIsDesktop;
// Start is called before the first frame update
void Start()
{
setupCard();
theHC = FindObjectOfType<HandController>();
_theCol = GetComponent<Collider>();
}
///
// Update is called once per frame
private void Update()
{
transform.position = Vector3.Lerp(transform.position, _targetPoint, moveSpeed * Time.deltaTime);
transform.rotation =
Quaternion.RotateTowards(transform.rotation, _targetRot, rotateSpeed * Time.deltaTime);
if (_isSelected)
{
// 射线 -> 鼠标
Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition);
RaycastHit hit;
if (Physics.Raycast(ray, out hit, 100f, whatIsDesktop))
{
// hit: 射线命中的对象
// 移动到鼠标所在位置
MoveToPoint(hit.point + new Vector3(0f, 2f, 0f), quaternion.identity);
}
}
}
///
private void OnMouseDown()
{
if (inHand)
{
_isSelected = true;
// 无法再hold浮起
_theCol.enabled = false;
}
}
}
2. Returning A Card To Hand
using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using TMPro;
using Unity.Mathematics;
using UnityEngine.UI;
public class Card : MonoBehaviour
{
///
// Update is called once per frame
private void Update()
{
transform.position = Vector3.Lerp(transform.position, _targetPoint, moveSpeed * Time.deltaTime);
transform.rotation =
Quaternion.RotateTowards(transform.rotation, _targetRot, rotateSpeed * Time.deltaTime);
if (_isSelected)
{
// 射线 -> 鼠标
Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition);
RaycastHit hit;
if (Physics.Raycast(ray, out hit, 100f, whatIsDesktop))
{
///
}
// 右键单击
if (Input.GetMouseButtonDown(1))
{
ReturnToHand();
}
}
}
///
private void ReturnToHand()
{
_isSelected = false;
_theCol.enabled = true;
MoveToPoint(theHC.cardPositions[handPosition], theHC.minPos.rotation);
}
}
3. Creating Card Placement Areas




新建图层



调整位置(从左到右的顺序), 然后重命名

新建脚本

给这五个绑定脚本

复制->改名 enemy card points->拖动->改色(黑)


isPlayerPoint check
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class CardPlacePoint : MonoBehaviour
{
public Card activeCard;
public bool isPlayerPoint;
}
4. Placing A Card On The Board


using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using TMPro;
using Unity.Mathematics;
using UnityEngine.UI;
public class Card : MonoBehaviour
{
///
public LayerMask whatIsDesktop;
public LayerMask whatIsPlacement;
private bool _justPressed;
public CardPlacePoint assignedPlace;
///
// Update is called once per frame
private void Update()
{
transform.position = Vector3.Lerp(transform.position, _targetPoint, moveSpeed * Time.deltaTime);
transform.rotation =
Quaternion.RotateTowards(transform.rotation, _targetRot, rotateSpeed * Time.deltaTime);
if (_isSelected)
{
///
// 右键单击
if (Input.GetMouseButtonDown(1))
{
ReturnToHand();
}
// 左键单击
if (Input.GetMouseButtonDown(0) && _justPressed == false)
{
if (Physics.Raycast(ray, out hit, 100f, whatIsPlacement))
{
// 当前选中的point
CardPlacePoint selectedPoint = hit.collider.GetComponent<CardPlacePoint>();
// 当前选中的point没有卡, 并且是玩家这边的
if (selectedPoint.activeCard == null && selectedPoint.isPlayerPoint)
{
selectedPoint.activeCard = this;
// 记录放在哪个位置
assignedPlace = selectedPoint;
// 放入场地
MoveToPoint(selectedPoint.transform.position, quaternion.identity);
inHand = false;
_isSelected = false;
}
else
{
ReturnToHand();
}
}
else
{
ReturnToHand();
}
}
}
// 隔一段时间没左键 就可以 再次左键 然后触发返回
_justPressed = false;
}
///
private void OnMouseDown()
{
if (inHand)
{
_isSelected = true;
// 无法再hold浮起
_theCol.enabled = false;
// 让card可以浮动
_justPressed = true;
}
}
///
}
5. Removing Card From Hand
using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class HandController : MonoBehaviour
{
///
public void RemoveCardFromHand(Card cardToRemove)
{
if (heldCards[cardToRemove.handPosition] == cardToRemove)
{
heldCards.RemoveAt(cardToRemove.handPosition);
}
else
{
Debug.LogError("Card at position " + cardToRemove.handPosition +
" is not the card being removed from hand");
}
// 重新分配卡片位置
SetCardPositionsInHand();
}
}
using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using TMPro;
using Unity.Mathematics;
using UnityEngine.UI;
public class Card : MonoBehaviour
{
///
// Update is called once per frame
private void Update()
{
transform.position = Vector3.Lerp(transform.position, _targetPoint, moveSpeed * Time.deltaTime);
transform.rotation =
Quaternion.RotateTowards(transform.rotation, _targetRot, rotateSpeed * Time.deltaTime);
if (_isSelected)
{
///
// 左键单击
if (Input.GetMouseButtonDown(0) && _justPressed == false)
{
if (Physics.Raycast(ray, out hit, 100f, whatIsPlacement))
{
// 当前选中的point
CardPlacePoint selectedPoint = hit.collider.GetComponent<CardPlacePoint>();
// 当前选中的point没有卡, 并且是玩家这边的
if (selectedPoint.activeCard == null && selectedPoint.isPlayerPoint)
{
///
_isSelected = false;
// 将卡牌移出数组(list)
theHC.RemoveCardFromHand(this);
}
else
{
ReturnToHand();
}
}
else
{
ReturnToHand();
}
}
}
// 隔一段时间没左键 就可以 再次左键 然后触发返回
_justPressed = false;
}
///
}

打出一张牌, 看到hand controller的held cards的数量-1
6. Mana System
1. Spending Mana

新脚本

empty object
using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class BattleController : MonoBehaviour
{
// 单例
public static BattleController instance;
private void Awake()
{
instance = this;
}
public int startingMana = 4, maxMana = 12;
public int playerMana;
// Start is called before the first frame update
void Start()
{
playerMana = startingMana;
}
// Update is called once per frame
void Update()
{
}
public void SpendPlayerMana(int amountToSpend)
{
playerMana = playerMana - amountToSpend;
// 防止意外情况
if (playerMana < 0)
{
playerMana = 0;
}
}
}
using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using TMPro;
using Unity.Mathematics;
using UnityEngine.UI;
public class Card : MonoBehaviour
{
///
// Update is called once per frame
private void Update()
{
transform.position = Vector3.Lerp(transform.position, _targetPoint, moveSpeed * Time.deltaTime);
transform.rotation =
Quaternion.RotateTowards(transform.rotation, _targetRot, rotateSpeed * Time.deltaTime);
if (_isSelected)
{
///
// 左键单击
if (Input.GetMouseButtonDown(0) && _justPressed == false)
{
if (Physics.Raycast(ray, out hit, 100f, whatIsPlacement))
{
// 当前选中的point
CardPlacePoint selectedPoint = hit.collider.GetComponent<CardPlacePoint>();
// 当前选中的point没有卡, 并且是玩家这边的
if (selectedPoint.activeCard == null && selectedPoint.isPlayerPoint)
{
if (BattleController.instance.playerMana < manaCost)
{
ReturnToHand();
return;
}
///
// 将卡牌移出数组(list)
theHC.RemoveCardFromHand(this);
// 花费mana点
BattleController.instance.SpendPlayerMana(manaCost);
}
else
{
ReturnToHand();
}
}
else
{
ReturnToHand();
}
}
}
// 隔一段时间没左键 就可以 再次左键 然后触发返回
_justPressed = false;
}
///
}
2. Showing Mana On UI











ctrl c v








绑定text
using System;
using System.Collections;
using System.Collections.Generic;
using TMPro;
using UnityEngine;
public class UIController : MonoBehaviour
{
// 单例
public static UIController instance;
private void Awake()
{
instance = this;
}
public TMP_Text playerManaText;
// Start is called before the first frame update
void Start()
{
}
// Update is called once per frame
void Update()
{
}
public void SetPlayerManaText(int manaAmount)
{
playerManaText.text = "Mana: " + manaAmount;
}
}
using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class BattleController : MonoBehaviour
{
///
// Start is called before the first frame update
void Start()
{
playerMana = startingMana;
UIController.instance.SetPlayerManaText(playerMana);
}
///
public void SpendPlayerMana(int amountToSpend)
{
playerMana = playerMana - amountToSpend;
// 防止意外情况
if (playerMana < 0)
{
playerMana = 0;
}
UIController.instance.SetPlayerManaText(playerMana);
}
}
3. Displaying A Low Mana Warning

添加警告文字(mana不足)



auto size->Alignment: Center->Font Assert: SDF outline->向上移动一点






绑定一下

using System;
using System.Collections;
using System.Collections.Generic;
using TMPro;
using UnityEngine;
public class UIController : MonoBehaviour
{
///
public TMP_Text playerManaText; // 左上角的MANA数量 txt
public GameObject manaWarning; // mana不足提示信息
public float manaWarningTime; // mana不足的提示信息的显示持续时间
private float _manaWarningCounter;
// Start is called before the first frame update
void Start()
{
manaWarning.SetActive(false);
}
// Update is called once per frame
void Update()
{
if (_manaWarningCounter > 0)
{
_manaWarningCounter -= Time.deltaTime;
if (_manaWarningCounter <= 0)
{
manaWarning.SetActive(false);
}
}
}
///
public void ShowManaWarning()
{
// ActivatesDeactivates the GameObject,
// depending on the given true or false/ value.
manaWarning.SetActive(true); // 启用manaWarning
_manaWarningCounter = manaWarningTime;
}
}
using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using TMPro;
using Unity.Mathematics;
using UnityEngine.UI;
public class Card : MonoBehaviour
{
///
// Update is called once per frame
private void Update()
{
transform.position = Vector3.Lerp(transform.position, _targetPoint, moveSpeed * Time.deltaTime);
transform.rotation =
Quaternion.RotateTowards(transform.rotation, _targetRot, rotateSpeed * Time.deltaTime);
if (_isSelected)
{
///
// 左键单击
if (Input.GetMouseButtonDown(0) && _justPressed == false)
{
if (Physics.Raycast(ray, out hit, 100f, whatIsPlacement))
{
// 当前选中的point
CardPlacePoint selectedPoint = hit.collider.GetComponent<CardPlacePoint>();
// 当前选中的point没有卡, 并且是玩家这边的
if (selectedPoint.activeCard == null && selectedPoint.isPlayerPoint)
{
if (BattleController.instance.playerMana < manaCost)
{
ReturnToHand();
// 显示mana不足的提示信息
UIController.instance.ShowManaWarning();
return;
}
///
}
else
{
ReturnToHand();
}
}
else
{
ReturnToHand();
}
}
}
// 隔一段时间没左键 就可以 再次左键 然后触发返回
_justPressed = false;
}
///
}
7. Decks
1. Creating A Deck




using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using Random = UnityEngine.Random;
public class DeskController : MonoBehaviour
{
public static DeskController instance;
private void Awake()
{
instance = this;
}
// 我们在unity面板填充的card
public List deskToUse = new List();
private List activeCards = new List();
// Start is called before the first frame update
void Start()
{
SetupDesk();
}
// Update is called once per frame
void Update()
{
}
public void SetupDesk()
{
activeCards.Clear();
// 临时卡堆
List tempDesk = new List();
tempDesk.AddRange(deskToUse);
int iterations = 0; // 防止无限循环
while (tempDesk.Count > 0 && iterations < 500)
{
// 0~3之间的随机数(不含3)
int selected = Random.Range(0, tempDesk.Count);
// 加入卡堆
activeCards.Add(tempDesk[selected]);
tempDesk.RemoveAt(selected);
iterations++;
}
}
}

2. Drawing A Card

创建文件夹Prefabs, 将Card拖入, 此时Card成为预制件, 可以重复使用

删除原来的卡片

拖入项目

复制3份->填入Hand Controller的heldCards

预制件实例的override可以改变预制件的属性, 但是我们一般只是为了复用预制件, 没必要更改

using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using Random = UnityEngine.Random;
public class DeskController : MonoBehaviour
{
///
public Card cardToSpawn;
///
// Update is called once per frame
void Update()
{
// 按下T
if (Input.GetKeyDown(KeyCode.T))
{
DrawCardToHand();
}
}
///
public void DrawCardToHand()
{
// 当前没有手牌, 就填充
if (activeCards.Count == 0)
{
SetupDesk();
}
// 创建cardToSpawn的副本
Card newCard = Instantiate(cardToSpawn, transform.position, transform.rotation);
newCard.cardSO = activeCards[0];
newCard.setupCard();
activeCards.RemoveAt(0);
}
}

记得绑定card -- deskController

按T, 出现很多副本
3. Moving Card To Hand
using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class HandController : MonoBehaviour
{
public static HandController instance;
private void Awake()
{
instance = this;
}
///
public void AddCardToHand(Card cardToAdd)
{
heldCards.Add(cardToAdd);
SetCardPositionsInHand();
}
}
using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using Random = UnityEngine.Random;
public class DeskController : MonoBehaviour
{
///
// Update is called once per frame
void Update()
{
// 按下T
if (Input.GetKeyDown(KeyCode.T))
{
DrawCardToHand();
}
}
///
public void DrawCardToHand()
{
// 当前没有手牌, 就填充
if (activeCards.Count == 0)
{
// 将deskToUse打乱设置到activeCards
SetupDesk();
}
// 创建cardToSpawn的副本
Card newCard = Instantiate(cardToSpawn, transform.position, transform.rotation) as Card;
newCard.cardSO = activeCards[0];
newCard.setupCard();
activeCards.RemoveAt(0);
HandController.instance.AddCardToHand(newCard);
}
}
4. Showing A Physical Deck

deskController(transform)移动一下

拖动CardModel到deskController


改变厚度

scale

rotation


cardmodel->transform->y->-0.18, 这样抽牌看起来才是从上面抽

deskController-transform-y


这样看起来牌会先翻过来再放到手中
5. Click To Draw Cards




font(SDF)

auto size

拖动blue到source image

拖动

TextMeshPro Text (Ul)->extra settings->

绑定点击事件

using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using Random = UnityEngine.Random;
public class DeskController : MonoBehaviour
{
///
// Update is called once per frame
void Update()
{
// 按下T
// if (Input.GetKeyDown(KeyCode.T))
// {
// DrawCardToHand();
// }
}
///
public void DrawCardForMana()
{
if (BattleController.instance.playerMana >= drawCardCost)
{
// 抽卡
DrawCardToHand();
// 消耗mana
BattleController.instance.SpendPlayerMana(drawCardCost);
}
else
{
// mana不足提示信息
UIController.instance.ShowManaWarning();
// 按钮消失
UIController.instance.drawCardButton.SetActive(false);
}
}
}
using System;
using System.Collections;
using System.Collections.Generic;
using TMPro;
using UnityEngine;
public class UIController : MonoBehaviour
{
///
public void DrawCard()
{
DeskController.instance.DrawCardForMana();
}
}

8. Turns System
1. Setting Up The Turn Order



遇到bug, 查了下

Window > General > Console打开输出控制台
public class BattleController : MonoBehaviour
{
///
public enum TurnOrder
{
playerActive,
playerCardAttacks,
enemyActive,
enemyCardAttacks
}
public TurnOrder currentPhase;
// Update is called once per frame
void Update()
{
if (Input.GetKeyDown(KeyCode.T))
{
AdvanceTurn();
}
}
///
public void AdvanceTurn()
{
currentPhase++;
if ((int)currentPhase >= System.Enum.GetValues(typeof(TurnOrder)).Length)
{
Debug.Log("0");
currentPhase = 0;
}
switch (currentPhase)
{
case TurnOrder.playerActive:
break;
case TurnOrder.playerCardAttacks:
Debug.Log("2");
AdvanceTurn();
break;
case TurnOrder.enemyActive:
Debug.Log("3");
AdvanceTurn();
break;
case TurnOrder.enemyCardAttacks:
Debug.Log("4");
AdvanceTurn();
break;
}
}
}
2. Limiting Actions To The Player’s Turn
编写玩家回合才能出牌的逻辑
public class BattleController : MonoBehaviour
{
///
public void AdvanceTurn()
{
///
switch (currentPhase)
{
case TurnOrder.playerActive:
break;
case TurnOrder.playerCardAttacks:
Debug.Log("2");
// AdvanceTurn();
break;
///
}
}
}
public class Card : MonoBehaviour
{
///
// Update is called once per frame
private void Update()
{
transform.position = Vector3.Lerp(transform.position, _targetPoint, moveSpeed * Time.deltaTime);
transform.rotation =
Quaternion.RotateTowards(transform.rotation, _targetRot, rotateSpeed * Time.deltaTime);
if (_isSelected)
{
///
// 左键单击
if (Input.GetMouseButtonDown(0) && _justPressed == false)
{
if (Physics.Raycast(ray, out hit, 100f, whatIsPlacement) &&
// 玩家回合才能出牌
BattleController.instance.currentPhase == BattleController.TurnOrder.playerActive)
{
///
}
else
{
ReturnToHand();
}
}
}
// 隔一段时间没左键 就可以 再次左键 然后触发返回
_justPressed = false;
}
///
private void OnMouseDown()
{
// 玩家的回合才能出牌
if (inHand && BattleController.instance.currentPhase == BattleController.TurnOrder.playerActive)
{
///
}
}
}
3. Ending The Player’s Turn
结束回合时禁用两个按钮

复制一个结束回合的button





记得绑定按钮
public class BattleController : MonoBehaviour
{
///
// 回合前进(玩家出牌->玩家卡牌攻击(auto)->敌人出牌->敌人卡牌攻击(auto)
public void AdvanceTurn()
{
///
switch (currentPhase)
{
case TurnOrder.playerActive:
// 启用: 结束回合btn 和 取牌btn
UIController.instance.endTurnButton.SetActive(true);
UIController.instance.drawCardButton.SetActive(true);
break;
///
}
}
public void EndPlayerTurn()
{
// 禁用 结束回合 按钮
UIController.instance.endTurnButton.SetActive(false);
// 禁用 取牌 按钮
UIController.instance.drawCardButton.SetActive(false);
AdvanceTurn();
}
}
public class UIController : MonoBehaviour
{
///
public void EndPlayerTurn()
{
BattleController.instance.EndPlayerTurn();
}
}
4. Refilling The Mana Pool
public class DeskController : MonoBehaviour
{
///
public float waitBetweenDrawingCards = .25f;
public void DrawMultipleCards(int amountToDraw)
{
StartCoroutine(DrawMultipleCo(amountToDraw));
}
IEnumerator DrawMultipleCo(int amountToDraw)
{
for (int i = 0; i < amountToDraw; i++)
{
DrawCardToHand();
yield return new WaitForSeconds(waitBetweenDrawingCards);
}
}
}
public class BattleController : MonoBehaviour
{
///
public int startingCardsAmount = 5;
// Start is called before the first frame update
void Start()
{
// playerMana = startingMana;
// UIController.instance.SetPlayerManaText(playerMana);
FillPlayerMana();
// 初始化: 拿几张卡到手牌
DeskController.instance.DrawMultipleCards(startingCardsAmount);
}
// 回复玩家的mana
public void FillPlayerMana()
{
playerMana = startingMana;
UIController.instance.SetPlayerManaText(playerMana);
}
// 回合前进(玩家出牌->玩家卡牌攻击(auto)->敌人出牌->敌人卡牌攻击(auto)
public void AdvanceTurn()
{
///
switch (currentPhase)
{
case TurnOrder.playerActive:
// 启用: 结束回合btn 和 取牌btn
UIController.instance.endTurnButton.SetActive(true);
UIController.instance.drawCardButton.SetActive(true);
// 重新填充mana
FillPlayerMana();
break;
case TurnOrder.playerCardAttacks:
Debug.Log("2");
AdvanceTurn();
break;
///
}
}
}
5. Mana Pool Growth
public class BattleController : MonoBehaviour
{
///
// 玩家当前最大mana数, 每回合+1
private int _currentPlayerMaxMana;
// Start is called before the first frame update
void Start()
{
// playerMana = startingMana;
// UIController.instance.SetPlayerManaText(playerMana);
_currentPlayerMaxMana = startingMana;
FillPlayerMana();
// 初始化: 拿几张卡到手牌
DeskController.instance.DrawMultipleCards(startingCardsAmount);
}
///
// 回复玩家的mana
public void FillPlayerMana()
{
// playerMana = startingMana;
playerMana = _currentPlayerMaxMana;
UIController.instance.SetPlayerManaText(playerMana);
}
// 回合前进(玩家出牌->玩家卡牌攻击(auto)->敌人出牌->敌人卡牌攻击(auto)
public void AdvanceTurn()
{
///
switch (currentPhase)
{
case TurnOrder.playerActive:
// 启用: 结束回合btn 和 取牌btn
UIController.instance.endTurnButton.SetActive(true);
UIController.instance.drawCardButton.SetActive(true);
// 回合开始, 玩家mana上限+1
if (_currentPlayerMaxMana < maxMana)
{
_currentPlayerMaxMana++;
}
// 重新填充mana
FillPlayerMana();
break;
///
}
}
}
6. Drawing Cards Each Turn
public class BattleController : MonoBehaviour
{
///
// 每回合开始抽多少张
public int cardsToDrawPerTurn = 2;
// Start is called before the first frame update
void Start()
{
// 可能unity里的设值会覆盖(默认0, 可以自己改为2, 就不用下面这行), 这里手动设置为2
cardsToDrawPerTurn = 2;
///
}
public void AdvanceTurn()
{
///
switch (currentPhase)
{
case TurnOrder.playerActive:
///
// 重新填充mana
FillPlayerMana();
// 回合开始, 抽卡
DeskController.instance.DrawMultipleCards(cardsToDrawPerTurn);
break;
///
}
}
}
9. Doing Damage

复制一张牌到敌方区域

他将卡牌转半圈, 卡牌信息又转回来, 但是我觉得没必要

作为新的预制件

enemy card 拖动到prefabs, 点击第二个创建为旧预制件的变体而不是覆盖


public class Card : MonoBehaviour
{
void Start()
{
// 如果没有这个, 我们刚才创建的enemy卡片会移动到player的point
if(_targetPoint == Vector3.zero)
{
_targetPoint = transform.position;
_targetRot = transform.rotation;
}
setupCard();
theHC = FindObjectOfType<HandController>();
_theCol = GetComponent<Collider>();
}
}
…