三. 卡牌使用效果,塞貝爾曲線引導(dǎo)箭頭繪制
1. 攻擊類型卡牌
①拖拽超過一定高度之后卡牌會(huì)移動(dòng)到手牌中心位置
②出現(xiàn)攻擊引導(dǎo)箭頭 (塞貝爾曲線)
③成功指向目標(biāo)怪物后打出
2. 技能能力類型卡牌
①可自由拖動(dòng)
②脫離手牌高度后打出
核心代碼展示
這里只展示此效果核心代碼內(nèi)容,重復(fù)代碼不做贅述,上期(二.鼠標(biāo)指向卡牌時(shí),卡牌強(qiáng)調(diào)動(dòng)畫)完整版?zhèn)魉烷T
1.繪制貝塞爾曲線引導(dǎo)箭頭(此腳本可作為獨(dú)立通用腳本使用,制作其他貝塞爾曲線也可引用)
完整版本代碼
public class ArrowEffectManager : MonoBehaviour
{
public GameObject reticleBlock;
public GameObject reticleArrow;
public int MaxCount = 18;
public Vector3 startPoint; // 起始點(diǎn)
private Vector3 controlPoint1; // 控制點(diǎn)1
private Vector3 controlPoint2; // 控制點(diǎn)2
private Vector3 endPoint; // 結(jié)束點(diǎn)
private List<GameObject> ArrowItemList;
private List<SpriteRenderer> RendererItemList;
private Animator Arrow_anim;
private bool isSelect;
public bool IsSelect
{
get => isSelect;
set
{
if (isSelect != value)
{
isSelect = value;
if (value==true)
{
PlayAnim();
}
}
}
}
private void Awake()
{ InitData();
}
private void InitData()
{
ArrowItemList = new List<GameObject>();
RendererItemList = new List<SpriteRenderer>();
for (int i = 0; i < MaxCount; i++)
{
GameObject Arrow = (i == MaxCount - 1) ? reticleArrow : reticleBlock;
GameObject temp = Instantiate(Arrow, this.transform);
if (i == MaxCount - 1)
{
Arrow_anim = temp.GetComponent<Animator>();
}
ArrowItemList.Add(temp);
SpriteRenderer re = temp.transform.GetChild(0).GetComponent<SpriteRenderer>();
if (re != null)
{
RendererItemList.Add(re);
}
}
}
public void Update()
{
Move();
DrawBezierCurve();
}
private void DrawBezierCurve()
{
for (int i = 0; i < ArrowItemList.Count; i++)
{
float t = i / (float) (ArrowItemList.Count - 1); // 參數(shù) t 在 0 到 1 之間
Vector3 position =CalculateBezierPoint(t, startPoint, controlPoint1, controlPoint2, endPoint);
ArrowItemList[i].gameObject.SetActive(i != ArrowItemList.Count - 2);
ArrowItemList[i].transform.position = position;
ArrowItemList[i].transform.localScale = Vector3.one * (t / 2f) + Vector3.one * 0.3f;
if (i > 0)
{
float SignedAngle = Vector2.SignedAngle(Vector2.up,
ArrowItemList[i].transform.position - ArrowItemList[i - 1].transform.position);
Vector3 euler = new Vector3(0, 0, SignedAngle);
ArrowItemList[i].transform.rotation = Quaternion.Euler(euler);
}
}
}
private void OnDrawGizmos()
{
// Gizmos.color = Color.yellow;
// Gizmos.DrawLine(startPoint, controlPoint1);
// Gizmos.DrawSphere(startPoint, 0.1f);
// Gizmos.DrawSphere(controlPoint1, 0.1f);
// Gizmos.DrawLine(endPoint, controlPoint2);
// Gizmos.DrawSphere(endPoint, 0.1f);
// Gizmos.DrawSphere(controlPoint2, 0.1f);
}
public void Move()
{
Vector3 mousePosition = Input.mousePosition; // 獲取鼠標(biāo)位置
mousePosition.z = Camera.main.nearClipPlane; // 設(shè)置z坐標(biāo)為攝像機(jī)近裁剪平面的位置
Vector3 worldPosition = Camera.main.ScreenToWorldPoint(mousePosition);
worldPosition.z = 2f;
endPoint = worldPosition;
controlPoint1 = (Vector2) startPoint + (worldPosition - startPoint) * new Vector2(-0.28f, 0.8f);
controlPoint2 = (Vector2) startPoint + (worldPosition - startPoint) * new Vector2(0.12f, 1.4f);
controlPoint1.z = startPoint.z;
controlPoint2.z = startPoint.z;
}
/// <summary>
/// 設(shè)置起始位置
/// </summary>
public void SetStartPos(Vector3 pos)
{
startPoint = pos;
}
/// <summary>
/// 設(shè)置顏色
/// </summary>
public void SetColor(Color color)
{
if (RendererItemList == null)
{
return;
}
for (int i = 0; i < RendererItemList.Count; i++)
{
RendererItemList[i].color = color;
}
}
/// <summary>
/// 設(shè)置顏色
/// </summary>
/// <param name="isSelect"></param>
public void SetColor(bool isSelect)
{
IsSelect = isSelect;
if (isSelect)
{
SetColor(Color.red);
}
else
{
SetColor(Color.white);
}
}
private void PlayAnim()
{
if (Arrow_anim == null)
{
return;
}
Arrow_anim.SetTrigger("select");
}
/// <summary>
/// 獲取貝塞爾曲線中間點(diǎn)
/// </summary>
/// <param name="t">參數(shù) t 在 0 到 1 之間</param>
/// <param name="startPoint">起始點(diǎn)</param>
/// <param name="controlPoint1">起點(diǎn)控制點(diǎn)</param>
/// <param name="controlPoint2">終點(diǎn)控制點(diǎn)</param>
/// <param name="endPoint">終點(diǎn)</param>
/// <returns></returns>
public static Vector3 CalculateBezierPoint(float t, Vector3 startPoint, Vector3 controlPoint1, Vector3 controlPoint2, Vector3 endPoint)
{
float u = 1 - t;
float tt = t * t;
float uu = u * u;
float uuu = uu * u;
float ttt = tt * t;
Vector3 point = uuu * startPoint; // (1-t)^3 * P0
point += 3 * uu * t * controlPoint1; // 3 * (1-t)^2 * t * P1
point += 3 * u * tt * controlPoint2; // 3 * (1-t) * t^2 * P2
point += ttt * endPoint; // t^3 * P3
return point;
}}
出牌整體效果實(shí)現(xiàn):
1. 選中卡牌拖拽后卡牌本體暫時(shí)隱藏(gameObject.SetActive(false))
2. 展示此卡牌的克隆體,克隆體鼠標(biāo)跟隨
3. 非攻擊類型卡牌:拖拽中無位置限制,拖拽結(jié)束超過基礎(chǔ)高度視為打出,出牌成功后隱藏克隆卡牌,銷毀卡牌本體,執(zhí)行使用成功指令。拖拽結(jié)束如果沒有超過基礎(chǔ)高度,隱藏克隆卡牌,卡牌本體隱藏狀態(tài)解除
4. 攻擊類型卡牌:拖拽超過基礎(chǔ)高度克隆體位移至手牌居中位置,生成塞貝爾曲線引導(dǎo)箭頭,箭頭指向目標(biāo)后,箭頭變色,播放選中動(dòng)畫。如果選中目標(biāo)隱藏克隆卡牌,銷毀卡牌本體,執(zhí)行使用成功指令。否則,隱藏克隆卡牌,卡牌本體隱藏狀態(tài)解除
【核心代碼片段】
//class CardManager
/// <summary>
/// 設(shè)置卡牌使用特效
/// </summary>
public void CardUseEffect()
{
//如果當(dāng)前沒有選中卡牌,隱藏所有效果,并返回
if (nowTaskItem == null)
{
temporaryCard.gameObject.SetActive(false);
lineEffect.gameObject.SetActive(false);
return;
}
temporaryCard.gameObject.SetActive(true);
// 獲取鼠標(biāo)位置
Vector3 mousePosition = Input.mousePosition;
// 設(shè)置z坐標(biāo)為攝像機(jī)近裁剪平面的位置
mousePosition.z = Camera.main.nearClipPlane;
//將屏幕上的鼠標(biāo)位置轉(zhuǎn)換為世界坐標(biāo)系中的位置。
Vector3 worldPosition = Camera.main.ScreenToWorldPoint(mousePosition);
worldPosition.z = 5f;
Vector3 centPos = new Vector3(0, -2.9f, 4);
bool isWaitAttack = false;
if (nowTaskItem.attackType == ECardAttackType.Single)
{
if (worldPosition.y > -2.4f)
{
//攻擊類型卡牌,拖拽超過基礎(chǔ)高度標(biāo)記為等待攻擊
isWaitAttack = true;
}
}
//如果是等待攻擊狀態(tài)將克隆卡牌移動(dòng)至centPos否則跟隨鼠標(biāo)移動(dòng)
temporaryCard.gameObject.transform.position = Vector3.Lerp(temporaryCard.gameObject.transform.position,
isWaitAttack ? centPos : worldPosition, Time.deltaTime * 15);
//設(shè)置塞貝爾曲線起始點(diǎn)
lineEffect.SetStartPos(isWaitAttack ? centPos : worldPosition);
//設(shè)置攻擊引導(dǎo)箭頭顏色
lineEffect.SetColor(nowSelectPlayer != null);
//攻擊引導(dǎo)箭頭顯示隱藏控制
lineEffect.gameObject.SetActive(isWaitAttack);
}
新增枚舉文章來源:http://www.zghlxwxcb.cn/news/detail-848414.html
public enum ECardAttackType
{
Skill,
Single,
Power
}
CardManager 當(dāng)前階段完整版本代碼
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class CardManager : MonoBehaviour
{
/// <summary>
/// 卡牌起始位置
/// </summary>
public Vector3 rootPos = new Vector3(0, -33.5f, 20);
/// <summary>
/// 卡牌對象
/// </summary>
public GameObject cardItem;
/// <summary>
/// 扇形半徑
/// </summary>
public float size = 30f;
/// <summary>
/// 卡牌出現(xiàn)最大位置
/// </summary>
private float minPos = 1.415f;
/// <summary>
/// 卡牌出現(xiàn)最小位置
/// </summary>
private float maxPos = 1.73f;
/// <summary>
/// 手牌列表
/// </summary>
private List<CardItem> cardList;
/// <summary>
/// 偶數(shù)手牌位置列表
/// </summary>
private List<float> rotPos_EvenNumber;
/// <summary>
/// 奇數(shù)手牌位置列表
/// </summary>
private List<float> rotPos_OddNumber;
/// <summary>
/// 最大手牌數(shù)量
/// </summary>
private int CardMaxCount = 8;
private CardItem nowSelectItem;
/// <summary>
/// 當(dāng)前鼠標(biāo)指向的卡牌
/// </summary>
public CardItem NowSelectItem
{
get => nowSelectItem;
set
{
if (nowSelectItem != value)
{
nowSelectItem = value;
RefreshSelectItem(nowSelectItem);
}
}
}
public GameObject temporaryCard;
/// <summary>
/// 當(dāng)前點(diǎn)擊選中的卡牌
/// </summary>
private CardItem nowTaskItem;
public ArrowEffectManager lineEffect;
/// <summary>
/// 當(dāng)前選中敵人
/// </summary>
private GameObject nowSelectPlayer;
private Vector3 temporaryCardStartPos;
void Start()
{
InitCard();
}
/// <summary>
/// 數(shù)據(jù)初始化
/// </summary>
public void InitCard()
{
int EvenNumber = CardMaxCount % 2 == 0 ? CardMaxCount : CardMaxCount - 1;
int OddNumber = CardMaxCount % 2 == 0 ? CardMaxCount-1 : CardMaxCount;
rotPos_EvenNumber=InitRotPos(EvenNumber);
rotPos_OddNumber=InitRotPos(OddNumber);
}
/// <summary>
/// 初始化位置
/// </summary>
/// <param name="count"></param>
/// <param name="interval"></param>
/// <returns></returns>
public List<float> InitRotPos(int count)
{
List<float> rotPos = new List<float>();
float interval = (maxPos - minPos) / count;
for (int i = 0; i < count; i++)
{
float nowPos = maxPos - interval * i;
rotPos.Add(nowPos);
}
return rotPos;
}
// Update is called once per frame
void Update()
{
TaskItemDetection();
RefereshCard();
SelectItemDetection();
CardUseEffect();
}
/// <summary>
/// 添加卡牌
/// </summary>
public void AddCard()
{
if (cardList == null)
{
cardList = new List<CardItem>();
}
if (cardList.Count >= CardMaxCount)
{
Debug.Log("手牌數(shù)量上限");
return;
}
GameObject item = Instantiate(cardItem, this.transform);
CardItem text = item.GetComponent<CardItem>();
text.RefreshData(rootPos, 0, 0, 0);
cardList.Add(text);
}
/// <summary>
/// 手牌狀態(tài)刷新
/// </summary>
public void RefereshCard()
{
if (cardList == null)
{
return;
}
int TaskIndex = 0;
//得到當(dāng)前選中的卡牌下標(biāo)
bool isTaskIndex = GetTaskIndex(out TaskIndex);
List<float> rotPos;
int strtCount = 0;
float interval;
if (cardList.Count%2==0)
{
rotPos = rotPos_EvenNumber;
strtCount= rotPos_EvenNumber.Count / 2 - cardList.Count / 2;
}
else
{
rotPos = rotPos_OddNumber;
strtCount= (rotPos_OddNumber.Count+1) / 2 - (cardList.Count+1) / 2;
}
for (int i = 0; i < cardList.Count; i++)
{
float shifting = 0;
float indexNowNumber = 0.0042f;
float Difference = TaskIndex - i;
float absDifference = Difference > 0 ? 4 - Difference : 4 + Difference;
if (absDifference < 0)
{
absDifference = 0;
}
if (isTaskIndex && TaskIndex != i)
{
shifting = (TaskIndex > i) ? indexNowNumber * absDifference : -indexNowNumber * absDifference;
}
cardList[i].RefreshData(rootPos, rotPos[strtCount+i] + shifting, size, i);
}
}
/// <summary>
/// 銷毀卡牌
/// </summary>
public void RemoveCard()
{
if (cardList == null)
{
return;
}
CardItem item = cardList[cardList.Count - 1];
cardList.Remove(item);
Destroy(item.gameObject);
}
/// <summary>
/// 銷毀卡牌
/// </summary>
/// <param name="item"></param>
public void RemoveCard(CardItem item)
{
if (cardList == null)
{
return;
}
cardList.Remove(item);
Destroy(item.gameObject);
}
private Vector3 oldmousePosition;
/// <summary>
/// 玩家操作檢測
/// </summary>
public void TaskItemDetection()
{
if (Input.GetKeyDown(KeyCode.A))
{
AddCard();
}
if (Input.GetMouseButtonDown(0))
{
if (nowTaskItem != null)
{
nowTaskItem.gameObject.SetActive(true);
nowTaskItem = null;
}
temporaryCardStartPos = temporaryCard.transform.position;
SelectCard();
}
if (Input.GetMouseButton(0))
{
SelectEnemy();
}
if (Input.GetMouseButtonUp(0))
{
if (nowTaskItem != null)
{
if (IsDestoryCard())
{
RemoveCard(nowTaskItem);
}
else
{
nowTaskItem.gameObject.SetActive(true);
nowTaskItem = null;
}
nowSelectPlayer = null;
}
}
}
/// <summary>
/// 是否需要銷毀卡牌
/// </summary>
/// <returns></returns>
public bool IsDestoryCard()
{
if (nowSelectPlayer != null)
{
return true;
}
float dis = temporaryCardStartPos.y - temporaryCard.transform.position.y;
float absDis = dis > 0 ? dis : -dis;
return absDis > 2.6f;
}
/// <summary>
/// 選中卡牌檢測
/// </summary>
public void SelectItemDetection()
{
if (oldmousePosition == Input.mousePosition)
{
return;
}
oldmousePosition = Input.mousePosition;
// 從鼠標(biāo)位置創(chuàng)建一條射線
Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition);
RaycastHit hit;
LayerMask layerMask = LayerMask.GetMask("Card");
// 檢測射線是否與物體相交
if (Physics.Raycast(ray, out hit, 1000, layerMask))
{
if (hit.collider.gameObject != null)
{
NowSelectItem = hit.collider.gameObject.GetComponent<CardItem>();
return;
}
}
NowSelectItem = null;
}
/// <summary>
/// 刷新當(dāng)前選中的卡牌
/// </summary>
/// <param name="selectItem"></param>
public void RefreshSelectItem(CardItem selectItem)
{
if (cardList == null)
{
return;
}
for (int i = 0; i < cardList.Count; i++)
{
cardList[i].isSelect = cardList[i] == selectItem;
if (cardList[i] == selectItem)
{
temporaryCard.gameObject.transform.position = cardList[i].gameObject.transform.position;
}
}
}
/// <summary>
/// 得到當(dāng)前選中的卡牌下標(biāo)
/// </summary>
/// <param name="index"></param>
/// <returns></returns>
public bool GetTaskIndex(out int index)
{
index = 0;
for (int i = 0; i < cardList.Count; i++)
{
if (cardList[i].isSelect)
{
index = i;
return true;
}
}
return false;
}
/// <summary>
/// 選中卡牌
/// </summary>
public void SelectCard()
{
// 從鼠標(biāo)位置創(chuàng)建一條射線
Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition);
RaycastHit hit;
LayerMask layerMask = LayerMask.GetMask("Card");
// 檢測射線是否與物體相交
if (Physics.Raycast(ray, out hit, 1000, layerMask))
{
if (hit.collider.gameObject != null)
{
nowTaskItem = hit.collider.gameObject.GetComponent<CardItem>();
nowTaskItem.gameObject.SetActive(false);
}
}
}
/// <summary>
/// 獲取當(dāng)前選中的對象
/// </summary>
/// <param name="layerName"></param>
/// <returns></returns>
public GameObject GetSelectPlayer(string layerName)
{
Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition);
RaycastHit hit;
LayerMask layerMask = LayerMask.GetMask(layerName);
// 檢測射線是否與物體相交
if (Physics.Raycast(ray, out hit, 1000, layerMask))
{
if (hit.collider.gameObject != null)
{
return hit.collider.gameObject;
}
}
return null;
}
/// <summary>
/// 選中對象
/// </summary>
public void SelectEnemy()
{
if (nowTaskItem == null)
{
nowSelectPlayer = null;
return;
}
ECardAttackType etype = nowTaskItem.attackType;
switch (etype)
{
case ECardAttackType.Power:
break;
case ECardAttackType.Single:
nowSelectPlayer = GetSelectPlayer("Enemy");
break;
case ECardAttackType.Skill:
break;
}
}
/// <summary>
/// 設(shè)置卡牌使用特效
/// </summary>
public void CardUseEffect()
{
//如果當(dāng)前沒有選中卡牌,隱藏所有效果,并返回
if (nowTaskItem == null)
{
temporaryCard.gameObject.SetActive(false);
lineEffect.gameObject.SetActive(false);
return;
}
temporaryCard.gameObject.SetActive(true);
// 獲取鼠標(biāo)位置
Vector3 mousePosition = Input.mousePosition;
// 設(shè)置z坐標(biāo)為攝像機(jī)近裁剪平面的位置
mousePosition.z = Camera.main.nearClipPlane;
//將屏幕上的鼠標(biāo)位置轉(zhuǎn)換為世界坐標(biāo)系中的位置。
Vector3 worldPosition = Camera.main.ScreenToWorldPoint(mousePosition);
worldPosition.z = 5f;
Vector3 centPos = new Vector3(0, -2.9f, 4);
bool isWaitAttack = false;
if (nowTaskItem.attackType == ECardAttackType.Single)
{
if (worldPosition.y > -2.4f)
{
//攻擊類型卡牌,拖拽超過基礎(chǔ)高度標(biāo)記為等待攻擊
isWaitAttack = true;
}
}
//如果是等待攻擊狀態(tài)將克隆卡牌移動(dòng)至centPos否則跟隨鼠標(biāo)移動(dòng)
temporaryCard.gameObject.transform.position = Vector3.Lerp(temporaryCard.gameObject.transform.position,
isWaitAttack ? centPos : worldPosition, Time.deltaTime * 15);
//設(shè)置塞貝爾曲線起始點(diǎn)
lineEffect.SetStartPos(isWaitAttack ? centPos : worldPosition);
//設(shè)置攻擊引導(dǎo)箭頭顏色
lineEffect.SetColor(nowSelectPlayer != null);
//攻擊引導(dǎo)箭頭顯示隱藏控制
lineEffect.gameObject.SetActive(isWaitAttack);
}
}
附件箭頭素材文章來源地址http://www.zghlxwxcb.cn/news/detail-848414.html
到了這里,關(guān)于Unity實(shí)現(xiàn)殺戮尖塔出牌效果( 三. 貝塞爾曲線引導(dǎo)箭頭繪制,卡牌使用效果制作)的文章就介紹完了。如果您還想了解更多內(nèi)容,請?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!