Unity肉鸽卡牌开发记录05-策略模式重构检索功能与修复弃牌Bug
Unity肉鸽卡牌开发记录05-策略模式重构检索功能与修复弃牌Bug
弃牌Bug
-
Bug描述
当玩家弃牌时,若玩家手上有多张同名牌且玩家选择丢弃靠右的同名牌时,但系统会丢弃从左往右的第一张同名牌
-
Bug出现频率
每次都出现
-
导致Bug的原因
程序中使用
List<Card>存储玩家手牌,虽然能够管理玩家手牌的类型,但无法管理顺序。当玩家弃牌时,系统会遍历HandZone下的所有手牌子物体,此时会先找到最左边的同名牌(Card类相同)并丢弃 -
Bug修复
见下方
重构手牌列表handList
-
HandList改为存储手牌GameObject,在之后的玩家弃牌中遍历handList,找到与玩家所选手牌GameObject相同的一项,执行丢弃
1
private List<GameObject> handList = new List<GameObject>();
-
CardInHand类加入handIndex字段,存储手牌在手中的位置,这与之后修复手牌展示Bug有关(肉鸽卡牌开发记录06)
1
public int handIndex = 0; //在手牌的位置
-
加入手牌相关函数重构(以抓牌为例)
在抓取一张牌后,设置其在手牌中的顺序,然后加入handList
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19public void DrawACard()
{
//如果牌库还有牌
if(deckList.Count>0)
{
//取出牌库顶的牌并加入手牌列表
Card card = deckList[0];
deckList.RemoveAt(0);
GameObject cardObjTemp = CardRevealTool.GetInstance().GenerateHandCard(card);
cardObjTemp.GetComponent<CardInHand>().handIndex = handList.Count; //必然排在最后
handList.Add(cardObjTemp); //将Card和GameObject加入List
CardRevealTool.GetInstance().ListHand();//先排列手牌
cardObjTemp.SetActive(true); //再将卡牌显示
}
else
{
Debug.Log("牌库已经没有牌了!");
}
} -
ListHand排列手牌方法重构
先对List中现有的手牌更新handIndex(使排列手牌不再依赖于HandZone子物体的顺序),之后按算法执行排列
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40public void ListHand()
{
//更新手牌下标
int count = 0;
foreach (GameObject item in CardAreaManager.GetInstance().HandList)
{
item.GetComponent<CardInHand>().handIndex = count;
count++;
}
Transform handZoneTransform = handZone.transform;
int childCount = handZoneTransform.childCount;
if (childCount <= 0) return; //没有手牌直接返回
//根据手牌大小获取牌张间隔
float cardWith = handZoneTransform.GetChild(0).GetComponent<RectTransform>().rect.width;
float space = cardWith / 25f * 16f;
float halfSpace = space / 2f;
//计算第一张牌的位置
float startXAxis = 0f;
if (childCount % 2 == 0)
{
startXAxis = -(childCount / 2) * space + halfSpace;
}
else
{
startXAxis = -(childCount - 1) / 2 * space;
}
//从第一张牌开始,按间隔排列手牌
Transform hand;
for (int i = 0; i < childCount; i++)
{
//横向排列手牌
hand = handZoneTransform.GetChild(i);
//Test
int index = hand.GetComponent<CardInHand>().handIndex;
hand.localPosition = new Vector3(startXAxis + index * space, 0f, 0f);
}
}
策略模式重构检索功能
重构手牌列表后,发现检索功能既要对Card进行操作,也要对GameObject进行操作,放回牌库的检索还要对DeckPosition进行操作,各种情况写在同一个SelectCardArea类里十分繁琐
想到用策略模式重构检索功能。
相关代码如下:
-
SelectCardArea
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40public class SelectCardArea : MonoBehaviour
{
//卡牌展示列表显示|隐藏
private bool isShow = true;
public SelectCardStrategy strategy;
public bool OnCardSelected(Card selectedCard,GameObject cardObj)
{
return strategy.OnCardHasSelected(selectedCard, cardObj);
}
public void OnCardCanceled(Card cancelCard,GameObject cardObj)
{
strategy.OnCardHasCanceled(cancelCard,cardObj);
}
public void OnConfirmed()
{
strategy.OnConfirmedSelected();
gameObject.SetActive(false);
}
/// <summary>
/// 显示|隐藏卡牌列表
/// </summary>
public void ChangeListAreaVisible()
{
//根据isShow改变显示|隐藏状态
if(isShow)
{
transform.Find("ListCard").gameObject.SetActive(false);
isShow = false;
}
else
{
transform.Find("ListCard").gameObject.SetActive(true);
isShow = true;
}
}
} -
SelectCardStrategy
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17public abstract class SelectCardStrategy
{
/// <summary>
/// 当卡牌被选中时
/// </summary>
public abstract bool OnCardHasSelected(Card selectedCard, GameObject cardObj);
/// <summary>
/// 当卡牌取消选中时
/// </summary>
public abstract void OnCardHasCanceled(Card cancelCard, GameObject cardObj);
/// <summary>
/// 当确认选择时
/// </summary>
public abstract void OnConfirmedSelected();
} -
SelectHandCard(其他策略类略)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45public class SelectHandCard : SelectCardStrategy
{
private Action<GameObject> selectedAction;
private int selectNumber = 0;
private List<GameObject> selectedCardList;
public SelectHandCard(Action<GameObject> action,int selectNum)
{
selectedAction = action;
selectNumber = selectNum;
selectedCardList = new List<GameObject>();
}
public override void OnCardHasCanceled(Card cancelCard, GameObject cardObj)
{
//从List移除
selectedCardList.Remove(cardObj);
}
public override bool OnCardHasSelected(Card selectedCard, GameObject cardObj)
{
//如果已经选满了,则返回false
if (selectedCardList.Count >= selectNumber) return false;
else
{
//将卡牌添加进List,返回true
selectedCardList.Add(cardObj);
return true;
}
}
public override void OnConfirmedSelected()
{
//如果还没选满,不管它,直接返回
if (selectedCardList.Count != selectNumber) return;
else
{
//否则对于List中的每一张卡牌,触发Action
foreach (GameObject item in selectedCardList)
{
selectedAction?.Invoke(item);
}
}
}
} -
检索卡牌方法
设置好SelectCardArea相关参数
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35/// <summary>
/// 检视牌库中所有牌并加入手牌
/// </summary>
public void showDeckAndPutToHand(int putToHandNum)
{
//设置好SelectCardArea相关参数
//规整化putToHandNum:与牌库中卡牌数量取最小
putToHandNum = Mathf.Min(CardAreaManager.GetInstance().GetDeckNum(), putToHandNum);
SelectCardArea selectCardArea = listComponent.GetComponent<SelectCardArea>();
selectCardArea.strategy = new SelectCardCommon(CardAreaManager.GetInstance().PutCardFromDeckToHand,putToHandNum);
//先清除content子物体
int childCount = content.transform.childCount;
for (int i = 0; i < childCount; i++)
{
DestroyImmediate(content.transform.GetChild(0).gameObject);
}
//获取检索到的卡牌
List<Card> cards = CardAreaManager.GetInstance().GetDeckList();
if (cards.Count <= 0)
{
Debug.Log("牌库中没有此类型的牌!");
}
else
{
foreach (Card item in cards) //展示卡牌
{
//实例化预制体,设置好父物体
GameObject temp = Instantiate(GenerateShowCard(item), Vector3.one, Quaternion.identity, content.transform);
temp.SetActive(true);
}
listComponent.SetActive(true);
}
}
本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来源 星辰微光的博客!



