ThreeMapGenerator.cs 85 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043204420452046204720482049205020512052205320542055205620572058205920602061206220632064206520662067206820692070207120722073207420752076207720782079208020812082208320842085208620872088208920902091209220932094209520962097209820992100210121022103210421052106210721082109211021112112211321142115211621172118211921202121212221232124212521262127212821292130213121322133213421352136213721382139214021412142214321442145214621472148214921502151215221532154215521562157215821592160216121622163216421652166216721682169217021712172217321742175217621772178217921802181218221832184218521862187218821892190219121922193219421952196219721982199220022012202220322042205220622072208220922102211221222132214221522162217221822192220222122222223222422252226222722282229223022312232223322342235223622372238223922402241224222432244224522462247224822492250225122522253225422552256225722582259226022612262226322642265226622672268226922702271227222732274227522762277227822792280228122822283228422852286228722882289229022912292229322942295229622972298229923002301230223032304230523062307230823092310231123122313231423152316231723182319232023212322232323242325232623272328232923302331233223332334233523362337233823392340234123422343234423452346234723482349235023512352235323542355235623572358235923602361236223632364236523662367236823692370237123722373237423752376237723782379238023812382238323842385238623872388238923902391239223932394239523962397239823992400240124022403240424052406240724082409241024112412241324142415241624172418241924202421242224232424242524262427242824292430243124322433243424352436243724382439244024412442244324442445244624472448244924502451245224532454245524562457245824592460246124622463246424652466246724682469247024712472247324742475247624772478247924802481248224832484248524862487248824892490249124922493249424952496249724982499250025012502250325042505250625072508250925102511251225132514251525162517251825192520252125222523252425252526252725282529253025312532253325342535253625372538253925402541254225432544254525462547254825492550255125522553255425552556255725582559256025612562256325642565256625672568256925702571257225732574257525762577257825792580258125822583258425852586258725882589259025912592259325942595259625972598259926002601260226032604260526062607260826092610261126122613261426152616261726182619262026212622262326242625262626272628262926302631263226332634263526362637263826392640264126422643
  1. using System.Collections.Generic;
  2. using System.Collections;
  3. using UnityEngine;
  4. using UnityEngine.UI;
  5. public class MapGenerator3D : MonoBehaviour
  6. {
  7. public MapConfigSO mapConfig1;
  8. public MapConfigSO mapConfig2;
  9. public MapConfigSO mapConfig3;
  10. private MapConfigSO mapConfig;
  11. [Header("3D布局参数")]
  12. public float depthGap = 12f;
  13. public float horizontalSpread = 6f;
  14. public float verticalSpacing = 8f;
  15. [Header("房间参数")]
  16. public float roomRadius = 1.5f;
  17. public float roomHeight = 1f;
  18. public Color roomColor = new Color(0.3f, 0.7f, 0.3f);
  19. [Header("房间数据")]
  20. public RoomDataSO minorEnemyRoomData;
  21. public RoomDataSO eliteEnemyRoomData;
  22. public RoomDataSO restRoomData;
  23. public RoomDataSO bossRoomData;
  24. [Header("房间颜色")]
  25. public Color minorEnemyColor = new Color(0.3f, 0.7f, 0.3f);
  26. public Color eliteEnemyColor = new Color(0.8f, 0.3f, 0.3f);
  27. public Color restRoomColor = new Color(0.5f, 0.8f, 0.5f);
  28. public Color bossColor = new Color(0.8f, 0.2f, 0.8f);
  29. [Header("路径参数")]
  30. public float pathWidth = 0.6f;
  31. public Color pathColor = new Color(1f, 0.8f, 0f);
  32. [Header("触发器参数")]
  33. public float triggerRadius = 2f;
  34. public float triggerHeight = 0.5f;
  35. [Header("题目库")]
  36. public List<MathQuestionSO> allQuestions = new List<MathQuestionSO>();
  37. [Header("UI参数")]
  38. public GameObject questionPanel;
  39. public Text questionText;
  40. public Button[] answerButtons;
  41. [Header("血量系统")]
  42. public int baseHealth = 10;
  43. public int currentHealth;
  44. public Text healthText;
  45. public Slider healthSlider;
  46. public GameObject gameOverPanel;
  47. [Header("玩家等级系统")]
  48. public int playerLevel = 1;
  49. public const int MAX_LEVEL = 30;
  50. public int currentExperience = 0;
  51. public int attackPower = 1;
  52. public Text levelText;
  53. public Text attackText;
  54. public Text expText;
  55. [Header("金币UI")]
  56. public Text goldText;
  57. [Header("战斗系统")]
  58. public GameObject battlePanel;
  59. public Image playerIcon;
  60. public Image monsterIcon;
  61. public Slider monsterHealthSlider;
  62. public Text monsterHealthText;
  63. public Text battleStatusText;
  64. [Header("怪物图标")]
  65. public Sprite minorEnemyIcon;
  66. public Sprite eliteEnemyIcon;
  67. public Sprite bossIcon;
  68. public Sprite playerBattleIcon;
  69. [Header("玩家战斗血条")]
  70. public Slider playerBattleHealthSlider;
  71. public Text playerBattleHealthText;
  72. [Header("战斗动画参数")]
  73. public float attackMoveDistance = 50f;
  74. public float attackDuration = 0.15f;
  75. public float shakeDuration = 0.2f;
  76. public float shakeIntensity = 10f;
  77. [Header("子弹效果参数")]
  78. public Color bulletColor = new Color(1f, 0.5f, 0f);
  79. public float bulletSize = 15f;
  80. public float bulletSpeed = 800f;
  81. public float bulletDuration = 0.2f;
  82. private bool isInBattle = false;
  83. private int monsterMaxHealth = 5;
  84. private int monsterCurrentHealth;
  85. private RoonType currentBattleRoomType;
  86. private bool isAnsweringQuestion = false;
  87. private List<GameObject> rooms = new();
  88. private List<GameObject> paths = new();
  89. private List<GameObject> triggers = new();
  90. private const string GUEST_STUDY_ABILITY = "guest_studyAbility";
  91. private const string GUEST_TOTAL_PLAYTIME = "guest_totalPlayTime";
  92. private const string GUEST_TOTAL_ROOMS = "guest_totalRooms";
  93. private const string GUEST_TOTAL_CORRECT = "guest_totalCorrect";
  94. private const string GUEST_TOTAL_WRONG = "guest_totalWrong";
  95. private const string GUEST_TOTAL_GOLD = "guest_totalGold";
  96. private const string GUEST_PLAYER_LEVEL = "guest_playerLevel";
  97. private const string GUEST_EXPERIENCE = "guest_experience";
  98. [Header("学习参数")]
  99. public int studyAbility = 1;
  100. [Header("游戏统计")]
  101. public float gameStartTime;
  102. public float totalPlayTime;
  103. public int currentDifficulty = 1;
  104. public int correctAnswerCount = 0;
  105. public int wrongAnswerCount = 0;
  106. public int totalRoomsCompleted = 0;
  107. public bool isGameCompleted = false;
  108. [Header("场景设置")]
  109. public string menuSceneName = "MenuScene";
  110. public int GetMaxHealth()
  111. {
  112. return baseHealth + (playerLevel - 1);
  113. }
  114. public int GetAttackPower()
  115. {
  116. return 1 + (playerLevel / 10);
  117. }
  118. public void AddExperience(int amount)
  119. {
  120. if (playerLevel >= MAX_LEVEL) return;
  121. currentExperience += amount;
  122. while (currentExperience >= GetExperienceToNextLevel() && playerLevel < MAX_LEVEL)
  123. {
  124. currentExperience -= GetExperienceToNextLevel();
  125. LevelUp();
  126. }
  127. UpdateLevelUI();
  128. }
  129. public int GetExperienceToNextLevel()
  130. {
  131. return 100 * playerLevel;
  132. }
  133. void LevelUp()
  134. {
  135. int oldMaxHealth = GetMaxHealth();
  136. playerLevel++;
  137. int newMaxHealth = GetMaxHealth();
  138. currentHealth += (newMaxHealth - oldMaxHealth);
  139. attackPower = GetAttackPower();
  140. Debug.Log($"升级!当前等级: {playerLevel}, 最大血量: {newMaxHealth}, 攻击力: {attackPower}");
  141. UpdateHealthUI();
  142. UpdatePlayerBattleHealthUI();
  143. }
  144. void UpdateLevelUI()
  145. {
  146. if (levelText != null)
  147. {
  148. levelText.text = $"Lv.{playerLevel}";
  149. }
  150. if (attackText != null)
  151. {
  152. attackText.text = $"ATK:{attackPower}";
  153. }
  154. if (expText != null)
  155. {
  156. expText.text = $"EXP: {currentExperience}/{GetExperienceToNextLevel()}";
  157. }
  158. }
  159. [Header("金币和道具系统")]
  160. public int currentGold = 0;
  161. public int shield = 0;
  162. public bool hasHint = false;
  163. public int doubleGoldTurns = 0;
  164. public List<ItemDataSO> inventory = new List<ItemDataSO>();
  165. public List<ItemDataSO> shopItems = new List<ItemDataSO>();
  166. private GameObject currentTriggerRoom;
  167. private MathQuestionSO currentQuestion;
  168. private QuestionData currentOnlineQuestion;
  169. private int currentQuestionId = -1;
  170. private List<QuestionData> onlineQuestions = new List<QuestionData>();
  171. private bool onlineQuestionsLoadStarted = false;
  172. private bool onlineQuestionsLoadFinished = false;
  173. private bool onlineQuestionsLoadFailed = false;
  174. private string onlineQuestionsLoadError = "";
  175. [Header("2D地图设置")]
  176. public GameObject mapPanelPrefab;
  177. public GameObject roomButtonPrefab;
  178. public GameObject pathLinePrefab;
  179. public Transform player;
  180. private GameObject mapPanel;
  181. private RectTransform mapPanelRect;
  182. private GameObject mapContent;
  183. private List<MapRoomButton> roomButtons = new();
  184. private Dictionary<GameObject, MapRoomButton> roomToButtonMap = new();
  185. // ========== 游戏进度相关 ==========
  186. private int currentMaxColumn = 0;
  187. private Dictionary<int, List<GameObject>> columnGroups = new();
  188. private HashSet<GameObject> completedRooms = new();
  189. private HashSet<GameObject> attemptedRooms = new();
  190. // ========== 房间连接关系(记录所有连接,无论方向) ==========
  191. private Dictionary<GameObject, List<GameObject>> allConnections = new(); // 所有房间之间的连接
  192. private GameObject currentPlayerRoom = null; // 玩家当前所在的房间
  193. private bool isAtRootNode = true; // 是否在逻辑根节点(初始状态)
  194. // ========== 小车移动相关 ==========
  195. [Header("小车移动参数")]
  196. public float maxSpeed = 8f;
  197. public float arrivalDistance = 2f;
  198. public float stopDistance = 0.5f;
  199. private Vector3 targetPosition;
  200. private bool isMoving = false;
  201. private GameObject targetRoom;
  202. // ========== 结束 ==========
  203. private void Start()
  204. {
  205. gameStartTime = Time.time;
  206. InitPlayerLevel();
  207. currentHealth = GetMaxHealth();
  208. currentGold = 0;
  209. shield = 0;
  210. hasHint = false;
  211. doubleGoldTurns = 0;
  212. inventory.Clear();
  213. isInBattle = false;
  214. correctAnswerCount = 0;
  215. wrongAnswerCount = 0;
  216. totalRoomsCompleted = 0;
  217. isGameCompleted = false;
  218. UpdateHealthUI();
  219. UpdateGoldUI();
  220. UpdateLevelUI();
  221. if (battlePanel != null)
  222. battlePanel.SetActive(false);
  223. if (LoginManager.Instance != null && !LoginManager.Instance.IsGuest())
  224. {
  225. studyAbility = LoginManager.Instance.GetStudyAbility();
  226. LoadOnlineQuestions();
  227. }
  228. else
  229. {
  230. LoadGuestData();
  231. }
  232. ChooseMap();
  233. CreateMap();
  234. Create2DMapPanel();
  235. Create2DMap();
  236. currentMaxColumn = 0;
  237. FindInitialPlayerRoom();
  238. if (player != null && columnGroups.ContainsKey(0) && columnGroups[0].Count > 0)
  239. {
  240. GameObject firstRoom = columnGroups[0][0];
  241. Vector3 initialPos = new Vector3(firstRoom.transform.position.x, 0.6f, firstRoom.transform.position.z);
  242. player.position = initialPos;
  243. Debug.Log($"玩家物理位置放置在第一关: {firstRoom.name}");
  244. }
  245. if (questionPanel != null)
  246. questionPanel.SetActive(false);
  247. if (gameOverPanel != null)
  248. gameOverPanel.SetActive(false);
  249. }
  250. void InitPlayerLevel()
  251. {
  252. if (LoginManager.Instance != null && !LoginManager.Instance.IsGuest())
  253. {
  254. UserData user = LoginManager.Instance.GetCurrentUser();
  255. if (user != null)
  256. {
  257. playerLevel = Mathf.Clamp(user.currentLevel, 1, MAX_LEVEL);
  258. currentExperience = user.experience;
  259. }
  260. }
  261. else
  262. {
  263. playerLevel = PlayerPrefs.GetInt(GUEST_PLAYER_LEVEL, 1);
  264. currentExperience = PlayerPrefs.GetInt(GUEST_EXPERIENCE, 0);
  265. }
  266. attackPower = GetAttackPower();
  267. Debug.Log($"初始化玩家等级: {playerLevel}, 攻击力: {attackPower}, 最大血量: {GetMaxHealth()}");
  268. }
  269. void LoadOnlineQuestions()
  270. {
  271. onlineQuestionsLoadStarted = true;
  272. onlineQuestionsLoadFinished = false;
  273. onlineQuestionsLoadFailed = false;
  274. onlineQuestionsLoadError = "";
  275. onlineQuestions.Clear();
  276. if (NetworkManager.Instance == null)
  277. {
  278. onlineQuestionsLoadFinished = true;
  279. onlineQuestionsLoadFailed = true;
  280. onlineQuestionsLoadError = "NetworkManager未初始化。";
  281. Debug.LogWarning($"在线题目不可用: {onlineQuestionsLoadError}");
  282. Debug.LogWarning("NetworkManager未初始化,使用本地题目");
  283. return;
  284. }
  285. int difficulty = currentDifficulty;
  286. bool isReviewMode = GameModeManager.Instance != null && GameModeManager.Instance.IsReviewMode();
  287. if (isReviewMode)
  288. {
  289. Debug.Log("加载复习题库...");
  290. NetworkManager.Instance.GetReviewQuestions(difficulty,
  291. response =>
  292. {
  293. if (response.success)
  294. {
  295. onlineQuestions.Clear();
  296. if (response.data != null)
  297. {
  298. onlineQuestions.AddRange(response.data);
  299. }
  300. onlineQuestionsLoadFinished = true;
  301. onlineQuestionsLoadFailed = onlineQuestions.Count == 0;
  302. onlineQuestionsLoadError = onlineQuestionsLoadFailed ? "复习题目接口返回0道题目。" : "";
  303. Debug.Log($"成功加载 {onlineQuestions.Count} 道复习题目");
  304. }
  305. else
  306. {
  307. onlineQuestionsLoadFinished = true;
  308. onlineQuestionsLoadFailed = true;
  309. onlineQuestionsLoadError = response.message;
  310. Debug.LogWarning($"加载复习题目失败: {response.message}");
  311. }
  312. },
  313. error =>
  314. {
  315. onlineQuestionsLoadFinished = true;
  316. onlineQuestionsLoadFailed = true;
  317. onlineQuestionsLoadError = error;
  318. Debug.LogWarning($"加载复习题目失败: {error}");
  319. }
  320. );
  321. }
  322. else
  323. {
  324. Debug.Log("加载普通题库...");
  325. NetworkManager.Instance.GetQuestionsByDifficulty(difficulty,
  326. response =>
  327. {
  328. if (response.success)
  329. {
  330. onlineQuestions.Clear();
  331. if (response.data != null)
  332. {
  333. onlineQuestions.AddRange(response.data);
  334. }
  335. onlineQuestionsLoadFinished = true;
  336. onlineQuestionsLoadFailed = onlineQuestions.Count == 0;
  337. onlineQuestionsLoadError = onlineQuestionsLoadFailed ? "题目接口返回0道题目。" : "";
  338. Debug.Log($"成功加载 {onlineQuestions.Count} 道在线题目");
  339. }
  340. else
  341. {
  342. onlineQuestionsLoadFinished = true;
  343. onlineQuestionsLoadFailed = true;
  344. onlineQuestionsLoadError = response.message;
  345. Debug.LogWarning($"加载在线题目失败: {response.message}");
  346. }
  347. },
  348. error =>
  349. {
  350. onlineQuestionsLoadFinished = true;
  351. onlineQuestionsLoadFailed = true;
  352. onlineQuestionsLoadError = error;
  353. Debug.LogWarning($"加载在线题目失败: {error}");
  354. }
  355. );
  356. }
  357. }
  358. void ChooseMap()
  359. {
  360. if (studyAbility <= 5) mapConfig = mapConfig1;
  361. else if (studyAbility <= 10) mapConfig = mapConfig2;
  362. else mapConfig = mapConfig3;
  363. }
  364. void CreateMap()
  365. {
  366. List<GameObject> previousColumn = new();
  367. columnGroups.Clear();
  368. allConnections.Clear();
  369. for (int column = 0; column < mapConfig.roomBlueprints.Count; column++)
  370. {
  371. var blueprint = mapConfig.roomBlueprints[column];
  372. int amount = Random.Range(blueprint.min, blueprint.max);
  373. float z = column * depthGap;
  374. List<GameObject> currentColumn = new();
  375. float totalWidth = (amount - 1) * horizontalSpread;
  376. float startX = -totalWidth / 2f;
  377. for (int i = 0; i < amount; i++)
  378. {
  379. float x;
  380. float y = 0f;
  381. if (column == 0 && i == 0)
  382. {
  383. x = 0f;
  384. }
  385. else
  386. {
  387. x = startX + i * horizontalSpread;
  388. }
  389. Vector3 pos = new Vector3(x, y, z);
  390. RoonType roomType = GetRandomRoomType(column, mapConfig.roomBlueprints.Count);
  391. GameObject room = CreateRoom(pos, roomType);
  392. room.name = $"Room_{column}_{i}";
  393. currentColumn.Add(room);
  394. rooms.Add(room);
  395. GameObject trigger = CreateTrigger(room, pos);
  396. triggers.Add(trigger);
  397. allConnections[room] = new List<GameObject>();
  398. }
  399. if (previousColumn.Count > 0)
  400. {
  401. CreateConnections(previousColumn, currentColumn);
  402. }
  403. previousColumn = currentColumn;
  404. columnGroups[column] = currentColumn;
  405. }
  406. // 输出连接关系用于调试
  407. Debug.Log("=== 房间连接关系 ===");
  408. foreach (var kvp in allConnections)
  409. {
  410. string connections = "";
  411. foreach (var target in kvp.Value)
  412. {
  413. connections += target.name + " ";
  414. }
  415. Debug.Log($"{kvp.Key.name} 连接到: {connections}");
  416. }
  417. }
  418. // =========================
  419. // 找到初始玩家房间
  420. // =========================
  421. void FindInitialPlayerRoom()
  422. {
  423. isAtRootNode = true;
  424. currentPlayerRoom = null;
  425. Debug.Log("初始化玩家位置:逻辑根节点,物理位置在1-1");
  426. }
  427. GameObject CreateRoom(Vector3 pos, RoonType roomType)
  428. {
  429. GameObject room = GameObject.CreatePrimitive(PrimitiveType.Cylinder);
  430. room.name = "Room";
  431. room.transform.position = pos;
  432. room.transform.localScale = new Vector3(
  433. roomRadius * 2,
  434. roomHeight * 0.5f,
  435. roomRadius * 2
  436. );
  437. Destroy(room.GetComponent<CapsuleCollider>());
  438. BoxCollider boxCollider = room.AddComponent<BoxCollider>();
  439. boxCollider.size = new Vector3(roomRadius * 2, roomHeight, roomRadius * 2);
  440. Renderer r = room.GetComponent<Renderer>();
  441. r.material = new Material(Shader.Find("Universal Render Pipeline/Lit"));
  442. r.material.color = GetRoomColor(roomType);
  443. RoomDataSO roomData = GetRoomData(roomType);
  444. RoomInfo roomInfo = room.AddComponent<RoomInfo>();
  445. roomInfo.roomType = roomType;
  446. roomInfo.roomData = roomData;
  447. return room;
  448. }
  449. RoomDataSO GetRoomData(RoonType roomType)
  450. {
  451. return roomType switch
  452. {
  453. RoonType.MinorEnemy => minorEnemyRoomData,
  454. RoonType.EliteEnemy => eliteEnemyRoomData,
  455. RoonType.RestRoom => restRoomData,
  456. RoonType.Boss => bossRoomData,
  457. _ => minorEnemyRoomData
  458. };
  459. }
  460. Color GetRoomColor(RoonType roomType)
  461. {
  462. return roomType switch
  463. {
  464. RoonType.MinorEnemy => minorEnemyColor,
  465. RoonType.EliteEnemy => eliteEnemyColor,
  466. RoonType.RestRoom => restRoomColor,
  467. RoonType.Boss => bossColor,
  468. _ => roomColor
  469. };
  470. }
  471. RoonType GetRandomRoomType(int column, int totalColumns)
  472. {
  473. if (column == 0)
  474. {
  475. return RoonType.MinorEnemy;
  476. }
  477. if (column == totalColumns - 1)
  478. {
  479. return RoonType.Boss;
  480. }
  481. float random = Random.value;
  482. if (random < 0.5f)
  483. {
  484. return RoonType.MinorEnemy;
  485. }
  486. else if (random < 0.75f)
  487. {
  488. return RoonType.EliteEnemy;
  489. }
  490. else
  491. {
  492. return RoonType.RestRoom;
  493. }
  494. }
  495. GameObject CreateTrigger(GameObject room, Vector3 roomPos)
  496. {
  497. GameObject triggerObj = new GameObject("RoomTrigger_" + room.name);
  498. triggerObj.transform.position = roomPos + Vector3.up * (roomHeight + triggerHeight);
  499. SphereCollider collider = triggerObj.AddComponent<SphereCollider>();
  500. collider.isTrigger = true;
  501. collider.radius = triggerRadius;
  502. RoomTrigger triggerScript = triggerObj.AddComponent<RoomTrigger>();
  503. triggerScript.SetRoom(room, this);
  504. return triggerObj;
  505. }
  506. void Create2DMapPanel()
  507. {
  508. if (mapPanelPrefab == null)
  509. {
  510. Canvas canvas = FindObjectOfType<Canvas>();
  511. if (canvas == null)
  512. {
  513. GameObject canvasObj = new GameObject("Canvas");
  514. canvas = canvasObj.AddComponent<Canvas>();
  515. canvas.renderMode = RenderMode.ScreenSpaceOverlay;
  516. canvasObj.AddComponent<CanvasScaler>();
  517. canvasObj.AddComponent<GraphicRaycaster>();
  518. }
  519. mapPanel = new GameObject("MapPanel");
  520. mapPanel.transform.SetParent(canvas.transform, false);
  521. mapPanelRect = mapPanel.AddComponent<RectTransform>();
  522. mapPanelRect.sizeDelta = new Vector2(620, 1080);
  523. mapPanelRect.anchorMin = new Vector2(1, 0.5f);
  524. mapPanelRect.anchorMax = new Vector2(1, 0.5f);
  525. mapPanelRect.pivot = new Vector2(1, 0.5f);
  526. mapPanelRect.anchoredPosition = new Vector2(-20, 0);
  527. Image panelImage = mapPanel.AddComponent<Image>();
  528. panelImage.color = new Color(0.1f, 0.1f, 0.1f, 0.8f);
  529. ScrollRect scrollRect = mapPanel.AddComponent<ScrollRect>();
  530. GameObject viewport = new GameObject("Viewport");
  531. viewport.transform.SetParent(mapPanel.transform, false);
  532. RectTransform viewportRect = viewport.AddComponent<RectTransform>();
  533. viewportRect.sizeDelta = new Vector2(0, 0);
  534. viewportRect.anchorMin = Vector2.zero;
  535. viewportRect.anchorMax = Vector2.one;
  536. viewportRect.offsetMin = new Vector2(10, 10);
  537. viewportRect.offsetMax = new Vector2(-10, -10);
  538. Image viewportImage = viewport.AddComponent<Image>();
  539. viewportImage.color = new Color(0.2f, 0.2f, 0.2f, 0.5f);
  540. Mask mask = viewport.AddComponent<Mask>();
  541. mask.showMaskGraphic = true;
  542. mapContent = new GameObject("Content");
  543. mapContent.transform.SetParent(viewport.transform, false);
  544. RectTransform contentRect = mapContent.AddComponent<RectTransform>();
  545. contentRect.anchorMin = new Vector2(0, 1);
  546. contentRect.anchorMax = new Vector2(1, 1);
  547. contentRect.pivot = new Vector2(0.5f, 1);
  548. contentRect.sizeDelta = new Vector2(0, 0);
  549. ContentSizeFitter sizeFitter = mapContent.AddComponent<ContentSizeFitter>();
  550. sizeFitter.verticalFit = ContentSizeFitter.FitMode.PreferredSize;
  551. VerticalLayoutGroup layoutGroup = mapContent.AddComponent<VerticalLayoutGroup>();
  552. layoutGroup.childAlignment = TextAnchor.UpperCenter;
  553. layoutGroup.spacing = 10;
  554. layoutGroup.padding = new RectOffset(10, 10, 20, 20);
  555. layoutGroup.childForceExpandWidth = true;
  556. layoutGroup.childForceExpandHeight = false;
  557. layoutGroup.childControlWidth = true;
  558. layoutGroup.childControlHeight = false;
  559. scrollRect.viewport = viewportRect;
  560. scrollRect.content = contentRect;
  561. scrollRect.horizontal = false;
  562. scrollRect.vertical = true;
  563. GameObject titleObj = new GameObject("Title");
  564. titleObj.transform.SetParent(mapPanel.transform, false);
  565. RectTransform titleRect = titleObj.AddComponent<RectTransform>();
  566. titleRect.anchorMin = new Vector2(0, 1);
  567. titleRect.anchorMax = new Vector2(1, 1);
  568. titleRect.sizeDelta = new Vector2(0, 40);
  569. titleRect.anchoredPosition = new Vector2(0, -20);
  570. Text titleText = titleObj.AddComponent<Text>();
  571. titleText.text = "地图导航";
  572. titleText.font = Resources.GetBuiltinResource<Font>("LegacyRuntime.ttf");
  573. titleText.fontSize = 20;
  574. titleText.alignment = TextAnchor.MiddleCenter;
  575. titleText.color = Color.white;
  576. }
  577. else
  578. {
  579. mapPanel = Instantiate(mapPanelPrefab, FindObjectOfType<Canvas>().transform);
  580. mapPanelRect = mapPanel.GetComponent<RectTransform>();
  581. mapContent = mapPanel.transform.Find("Viewport/Content")?.gameObject;
  582. if (mapContent == null)
  583. {
  584. Debug.LogError("MapPanel预制体结构不正确!");
  585. }
  586. }
  587. if (player == null)
  588. {
  589. player = GameObject.FindGameObjectWithTag("Player")?.transform;
  590. }
  591. CreateHealthUI();
  592. }
  593. void CreateHealthUI()
  594. {
  595. Canvas canvas = FindObjectOfType<Canvas>();
  596. if (canvas == null) return;
  597. GameObject healthPanel = new GameObject("HealthPanel");
  598. healthPanel.transform.SetParent(canvas.transform, false);
  599. RectTransform healthPanelRect = healthPanel.AddComponent<RectTransform>();
  600. healthPanelRect.anchorMin = new Vector2(0, 0);
  601. healthPanelRect.anchorMax = new Vector2(0, 0);
  602. healthPanelRect.pivot = new Vector2(0, 0);
  603. healthPanelRect.anchoredPosition = new Vector2(20, 20);
  604. healthPanelRect.sizeDelta = new Vector2(300, 60);
  605. Image panelBg = healthPanel.AddComponent<Image>();
  606. panelBg.color = new Color(0, 0, 0, 0.5f);
  607. GameObject sliderBg = new GameObject("HealthSlider");
  608. sliderBg.transform.SetParent(healthPanel.transform, false);
  609. RectTransform sliderRect = sliderBg.AddComponent<RectTransform>();
  610. sliderRect.anchorMin = new Vector2(0, 0.5f);
  611. sliderRect.anchorMax = new Vector2(1, 0.5f);
  612. sliderRect.pivot = new Vector2(0.5f, 0.5f);
  613. sliderRect.anchoredPosition = Vector2.zero;
  614. sliderRect.sizeDelta = new Vector2(-20, 20);
  615. Slider slider = sliderBg.AddComponent<Slider>();
  616. Image sliderBgImage = sliderBg.AddComponent<Image>();
  617. sliderBgImage.color = Color.gray;
  618. GameObject fillArea = new GameObject("Fill Area");
  619. fillArea.transform.SetParent(sliderBg.transform, false);
  620. RectTransform fillRect = fillArea.AddComponent<RectTransform>();
  621. fillRect.anchorMin = Vector2.zero;
  622. fillRect.anchorMax = Vector2.one;
  623. fillRect.sizeDelta = Vector2.zero;
  624. GameObject fill = new GameObject("Fill");
  625. fill.transform.SetParent(fillArea.transform, false);
  626. RectTransform fillImageRect = fill.AddComponent<RectTransform>();
  627. fillImageRect.anchorMin = Vector2.zero;
  628. fillImageRect.anchorMax = Vector2.one;
  629. fillImageRect.sizeDelta = Vector2.zero;
  630. Image fillImage = fill.AddComponent<Image>();
  631. fillImage.color = Color.red;
  632. slider.fillRect = fillImageRect;
  633. slider.handleRect = null;
  634. slider.maxValue = GetMaxHealth();
  635. slider.value = currentHealth;
  636. slider.interactable = false;
  637. healthSlider = slider;
  638. GameObject healthTextObj = new GameObject("HealthText");
  639. healthTextObj.transform.SetParent(healthPanel.transform, false);
  640. RectTransform textRect = healthTextObj.AddComponent<RectTransform>();
  641. textRect.anchorMin = Vector2.zero;
  642. textRect.anchorMax = Vector2.one;
  643. textRect.sizeDelta = Vector2.zero;
  644. healthText = healthTextObj.AddComponent<Text>();
  645. healthText.text = $"{currentHealth}/{GetMaxHealth()}";
  646. healthText.font = Resources.GetBuiltinResource<Font>("LegacyRuntime.ttf");
  647. healthText.fontSize = 18;
  648. healthText.alignment = TextAnchor.MiddleCenter;
  649. healthText.color = Color.white;
  650. CreateLevelUI();
  651. }
  652. void CreateLevelUI()
  653. {
  654. Canvas canvas = FindObjectOfType<Canvas>();
  655. if (canvas == null) return;
  656. GameObject levelPanel = new GameObject("LevelPanel");
  657. levelPanel.transform.SetParent(canvas.transform, false);
  658. RectTransform levelPanelRect = levelPanel.AddComponent<RectTransform>();
  659. levelPanelRect.anchorMin = new Vector2(0, 0);
  660. levelPanelRect.anchorMax = new Vector2(0, 0);
  661. levelPanelRect.pivot = new Vector2(0, 0);
  662. levelPanelRect.anchoredPosition = new Vector2(20, 90);
  663. levelPanelRect.sizeDelta = new Vector2(200, 70);
  664. Image panelBg = levelPanel.AddComponent<Image>();
  665. panelBg.color = new Color(0, 0, 0, 0.5f);
  666. GameObject levelTextObj = new GameObject("LevelText");
  667. levelTextObj.transform.SetParent(levelPanel.transform, false);
  668. RectTransform levelTextRect = levelTextObj.AddComponent<RectTransform>();
  669. levelTextRect.anchorMin = new Vector2(0, 0.5f);
  670. levelTextRect.anchorMax = new Vector2(0.5f, 1);
  671. levelTextRect.sizeDelta = Vector2.zero;
  672. levelText = levelTextObj.AddComponent<Text>();
  673. levelText.text = $"Lv.{playerLevel}";
  674. levelText.font = Resources.GetBuiltinResource<Font>("LegacyRuntime.ttf");
  675. levelText.fontSize = 22;
  676. levelText.alignment = TextAnchor.MiddleCenter;
  677. levelText.color = Color.yellow;
  678. levelText.fontStyle = FontStyle.Bold;
  679. GameObject expTextObj = new GameObject("ExpText");
  680. expTextObj.transform.SetParent(levelPanel.transform, false);
  681. RectTransform expTextRect = expTextObj.AddComponent<RectTransform>();
  682. expTextRect.anchorMin = new Vector2(0, 0);
  683. expTextRect.anchorMax = new Vector2(1, 0.5f);
  684. expTextRect.sizeDelta = Vector2.zero;
  685. expText = expTextObj.AddComponent<Text>();
  686. expText.text = $"EXP: {currentExperience}/{GetExperienceToNextLevel()}";
  687. expText.font = Resources.GetBuiltinResource<Font>("LegacyRuntime.ttf");
  688. expText.fontSize = 14;
  689. expText.alignment = TextAnchor.MiddleCenter;
  690. expText.color = Color.white;
  691. GameObject attackTextObj = new GameObject("AttackText");
  692. attackTextObj.transform.SetParent(levelPanel.transform, false);
  693. RectTransform attackTextRect = attackTextObj.AddComponent<RectTransform>();
  694. attackTextRect.anchorMin = new Vector2(0.5f, 0.5f);
  695. attackTextRect.anchorMax = new Vector2(1, 1);
  696. attackTextRect.sizeDelta = Vector2.zero;
  697. attackText = attackTextObj.AddComponent<Text>();
  698. attackText.text = $"ATK:{attackPower}";
  699. attackText.font = Resources.GetBuiltinResource<Font>("LegacyRuntime.ttf");
  700. attackText.fontSize = 16;
  701. attackText.alignment = TextAnchor.MiddleCenter;
  702. attackText.color = Color.red;
  703. }
  704. void UpdateHealthUI()
  705. {
  706. if (healthText != null)
  707. {
  708. healthText.text = $"{currentHealth}/{GetMaxHealth()}";
  709. }
  710. if (healthSlider != null)
  711. {
  712. healthSlider.value = currentHealth;
  713. }
  714. }
  715. void TakeDamage(int damage)
  716. {
  717. if (shield > 0)
  718. {
  719. int absorbed = Mathf.Min(shield, damage);
  720. shield -= absorbed;
  721. damage -= absorbed;
  722. Debug.Log($"护盾吸收了 {absorbed} 点伤害!剩余护盾: {shield}");
  723. }
  724. currentHealth -= damage;
  725. currentHealth = Mathf.Max(0, currentHealth);
  726. UpdateHealthUI();
  727. UpdatePlayerBattleHealthUI();
  728. Debug.Log($"受到{damage}点伤害!当前血量:{currentHealth}/{GetMaxHealth()}");
  729. if (currentHealth <= 0)
  730. {
  731. GameOver();
  732. }
  733. }
  734. void UpdateGoldUI()
  735. {
  736. if (goldText != null)
  737. {
  738. goldText.text = $"金币: {currentGold}";
  739. }
  740. }
  741. void AddGold(int amount)
  742. {
  743. if (doubleGoldTurns > 0)
  744. {
  745. amount *= 2;
  746. Debug.Log($"金币翻倍!获得 {amount} 金币");
  747. }
  748. currentGold += amount;
  749. UpdateGoldUI();
  750. Debug.Log($"获得 {amount} 金币!当前金币: {currentGold}");
  751. }
  752. public void HealPlayer(int amount)
  753. {
  754. currentHealth = Mathf.Min(currentHealth + amount, GetMaxHealth());
  755. UpdateHealthUI();
  756. UpdatePlayerBattleHealthUI();
  757. Debug.Log($"恢复 {amount} 点生命!当前生命: {currentHealth}/{GetMaxHealth()}");
  758. }
  759. public void AddShield(int amount)
  760. {
  761. shield += amount;
  762. Debug.Log($"获得 {amount} 点护盾!当前护盾: {shield}");
  763. }
  764. public void ActivateDoubleGold(int turns)
  765. {
  766. doubleGoldTurns = turns;
  767. Debug.Log($"激活金币翻倍!持续 {turns} 个房间");
  768. }
  769. public void ActivateHint()
  770. {
  771. hasHint = true;
  772. Debug.Log("激活提示!下次答题将显示提示");
  773. }
  774. void UseHint()
  775. {
  776. if (!hasHint) return;
  777. hasHint = false;
  778. if (currentQuestion != null)
  779. {
  780. Debug.Log($"提示:正确答案是 {currentQuestion.correctAnswer}");
  781. }
  782. }
  783. void AddItemToInventory(ItemDataSO item)
  784. {
  785. inventory.Add(item);
  786. Debug.Log($"获得道具:{item.itemName}");
  787. }
  788. void UseItem(int index)
  789. {
  790. if (index < 0 || index >= inventory.Count) return;
  791. ItemDataSO item = inventory[index];
  792. IItemEffect effect = item.GetEffect();
  793. if (effect != null)
  794. {
  795. effect.ApplyEffect(this);
  796. inventory.RemoveAt(index);
  797. Debug.Log($"使用道具:{item.itemName}");
  798. }
  799. }
  800. void GameOver()
  801. {
  802. Debug.Log("游戏结束!");
  803. totalPlayTime = Time.time - gameStartTime;
  804. UpdateAchievementsOnGameEnd();
  805. SaveGameSession();
  806. Time.timeScale = 0f;
  807. if (gameOverPanel != null)
  808. {
  809. gameOverPanel.SetActive(true);
  810. }
  811. else
  812. {
  813. CreateGameOverPanel(false);
  814. }
  815. }
  816. void Victory()
  817. {
  818. Debug.Log("通关成功!");
  819. totalPlayTime = Time.time - gameStartTime;
  820. UpdateAchievementsOnGameEnd();
  821. SaveGameSession();
  822. Time.timeScale = 0f;
  823. if (gameOverPanel != null)
  824. {
  825. gameOverPanel.SetActive(true);
  826. }
  827. else
  828. {
  829. CreateGameOverPanel(true);
  830. }
  831. }
  832. void UpdateAchievementsOnGameEnd()
  833. {
  834. if (AchievementManager.Instance != null)
  835. {
  836. AchievementManager.Instance.UpdateAchievement(AchievementType.CollectGold, currentGold);
  837. AchievementManager.Instance.UpdateAchievement(AchievementType.PlayTime, (int)totalPlayTime);
  838. }
  839. }
  840. void UpdateAchievementProgress(AchievementType type, int value)
  841. {
  842. if (AchievementManager.Instance != null)
  843. {
  844. AchievementManager.Instance.UpdateAchievement(type, value);
  845. }
  846. }
  847. void SaveGameSession()
  848. {
  849. if (LoginManager.Instance == null || LoginManager.Instance.IsGuest())
  850. {
  851. SaveGuestData();
  852. return;
  853. }
  854. if (NetworkManager.Instance == null)
  855. {
  856. Debug.LogWarning("NetworkManager未初始化,无法保存游戏数据");
  857. return;
  858. }
  859. GameResultRequest resultRequest = new GameResultRequest
  860. {
  861. playTime = totalPlayTime,
  862. roomsCompleted = totalRoomsCompleted,
  863. correctAnswers = correctAnswerCount,
  864. wrongAnswers = wrongAnswerCount,
  865. difficulty = currentDifficulty,
  866. isCompleted = isGameCompleted,
  867. goldCollected = currentGold
  868. };
  869. NetworkManager.Instance.SaveGameResult(resultRequest,
  870. response =>
  871. {
  872. if (response.success)
  873. {
  874. ApplyServerStudyAbility(response.data);
  875. Debug.Log($"游戏数据保存成功!时长: {totalPlayTime:F1}秒, 房间: {totalRoomsCompleted}, 正确: {correctAnswerCount}, 错误: {wrongAnswerCount}");
  876. }
  877. else
  878. {
  879. Debug.LogWarning($"保存游戏数据失败: {response.message}");
  880. }
  881. },
  882. error =>
  883. {
  884. Debug.LogWarning($"保存游戏数据失败: {error}");
  885. }
  886. );
  887. SyncPlayerLevelToServer();
  888. SyncAchievementsToServer();
  889. }
  890. void ApplyServerStudyAbility(GameResultData resultData)
  891. {
  892. if (resultData == null || resultData.nextStudyAbility <= 0)
  893. {
  894. return;
  895. }
  896. studyAbility = resultData.nextStudyAbility;
  897. if (LoginManager.Instance != null)
  898. {
  899. LoginManager.Instance.SetStudyAbility(studyAbility);
  900. }
  901. Debug.Log($"服务器学习力已同步,新学习力: {studyAbility}");
  902. }
  903. void SyncPlayerLevelToServer()
  904. {
  905. if (LoginManager.Instance == null || LoginManager.Instance.IsGuest()) return;
  906. if (NetworkManager.Instance == null) return;
  907. NetworkManager.Instance.UpdatePlayerLevel(
  908. playerLevel,
  909. currentExperience,
  910. response =>
  911. {
  912. if (response.success)
  913. {
  914. Debug.Log($"等级同步成功!等级: {playerLevel}, 经验: {currentExperience}");
  915. }
  916. else
  917. {
  918. Debug.LogWarning($"等级同步失败: {response.message}");
  919. }
  920. },
  921. error =>
  922. {
  923. Debug.LogWarning($"等级同步请求失败: {error}");
  924. }
  925. );
  926. }
  927. void SaveGuestData()
  928. {
  929. float savedPlayTime = PlayerPrefs.GetFloat(GUEST_TOTAL_PLAYTIME, 0f);
  930. int savedRooms = PlayerPrefs.GetInt(GUEST_TOTAL_ROOMS, 0);
  931. int savedCorrect = PlayerPrefs.GetInt(GUEST_TOTAL_CORRECT, 0);
  932. int savedWrong = PlayerPrefs.GetInt(GUEST_TOTAL_WRONG, 0);
  933. int savedGold = PlayerPrefs.GetInt(GUEST_TOTAL_GOLD, 0);
  934. PlayerPrefs.SetFloat(GUEST_TOTAL_PLAYTIME, savedPlayTime + totalPlayTime);
  935. PlayerPrefs.SetInt(GUEST_TOTAL_ROOMS, savedRooms + totalRoomsCompleted);
  936. PlayerPrefs.SetInt(GUEST_TOTAL_CORRECT, savedCorrect + correctAnswerCount);
  937. PlayerPrefs.SetInt(GUEST_TOTAL_WRONG, savedWrong + wrongAnswerCount);
  938. PlayerPrefs.SetInt(GUEST_TOTAL_GOLD, savedGold + currentGold);
  939. PlayerPrefs.SetInt(GUEST_PLAYER_LEVEL, playerLevel);
  940. PlayerPrefs.SetInt(GUEST_EXPERIENCE, currentExperience);
  941. PlayerPrefs.Save();
  942. UpdateGuestStudyAbility();
  943. if (AchievementManager.Instance != null)
  944. {
  945. AchievementManager.Instance.SaveGuestAchievements();
  946. }
  947. Debug.Log($"游客数据已保存!等级: {playerLevel}, 经验: {currentExperience}, 总时长: {savedPlayTime + totalPlayTime:F1}秒");
  948. }
  949. void UpdateGuestStudyAbility()
  950. {
  951. float correctRate = (correctAnswerCount + wrongAnswerCount) > 0
  952. ? (float)correctAnswerCount / (correctAnswerCount + wrongAnswerCount)
  953. : 0f;
  954. int abilityChange = 0;
  955. if (correctRate >= 0.9f && totalRoomsCompleted >= 5)
  956. {
  957. abilityChange = 1;
  958. }
  959. else if (correctRate < 0.5f && wrongAnswerCount > 5)
  960. {
  961. abilityChange = -1;
  962. }
  963. if (abilityChange != 0)
  964. {
  965. int currentAbility = PlayerPrefs.GetInt(GUEST_STUDY_ABILITY, 1);
  966. int newAbility = Mathf.Max(1, currentAbility + abilityChange);
  967. PlayerPrefs.SetInt(GUEST_STUDY_ABILITY, newAbility);
  968. PlayerPrefs.Save();
  969. studyAbility = newAbility;
  970. Debug.Log($"游客学习力更新!新学习力: {newAbility}");
  971. }
  972. }
  973. void LoadGuestData()
  974. {
  975. studyAbility = PlayerPrefs.GetInt(GUEST_STUDY_ABILITY, 1);
  976. Debug.Log($"加载游客数据,学习力: {studyAbility}");
  977. }
  978. public static int GetGuestStudyAbility()
  979. {
  980. return PlayerPrefs.GetInt(GUEST_STUDY_ABILITY, 1);
  981. }
  982. public static float GetGuestTotalPlayTime()
  983. {
  984. return PlayerPrefs.GetFloat(GUEST_TOTAL_PLAYTIME, 0f);
  985. }
  986. public static int GetGuestTotalRooms()
  987. {
  988. return PlayerPrefs.GetInt(GUEST_TOTAL_ROOMS, 0);
  989. }
  990. public static int GetGuestTotalCorrect()
  991. {
  992. return PlayerPrefs.GetInt(GUEST_TOTAL_CORRECT, 0);
  993. }
  994. public static int GetGuestTotalWrong()
  995. {
  996. return PlayerPrefs.GetInt(GUEST_TOTAL_WRONG, 0);
  997. }
  998. void UpdateStudyAbilityOnGameEnd()
  999. {
  1000. if (LoginManager.Instance == null || LoginManager.Instance.IsGuest()) return;
  1001. float correctRate = (correctAnswerCount + wrongAnswerCount) > 0
  1002. ? (float)correctAnswerCount / (correctAnswerCount + wrongAnswerCount)
  1003. : 0f;
  1004. int abilityChange = 0;
  1005. if (correctRate >= 0.9f && totalRoomsCompleted >= 5)
  1006. {
  1007. abilityChange = 1;
  1008. }
  1009. else if (correctRate < 0.5f && wrongAnswerCount > 5)
  1010. {
  1011. abilityChange = -1;
  1012. }
  1013. if (abilityChange != 0)
  1014. {
  1015. int newAbility = Mathf.Max(1, studyAbility + abilityChange);
  1016. NetworkManager.Instance.UpdateStudyAbility(newAbility,
  1017. response =>
  1018. {
  1019. if (response.success)
  1020. {
  1021. studyAbility = newAbility;
  1022. if (LoginManager.Instance != null)
  1023. {
  1024. LoginManager.Instance.SetStudyAbility(newAbility);
  1025. }
  1026. Debug.Log($"学习力更新成功!新学习力: {newAbility}");
  1027. }
  1028. },
  1029. error =>
  1030. {
  1031. Debug.LogWarning($"更新学习力失败: {error}");
  1032. }
  1033. );
  1034. }
  1035. }
  1036. void SyncAchievementsToServer()
  1037. {
  1038. if (LoginManager.Instance == null || LoginManager.Instance.IsGuest()) return;
  1039. if (AchievementManager.Instance == null) return;
  1040. AchievementManager.Instance.SyncToServer();
  1041. }
  1042. void CreateGameOverPanel(bool isVictory)
  1043. {
  1044. Canvas canvas = FindObjectOfType<Canvas>();
  1045. if (canvas == null) return;
  1046. gameOverPanel = new GameObject("GameOverPanel");
  1047. gameOverPanel.transform.SetParent(canvas.transform, false);
  1048. RectTransform panelRect = gameOverPanel.AddComponent<RectTransform>();
  1049. panelRect.anchorMin = Vector2.zero;
  1050. panelRect.anchorMax = Vector2.one;
  1051. panelRect.sizeDelta = Vector2.zero;
  1052. Image panelImage = gameOverPanel.AddComponent<Image>();
  1053. panelImage.color = isVictory ? new Color(0, 0.3f, 0, 0.85f) : new Color(0.3f, 0, 0, 0.85f);
  1054. GameObject textObj = new GameObject("GameOverText");
  1055. textObj.transform.SetParent(gameOverPanel.transform, false);
  1056. RectTransform textRect = textObj.AddComponent<RectTransform>();
  1057. textRect.anchorMin = new Vector2(0.5f, 0.5f);
  1058. textRect.anchorMax = new Vector2(0.5f, 0.5f);
  1059. textRect.pivot = new Vector2(0.5f, 0.5f);
  1060. textRect.sizeDelta = new Vector2(500, 200);
  1061. Text gameOverText = textObj.AddComponent<Text>();
  1062. if (isVictory)
  1063. {
  1064. gameOverText.text = $"通关成功!\n\n用时: {totalPlayTime:F1}秒\n完成房间: {totalRoomsCompleted}\n正确率: {(correctAnswerCount + wrongAnswerCount > 0 ? (float)correctAnswerCount / (correctAnswerCount + wrongAnswerCount) * 100 : 0):F1}%\n\n点击返回菜单";
  1065. gameOverText.color = Color.yellow;
  1066. }
  1067. else
  1068. {
  1069. gameOverText.text = $"游戏结束\n\n用时: {totalPlayTime:F1}秒\n完成房间: {totalRoomsCompleted}\n正确率: {(correctAnswerCount + wrongAnswerCount > 0 ? (float)correctAnswerCount / (correctAnswerCount + wrongAnswerCount) * 100 : 0):F1}%\n\n点击返回菜单";
  1070. gameOverText.color = Color.white;
  1071. }
  1072. gameOverText.font = Resources.GetBuiltinResource<Font>("LegacyRuntime.ttf");
  1073. gameOverText.fontSize = 28;
  1074. gameOverText.alignment = TextAnchor.MiddleCenter;
  1075. Button restartButton = gameOverPanel.AddComponent<Button>();
  1076. restartButton.onClick.AddListener(RestartGame);
  1077. }
  1078. void RestartGame()
  1079. {
  1080. Time.timeScale = 1f;
  1081. UnityEngine.SceneManagement.SceneManager.LoadScene(menuSceneName);
  1082. }
  1083. void Create2DMap()
  1084. {
  1085. if (mapContent == null)
  1086. {
  1087. Debug.LogError("Map Content未创建!");
  1088. return;
  1089. }
  1090. roomToButtonMap.Clear();
  1091. roomButtons.Clear();
  1092. for (int col = 0; col < mapConfig.roomBlueprints.Count; col++)
  1093. {
  1094. if (!columnGroups.ContainsKey(col)) continue;
  1095. GameObject columnHeader = new GameObject($"Column_{col}_Header");
  1096. columnHeader.transform.SetParent(mapContent.transform, false);
  1097. RectTransform headerRect = columnHeader.AddComponent<RectTransform>();
  1098. headerRect.sizeDelta = new Vector2(0, 30);
  1099. Text headerText = columnHeader.AddComponent<Text>();
  1100. headerText.text = $"第{col + 1}关";
  1101. headerText.font = Resources.GetBuiltinResource<Font>("LegacyRuntime.ttf");
  1102. headerText.fontSize = 18;
  1103. headerText.fontStyle = FontStyle.Bold;
  1104. headerText.alignment = TextAnchor.MiddleCenter;
  1105. headerText.color = Color.white;
  1106. foreach (GameObject room in columnGroups[col])
  1107. {
  1108. CreateRoomButton(room, col);
  1109. }
  1110. if (col < mapConfig.roomBlueprints.Count - 1)
  1111. {
  1112. GameObject separator = new GameObject("Separator");
  1113. separator.transform.SetParent(mapContent.transform, false);
  1114. RectTransform sepRect = separator.AddComponent<RectTransform>();
  1115. sepRect.sizeDelta = new Vector2(0, 2);
  1116. Image sepImage = separator.AddComponent<Image>();
  1117. sepImage.color = new Color(0.5f, 0.5f, 0.5f, 0.5f);
  1118. }
  1119. }
  1120. UpdateRoomButtonsState();
  1121. }
  1122. void CreateRoomButton(GameObject room, int columnIndex)
  1123. {
  1124. GameObject buttonObj;
  1125. if (roomButtonPrefab != null)
  1126. {
  1127. buttonObj = Instantiate(roomButtonPrefab, mapContent.transform);
  1128. }
  1129. else
  1130. {
  1131. buttonObj = new GameObject($"RoomButton_{room.name}");
  1132. buttonObj.transform.SetParent(mapContent.transform, false);
  1133. RectTransform btnRect = buttonObj.AddComponent<RectTransform>();
  1134. btnRect.sizeDelta = new Vector2(0, 50);
  1135. Image btnImage = buttonObj.AddComponent<Image>();
  1136. btnImage.color = new Color(0.3f, 0.6f, 0.3f);
  1137. Button button = buttonObj.AddComponent<Button>();
  1138. button.targetGraphic = btnImage;
  1139. GameObject textObj = new GameObject("Text");
  1140. textObj.transform.SetParent(buttonObj.transform, false);
  1141. RectTransform textRect = textObj.AddComponent<RectTransform>();
  1142. textRect.anchorMin = Vector2.zero;
  1143. textRect.anchorMax = Vector2.one;
  1144. textRect.sizeDelta = Vector2.zero;
  1145. Text text = textObj.AddComponent<Text>();
  1146. text.text = $"房间 {columnIndex + 1}-{room.name.Split('_')[2]}";
  1147. text.font = Resources.GetBuiltinResource<Font>("LegacyRuntime.ttf");
  1148. text.fontSize = 14;
  1149. text.alignment = TextAnchor.MiddleCenter;
  1150. text.color = Color.white;
  1151. }
  1152. MapRoomButton roomButton = buttonObj.GetComponent<MapRoomButton>();
  1153. if (roomButton == null)
  1154. {
  1155. roomButton = buttonObj.AddComponent<MapRoomButton>();
  1156. }
  1157. roomButton.Initialize(room, this, columnIndex);
  1158. roomToButtonMap[room] = roomButton;
  1159. roomButtons.Add(roomButton);
  1160. }
  1161. private int GetRoomColumnIndex(GameObject room)
  1162. {
  1163. string[] nameParts = room.name.Split('_');
  1164. if (nameParts.Length >= 2)
  1165. {
  1166. int columnIndex;
  1167. if (int.TryParse(nameParts[1], out columnIndex))
  1168. {
  1169. return columnIndex;
  1170. }
  1171. }
  1172. return -1;
  1173. }
  1174. // =========================
  1175. // 更新房间按钮状态(只允许向前移动)
  1176. // =========================
  1177. public void UpdateRoomButtonsState()
  1178. {
  1179. if (player == null) return;
  1180. // 更新玩家当前所在的房间
  1181. UpdateCurrentPlayerRoom();
  1182. // 获取从当前房间可到达的房间列表(只向前)
  1183. HashSet<GameObject> reachableRooms = GetForwardReachableRoomsFromCurrent();
  1184. foreach (var button in roomButtons)
  1185. {
  1186. if (button.room == null) continue;
  1187. float distance = Vector3.Distance(player.position, button.room.transform.position);
  1188. GameObject trigger = triggers.Find(t =>
  1189. t.GetComponent<RoomTrigger>()?.GetRoom() == button.room);
  1190. bool isTriggerActive = trigger != null;
  1191. bool isCompleted = completedRooms.Contains(button.room);
  1192. bool isAttempted = attemptedRooms.Contains(button.room);
  1193. int roomColumn = button.columnIndex;
  1194. bool isCurrentRoom = (currentPlayerRoom == button.room);
  1195. // 判断是否可以传送
  1196. bool canTeleport = !isAnsweringQuestion && !isMoving && !isCompleted && isTriggerActive &&
  1197. (isCurrentRoom || reachableRooms.Contains(button.room));
  1198. // 额外检查:不能传送到当前列或之前的房间(当前房间除外)
  1199. if (currentPlayerRoom != null && !isCurrentRoom)
  1200. {
  1201. int currentColumn = GetRoomColumnIndex(currentPlayerRoom);
  1202. if (roomColumn <= currentColumn)
  1203. {
  1204. canTeleport = false;
  1205. }
  1206. }
  1207. // 更新按钮状态
  1208. button.UpdateState(distance, isTriggerActive, isCompleted, isAttempted, canTeleport, isCurrentRoom);
  1209. }
  1210. UpdateColumnHeaders();
  1211. }
  1212. // =========================
  1213. // 更新玩家当前所在的房间
  1214. // =========================
  1215. private void UpdateCurrentPlayerRoom()
  1216. {
  1217. if (player == null) return;
  1218. if (isAtRootNode)
  1219. {
  1220. return;
  1221. }
  1222. GameObject closestRoom = null;
  1223. float minDistance = float.MaxValue;
  1224. foreach (var room in rooms)
  1225. {
  1226. float distance = Vector3.Distance(player.position, room.transform.position);
  1227. if (distance < minDistance)
  1228. {
  1229. minDistance = distance;
  1230. closestRoom = room;
  1231. }
  1232. }
  1233. // 如果玩家距离房间足够近(5个单位内),则认为玩家在这个房间
  1234. if (closestRoom != null && minDistance < 5f)
  1235. {
  1236. if (currentPlayerRoom != closestRoom)
  1237. {
  1238. currentPlayerRoom = closestRoom;
  1239. Debug.Log($"玩家进入房间: {currentPlayerRoom.name}");
  1240. }
  1241. }
  1242. }
  1243. // =========================
  1244. // 核心方法:获取从当前房间可向前到达的房间
  1245. // 使用BFS探索所有连接,但只允许向列索引更大的方向移动
  1246. // =========================
  1247. private HashSet<GameObject> GetForwardReachableRoomsFromCurrent()
  1248. {
  1249. HashSet<GameObject> reachable = new HashSet<GameObject>();
  1250. if (currentPlayerRoom == null)
  1251. {
  1252. // 初始状态,只能进入第一列第一个房间
  1253. if (columnGroups.ContainsKey(0) && columnGroups[0].Count > 0)
  1254. {
  1255. reachable.Add(columnGroups[0][0]);
  1256. }
  1257. return reachable;
  1258. }
  1259. int currentColumn = GetRoomColumnIndex(currentPlayerRoom);
  1260. // 使用BFS查找所有可到达的未完成房间
  1261. // 关键:探索所有连接,但在添加到可达列表时只添加列索引大于等于当前列的房间
  1262. Queue<GameObject> queue = new Queue<GameObject>();
  1263. HashSet<GameObject> visited = new HashSet<GameObject>();
  1264. queue.Enqueue(currentPlayerRoom);
  1265. visited.Add(currentPlayerRoom);
  1266. while (queue.Count > 0)
  1267. {
  1268. GameObject current = queue.Dequeue();
  1269. int currentCol = GetRoomColumnIndex(current);
  1270. // 获取当前房间的所有连接
  1271. if (allConnections.ContainsKey(current))
  1272. {
  1273. foreach (GameObject nextRoom in allConnections[current])
  1274. {
  1275. if (!visited.Contains(nextRoom))
  1276. {
  1277. visited.Add(nextRoom);
  1278. int nextCol = GetRoomColumnIndex(nextRoom);
  1279. // 如果房间未完成,并且列索引大于当前玩家所在列,添加到可达列表
  1280. if (!completedRooms.Contains(nextRoom))
  1281. {
  1282. // 只允许向前移动:目标列索引 > 玩家当前所在列
  1283. if (nextCol > currentColumn)
  1284. {
  1285. reachable.Add(nextRoom);
  1286. }
  1287. }
  1288. // 如果房间已完成或已尝试,可以继续探索其所有连接
  1289. if (completedRooms.Contains(nextRoom) || attemptedRooms.Contains(nextRoom))
  1290. {
  1291. queue.Enqueue(nextRoom);
  1292. }
  1293. }
  1294. }
  1295. }
  1296. }
  1297. return reachable;
  1298. }
  1299. // =========================
  1300. // 获取玩家当前所在列
  1301. // =========================
  1302. private int GetPlayerCurrentColumn()
  1303. {
  1304. if (currentPlayerRoom != null)
  1305. {
  1306. return GetRoomColumnIndex(currentPlayerRoom);
  1307. }
  1308. return -1;
  1309. }
  1310. // =========================
  1311. // 判断是否可以传送
  1312. // =========================
  1313. private bool CanTeleportToRoom(GameObject room)
  1314. {
  1315. if (isAnsweringQuestion) return false;
  1316. if (isMoving) return false;
  1317. if (completedRooms.Contains(room)) return false;
  1318. // 检查触发器是否存在
  1319. GameObject trigger = triggers.Find(t =>
  1320. t.GetComponent<RoomTrigger>()?.GetRoom() == room);
  1321. if (trigger == null) return false;
  1322. // 如果是当前房间,可以传送
  1323. if (currentPlayerRoom == room) return true;
  1324. // 检查列索引:不能传送到当前列或之前的房间(当前房间除外)
  1325. if (currentPlayerRoom != null)
  1326. {
  1327. int currentColumn = GetRoomColumnIndex(currentPlayerRoom);
  1328. int targetColumn = GetRoomColumnIndex(room);
  1329. if (targetColumn <= currentColumn)
  1330. {
  1331. return false; // 不能后退或停留在同一列
  1332. }
  1333. }
  1334. // 检查是否在可达房间列表中
  1335. HashSet<GameObject> reachable = GetForwardReachableRoomsFromCurrent();
  1336. return reachable.Contains(room);
  1337. }
  1338. private void UpdateColumnHeaders()
  1339. {
  1340. int playerColumn = GetPlayerCurrentColumn();
  1341. for (int i = 0; i < mapContent.transform.childCount; i++)
  1342. {
  1343. Transform child = mapContent.transform.GetChild(i);
  1344. if (child.name.StartsWith("Column_") && child.name.EndsWith("_Header"))
  1345. {
  1346. Text headerText = child.GetComponent<Text>();
  1347. if (headerText != null)
  1348. {
  1349. string[] parts = child.name.Split('_');
  1350. if (parts.Length >= 2)
  1351. {
  1352. int colIndex;
  1353. if (int.TryParse(parts[1], out colIndex))
  1354. {
  1355. headerText.color = colIndex == playerColumn ? Color.yellow : Color.white;
  1356. }
  1357. }
  1358. }
  1359. }
  1360. }
  1361. }
  1362. // =========================
  1363. // 传送逻辑
  1364. // =========================
  1365. public void TeleportToRoom(GameObject room)
  1366. {
  1367. if (player == null)
  1368. {
  1369. Debug.LogError("Player未设置!");
  1370. return;
  1371. }
  1372. if (isAnsweringQuestion)
  1373. {
  1374. Debug.LogWarning("正在答题中,无法移动!");
  1375. return;
  1376. }
  1377. if (isMoving)
  1378. {
  1379. Debug.LogWarning("正在移动中,无法切换目标!");
  1380. return;
  1381. }
  1382. if (!CanTeleportToRoom(room))
  1383. {
  1384. Debug.LogWarning($"无法移动到房间 {room.name}!只能向前移动到已连接且未完成的房间。");
  1385. return;
  1386. }
  1387. targetRoom = room;
  1388. targetPosition = new Vector3(room.transform.position.x, 0.6f, room.transform.position.z);
  1389. isMoving = true;
  1390. Debug.Log($"开始移动到房间: {room.name}");
  1391. UpdateRoomButtonsState();
  1392. }
  1393. void SelectRandomQuestion(RoonType roomType)
  1394. {
  1395. int targetDifficulty = GetDifficultyByRoomType(roomType);
  1396. currentOnlineQuestion = null;
  1397. currentQuestion = null;
  1398. currentQuestionId = -1;
  1399. bool isLoggedInUser = LoginManager.Instance != null && !LoginManager.Instance.IsGuest();
  1400. bool isReviewMode = GameModeManager.Instance != null && GameModeManager.Instance.IsReviewMode();
  1401. if (isLoggedInUser && onlineQuestions.Count > 0)
  1402. {
  1403. SelectOnlineQuestion(targetDifficulty);
  1404. }
  1405. else if (isLoggedInUser && isReviewMode)
  1406. {
  1407. if (!onlineQuestionsLoadStarted)
  1408. {
  1409. Debug.LogError("复习模式需要接口题目,但题目加载尚未开始。");
  1410. }
  1411. else if (!onlineQuestionsLoadFinished)
  1412. {
  1413. Debug.LogWarning("复习题目仍在加载,请等待接口请求完成。");
  1414. }
  1415. else
  1416. {
  1417. Debug.LogError($"复习模式没有可用的接口题目。{onlineQuestionsLoadError}");
  1418. }
  1419. }
  1420. else
  1421. {
  1422. SelectLocalQuestion(targetDifficulty);
  1423. }
  1424. }
  1425. void SelectOnlineQuestion(int targetDifficulty)
  1426. {
  1427. if (onlineQuestions == null || onlineQuestions.Count == 0)
  1428. {
  1429. Debug.LogError("没有可用的在线题目!");
  1430. return;
  1431. }
  1432. List<QuestionData> availableQuestions = onlineQuestions.FindAll(q =>
  1433. q.difficulty == targetDifficulty);
  1434. if (availableQuestions.Count == 0)
  1435. {
  1436. Debug.LogWarning($"没有找到难度 {targetDifficulty} 的在线题目,使用所有题目");
  1437. availableQuestions = onlineQuestions;
  1438. }
  1439. int randomIndex = Random.Range(0, availableQuestions.Count);
  1440. currentOnlineQuestion = availableQuestions[randomIndex];
  1441. currentQuestionId = currentOnlineQuestion.questionId;
  1442. currentQuestion = ScriptableObject.CreateInstance<MathQuestionSO>();
  1443. currentQuestion.questionText = currentOnlineQuestion.questionText;
  1444. currentQuestion.correctAnswer = currentOnlineQuestion.correctAnswer;
  1445. currentQuestion.wrongAnswers = new List<int>(currentOnlineQuestion.wrongAnswers);
  1446. currentQuestion.difficulty = currentOnlineQuestion.difficulty;
  1447. }
  1448. void SelectLocalQuestion(int targetDifficulty)
  1449. {
  1450. if (allQuestions == null || allQuestions.Count == 0)
  1451. {
  1452. Debug.LogError("没有可用的题目!请在Inspector中添加MathQuestionSO资源。");
  1453. return;
  1454. }
  1455. List<MathQuestionSO> availableQuestions = allQuestions.FindAll(q =>
  1456. q.difficulty == targetDifficulty);
  1457. if (availableQuestions.Count == 0)
  1458. {
  1459. Debug.LogWarning($"没有找到难度 {targetDifficulty} 的题目,使用所有题目");
  1460. availableQuestions = allQuestions;
  1461. }
  1462. int randomIndex = Random.Range(0, availableQuestions.Count);
  1463. currentQuestion = availableQuestions[randomIndex];
  1464. currentQuestionId = -1;
  1465. }
  1466. //
  1467. int GetDifficultyByRoomType(RoonType roomType)
  1468. {
  1469. return roomType switch
  1470. {
  1471. RoonType.MinorEnemy => 1,
  1472. RoonType.EliteEnemy => 2,
  1473. RoonType.Boss => 3,
  1474. _ => 1
  1475. };
  1476. }
  1477. int GetGoldRewardByRoomType(RoonType roomType)
  1478. {
  1479. return roomType switch
  1480. {
  1481. RoonType.MinorEnemy => 10,
  1482. RoonType.EliteEnemy => 30,
  1483. RoonType.Boss => 100,
  1484. _ => 10
  1485. };
  1486. }
  1487. int GetExperienceRewardByRoomType(RoonType roomType)
  1488. {
  1489. return roomType switch
  1490. {
  1491. RoonType.MinorEnemy => 20,
  1492. RoonType.EliteEnemy => 50,
  1493. RoonType.Boss => 200,
  1494. _ => 20
  1495. };
  1496. }
  1497. public void OnPlayerEnterRoom(GameObject room)
  1498. {
  1499. if (currentTriggerRoom != null) return;
  1500. if (currentHealth <= 0) return;
  1501. if (attemptedRooms.Contains(room))
  1502. {
  1503. Debug.Log("这个房间已经尝试过了!");
  1504. return;
  1505. }
  1506. currentTriggerRoom = room;
  1507. RoomInfo roomInfo = room.GetComponent<RoomInfo>();
  1508. if (roomInfo == null)
  1509. {
  1510. Debug.LogError("房间没有RoomInfo组件!");
  1511. return;
  1512. }
  1513. switch (roomInfo.roomType)
  1514. {
  1515. case RoonType.MinorEnemy:
  1516. case RoonType.EliteEnemy:
  1517. case RoonType.Boss:
  1518. StartBattle(room);
  1519. break;
  1520. case RoonType.RestRoom:
  1521. Rest(room);
  1522. break;
  1523. }
  1524. UpdateRoomButtonsState();
  1525. }
  1526. void StartBattle(GameObject room)
  1527. {
  1528. RoomInfo roomInfo = room.GetComponent<RoomInfo>();
  1529. if (roomInfo == null) return;
  1530. currentBattleRoomType = roomInfo.roomType;
  1531. monsterMaxHealth = GetMonsterHealthByRoomType(currentBattleRoomType);
  1532. monsterCurrentHealth = monsterMaxHealth;
  1533. isInBattle = true;
  1534. isAnsweringQuestion = true;
  1535. SetMonsterIcon(currentBattleRoomType);
  1536. if (battlePanel != null)
  1537. {
  1538. battlePanel.SetActive(true);
  1539. UpdateBattleUI();
  1540. }
  1541. UpdatePlayerBattleHealthUI();
  1542. ShowNextQuestion();
  1543. Debug.Log($"开始战斗!怪物类型:{currentBattleRoomType},怪物血量:{monsterMaxHealth}");
  1544. }
  1545. void SetMonsterIcon(RoonType roomType)
  1546. {
  1547. if (monsterIcon == null) return;
  1548. Sprite iconToUse = roomType switch
  1549. {
  1550. RoonType.MinorEnemy => minorEnemyIcon,
  1551. RoonType.EliteEnemy => eliteEnemyIcon,
  1552. RoonType.Boss => bossIcon,
  1553. _ => minorEnemyIcon
  1554. };
  1555. if (iconToUse != null)
  1556. {
  1557. monsterIcon.sprite = iconToUse;
  1558. }
  1559. if (playerIcon != null && playerBattleIcon != null)
  1560. {
  1561. playerIcon.sprite = playerBattleIcon;
  1562. }
  1563. }
  1564. void UpdatePlayerBattleHealthUI()
  1565. {
  1566. if (playerBattleHealthSlider != null)
  1567. {
  1568. playerBattleHealthSlider.maxValue = GetMaxHealth();
  1569. playerBattleHealthSlider.value = currentHealth;
  1570. }
  1571. if (playerBattleHealthText != null)
  1572. {
  1573. playerBattleHealthText.text = $"玩家血量: {currentHealth}/{GetMaxHealth()}";
  1574. }
  1575. }
  1576. int GetMonsterHealthByRoomType(RoonType roomType)
  1577. {
  1578. return roomType switch
  1579. {
  1580. RoonType.MinorEnemy => 3,
  1581. RoonType.EliteEnemy => 5,
  1582. RoonType.Boss => 10,
  1583. _ => 3
  1584. };
  1585. }
  1586. void UpdateBattleUI()
  1587. {
  1588. if (monsterHealthSlider != null)
  1589. {
  1590. monsterHealthSlider.maxValue = monsterMaxHealth;
  1591. monsterHealthSlider.value = monsterCurrentHealth;
  1592. }
  1593. if (monsterHealthText != null)
  1594. {
  1595. monsterHealthText.text = $"怪物血量: {monsterCurrentHealth}/{monsterMaxHealth}";
  1596. }
  1597. if (battleStatusText != null)
  1598. {
  1599. string roomTypeName = currentBattleRoomType switch
  1600. {
  1601. RoonType.MinorEnemy => "普通怪物",
  1602. RoonType.EliteEnemy => "精英怪物",
  1603. RoonType.Boss => "Boss",
  1604. _ => "怪物"
  1605. };
  1606. battleStatusText.text = $"战斗中 - {roomTypeName}";
  1607. }
  1608. }
  1609. void ShowNextQuestion()
  1610. {
  1611. SelectRandomQuestion(currentBattleRoomType);
  1612. if (currentQuestion == null)
  1613. {
  1614. Debug.LogError("无法获取题目!");
  1615. EndBattle(false);
  1616. return;
  1617. }
  1618. if (hasHint)
  1619. {
  1620. UseHint();
  1621. }
  1622. ShowQuestionUI();
  1623. }
  1624. void EndBattle(bool playerWon)
  1625. {
  1626. isInBattle = false;
  1627. isAnsweringQuestion = false;
  1628. if (battlePanel != null)
  1629. {
  1630. battlePanel.SetActive(false);
  1631. }
  1632. if (playerWon)
  1633. {
  1634. Debug.Log("战斗胜利!");
  1635. totalRoomsCompleted++;
  1636. UpdateAchievementProgress(AchievementType.CompleteRooms, 1);
  1637. int expReward = GetExperienceRewardByRoomType(currentBattleRoomType);
  1638. AddExperience(expReward);
  1639. if (currentBattleRoomType == RoonType.Boss)
  1640. {
  1641. isGameCompleted = true;
  1642. Victory();
  1643. }
  1644. if (currentTriggerRoom != null)
  1645. {
  1646. completedRooms.Add(currentTriggerRoom);
  1647. attemptedRooms.Add(currentTriggerRoom);
  1648. int goldReward = GetGoldRewardByRoomType(currentBattleRoomType);
  1649. AddGold(goldReward);
  1650. if (doubleGoldTurns > 0)
  1651. {
  1652. doubleGoldTurns--;
  1653. Debug.Log($"金币翻倍剩余回合:{doubleGoldTurns}");
  1654. }
  1655. GameObject triggerToDestroy = triggers.Find(t =>
  1656. t.GetComponent<RoomTrigger>()?.GetRoom() == currentTriggerRoom);
  1657. if (triggerToDestroy != null)
  1658. {
  1659. triggers.Remove(triggerToDestroy);
  1660. Destroy(triggerToDestroy);
  1661. }
  1662. }
  1663. }
  1664. else
  1665. {
  1666. Debug.Log("战斗失败!游戏结束!");
  1667. GameOver();
  1668. }
  1669. currentTriggerRoom = null;
  1670. currentQuestion = null;
  1671. UpdateRoomButtonsState();
  1672. }
  1673. void Rest(GameObject room)
  1674. {
  1675. Debug.Log("休息室!");
  1676. HealPlayer(1);
  1677. completedRooms.Add(room);
  1678. attemptedRooms.Add(room);
  1679. totalRoomsCompleted++;
  1680. UpdateAchievementProgress(AchievementType.CompleteRooms, 1);
  1681. currentTriggerRoom = null;
  1682. Debug.Log($"休息恢复 1 点生命!当前生命: {currentHealth}/{GetMaxHealth()}");
  1683. }
  1684. void ShowQuestionUI()
  1685. {
  1686. if (questionPanel == null)
  1687. {
  1688. Debug.LogError("Question Panel is not assigned!");
  1689. return;
  1690. }
  1691. if (currentQuestion == null)
  1692. {
  1693. Debug.LogError("当前题目为空!");
  1694. return;
  1695. }
  1696. questionPanel.SetActive(true);
  1697. if (questionText != null)
  1698. {
  1699. questionText.text = currentQuestion.questionText;
  1700. }
  1701. List<int> allAnswers = new List<int>(currentQuestion.wrongAnswers);
  1702. allAnswers.Add(currentQuestion.correctAnswer);
  1703. if (currentQuestion.wrongAnswers.Count != 3)
  1704. {
  1705. Debug.LogWarning($"题目 '{currentQuestion.questionText}' 的错误答案数量不是3个,当前为{currentQuestion.wrongAnswers.Count}个");
  1706. }
  1707. for (int i = 0; i < allAnswers.Count; i++)
  1708. {
  1709. int randomIndex = Random.Range(i, allAnswers.Count);
  1710. int temp = allAnswers[i];
  1711. allAnswers[i] = allAnswers[randomIndex];
  1712. allAnswers[randomIndex] = temp;
  1713. }
  1714. for (int i = 0; i < answerButtons.Length && i < allAnswers.Count; i++)
  1715. {
  1716. int answer = allAnswers[i];
  1717. answerButtons[i].gameObject.SetActive(true);
  1718. answerButtons[i].GetComponentInChildren<Text>().text = answer.ToString();
  1719. answerButtons[i].onClick.RemoveAllListeners();
  1720. answerButtons[i].onClick.AddListener(() => OnAnswerSelected(answer));
  1721. }
  1722. Debug.Log($"显示题目: {currentQuestion.questionText}, 题目ID: {currentQuestionId}");
  1723. }
  1724. void OnAnswerSelected(int selectedAnswer)
  1725. {
  1726. StartCoroutine(OnAnswerSelectedWithAnimation(selectedAnswer));
  1727. }
  1728. GameObject CreateBullet(Vector2 startPos)
  1729. {
  1730. GameObject bullet = new GameObject("Bullet");
  1731. Image bulletImage = bullet.AddComponent<Image>();
  1732. Texture2D bulletTexture = new Texture2D(1, 1);
  1733. bulletTexture.SetPixel(0, 0, bulletColor);
  1734. bulletTexture.Apply();
  1735. Sprite bulletSprite = Sprite.Create(bulletTexture, new Rect(0, 0, 1, 1), new Vector2(0.5f, 0.5f));
  1736. bulletImage.sprite = bulletSprite;
  1737. bulletImage.color = bulletColor;
  1738. RectTransform rectTransform = bullet.GetComponent<RectTransform>();
  1739. rectTransform.sizeDelta = new Vector2(bulletSize, bulletSize);
  1740. rectTransform.anchoredPosition = startPos;
  1741. if (battlePanel != null)
  1742. {
  1743. bullet.transform.SetParent(battlePanel.transform, false);
  1744. }
  1745. return bullet;
  1746. }
  1747. IEnumerator PlayAttackAnimation(bool playerAttacking)
  1748. {
  1749. if (playerIcon == null || monsterIcon == null)
  1750. {
  1751. yield break;
  1752. }
  1753. RectTransform attackerRect = playerAttacking ? playerIcon.rectTransform : monsterIcon.rectTransform;
  1754. RectTransform targetRect = playerAttacking ? monsterIcon.rectTransform : playerIcon.rectTransform;
  1755. Vector2 attackerOriginalPos = attackerRect.anchoredPosition;
  1756. Vector2 targetOriginalPos = targetRect.anchoredPosition;
  1757. Vector2 direction = (targetOriginalPos - attackerOriginalPos).normalized;
  1758. float elapsed = 0f;
  1759. while (elapsed < attackDuration)
  1760. {
  1761. elapsed += Time.deltaTime;
  1762. float progress = elapsed / attackDuration;
  1763. float curve = Mathf.Sin(progress * Mathf.PI);
  1764. Vector2 offset = direction * (attackMoveDistance * curve);
  1765. attackerRect.anchoredPosition = attackerOriginalPos + offset;
  1766. yield return null;
  1767. }
  1768. attackerRect.anchoredPosition = attackerOriginalPos;
  1769. GameObject bullet = CreateBullet(attackerOriginalPos);
  1770. RectTransform bulletRect = bullet.GetComponent<RectTransform>();
  1771. elapsed = 0f;
  1772. while (elapsed < bulletDuration)
  1773. {
  1774. elapsed += Time.deltaTime;
  1775. float progress = elapsed / bulletDuration;
  1776. bulletRect.anchoredPosition = Vector2.Lerp(attackerOriginalPos, targetOriginalPos, progress);
  1777. yield return null;
  1778. }
  1779. Destroy(bullet);
  1780. elapsed = 0f;
  1781. Vector3 originalRotation = Vector3.zero;
  1782. while (elapsed < shakeDuration)
  1783. {
  1784. elapsed += Time.deltaTime;
  1785. float progress = elapsed / shakeDuration;
  1786. float decay = 1f - progress;
  1787. float offsetX = Mathf.Sin(elapsed * 30f) * shakeIntensity * decay;
  1788. targetRect.anchoredPosition = targetOriginalPos + new Vector2(offsetX, 0);
  1789. yield return null;
  1790. }
  1791. targetRect.anchoredPosition = targetOriginalPos;
  1792. }
  1793. IEnumerator OnAnswerSelectedWithAnimation(int selectedAnswer)
  1794. {
  1795. bool isCorrect = (selectedAnswer == currentQuestion.correctAnswer);
  1796. ReportQuestionStatsToServer(isCorrect);
  1797. if (isCorrect)
  1798. {
  1799. correctAnswerCount++;
  1800. UpdateAchievementProgress(AchievementType.AnswerCorrect, 1);
  1801. Debug.Log($"回答正确!题目:{currentQuestion.questionText} 答案:{currentQuestion.correctAnswer}");
  1802. if (isInBattle)
  1803. {
  1804. yield return StartCoroutine(PlayAttackAnimation(true));
  1805. monsterCurrentHealth -= attackPower;
  1806. UpdateBattleUI();
  1807. Debug.Log($"怪物受到{attackPower}点伤害!剩余血量:{monsterCurrentHealth}/{monsterMaxHealth}");
  1808. if (monsterCurrentHealth <= 0)
  1809. {
  1810. questionPanel.SetActive(false);
  1811. EndBattle(true);
  1812. yield break;
  1813. }
  1814. }
  1815. }
  1816. else
  1817. {
  1818. wrongAnswerCount++;
  1819. Debug.Log($"回答错误!题目:{currentQuestion.questionText} 正确答案是:{currentQuestion.correctAnswer},你选择了:{selectedAnswer}");
  1820. if (isInBattle)
  1821. {
  1822. yield return StartCoroutine(PlayAttackAnimation(false));
  1823. }
  1824. TakeDamage(1);
  1825. if (currentHealth <= 0)
  1826. {
  1827. questionPanel.SetActive(false);
  1828. if (isInBattle)
  1829. {
  1830. EndBattle(false);
  1831. }
  1832. else
  1833. {
  1834. isAnsweringQuestion = false;
  1835. currentTriggerRoom = null;
  1836. currentQuestion = null;
  1837. }
  1838. yield break;
  1839. }
  1840. }
  1841. if (isInBattle)
  1842. {
  1843. ShowNextQuestion();
  1844. }
  1845. else
  1846. {
  1847. questionPanel.SetActive(false);
  1848. isAnsweringQuestion = false;
  1849. currentTriggerRoom = null;
  1850. currentQuestion = null;
  1851. UpdateRoomButtonsState();
  1852. }
  1853. }
  1854. void ReportQuestionStatsToServer(bool isCorrect)
  1855. {
  1856. if (LoginManager.Instance == null || LoginManager.Instance.IsGuest())
  1857. {
  1858. return;
  1859. }
  1860. if (NetworkManager.Instance == null)
  1861. {
  1862. return;
  1863. }
  1864. if (currentQuestionId <= 0)
  1865. {
  1866. return;
  1867. }
  1868. NetworkManager.Instance.ReportQuestionStats(
  1869. currentQuestionId,
  1870. isCorrect,
  1871. currentQuestion.difficulty,
  1872. response =>
  1873. {
  1874. if (response.success)
  1875. {
  1876. Debug.Log($"题目统计上报成功!ID: {currentQuestionId}, 正确: {isCorrect}");
  1877. }
  1878. },
  1879. error =>
  1880. {
  1881. Debug.LogWarning($"题目统计上报失败: {error}");
  1882. }
  1883. );
  1884. AddQuestionToReviewPool();
  1885. }
  1886. void AddQuestionToReviewPool()
  1887. {
  1888. if (LoginManager.Instance == null || LoginManager.Instance.IsGuest())
  1889. {
  1890. return;
  1891. }
  1892. if (NetworkManager.Instance == null)
  1893. {
  1894. return;
  1895. }
  1896. if (currentQuestionId <= 0)
  1897. {
  1898. return;
  1899. }
  1900. bool isReviewMode = GameModeManager.Instance != null && GameModeManager.Instance.IsReviewMode();
  1901. if (isReviewMode)
  1902. {
  1903. return;
  1904. }
  1905. NetworkManager.Instance.AddQuestionToReview(
  1906. currentQuestionId,
  1907. response =>
  1908. {
  1909. if (response.success)
  1910. {
  1911. Debug.Log($"题目已添加到复习题库!ID: {currentQuestionId}");
  1912. }
  1913. },
  1914. error =>
  1915. {
  1916. Debug.LogWarning($"添加题目到复习题库失败: {error}");
  1917. }
  1918. );
  1919. }
  1920. // =========================
  1921. // 创建连接时记录所有连接关系(双向记录)
  1922. // =========================
  1923. void CreateConnections(List<GameObject> col1, List<GameObject> col2)
  1924. {
  1925. HashSet<GameObject> connected = new();
  1926. foreach (var r in col1)
  1927. {
  1928. var target = Connect(r, col2);
  1929. connected.Add(target);
  1930. // 记录双向连接
  1931. allConnections[r].Add(target);
  1932. allConnections[target].Add(r);
  1933. }
  1934. foreach (var r in col2)
  1935. {
  1936. if (!connected.Contains(r))
  1937. {
  1938. var source = Connect(r, col1);
  1939. // 记录双向连接
  1940. allConnections[r].Add(source);
  1941. allConnections[source].Add(r);
  1942. }
  1943. }
  1944. }
  1945. GameObject Connect(GameObject a, List<GameObject> targets)
  1946. {
  1947. GameObject b = targets[Random.Range(0, targets.Count)];
  1948. GameObject path = CreateBridge(a.transform.position, b.transform.position);
  1949. paths.Add(path);
  1950. return b;
  1951. }
  1952. GameObject CreateBridge(Vector3 start, Vector3 end)
  1953. {
  1954. GameObject bridge = GameObject.CreatePrimitive(PrimitiveType.Cube);
  1955. bridge.name = "Path";
  1956. Vector3 dir = end - start;
  1957. float length = dir.magnitude;
  1958. bridge.transform.position = (start + end) / 2f;
  1959. bridge.transform.rotation = Quaternion.LookRotation(dir);
  1960. bridge.transform.localScale = new Vector3(pathWidth, pathWidth, length);
  1961. Renderer r = bridge.GetComponent<Renderer>();
  1962. r.material = new Material(Shader.Find("Universal Render Pipeline/Lit"));
  1963. r.material.color = pathColor;
  1964. return bridge;
  1965. }
  1966. [ContextMenu("ReGenerate")]
  1967. public void ReGenerate()
  1968. {
  1969. ChooseMap();
  1970. foreach (var r in rooms) Destroy(r);
  1971. foreach (var p in paths) Destroy(p);
  1972. foreach (var t in triggers) Destroy(t);
  1973. rooms.Clear();
  1974. paths.Clear();
  1975. triggers.Clear();
  1976. completedRooms.Clear();
  1977. attemptedRooms.Clear();
  1978. allConnections.Clear();
  1979. currentMaxColumn = 0;
  1980. currentPlayerRoom = null;
  1981. isAtRootNode = true;
  1982. currentHealth = 10;
  1983. currentGold = 0;
  1984. shield = 0;
  1985. hasHint = false;
  1986. doubleGoldTurns = 0;
  1987. inventory.Clear();
  1988. isInBattle = false;
  1989. isAnsweringQuestion = false;
  1990. isMoving = false;
  1991. targetRoom = null;
  1992. if (battlePanel != null)
  1993. battlePanel.SetActive(false);
  1994. UpdateHealthUI();
  1995. UpdateGoldUI();
  1996. if (mapContent != null)
  1997. {
  1998. foreach (Transform child in mapContent.transform)
  1999. {
  2000. Destroy(child.gameObject);
  2001. }
  2002. }
  2003. roomButtons.Clear();
  2004. roomToButtonMap.Clear();
  2005. columnGroups.Clear();
  2006. CreateMap();
  2007. Create2DMap();
  2008. FindInitialPlayerRoom();
  2009. }
  2010. private void Update()
  2011. {
  2012. if (Time.frameCount % 30 == 0)
  2013. {
  2014. UpdateRoomButtonsState();
  2015. }
  2016. if (isMoving && player != null)
  2017. {
  2018. float distance = Vector3.Distance(player.position, targetPosition);
  2019. if (distance <= stopDistance)
  2020. {
  2021. isMoving = false;
  2022. if (isAtRootNode)
  2023. {
  2024. isAtRootNode = false;
  2025. Debug.Log("离开根节点,进入第一关");
  2026. }
  2027. Debug.Log($"已到达目标房间!");
  2028. if (targetRoom != null)
  2029. {
  2030. currentPlayerRoom = targetRoom;
  2031. OnPlayerEnterRoom(targetRoom);
  2032. targetRoom = null;
  2033. }
  2034. UpdateRoomButtonsState();
  2035. }
  2036. }
  2037. }
  2038. private void FixedUpdate()
  2039. {
  2040. if (!isMoving || player == null)
  2041. return;
  2042. Vector3 currentPos = player.position;
  2043. Vector3 targetPos = new Vector3(targetPosition.x, 0.6f, targetPosition.z);
  2044. Vector3 direction = (targetPos - currentPos);
  2045. direction.y = 0;
  2046. direction = direction.normalized;
  2047. float distance = Vector3.Distance(new Vector3(currentPos.x, 0, currentPos.z),
  2048. new Vector3(targetPos.x, 0, targetPos.z));
  2049. float currentSpeed = maxSpeed;
  2050. if (distance <= arrivalDistance)
  2051. {
  2052. float slowdownFactor = distance / arrivalDistance;
  2053. currentSpeed = maxSpeed * slowdownFactor;
  2054. }
  2055. Vector3 newPos = Vector3.MoveTowards(
  2056. new Vector3(currentPos.x, 0.6f, currentPos.z),
  2057. targetPos,
  2058. currentSpeed * Time.fixedDeltaTime
  2059. );
  2060. newPos.y = 0.6f;
  2061. player.position = newPos;
  2062. if (direction != Vector3.zero)
  2063. {
  2064. Quaternion targetRotation = Quaternion.LookRotation(direction);
  2065. player.rotation = Quaternion.Slerp(player.rotation, targetRotation, Time.fixedDeltaTime * 5f);
  2066. }
  2067. }
  2068. }
  2069. public class MapRoomButton : MonoBehaviour
  2070. {
  2071. public GameObject room { get; private set; }
  2072. public int columnIndex { get; private set; }
  2073. private MapGenerator3D mapGenerator;
  2074. private Button button;
  2075. private Image buttonImage;
  2076. private Text buttonText;
  2077. private Color lockedColor = new Color(0.3f, 0.3f, 0.3f);
  2078. private Color availableColor = new Color(0.3f, 0.7f, 0.3f);
  2079. private Color completedColor = new Color(0.2f, 0.5f, 0.8f);
  2080. private Color attemptedColor = new Color(0.8f, 0.5f, 0.2f);
  2081. private Color currentColor = new Color(1f, 0.8f, 0f);
  2082. private Color unavailableColor = new Color(0.5f, 0.5f, 0.5f);
  2083. public void Initialize(GameObject roomObj, MapGenerator3D generator, int colIndex)
  2084. {
  2085. this.room = roomObj;
  2086. this.mapGenerator = generator;
  2087. this.columnIndex = colIndex;
  2088. button = GetComponent<Button>();
  2089. buttonImage = GetComponent<Image>();
  2090. buttonText = GetComponentInChildren<Text>();
  2091. if (button != null)
  2092. {
  2093. button.onClick.AddListener(OnButtonClick);
  2094. }
  2095. if (buttonText != null)
  2096. {
  2097. string[] nameParts = room.name.Split('_');
  2098. string roomTypeText = GetRoomTypeText(room);
  2099. if (nameParts.Length >= 3)
  2100. {
  2101. buttonText.text = $"{roomTypeText}\n{int.Parse(nameParts[1]) + 1}-{int.Parse(nameParts[2]) + 1}";
  2102. }
  2103. else
  2104. {
  2105. buttonText.text = $"{roomTypeText}\n{room.name}";
  2106. }
  2107. }
  2108. }
  2109. string GetRoomTypeText(GameObject room)
  2110. {
  2111. RoomInfo roomInfo = room.GetComponent<RoomInfo>();
  2112. if (roomInfo == null) return "普通";
  2113. return roomInfo.roomType switch
  2114. {
  2115. RoonType.MinorEnemy => "普通",
  2116. RoonType.EliteEnemy => "精英",
  2117. RoonType.RestRoom => "休息",
  2118. RoonType.Boss => "Boss",
  2119. _ => "普通"
  2120. };
  2121. }
  2122. public void UpdateState(float distanceToPlayer, bool isTriggerActive, bool isCompleted, bool isAttempted, bool canTeleport, bool isCurrentRoom)
  2123. {
  2124. if (buttonImage == null) return;
  2125. if (buttonText != null)
  2126. {
  2127. buttonText.text = buttonText.text.Replace(" ✓", "").Replace(" ✗", "").Replace(" 🔒", "").Replace(" 📍", "");
  2128. }
  2129. if (isCompleted)
  2130. {
  2131. buttonImage.color = completedColor;
  2132. button.interactable = false;
  2133. if (buttonText != null)
  2134. {
  2135. buttonText.text += " ✓";
  2136. }
  2137. }
  2138. else if (isAttempted && !isCompleted)
  2139. {
  2140. buttonImage.color = attemptedColor;
  2141. button.interactable = false;
  2142. if (buttonText != null)
  2143. {
  2144. buttonText.text += " ✗";
  2145. }
  2146. }
  2147. else if (!canTeleport)
  2148. {
  2149. buttonImage.color = lockedColor;
  2150. button.interactable = false;
  2151. if (buttonText != null && !isCurrentRoom)
  2152. {
  2153. buttonText.text += " 🔒";
  2154. }
  2155. }
  2156. else if (isCurrentRoom)
  2157. {
  2158. buttonImage.color = currentColor;
  2159. button.interactable = true;
  2160. if (buttonText != null)
  2161. {
  2162. buttonText.text += " 📍";
  2163. }
  2164. }
  2165. else if (canTeleport && isTriggerActive)
  2166. {
  2167. buttonImage.color = availableColor;
  2168. button.interactable = true;
  2169. }
  2170. else
  2171. {
  2172. buttonImage.color = unavailableColor;
  2173. button.interactable = false;
  2174. }
  2175. }
  2176. private void OnButtonClick()
  2177. {
  2178. if (room != null && mapGenerator != null)
  2179. {
  2180. mapGenerator.TeleportToRoom(room);
  2181. }
  2182. }
  2183. }
  2184. public class RoomTrigger : MonoBehaviour
  2185. {
  2186. private GameObject room;
  2187. private MapGenerator3D mapGenerator;
  2188. public void SetRoom(GameObject roomObj, MapGenerator3D generator)
  2189. {
  2190. room = roomObj;
  2191. mapGenerator = generator;
  2192. }
  2193. public GameObject GetRoom()
  2194. {
  2195. return room;
  2196. }
  2197. private void OnTriggerEnter(Collider other)
  2198. {
  2199. if (other.CompareTag("Player"))
  2200. {
  2201. if (mapGenerator != null)
  2202. {
  2203. mapGenerator.OnPlayerEnterRoom(room);
  2204. }
  2205. }
  2206. }
  2207. }