Unity 实现原神中的元素反应

一、元素反应

原神中共有七种元素,分别是水、火、冰、岩、风、雷、草。这七种元素能互相作用
元素反应表格图示,可能不够精准
/ 绽放 原激化
/ 蒸发 超载 融化 燃烧 结晶 扩散 烈绽放 /
蒸发 / 感电 冻结/碎冰 绽放 结晶 扩散 / /
超载 感电 / 超导 原激化 结晶 扩散 超绽放 超激化
融化 冻结/碎冰 超导 / / 结晶 扩散 / /
燃烧 绽放 原激化 / / / / / 蔓激化
结晶 结晶 结晶 结晶 / / / / /
扩散 扩散 扩散 扩散 / / / / /

二、实现效果

将每种元素当做卡牌打出,本篇文章只谈元素反应,不谈论元素反应倍率。大概的反应如上图和上表所示。
依次打出两张卡牌,判断两张卡牌是否会发生元素反应,如果发生反应,打印相应的日志(具体的实现自行设置)
如果不发生反应,删除上一张卡牌(场上同时只保存一种元素)
绽放和原激化反应是两种元素结合产生的特殊反应,理解困难可以忽略

三、代码示例

卡牌类

既然我们是以卡牌打出的方式实现元素反应,首先我们要定义一个卡牌类

C# 复制代码
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多种,如果要考虑先后顺序的话可能会更多。所以我们要创建一个接口,接口里定义一个事件和反应方法。

C# 复制代码
using System;

public interface IElementalReaction
{
    event Action OnReactionOccurred;

    void React(Card card1, Card card2);
}
实现接口

假设火元素卡牌和水元素卡牌打出会触发蒸发反应,那么我们需要创建一个蒸发反应的类,并继承元素反应的接口

C# 复制代码
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语句来返回产生反应的类型。

C# 复制代码
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清空卡池,代码写的不规范,只做功能示意。

C# 复制代码
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);
        }
    }

}

以上代码仅代表个人水平,水平有限,这里仅提供想法思路,如有更好的方法欢迎评论区讨论。

相关推荐
云上空14 小时前
腾讯云使用对象存储托管并分享WebGL小游戏(unity3d)(需要域名)
unity·腾讯云·webgl·游戏开发·对象存储·网页托管
小贺儿开发16 小时前
Unity3D VR党史主题展馆
unity·人机交互·vr·urp·展馆·党史
TopGames17 小时前
Unity实现10万人同屏动态避障和导航寻路系统 支持3D地形
unity·性能优化·游戏引擎
在路上看风景20 小时前
01. GUIContent
unity
托洛夫斯基扎沙耶20 小时前
Unity中状态机与行为树的简单实现
unity·游戏引擎
TrudgeCarrot1 天前
unity打包使用SPB管线出现 DontSava错误解决
unity·游戏引擎·dontsave
3D霸霸1 天前
unity 创建URP新场景
unity·游戏引擎
玉梅小洋2 天前
Unity 2D游戏开发 Ruby‘s Adventure 1:课程介绍和资源导入
游戏·unity·游戏引擎·游戏程序·ruby
托洛夫斯基扎沙耶2 天前
Unity可视化工具链基础
unity·编辑器·游戏引擎
浅陌sss2 天前
检查Unity对象要始终使用 != null而不是?.
unity