一、元素反应
-
原神中共有七种元素,分别是水、火、冰、岩、风、雷、草。这七种元素能互相作用
- Demo下载:Download
/ | 火 | 水 | 雷 | 冰 | 草 | 岩 | 风 | 绽放 | 原激化 |
---|---|---|---|---|---|---|---|---|---|
火 | / | 蒸发 | 超载 | 融化 | 燃烧 | 结晶 | 扩散 | 烈绽放 | / |
水 | 蒸发 | / | 感电 | 冻结/碎冰 | 绽放 | 结晶 | 扩散 | / | / |
雷 | 超载 | 感电 | / | 超导 | 原激化 | 结晶 | 扩散 | 超绽放 | 超激化 |
冰 | 融化 | 冻结/碎冰 | 超导 | / | / | 结晶 | 扩散 | / | / |
草 | 燃烧 | 绽放 | 原激化 | / | / | / | / | / | 蔓激化 |
岩 | 结晶 | 结晶 | 结晶 | 结晶 | / | / | / | / | / |
风 | 扩散 | 扩散 | 扩散 | 扩散 | / | / | / | / | / |
二、实现效果
-
将每种元素当做卡牌打出,本篇文章只谈元素反应,不谈论元素反应倍率。大概的反应如上图和上表所示。
-
依次打出两张卡牌,判断两张卡牌是否会发生元素反应,如果发生反应,打印相应的日志(具体的实现自行设置)
-
如果不发生反应,删除上一张卡牌(场上同时只保存一种元素)
-
绽放和原激化反应是两种元素结合产生的特殊反应,理解困难可以忽略
三、代码示例
既然我们是以卡牌打出的方式实现元素反应,首先我们要定义一个卡牌类
using System;
using UnityEngine;
public enum CardElement
{
Fire, // 火
Water, // 水
Thunder, // 雷
Ice, // 冰
Grass, // 草
Rock, // 岩
Wind, // 风
//Bloom, // 绽放
//Sharpen // 激化
}
[Serializable]
public class Card
{
public string EName;
public CardElement Element; // 卡牌的属性
public Card(CardElement element, int value)
{
Element = element;
EValue = value;
}
}
因为元素反应涉及10多种,如果要考虑先后顺序的话可能会更多。所以我们要创建一个接口,接口里定义一个事件和反应方法。
using System;
public interface IElementalReaction
{
event Action OnReactionOccurred;
void React(Card card1, Card card2);
}
假设火元素卡牌和水元素卡牌打出会触发蒸发反应,那么我们需要创建一个蒸发反应的类,并继承元素反应的接口
using System;
using UnityEngine;
//蒸发=水+火
public class EvaporationReaction : IElementalReaction
{
public event Action OnReactionOccurred;
public void React(Card card1, Card card2)
{
Debug.Log($"{card1.Element} + {card2.Element} = 蒸发");
OnReactionOccurred?.Invoke();
}
}
如果你有多个反应类,那么就要创建多个元素反应类,这里就不一一展示。都是同样的代码。
光有元素元素反应类还不够,我们还需要注册那些元素之间会触发对应的反应,所以这里我定义了一个元素反应类,在不考虑卡牌先后顺序的情况下,我们使用switch语句来返回产生反应的类型。
public static class ElementalReactionFactory
{
public static IElementalReaction GetReaction(CardElement element1, CardElement element2)
{
// 使用元组排序元素,确保顺序无关性
var key = element1 < element2 ? (element1, element2) : (element2, element1);
return key switch
{
(CardElement.Fire, CardElement.Water) => new EvaporationReaction(),//蒸发
(CardElement.Fire, CardElement.Thunder) => new OverloadReaction(),//超载
(CardElement.Fire, CardElement.Ice) => new MeltReaction(),//融化
(CardElement.Fire, CardElement.Grass) => new BurningReaction(),//燃烧
(CardElement.Water, CardElement.Thunder) => new ElectrifyReaction(),//感电
(CardElement.Water, CardElement.Ice) => new FrozenReaction(),//冻结
(CardElement.Water, CardElement.Grass) => new BloomReaction(),//绽放
(CardElement.Thunder, CardElement.Ice) => new SuperconDuctivityReaction(),//超导
(CardElement.Thunder, CardElement.Grass) => new SharpenReaction(),//原激化
(CardElement.Water, CardElement.Rock) => new RockCrystallizeReaction(),//结晶
(CardElement.Thunder, CardElement.Rock) => new RockCrystallizeReaction(),
(CardElement.Ice, CardElement.Rock) => new RockCrystallizeReaction(),
(CardElement.Fire, CardElement.Rock) => new RockCrystallizeReaction(),
(CardElement.Water, CardElement.Wind) => new WindDiffuseReaction(),//扩散
(CardElement.Thunder, CardElement.Wind) => new WindDiffuseReaction(),
(CardElement.Ice, CardElement.Wind) => new WindDiffuseReaction(),
(CardElement.Fire, CardElement.Wind) => new WindDiffuseReaction(),
//(CardElement.Fire, CardElement.Bloom) => new FierceBloomReaction(),//烈绽放
// (CardElement.Thunder, CardElement.Bloom) => new OverBloomReaction(),//超绽放
// (CardElement.Thunder, CardElement.Sharpen) => new HyperActivationReaction(),//超激化
//(CardElement.Grass, CardElement.Sharpen) => new RamificationReaction(),//蔓激化
_ => null,
};
}
}
基础设置已经差不多了,接下来我们创建一个Test脚本用来实现卡牌,这里不要照抄我的,可以根据自己的需求来实现。
简单介绍下Test脚本的内容。大概是设置了打出的卡牌和卡牌按键的预制体,然后运行时动态创建每一种卡牌按键,单机卡牌按键会向上打出卡牌,同时卡池内的卡牌是否会发生元素反应。按Esc清空卡池,代码写的不规范,只做功能示意。
using System;
using System.Collections.Generic;
using TMPro;
using UnityEngine;
using UnityEngine.UI;
public class Test : MonoBehaviour
{
private List<Card> playedCards;
public Transform beforContent;
public GameObject beforPrefab;
public Transform afterContent;
public GameObject afterPrefab;
private IElementalReaction reaction;
private void Start()
{
InitializedCard();
}
private void Update()
{
if (Input.GetKeyDown(KeyCode.Escape))
{
ClearCards();
}
}
// 初始化
private void InitializedCard()
{
playedCards = new List<Card>();
foreach (Transform item in beforContent)
Destroy(item.gameObject);
foreach (Transform item in afterContent)
Destroy(item.gameObject);
for (int i = 0; i < Enum.GetValues(typeof(CardElement)).Length; i++)
{
Card card = new((CardElement)i, i);
CreateCardButton(card);
}
}
// 创建卡牌按钮
private void CreateCardButton(Card newCard)
{
GameObject cardObj = Instantiate(beforPrefab, beforContent);
Card card = new(newCard.Element, newCard.EValue);
cardObj.GetComponentInChildren<TextMeshProUGUI>().text = card.EName;
cardObj.GetComponentInChildren<TextMeshProUGUI>().color = card.EColor;
cardObj.GetComponent<Button>().onClick.AddListener(() => PlayCard(card));
}
// 玩家打出一张卡牌时调用此方法
private void PlayCard(Card card)
{
CreateCard(card);
playedCards.Add(card);
CheckForElementalReaction();// 检查是否可以触发元素反应
}
// 创建打出的卡牌
private void CreateCard(Card card)
{
GameObject game = Instantiate(afterPrefab, afterContent);
game.name = card.EName;
game.transform.GetChild(0).GetComponent<TextMeshProUGUI>().text = card.EName;
game.transform.GetChild(0).GetComponent<TextMeshProUGUI>().color = card.EColor;
game.transform.GetChild(1).GetComponent<TextMeshProUGUI>().text = card.EValue.ToString();
}
// 检查并触发元素反应
private void CheckForElementalReaction()
{
// 需要至少两张卡来触发反应
if (playedCards.Count < 2) return;
// 检查最新的两张卡是否会产生元素反应
var card1 = playedCards[^2];
var card2 = playedCards[^1];
reaction = ElementalReactionFactory.GetReaction(card1.Element, card2.Element);
// 如果存在反应,则执行
if (reaction != null)
{
reaction.OnReactionOccurred += Reaction_OnReactionOccurred;
reaction.React(card1, card2);
}
else
{
ClearCards();
PlayCard(card2);
Debug.Log($"{card1.EName}:{card2.EName} 不发生反应");
}
}
// 所有反应通用的特效
private void Reaction_OnReactionOccurred()
{
reaction.OnReactionOccurred -= Reaction_OnReactionOccurred;
ClearCards();
//if (reaction is BloomReaction)
//PlayCard(new Card(CardElement.Bloom, (int)CardElement.Bloom));
//else if (reaction is SharpenReaction)
//PlayCard(new Card(CardElement.Bloom, (int)CardElement.Bloom));
}
// 清空卡池
private void ClearCards()
{
playedCards.Clear(); // 清除所有已打出的卡牌
foreach (Transform item in afterContent)
{
Destroy(item.gameObject);
}
}
}
以上代码仅代表个人水平,水平有限,这里仅提供想法思路,如有更好的方法欢迎评论区讨论。