设置奖励的数据类型
//
// Auto Generated Code By excel2json
// https://neil3d.gitee.io/coding/excel2json.html
// 1. 每个 Sheet 形成一个 Struct 定义, Sheet 的名称作为 Struct 的名称
// 2. 表格约定:第一行是变量名称,第二行是变量类型// Generate From C:\Users\wasi\Desktop\Reward.xlsx.xlsxpublic class Reward
{public int RewardID; // id值public string RewardName; // 名字public string RewardType; // 类型public int RewardValue; // 值public string Descript; // 描述public string BG; // 背景图public string Icon; // 图标public string AddObjcetName; // 增加物体public int MaxCount; // 最多出现次数public float CumulativeProbability; // 出现概率public string Level; // 奖励品质
}// End of Auto Generated Code
奖励接口
// 定义奖励接口
using UnityEngine;
using System;public interface IRewardable
{string Name { get; }Reward Reward { get; set; }/// <summary>/// 异步返回奖励信息的方法/// </summary>/// <param name="callback">加载完成后的回调,返回加载的奖励信息</param>void ReturnRewardInfo(Action<RewardInfo> callback);/// <summary>/// 授予奖励的方法/// </summary>void GetReward();
}
public class RewardInfo
{public string name; // 奖励名称public Sprite bgSprite; // 背景图片public Sprite headSprite; // 头像图片public string description; // 描述public int currentConut;public int maxCount;
}
奖励父类
using UnityEngine;
using UnityEngine.AddressableAssets;
using UnityEngine.ResourceManagement.AsyncOperations;
using System;public class Rewardable : IRewardable
{public Reward Reward { get; set; }public string Name => GetType().Name;public RewardInfo info;public Rewardable(Reward reward){Debug.Log("初始化数据"+Name);Reward = reward;info = new RewardInfo();info.name = Reward.RewardName;info.description = Reward.Descript;info.maxCount = Reward.MaxCount;info.currentConut=0;}public void ReturnRewardInfo(Action<RewardInfo> callback){// 异步加载背景图AsyncOperationHandle<Sprite> bgHandle = Addressables.LoadAssetAsync<Sprite>(Reward.BG);bgHandle.Completed += (op) =>{if (op.Status == AsyncOperationStatus.Succeeded){info.bgSprite = op.Result;}else{Debug.LogError($"Failed to load sprite with key {Reward.BG}");}// 异步加载图标AsyncOperationHandle<Sprite> iconHandle = Addressables.LoadAssetAsync<Sprite>(Reward.Icon);iconHandle.Completed += (opIcon) =>{if (opIcon.Status == AsyncOperationStatus.Succeeded){info.headSprite = opIcon.Result;}else{Debug.LogError($"Failed to load sprite with key {Reward.Icon}");}// 调用回调,返回加载完成的 RewardInfocallback?.Invoke(info);};};}public virtual void GetReward(){// 实现领取奖励的逻辑}public bool CanDraw()//是否能抽取{return info.currentConut < info.maxCount; // 检查是否可以抽取}
}
奖励系统制作成持久性单列
using System.Collections;
using System.Collections.Generic;
using UnityEngine;public class PersistentSingleton<T> : MonoBehaviour where T : Component
{public static T instance { get; private set; }protected virtual void Awake(){if (instance == null){instance=this as T;DontDestroyOnLoad(gameObject);}else if (instance != this){Destroy(gameObject);}}
}
奖励管理器和奖励工厂 用Newtonsoft来解析数据
using Newtonsoft.Json;
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using UnityEngine;
using UnityEngine.AddressableAssets;
using UnityEngine.ResourceManagement.AsyncOperations;
public class RewardManager :PersistentSingleton<RewardManager>
{//全部的奖励private List<Reward> rewardDataList = new List<Reward>();private List<Rewardable> rewardList = new List<Rewardable>();//拥有的奖励private List<Rewardable> owerRawardList = new List<Rewardable>();void Start(){LoadJson();foreach (string name in RewardFactory.GetRewardNames()){// Debug.Log("奖励名字"+ name);}}private void LoadJson(){// 使用 Addressables 异步加载 JSON 文件Addressables.LoadAssetAsync<TextAsset>("Data/Reward.json").Completed += OnJsonLoaded;}private void OnJsonLoaded(AsyncOperationHandle<TextAsset> handle){if (handle.Status == AsyncOperationStatus.Succeeded){// JSON 文件加载成功TextAsset jsonAsset = handle.Result;string json = jsonAsset.text;rewardDataList = JsonConvert.DeserializeObject<List<Reward>>(json);foreach (var reward in rewardDataList){GrantReward(reward.RewardID);}// 在这里处理 JSON 数据//暂时获得全部奖励 owerRawardList=rewardList;//每次被重新生成了Debug.Log("重新刷新奖励-------------------------");}else{Debug.LogError("Failed to load JSON: " + handle);}}private void GrantReward(int rewardID){var data = rewardDataList.Find(r => r.RewardID == rewardID);Rewardable rewardable = RewardFactory.GetReward(data.RewardType, data);if (!rewardList.Contains(rewardable)){rewardList.Add(rewardable);}}public Rewardable[] GetOwerRewardable(int number){Rewardable[] newRewards=new Rewardable[number];HashSet<int> rewardIdList = new HashSet<int>();//记录已经抽过的奖励int i = 0;while (i<number){float randomValue =UnityEngine.Random.Range(0f, 1f);Rewardable reward = owerRawardList[UnityEngine.Random.Range(0, owerRawardList.Count)];if (reward.Reward.CumulativeProbability>randomValue&&reward.CanDraw()){if (!rewardIdList.Contains(reward.Reward.RewardID)){rewardIdList.Add(reward.Reward.RewardID);newRewards[i] = reward;i++;}}}return newRewards;}}public static class RewardFactory
{private static Dictionary<string, Type> rewardByName;private static bool isInit => rewardByName != null;private static void Init(){if (isInit) return;// Debug.Log("初始化奖励脚本");//获得脚本中所有的 IRewardable 类 同时 要是类 和不是接口 而且要是 Rewardable 子类var rewardTypes = Assembly.GetAssembly(typeof(IRewardable)).GetTypes().Where(myType => myType.IsClass && !myType.IsInterface && myType.IsSubclassOf(typeof(Rewardable)));rewardByName = new Dictionary<string, Type>();foreach (var rewardType in rewardTypes){//运用反射创建类 添加到字典里var tempEffect = Activator.CreateInstance(rewardType, new Reward()) as IRewardable;rewardByName.Add(tempEffect.Name, rewardType);}}public static Rewardable GetReward(string rewardType, Reward reward){Init();if (rewardByName.ContainsKey(rewardType)){Type type = rewardByName[rewardType];try{//反射创建实例var rewardInstance = Activator.CreateInstance(type, reward) as Rewardable;return rewardInstance;}catch (Exception ex){Debug.LogError($"无法创建奖励实例 {rewardType}: {ex.Message}");}}else{Debug.LogWarning($"未找到奖励类型 {rewardType}");}return null;}public static IEnumerable<string> GetRewardNames(){// Debug.Log("获取所有奖励名称");Init();return rewardByName.Keys;}
}
选取奖励制作成按钮
using UnityEngine;
using UnityEngine.UI;
using System;
using System.Collections;public class RewardButton : MonoBehaviour
{[SerializeField] private Image BG;[SerializeField] private Image headImage;[SerializeField] private Text nameText;[SerializeField] private Text descriptText;[SerializeField] private GameObject BackGround;private Button button;private bool isOnclik;private Action action;private void Awake(){button = GetComponent<Button>();}private void Start(){button.onClick.AddListener(() =>{OnButtonOnClick();});}public void Init(IRewardable rewardable){// 异步加载并显示奖励信息rewardable.ReturnRewardInfo((info) =>{if (info != null){BackGround.SetActive(false);BG.sprite = info.bgSprite;headImage.sprite = info.headSprite;nameText.text = info.name;descriptText.text = info.description;// 添加按钮点击事件action+=()=>rewardable.GetReward();}else{Debug.LogError("Failed to load reward info");}});}public void ButtonAddAction(Action action){this.action += action;}void OnButtonOnClick(){if (isOnclik){action?.Invoke();}StartCoroutine(DoubleOnClikCoroutine());}IEnumerator DoubleOnClikCoroutine(){isOnclik=true;yield return new WaitForSeconds(0.5f);isOnclik = false;}
}
奖励界面
using DG.Tweening;
using System;
using System.Collections;
using System.Collections.Generic;
using TMPro;
using UnityEngine;
using UnityEngine.UI;public class RewardUI : MonoBehaviour
{[SerializeField] Canvas canvas;[SerializeField] RewardButton rewardButton;public Transform cardParent;public Transform cardStartPosT;public int cardCount = 10;public float cardWidth = 1f;public float dealDuration = 1f;public float delayBetweenCards = 0.2f;private void OnEnable(){WaveUI.OnRewardEvent+=onRewardEvent;}private void OnDisable(){WaveUI.OnRewardEvent-=onRewardEvent;}private void onRewardEvent(){Debug.Log("调用奖励");CreateCard();}void CreateCard(){canvas.enabled=true;foreach (Transform item in cardParent){Destroy(item.gameObject);}IRewardable[] rewardables = RewardManager.instance.GetOwerRewardable(cardCount);int cardNumber =cardCount;float totalWidth = cardNumber * cardWidth;float startingX = cardParent.position.x - totalWidth / 2 + cardWidth / 2;for (int i = 0; i < cardNumber; i++){var card = Instantiate(rewardButton);// Position the cardfloat cardX = startingX + i * cardWidth;card.transform.position = cardStartPosT.position;card.transform.SetParent(cardParent);card.transform.localScale = Vector3.one;DealCard(card.gameObject, i * delayBetweenCards, new Vector3(cardX, cardParent.position.y, cardParent.position.z), rewardables[i]);}}private void DealCard(GameObject card, float delay, Vector3 targetPos, IRewardable rewardable){// Store the initial rotationQuaternion initialRotation = card.transform.rotation;// Create rotate animationvar sequence = DOTween.Sequence();sequence.AppendInterval(delay);// Move card to target positionsequence.Append(card.transform.DOMove(targetPos, 0.1f));// Rotate card to half way over dealDuration / 2sequence.Append(card.transform.DORotate(new Vector3(0, 90, 0), dealDuration / 2));// Add a function callback here to do something when the card is half way turned// For example, change the card's sprite to show its facesequence.AppendCallback(() => HalfWayThere(card, rewardable));// Rotate card to fully turned over dealDuration / 2sequence.Append(card.transform.DORotate(initialRotation.eulerAngles, dealDuration / 2));// Add a callback for completion after the animation finishes// sequence.OnComplete(() => ResetReawardPlane());}private void HalfWayThere(GameObject card, IRewardable rewardable){card.GetComponent<RewardButton>().Init(rewardable);card.GetComponent<RewardButton>().ButtonAddAction(() =>{canvas.enabled=false;GameManager.GameState=GameState.UI;});}//private void Update()//{// if (Input.GetKeyDown(KeyCode.Escape))// {// Debug.Log("生成奖励");// // CreateReward();// CreateCard();// }//}
}
具体的奖励类
比如我增加枪支
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.AddressableAssets;
using UnityEngine.ResourceManagement.AsyncOperations;public class AddWeaponReward : Rewardable
{public AddWeaponReward(Reward reward) : base(reward){}public override void GetReward(){info.currentConut++;//计入使用次数AsyncOperationHandle<GameObject> bgHandle = Addressables.LoadAssetAsync<GameObject>(Reward.AddObjcetName);bgHandle.Completed += (op) =>{if (op.Status == AsyncOperationStatus.Succeeded){GameObject weapon=op.Result;GameObject.FindAnyObjectByType<PlayerWeapon>().AddGun(weapon);}else{Debug.LogError($"Failed to load sprite with key {Reward.BG}");}};Debug.Log("增加武器");}
}
我的excel配置表,我用excel2json 转换为json数据
保存在 data下面,这里用Addressable标记