Unity 实现原神中的元素反应

ops/2024/11/20 6:12:26/

一、元素反应

  • 原神中共有七种元素,分别是水、火、冰、岩、风、雷、草。这七种元素能互相作用
  • Demo下载:Download

image-20240424150440824

  • 元素反应表格图示,可能不够精准
/绽放原激化
/蒸发超载融化燃烧结晶扩散烈绽放/
蒸发/感电冻结/碎冰绽放结晶扩散//
超载感电/超导原激化结晶扩散超绽放超激化
融化冻结/碎冰超导//结晶扩散//
燃烧绽放原激化/////蔓激化
结晶结晶结晶结晶/////
扩散扩散扩散扩散/////

二、实现效果

GIF 2024-4-24 星期三 11-17-38

三、代码示例

  • 卡牌类

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

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

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


http://www.ppmy.cn/ops/17225.html

相关文章

rabbitmq 使用SAC队列实现顺序消息

rabbitmq 使用SAC队列实现顺序消息 前提 SAC: single active consumer, 是指如果有多个实例&#xff0c;只允许其中一个实例消费&#xff0c;其他实例为空闲 目的 实现消息顺序消费&#xff0c;操作&#xff1a; 创建4个SAC队列,消息的路由key 取队列个数模&#xff0c;这…

ZJGSU 1850 不同出栈情况

描述 假设有n个元素依次进栈&#xff0c;给出他们可能的不同的出栈情况。 输入 3 1 2 3 输出 1 2 3 1 3 2 2 1 3 2 3 1 3 2 1 输入样例 1 3 1 2 3 输出样例 1 1 2 3 1 3 2 2 1 3 2 3 1 3 2 1 #include <stdio.h>int tot, res, sta, n; int r[2005], s[2005…

vue项目中显示第三方外部链接的页面

1、打开窗口 window.open(URL, name, specs, replace);window.open()方法用于在浏览器中打开一个新的窗口或标签页。如果不指定第二个参数&#xff0c;则链接通常会在当前窗口中打开&#xff0c;这相当于_self。 name 窗口的名称&#xff0c;如果指定相同的名称&#xff0c;那…

天星金融消保课堂开讲,金融健康意识再提升

近年来&#xff0c;随着消费者对投资理财等金融服务需求的日益增长&#xff0c;金融广告成为消费者获取金融信息的重要途径。然而&#xff0c;一些不法分子通过投放非法金融广告&#xff0c;诱导消费者参与非法金融活动&#xff0c;给消费者的权益带来了严重威胁。为此&#xf…

还在为如何进行视频格式转换而烦恼?别担心,教你2招

在数字化时代&#xff0c;视频格式转换已经成为了我们日常生活中的常见需求。无论是为了在不同的设备、软件上播放&#xff0c;还是为了满足特定的编辑需求&#xff0c;视频格式转换都显得非常重要。然而&#xff0c;对于许多初学者来说&#xff0c;如何进行视频格式转换却成了…

CERLAB无人机自主框架: 2-动态目标检测与跟踪

前言&#xff1a;更多更新文章详见我的个人博客主页【MGodmonkeyの世界】 描述&#xff1a;欢迎来到CERLAB无人机自主框架&#xff0c;这是一个用于自主无人飞行器 (UAV) 的多功能模块化框架。该框架包括不同的组件 (模拟器&#xff0c;感知&#xff0c;映射&#xff0c;规划和…

【SAP ME 12】SAP NWDS(eclipse)下载、安装,配置

1、下载 1.1、描述 1.2、下载 2、安装 3、配置 3.1、域名映射

面向初学者的网络安全(二)

原文&#xff1a;annas-archive.org/md5/8570b4b9b47974c7302ce023e1eb9bc8 译者&#xff1a;飞龙 协议&#xff1a;CC BY-NC-SA 4.0 九、攻击和防御方法的演变 当我写下这本书的第一版时&#xff0c;网络安全只是网络安全专家和精明的高管感兴趣的话题。这本书是为了让任何人…