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);
        }
    }

}

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

相关推荐
Solange__19 小时前
在线3D预览器、Blender和游戏引擎怎么选?按5类任务比较格式、隐私与编辑性
人工智能·游戏·3d·游戏引擎·blender·1024程序员节·关卡设计
peter676821 小时前
unity2d游戏开发经验总结
unity
郝学胜-神的一滴21 小时前
游戏设计:原型与开发 19:U3D工位布置与性格说明书
人工智能·算法·unity·unreal engine·游戏策划·游戏设计
Aman19841 天前
Claude Code做Unity项目移植(二): 自动执行只读操作,告别频繁的
unity·游戏开发·vibe coding·claude code
暴走约伯2 天前
【VFX工具链源码拆解 02】告别机械劳动(上):一键统改百个子粒子颜色与批量重命名
3d·unity·c#·材质
郝学胜-神的一滴2 天前
游戏设计:原型与开发 18:Unity入门的第一课
人工智能·程序人生·游戏·ui·unity·游戏引擎
淡海水2 天前
09-01-内存-Unity资源生命周期全景
unity·c#·游戏引擎·.netcore·gc
Solange__2 天前
AI世界模型、3D场景生成和游戏引擎怎么选?按5类任务比较交互、物理与导出
开发语言·人工智能·游戏·平面·3d·自然语言处理·游戏引擎
LF男男2 天前
Flower Defense—UIFacade
unity
呆呆敲代码的小Y2 天前
【实战精选面试题】Unity精选面试题详细解答,按模块整理汇总
人工智能·游戏·unity·游戏引擎