TH1/Unity/Assets/Scripts/TH1_UI/TechTreeUI.cs
2025-09-22 13:30:07 +08:00

532 lines
24 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Rendering;
using UnityEngine.UI;
using TMPro;
using Animancer;
using Logic;
using Logic.Action;
using Logic.Audio;
using Logic.CrashSight;
using RuntimeData;
using Unity.VisualScripting;
using Logic.Multilingual;
using Logic.Skill;
using NUnit.Framework.Constraints;
using TH1_Core.Managers;
using TH1_Logic.Core;
using TH1_UI.HintUI;
using TH1Renderer;
using TH1Resource;
using UI;
using UI.HintUI;
public class TechTreeUI
{
public GameObject ROTechTreeUI;
public GameObject ROTechCheckPanel;
private Transform _techTreeArea;
private Transform _techPointArea;
//-------- UI表现层RenderData --------//
public bool TechTreeUIStatus = false;
public bool ShowHideAnimMark = false;
public bool TechTreeUIDataRenderMark = false;
public bool TechCheckPanelStatus = false;
public bool TechCheckPanelShowHideAnimMark = false;
public TechType TechCheckTechType;
private int _playerWealthCache;
//------- 动画数据 ---------//
private bool _isAnimating = false;
private float _fadeTime = 0f;
private bool _isShow = false;
private float _fadeDuration = 0.2f;
private TechType _techTypeChoose;
private int _techNoChooseCost = 0;
public TechTreeUI()
{
ROTechTreeUI = UIManager.Instance.ROUIManager.transform.Find("TechTreePanel").gameObject;
ROTechCheckPanel = ROTechTreeUI.transform.Find("TechCheckPanel").gameObject;
//绑定关闭按钮点击事件
var btn = ROTechTreeUI.transform.Find("CloseButton").GetComponent<Button>();
btn.onClick.RemoveAllListeners();
btn.onClick.AddListener(()=>PlayAnimTechTreeHide());
HideTechCheckPanel();//隐藏TechCheckPanel
Button techCheckPanelCloseButton = ROTechCheckPanel.transform.Find("ButtonGroup").transform.Find("ButtonCancel").GetComponent<Button>();
Button techCheckPanelReserchButton = ROTechCheckPanel.transform.Find("ButtonGroup").transform.Find("ButtonResearch").GetComponent<Button>();
Button techCheckPanelBackButton = ROTechCheckPanel.transform.Find("UIMask").GetComponent<Button>();
techCheckPanelCloseButton.onClick.RemoveAllListeners();
techCheckPanelCloseButton.onClick.AddListener(HideTechCheckPanel);
techCheckPanelReserchButton.onClick.RemoveAllListeners();
techCheckPanelReserchButton.onClick.AddListener(ResearchTech);
techCheckPanelBackButton.onClick.RemoveAllListeners();
techCheckPanelBackButton.onClick.AddListener(HideTechCheckPanel);
// 找到techTree和 techPoint的对象
_techTreeArea = ROTechTreeUI.transform.Find("TechTree");
_techPointArea = ROTechTreeUI.transform.Find("TechPointArea");
InitTechTreeData();
}
private void UpdateTechTreeLine()
{
var lines = ROTechTreeUI.transform.Find("Lines");
var sublines = lines.Find("FatherControl");
if (!Table.Instance.PlayerDataAssets.GetPlayerInfo(Main.MapData.PlayerMap.SelfPlayerData, out var playerInfo))
return;
var techPool = playerInfo.TechPool;
var hasPool = Main.MapData.PlayerMap.SelfPlayerData.TechTree.TechSet;
var techTreeShowPool = new HashSet<TechType>();
foreach (var t in techPool)
{
//如果已经拥有就加入showPool
if(hasPool.Contains(t))
techTreeShowPool.Add(t);
//如果父科技也已经拥有就加入showPool
var fatherTech = Table.Instance.TechDataAssets.GetFatherTech(t);
if(hasPool.Contains(fatherTech))
techTreeShowPool.Add(t);
}
//先处理三级科技
foreach (Transform line in lines)
{
var techListMono = line.GetComponent<TechListMono>();;
if (techListMono == null) continue;
foreach (var t in techListMono.TechList)
if(techTreeShowPool.Contains(t))
{
line.gameObject.SetActive(true);
break;
}
}
//处理一级科技连出来的那根线
foreach (Transform subline in sublines)
{
subline.gameObject.SetActive(false);
var techListMono = subline.GetComponent<TechListMono>();
if (techListMono == null) continue;
foreach(var t in techListMono.TechList)
if (hasPool.Contains(t))
subline.gameObject.SetActive(true);
}
}
private void InitTechTreeData()
{
Transform techTree = ROTechTreeUI.transform.Find("TechTree");
//先根据当前玩家的TechPool 决定哪些techTree显示哪些不显示
//再设置每个科技圈圈内显示的图标是什么
Table.Instance.PlayerDataAssets.GetPlayerInfo(Main.MapData.PlayerMap.SelfPlayerData, out var playerInfo);
foreach (Transform techNode in techTree)
{
TechType techType = techNode.GetComponent<TechTypeMono>().TechType;
if (!playerInfo.TechPool.Contains(techType))
{
techNode.gameObject.SetActive(false);
continue;
}
techNode.gameObject.SetActive(true);
var techInfo = Table.Instance.TechDataAssets.GetTechInfo(techType);
Button btn = techNode.GetComponent<Button>();
btn.onClick.RemoveAllListeners();
btn.onClick.AddListener(() => SetTechCheckPanelShowHide(true,techType));
//设置英雄圈圈的背景和内容 TODO 暂时不用做内容,之后要改内容
if (techInfo.GiantTech)
{
continue;
}
TextMeshProUGUI txt = techNode.Find("Text").GetComponent<TextMeshProUGUI>();
int rk = 0;
foreach(var techItem in techInfo.techActions)
{
rk++;
Image img = techNode.Find("ImageGroup").transform.Find($"Circle{rk.ToString()}/Image").GetComponent<Image>();
MultilingualManager.Instance.SetUIText(txt, techInfo.TechName);
//txt.text = MultilingualManager.Instance.GetMultilingualText(uint.Parse(techInfo.TechName));
if (!Table.Instance.ActionDataAssets.GetActionInfo(techItem, out var actionInfo))
continue;
RectTransform rectTransform = img.rectTransform;
rectTransform.pivot = new Vector2(0.5f, 0.5f);
rectTransform.anchorMin = new Vector2(0.5f, 0.5f);
rectTransform.anchorMax = new Vector2(0.5f, 0.5f);
rectTransform.anchoredPosition = Vector2.zero;
rectTransform.anchoredPosition = new Vector2(0, 20);
var gridSpType = GridSpType.None;
if (Main.MapData.PlayerMap.SelfPlayerData.PlayerCivId == 1) gridSpType = GridSpType.KaguyaGrid;
//img.sprite = actionInfo.GetIcon(Main.MapData.PlayerMap.SelfPlayerData.PlayerCivId, Main.MapData.PlayerMap.SelfPlayerData.PlayerForceId);
if (actionInfo.VarientIcon)
img.sprite = actionInfo.GetIcon(Main.MapData.PlayerMap.SelfPlayerData.PlayerCivId, Main.MapData.PlayerMap.SelfPlayerData.PlayerForceId,gridSpType);
else
img.sprite = actionInfo.Icon;
//处理偏移和尺寸
if(actionInfo.SpriteSize != Vector2.zero)
rectTransform.sizeDelta = actionInfo.SpriteSize;
if(actionInfo.SpritePos != Vector2.zero)
rectTransform.anchoredPosition = actionInfo.SpritePos;
//如果是防御类图标手动将防御小icon贴上去
if (actionInfo.ActionId.SkillType == SkillType.OCEANDEFENSE ||
actionInfo.ActionId.SkillType == SkillType.WATERDEFENSE ||
actionInfo.ActionId.SkillType == SkillType.FORESTDEFENSE ||
actionInfo.ActionId.SkillType == SkillType.MOUNTAINDEFENSE)
{
var t = techNode.Find($"ImageGroup/Circle{rk.ToString()}");
GameObject def = new GameObject("DefenseImg");
def.transform.SetParent(t, false);
var image = def.AddComponent<Image>();
image.sprite = ResourceCache.Instance.SpriteCache.TH1Icon_ActionDefense;
image.preserveAspect = true;
RectTransform rt = image.GetComponent<RectTransform>();
rt.sizeDelta = new Vector2(50, 50);
rt.anchoredPosition = new Vector2(15, -10);
}
}
for (int j = techInfo.techActions.Count + 1; j <= 3; j++)
techNode.Find("ImageGroup").transform.Find($"Circle{j.ToString()}").gameObject.SetActive(false);
}
UpdateTechTreeLine();
ROTechTreeUI.SetActive(false);
}
//当需要更新渲染科技树的时候调用
private void RenderUpdateTechTreeData()
{
//step #1 更新每一个科技模块
foreach (Transform techNode in _techTreeArea)
{
TechType techType = techNode.GetComponent<TechTypeMono>().TechType;
var techInfo = Table.Instance.TechDataAssets.GetTechInfo(techType);
//如果该科技已经学习
if (Main.MapData.PlayerMap.SelfPlayerData.TechTree.CheckIfHasTech(techType))
{
Image img = techNode.transform.Find("Background").GetComponent<Image>();
img.sprite = ResourceCache.Instance.SpriteCache.TechCompleteBackground;
techNode.Find("costNumber").gameObject.SetActive(false);
techNode.Find("star").gameObject.SetActive(false);
//如果不是giant科技就把下面3个circle对象的背景都变成绿的已完成
if(!techInfo.GiantTech)
for (int i = 1; i <= techInfo.techActions.Count; i++)
{
techNode.transform.Find($"ImageGroup/Circle{i}")?.GetComponent<Image>().gameObject.SetActive(true);
if (techNode.transform.Find($"ImageGroup/Circle{i}") != null)
techNode.transform.Find($"ImageGroup/Circle{i}").GetComponent<Image>().sprite = ResourceCache.Instance.SpriteCache.TechCompleteBackground;
}
}
else
//如果该科技未解锁
if (Main.MapData.PlayerMap.SelfPlayerData.TechTree.CheckIfTechUnsee(techType))
{
Image img = techNode.Find("Background").GetComponent<Image>();
img.sprite = ResourceCache.Instance.SpriteCache.TechLockedBackground;
techNode.Find("costNumber").gameObject.SetActive(false);
techNode.Find("star").gameObject.SetActive(false);
for (int i = 1; i <= techInfo.techActions.Count; i++)
techNode.transform.Find($"ImageGroup/Circle{i}")?.GetComponent<Image>().gameObject.SetActive(false);
}
else //if(Main.MapData.PlayerMap.SelfPlayerData.TechTree.CheckIfTechCanLearn(techType))
//如果该科技可以学习
{
TextMeshProUGUI costTxt = techNode.Find("costNumber").GetComponent<TextMeshProUGUI>();
//计算目前该tech的学习成本=科技基础cost * 城市数 + 4
var img = techNode.Find("Background").GetComponent<Image>();
img.sprite = ResourceCache.Instance.SpriteCache.TechAvailableBackground;
int tmpCost = (Main.MapData.GetCityCount(Main.MapData.PlayerMap.SelfPlayerData.Id)
* techInfo.CostLevel + 4);
costTxt.text = tmpCost.ToString();
if (tmpCost > Main.MapData.PlayerMap.SelfPlayerData.PlayerWealth + Main.MapData.PlayerMap.SelfPlayerData.PlayerTechPoint)
costTxt.color = Color.red;
else costTxt.color = Color.white;
costTxt.gameObject.SetActive(true);
techNode.Find("star").gameObject.SetActive(true);
for (int i = 1; i <= techInfo.techActions.Count; i++)
{
techNode.transform.Find($"ImageGroup/Circle{i}")?.GetComponent<Image>().gameObject.SetActive(true);
if (techNode.transform.Find($"ImageGroup/Circle{i}") != null)
techNode.transform.Find($"ImageGroup/Circle{i}").GetComponent<Image>().sprite = ResourceCache.Instance.SpriteCache.TechAvailableBackground;
}
}
}
//step #2 更新科技点的显示
var txt = _techPointArea?.Find("TechPoint")?.GetComponent<TextMeshProUGUI>();
if (txt != null)
{
var techPoint = Main.MapData.PlayerMap.SelfPlayerData.PlayerTechPoint;
var techPointPerTurn = Main.PlayerLogic.GetPlayerTechPointPerTurn(Main.MapData,Main.MapData.PlayerMap.SelfPlayerId);
var str = $"{techPoint}<color=lightblue>+{techPointPerTurn}</color>";
txt.text = str;
}
else
LogSystem.LogError("TechTree - TechPoint UIArea is Null!");
//step #3 更新科技树的线条
UpdateTechTreeLine();
}
public void Update()
{
//如果则当前不需要显示直接return
if (!ROTechTreeUI.activeSelf && !TechTreeUIStatus)
return;
//先更新data
if (TechTreeUIDataRenderMark)
{
RenderUpdateTechTreeData();
TechTreeUIDataRenderMark = false;
}
if (_playerWealthCache != Main.MapData.PlayerMap.SelfPlayerData.PlayerWealth)
{
RenderUpdateTechTreeData();
_playerWealthCache = Main.MapData.PlayerMap.SelfPlayerData.PlayerWealth;
}
//处理动画播放or关闭动画
if (ROTechTreeUI.activeSelf != TechTreeUIStatus && ShowHideAnimMark)
{
ShowHideAnimMark = false;
if(TechTreeUIStatus)
PlayAnimTechTreeShow();
else PlayAnimTechTreeHide();
}
//处理techcheckpanel的fade动画
if (TechCheckPanelStatus != ROTechCheckPanel.activeSelf && TechCheckPanelShowHideAnimMark)
{
TechCheckPanelShowHideAnimMark = false;
if (TechCheckPanelStatus)
ShowTechCheckPanel(TechCheckTechType);
else
HideTechCheckPanel();
}
}
private void RenderUpdateTechCheckPanel()
{
TechInfo techInfo = Table.Instance.TechDataAssets.GetTechInfo(_techTypeChoose);
PlayerData selfPlayer = Main.MapData.PlayerMap.SelfPlayerData;
ROTechCheckPanel.SetActive(true);
//记录一下目前点击的techNo和费用以供点击Research时判断使用
_techNoChooseCost = (Main.MapData.GetCityCount(selfPlayer.Id) * techInfo.CostLevel + 4);
TextMeshProUGUI title = ROTechCheckPanel.transform.Find("Title").GetComponent<TextMeshProUGUI>();
TextMeshProUGUI cost = ROTechCheckPanel.transform.Find("Title").transform.Find("costNumber").GetComponent<TextMeshProUGUI>();
cost.text = _techNoChooseCost.ToString();
MultilingualManager.Instance.SetUIText(title, techInfo.TechName);
//title.text = MultilingualManager.Instance.GetMultilingualText(uint.Parse(techInfo.TechName));
//设置研发按钮的表现
ROTechCheckPanel.transform.Find("ButtonGroup").transform.Find("ButtonResearch").gameObject.SetActive(true);
int itemsCount = techInfo.techActions.Count;
Transform items = ROTechCheckPanel.transform.Find("UnlockItems");
int rk = 0;
foreach(var techItem in techInfo.techActions)
{
// step #0 check 对应的action是否存在
if (!Table.Instance.ActionDataAssets.GetActionInfo(techItem, out var actionInfo)) continue;
// step #1 check 找到操作的对象
++rk;
Transform child = items.GetChild(rk - 1);
child.gameObject.SetActive(true);
var img = child.Find("Image").GetComponent<Image>();
var rect = child.Find("Image").GetComponent<RectTransform>();
//step #2 设置actionMono 和hintTrigger的数据
var hintTrigger = child.GetComponent<HintTrigger>();
hintTrigger.DataProvider.HintDataType = HintDataType.ActionHintData;
hintTrigger.DataProvider.ActionIdData = actionInfo.ActionId;
var gridSpType = GridSpType.None;
if (Main.MapData.PlayerMap.SelfPlayerData.PlayerCivId == 1) gridSpType = GridSpType.KaguyaGrid;
if (actionInfo.VarientIcon)
img.sprite = actionInfo.GetIcon(Main.MapData.PlayerMap.SelfPlayerData.PlayerCivId,Main.MapData.PlayerMap.SelfPlayerData.PlayerForceId,gridSpType);
else
img.sprite = actionInfo.Icon;
if (actionInfo.SpritePos != Vector2.zero)
rect.anchoredPosition = actionInfo.SpritePos;
if (actionInfo.SpriteSize != Vector2.zero)
rect.sizeDelta = actionInfo.SpriteSize;
TextMeshProUGUI txt = child.Find("Text").GetComponent<TextMeshProUGUI>();
MultilingualManager.Instance.SetUIText(txt, actionInfo.ActionName);
//处理defend
var t = child.Find("Defend")?.gameObject;
bool showDefend = (techItem.SkillType == SkillType.OCEANDEFENSE
|| techItem.SkillType == SkillType.WATERDEFENSE
|| techItem.SkillType == SkillType.FORESTDEFENSE
|| techItem.SkillType == SkillType.MOUNTAINDEFENSE);
if(t !=null)
t.SetActive(showDefend);
}
for(int i = itemsCount; i< 3; i++)
{
Transform child = items.GetChild(i);
child.gameObject.SetActive(false);
}
//如果已经研发完成
if (selfPlayer.TechTree.CheckIfHasTech(_techTypeChoose))
{
title.text += "(已完成)";
ROTechCheckPanel.transform.Find("ButtonGroup").transform.Find("ButtonResearch").gameObject.SetActive(false);
ROTechCheckPanel.transform.Find("Title").transform.Find("costNumber").gameObject.SetActive(false);
ROTechCheckPanel.transform.Find("Title").transform.Find("star").gameObject.SetActive(false);
}
else
//如果还没解锁
if (selfPlayer.TechTree.CheckIfTechUnsee(_techTypeChoose))
{
if (techInfo.GiantTech)
title.text = "研发该区任意3科技后解锁";
else
title.text += "(需完成前置科技)";
ROTechCheckPanel.transform.Find("ButtonGroup").transform.Find("ButtonResearch").gameObject.SetActive(false);
ROTechCheckPanel.transform.Find("Title").transform.Find("costNumber").gameObject.SetActive(true);
ROTechCheckPanel.transform.Find("Title").transform.Find("star").gameObject.SetActive(true);
}
else
//如果可以研发但是买不起则cost显示红色按钮disable
if (selfPlayer.PlayerWealth + selfPlayer.PlayerTechPoint < _techNoChooseCost)
{
title.text += "(费用不足)";
cost.color = Color.red;
ROTechCheckPanel.transform.Find("ButtonGroup").transform.Find("ButtonResearch").GetComponent<Button>().interactable = false;
ROTechCheckPanel.transform.Find("Title").transform.Find("costNumber").gameObject.SetActive(true);
ROTechCheckPanel.transform.Find("Title").transform.Find("star").gameObject.SetActive(true);
}
//其他情况,那只能是可以研发了这一种情况了
else
{
cost.color = Color.white;
ROTechCheckPanel.transform.Find("ButtonGroup").transform.Find("ButtonResearch").GetComponent<Button>().interactable = true;
ROTechCheckPanel.transform.Find("Title").transform.Find("costNumber").gameObject.SetActive(true);
ROTechCheckPanel.transform.Find("Title").transform.Find("star").gameObject.SetActive(true);
}
}
//点击某个科技树后显示科技树状态
private void ShowTechCheckPanel(TechType techType)//techNo是0开始的顺序编号techId是不连续的,11,121,这种代号
{
_techTypeChoose = techType;
RenderUpdateTechCheckPanel();
//播放fadeIn动画
AnimancerComponent animancer = ROTechCheckPanel.GetComponent<AnimancerComponent>();
AnimationClip animationB = Resources.Load<AnimationClip>("Animations/UI/TechButton/TechCheckPanelFadeIn");
animancer.Play(animationB);
}
private void HideTechCheckPanel()
{
AnimancerComponent animancer = ROTechCheckPanel.GetComponent<AnimancerComponent>();
AnimationClip animationB = Resources.Load<AnimationClip>("Animations/UI/TechButton/TechCheckPanelFadeOut"); // 需要播放的动画片段
animancer.Play(animationB); // 直接播放动画B
Timer.Instance.TimerRegister(ROTechTreeUI, OnTechCheckPanelFadeOutComplete, 0.2f,"TechTreeUI_HideTechCheckPanel");
}
private void SetTechCheckPanelShowHide(bool status,TechType techType = TechType.None)
{
TechCheckPanelStatus = status;
TechCheckPanelShowHideAnimMark = true;
TechCheckTechType = techType;
}
private void ResearchTech()
{
//钱不够就不能研发。这种情况理应不会发生
if (Main.MapData.PlayerMap.SelfPlayerData.PlayerWealth + Main.MapData.PlayerMap.SelfPlayerData.PlayerTechPoint < _techNoChooseCost)
return;
Main.PlayerLogic.ResearchTech(Main.MapData,Main.MapData.PlayerMap.SelfPlayerData,_techTypeChoose,_techNoChooseCost);
SetTechCheckPanelShowHide(false);
RenderUpdateTechTreeData();
UIManager.Instance.TopBarUI.RenderMark = true;
}
private void PlayAnimTechTreeShow()
{
ROTechTreeUI.SetActive(true);
AnimancerComponent animancer = ROTechTreeUI.GetComponent<AnimancerComponent>();
AnimationClip animationB = Resources.Load<AnimationClip>("Animations/UI/TechTreePanelFadeIn"); // 需要播放的动画片段
animancer.Play(animationB); // 直接播放动画B
//播放音乐
var civ = Main.MapData.PlayerMap.SelfPlayerData.PlayerCivId;
if(civ == 0)AudioManager.Instance.PlayMusic("RemiliaEgyptian", 1f, 0.3f, true);
if(civ == 1)AudioManager.Instance.PlayMusic("KaguyaFrench",0.3f,0.3f,true);
}
private void PlayAnimTechTreeHide()
{
AnimancerComponent animancer = ROTechTreeUI.GetComponent<AnimancerComponent>();
AnimationClip animationB = Resources.Load<AnimationClip>("Animations/UI/TechTreePanelFadeOut"); ; // 需要播放的动画片段
animancer.Play(animationB); // 直接播放动画B
UIManager.Instance.BottomBarUI.SetBottomBarShowHide(true);
Timer.Instance.TimerRegister(ROTechTreeUI, OnFadeOutComplete,0.2f,"TechTreeUI_PlayAnimTechTreeHide");
//停止播放音乐
AudioManager.Instance.StopMusic();
}
//对外方法设置techtree为show或者hide
public void SetTechTreeShowHide(bool status)
{
ShowHideAnimMark = true;
TechTreeUIStatus = status;
if (status)
TechTreeUIDataRenderMark = true;
}
private void OnFadeOutComplete()
{
ROTechTreeUI.SetActive(false);
}
private void OnTechCheckPanelFadeOutComplete()
{
ROTechCheckPanel.SetActive(false);
}
}