ui基本完毕,修了一大把的bug
This commit is contained in:
@@ -0,0 +1,383 @@
|
||||
#if UNITY_EDITOR
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Text;
|
||||
using UnityEditor;
|
||||
using UnityEngine;
|
||||
|
||||
/// <summary>
|
||||
/// 打包前一键初始化工具:把随包发布的玩家进度状态恢复到默认(零)值。
|
||||
///
|
||||
/// 只重置“玩家进度状态”,绝不改动业务/配置数据。具体范围见菜单确认框。
|
||||
/// 明确不碰:isUnlocked(角色/歌曲/剧情解锁)、itemPurchaseQuota(限购配置)、
|
||||
/// equippedSkillGroupIDs(技能配装)、associatedAllyHero 等引用、DLC 配置、
|
||||
/// 拥有权账本 .own.dat、以及技能/羁绊/皮肤等定义资产。
|
||||
/// </summary>
|
||||
public static class PackagingResetTool
|
||||
{
|
||||
private const string GeneratedEquipmentFolder = "Assets/Resources/so/uEquip";
|
||||
private const string NextGeneratedIdPrefsKey = "bansonic_equipment_next_id_v1";
|
||||
|
||||
// .cache_bridge 下按固定文件名持久化的本机测试存档(不进包,但编辑器 Play 会回写 .asset)。
|
||||
// 注意:故意不含 .own.dat / .own.bak(拥有权账本),以免影响解锁业务逻辑。
|
||||
private static readonly string[] CacheBridgeSaveFiles =
|
||||
{
|
||||
".eco.dat", ".eco.bak", ".eco.tmp", // PlayerEconomyStorage(货币)
|
||||
".ahd.dat", ".ahd.bak", ".ahd.tmp", // AllyHeroDeployLedgerStorage(英雄成长)
|
||||
};
|
||||
|
||||
[MenuItem("Tools/Packaging/一键初始化打包设置(恢复默认)", false, 0)]
|
||||
public static void ResetForPackaging()
|
||||
{
|
||||
bool confirmed = EditorUtility.DisplayDialog(
|
||||
"一键初始化打包设置",
|
||||
"将执行以下重置(仅玩家进度,不改配置/解锁/技能配装):\n\n" +
|
||||
"1. 玩家:金币、材料、经验、等级清零\n" +
|
||||
"2. 英雄:经验、成长阶位、自动突破、等级锁、出战/完成/MVP 统计、入队日期清零\n" +
|
||||
"3. 装备:删除 " + GeneratedEquipmentFolder + " 下所有生成装备,清除英雄已装备引用\n" +
|
||||
"4. 商店:所有物品 purchasedCount、user_has_read 清零(限购配置 itemPurchaseQuota 不动)\n" +
|
||||
"5. 清理本机测试存档(.cache_bridge 货币/成长/装备/商店存档,不含拥有权账本)与相关 PlayerPrefs\n\n" +
|
||||
"此操作会修改并保存资产文件,建议在版本控制下执行。是否继续?",
|
||||
"执行重置",
|
||||
"取消");
|
||||
|
||||
if (!confirmed)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var report = new StringBuilder();
|
||||
int errors = 0;
|
||||
|
||||
try
|
||||
{
|
||||
AssetDatabase.StartAssetEditing();
|
||||
|
||||
errors += ResetPlayers(report);
|
||||
errors += ResetHeroes(report);
|
||||
errors += DeleteGeneratedEquipmentAssets(report);
|
||||
errors += ResetStoreItems(report);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
errors++;
|
||||
report.AppendLine("资产重置阶段异常:" + ex.Message);
|
||||
}
|
||||
finally
|
||||
{
|
||||
AssetDatabase.StopAssetEditing();
|
||||
AssetDatabase.SaveAssets();
|
||||
AssetDatabase.Refresh();
|
||||
}
|
||||
|
||||
// 运行时存档 / PlayerPrefs 清理(放在资产保存之后,避免被回写覆盖)。
|
||||
errors += ClearRuntimeSaves(report);
|
||||
errors += ClearPlayerPrefs(report);
|
||||
|
||||
report.Insert(0, errors == 0
|
||||
? "打包初始化完成,未发现错误。\n\n"
|
||||
: "打包初始化完成,但有 " + errors + " 处警告/错误,请查看下方明细。\n\n");
|
||||
|
||||
Debug.Log("[PackagingReset]\n" + report);
|
||||
EditorUtility.DisplayDialog(
|
||||
errors == 0 ? "初始化完成" : "初始化完成(有警告)",
|
||||
report.ToString(),
|
||||
"好");
|
||||
}
|
||||
|
||||
private static int ResetPlayers(StringBuilder report)
|
||||
{
|
||||
int errors = 0;
|
||||
int count = 0;
|
||||
foreach (var guid in AssetDatabase.FindAssets("t:Player_SO"))
|
||||
{
|
||||
string path = AssetDatabase.GUIDToAssetPath(guid);
|
||||
var so = AssetDatabase.LoadAssetAtPath<Player_SO>(path);
|
||||
if (so == null)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
var serialized = new SerializedObject(so);
|
||||
SetIntIfPresent(serialized, "player_coins", 0);
|
||||
SetIntIfPresent(serialized, "player_material", 0);
|
||||
SetIntIfPresent(serialized, "player_currentEXP", 0);
|
||||
SetIntIfPresent(serialized, "player_currentLevel", 0);
|
||||
SetFloatIfPresent(serialized, "uRankingScore", 0f);
|
||||
// 经验瓶 / 突破材料等消耗品库存也一并清零(属于玩家进度,非配置)。
|
||||
SetIntIfPresent(serialized, "commonExpBottle78001", 0);
|
||||
SetIntIfPresent(serialized, "mediumExpBottle78002", 0);
|
||||
SetIntIfPresent(serialized, "superiorExpBottle78003", 0);
|
||||
SetIntIfPresent(serialized, "supremeExpBottle78004", 0);
|
||||
SetIntIfPresent(serialized, "extraordinaryExpBottle78005", 0);
|
||||
SetIntIfPresent(serialized, "celestialExpBottle78006", 0);
|
||||
SetIntIfPresent(serialized, "dushMaterial78021", 0);
|
||||
SetIntIfPresent(serialized, "dushMaterial78022", 0);
|
||||
SetIntIfPresent(serialized, "dushMaterial78023", 0);
|
||||
SetIntIfPresent(serialized, "dushMaterial78024", 0);
|
||||
serialized.ApplyModifiedPropertiesWithoutUndo();
|
||||
EditorUtility.SetDirty(so);
|
||||
count++;
|
||||
}
|
||||
|
||||
report.AppendLine("玩家资产已重置:" + count + " 个 Player_SO。");
|
||||
return errors;
|
||||
}
|
||||
|
||||
private static int ResetHeroes(StringBuilder report)
|
||||
{
|
||||
int count = 0;
|
||||
foreach (var guid in AssetDatabase.FindAssets("t:AllyHero_SO"))
|
||||
{
|
||||
string path = AssetDatabase.GUIDToAssetPath(guid);
|
||||
var so = AssetDatabase.LoadAssetAtPath<AllyHero_SO>(path);
|
||||
if (so == null)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
var serialized = new SerializedObject(so);
|
||||
// 成长进度(不动 isUnlocked / equippedSkillGroupIDs)。
|
||||
SetIntIfPresent(serialized, "ally_currentEXP", 0);
|
||||
SetIntIfPresent(serialized, "ally_growthUnlockedTierIndex", 0);
|
||||
SetBoolIfPresent(serialized, "ally_autoBreakthroughEnabled", false);
|
||||
SetBoolIfPresent(serialized, "level_lock", false);
|
||||
// 战斗统计。
|
||||
SetIntIfPresent(serialized, "ally_battleDeployCount", 0);
|
||||
SetIntIfPresent(serialized, "ally_finishCount", 0);
|
||||
SetIntIfPresent(serialized, "ally_mvpCount", 0);
|
||||
SetLongIfPresent(serialized, "ally_joinDateUtcTicks", 0L);
|
||||
// 装备引用:因为要删除全部生成装备,这里同步清空引用避免悬空。
|
||||
SetObjectIfPresent(serialized, "equippedEquipment", null);
|
||||
SetStringIfPresent(serialized, "equippedEquipmentId", string.Empty);
|
||||
serialized.ApplyModifiedPropertiesWithoutUndo();
|
||||
EditorUtility.SetDirty(so);
|
||||
count++;
|
||||
}
|
||||
|
||||
report.AppendLine("英雄资产已重置:" + count + " 个 AllyHero_SO(经验/阶位/统计/装备引用)。");
|
||||
return 0;
|
||||
}
|
||||
|
||||
private static int DeleteGeneratedEquipmentAssets(StringBuilder report)
|
||||
{
|
||||
if (!AssetDatabase.IsValidFolder(GeneratedEquipmentFolder))
|
||||
{
|
||||
report.AppendLine("装备目录不存在,跳过删除:" + GeneratedEquipmentFolder);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int errors = 0;
|
||||
int deleted = 0;
|
||||
foreach (var guid in AssetDatabase.FindAssets("t:equipmentSO", new[] { GeneratedEquipmentFolder }))
|
||||
{
|
||||
string path = AssetDatabase.GUIDToAssetPath(guid);
|
||||
if (AssetDatabase.DeleteAsset(path))
|
||||
{
|
||||
deleted++;
|
||||
}
|
||||
else
|
||||
{
|
||||
errors++;
|
||||
report.AppendLine("删除装备失败:" + path);
|
||||
}
|
||||
}
|
||||
|
||||
report.AppendLine("已删除生成装备资产:" + deleted + " 个(目录 " + GeneratedEquipmentFolder + ")。");
|
||||
return errors;
|
||||
}
|
||||
|
||||
private static int ResetStoreItems(StringBuilder report)
|
||||
{
|
||||
int count = 0;
|
||||
foreach (var guid in AssetDatabase.FindAssets("t:storeItemSO"))
|
||||
{
|
||||
string path = AssetDatabase.GUIDToAssetPath(guid);
|
||||
var so = AssetDatabase.LoadAssetAtPath<storeItemSO>(path);
|
||||
if (so == null)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
var serialized = new SerializedObject(so);
|
||||
// 只清运行时计数,不动 itemPurchaseQuota / isOnShelf / 关联引用等配置。
|
||||
SetIntIfPresent(serialized, "purchasedCount", 0);
|
||||
SetBoolIfPresent(serialized, "user_has_read", false);
|
||||
serialized.ApplyModifiedPropertiesWithoutUndo();
|
||||
EditorUtility.SetDirty(so);
|
||||
count++;
|
||||
}
|
||||
|
||||
report.AppendLine("商店物品已重置:" + count + " 个 storeItemSO(purchasedCount / user_has_read 清零)。");
|
||||
return 0;
|
||||
}
|
||||
|
||||
private static int ClearRuntimeSaves(StringBuilder report)
|
||||
{
|
||||
int errors = 0;
|
||||
|
||||
// 商店 purchasedCount 存档(SecureSaveVault + 旧版明文文件)。
|
||||
try
|
||||
{
|
||||
string legacyStorePath = Path.Combine(Application.persistentDataPath, "storeSystem_state.json");
|
||||
SecureSaveVault.Delete("store_state", "runtime", legacyStorePath);
|
||||
report.AppendLine("已清理商店运行时存档(store_state/runtime)。");
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
errors++;
|
||||
report.AppendLine("清理商店存档失败:" + ex.Message);
|
||||
}
|
||||
|
||||
// 生成装备运行时存档。
|
||||
try
|
||||
{
|
||||
SecureSaveVault.Delete("runtime_equipment", "generated_list");
|
||||
report.AppendLine("已清理生成装备运行时存档(runtime_equipment/generated_list)。");
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
errors++;
|
||||
report.AppendLine("清理装备存档失败:" + ex.Message);
|
||||
}
|
||||
|
||||
// 货币 / 英雄成长的独立加密存档(按固定文件名)。
|
||||
string cacheBridge = Path.Combine(Application.persistentDataPath, ".cache_bridge");
|
||||
int fileDeleted = 0;
|
||||
foreach (var fileName in CacheBridgeSaveFiles)
|
||||
{
|
||||
try
|
||||
{
|
||||
string full = Path.Combine(cacheBridge, fileName);
|
||||
if (File.Exists(full))
|
||||
{
|
||||
File.Delete(full);
|
||||
fileDeleted++;
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
errors++;
|
||||
report.AppendLine("删除存档文件失败(" + fileName + "):" + ex.Message);
|
||||
}
|
||||
}
|
||||
|
||||
report.AppendLine("已清理 .cache_bridge 货币/成长存档文件:" + fileDeleted + " 个(保留拥有权账本 .own.*)。");
|
||||
return errors;
|
||||
}
|
||||
|
||||
private static int ClearPlayerPrefs(StringBuilder report)
|
||||
{
|
||||
PlayerPrefs.DeleteKey(NextGeneratedIdPrefsKey);
|
||||
|
||||
int equipKeysCleared = 0;
|
||||
int debtKeysCleared = 0;
|
||||
foreach (var guid in AssetDatabase.FindAssets("t:AllyHero_SO"))
|
||||
{
|
||||
string path = AssetDatabase.GUIDToAssetPath(guid);
|
||||
var so = AssetDatabase.LoadAssetAtPath<AllyHero_SO>(path);
|
||||
if (so == null)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
var serialized = new SerializedObject(so);
|
||||
var idProp = serialized.FindProperty("ally_heroID");
|
||||
if (idProp == null)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
int heroId = idProp.intValue;
|
||||
// 已装备装备的本机记录会指向被删装备,需清理(不动技能配装记录)。
|
||||
string equipKey = "ally_equippedEquipment_" + heroId;
|
||||
if (PlayerPrefs.HasKey(equipKey))
|
||||
{
|
||||
PlayerPrefs.DeleteKey(equipKey);
|
||||
equipKeysCleared++;
|
||||
}
|
||||
|
||||
// 升阶欠账镜像键(idolUpgrade 用):属于成长进度残留。
|
||||
string debtKey = "idol_upgrade_pending_debt_" + heroId;
|
||||
if (PlayerPrefs.HasKey(debtKey))
|
||||
{
|
||||
PlayerPrefs.DeleteKey(debtKey);
|
||||
debtKeysCleared++;
|
||||
}
|
||||
}
|
||||
|
||||
// 队伍槽位经验镜像键(selected_heroSlot0X_exp)。不动 _heroID 槽位编队本身,
|
||||
// 仅把已归零英雄对应的槽位经验一并清零,避免下次启动被读回覆盖 .asset。
|
||||
int slotExpCleared = 0;
|
||||
for (int slot = 1; slot <= 5; slot++)
|
||||
{
|
||||
string expKey = "selected_heroSlot0" + slot + "_exp";
|
||||
if (PlayerPrefs.HasKey(expKey))
|
||||
{
|
||||
PlayerPrefs.DeleteKey(expKey);
|
||||
slotExpCleared++;
|
||||
}
|
||||
}
|
||||
|
||||
PlayerPrefs.Save();
|
||||
report.AppendLine("已清理 PlayerPrefs:next-id 键 + " + equipKeysCleared + " 个已装备记录 + "
|
||||
+ debtKeysCleared + " 个升阶欠账键 + " + slotExpCleared + " 个槽位经验键。");
|
||||
return 0;
|
||||
}
|
||||
|
||||
private static void SetIntIfPresent(SerializedObject so, string field, int value)
|
||||
{
|
||||
var prop = so.FindProperty(field);
|
||||
if (prop != null)
|
||||
{
|
||||
prop.intValue = value;
|
||||
}
|
||||
}
|
||||
|
||||
private static void SetLongIfPresent(SerializedObject so, string field, long value)
|
||||
{
|
||||
var prop = so.FindProperty(field);
|
||||
if (prop != null)
|
||||
{
|
||||
prop.longValue = value;
|
||||
}
|
||||
}
|
||||
|
||||
private static void SetFloatIfPresent(SerializedObject so, string field, float value)
|
||||
{
|
||||
var prop = so.FindProperty(field);
|
||||
if (prop != null)
|
||||
{
|
||||
prop.floatValue = value;
|
||||
}
|
||||
}
|
||||
|
||||
private static void SetBoolIfPresent(SerializedObject so, string field, bool value)
|
||||
{
|
||||
var prop = so.FindProperty(field);
|
||||
if (prop != null)
|
||||
{
|
||||
prop.boolValue = value;
|
||||
}
|
||||
}
|
||||
|
||||
private static void SetStringIfPresent(SerializedObject so, string field, string value)
|
||||
{
|
||||
var prop = so.FindProperty(field);
|
||||
if (prop != null)
|
||||
{
|
||||
prop.stringValue = value;
|
||||
}
|
||||
}
|
||||
|
||||
private static void SetObjectIfPresent(SerializedObject so, string field, UnityEngine.Object value)
|
||||
{
|
||||
var prop = so.FindProperty(field);
|
||||
if (prop != null)
|
||||
{
|
||||
prop.objectReferenceValue = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
@@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: e5c70360031a60640b952e6203ff59dd
|
||||
@@ -0,0 +1,70 @@
|
||||
#if UNITY_EDITOR
|
||||
using System.IO;
|
||||
using UnityEditor;
|
||||
using UnityEngine;
|
||||
|
||||
/// <summary>
|
||||
/// Editor utility that builds ready-to-use materials from the Rainbow-Cats
|
||||
/// "Unity Dissolve HDR Shaders" pack. We resolve shaders through Shader.Find so
|
||||
/// Unity fills in each graph's real fileID + default property values — hand-writing
|
||||
/// a .mat's shader reference is error-prone and usually ends in a pink material.
|
||||
/// Menu: Tools > Rainbow Dissolve > Create Materials
|
||||
/// </summary>
|
||||
public static class RainbowDissolveMaterialCreator
|
||||
{
|
||||
// Shader Graph registers its shader name as "<m_Path>/<asset file name>".
|
||||
private const string DissolveUrpShaderName = "Shader Graphs/Dissolve Lit URP Shader";
|
||||
private const string OutlineUrpShaderName = "Shader Graphs/HDR Outline Lit URP Shader";
|
||||
private const string DissolveHdrpShaderName = "Shader Graphs/Dissolve Lit HDRP Shader";
|
||||
|
||||
private const string OutputFolder = "Assets/Rainbow-Cats-Unity-Dissolve-HDR-Shaders-main/Materials";
|
||||
|
||||
[MenuItem("Tools/Rainbow Dissolve/Create Materials")]
|
||||
public static void CreateMaterials()
|
||||
{
|
||||
EnsureFolder(OutputFolder);
|
||||
|
||||
// URP shaders (this project is URP 17). HDRP variant is created only if that
|
||||
// shader compiles in the current pipeline, otherwise it would be pink.
|
||||
CreateMaterial(DissolveUrpShaderName, "M_Dissolve_URP");
|
||||
CreateMaterial(OutlineUrpShaderName, "M_HDROutline_URP");
|
||||
CreateMaterial(DissolveHdrpShaderName, "M_Dissolve_HDRP");
|
||||
|
||||
AssetDatabase.SaveAssets();
|
||||
AssetDatabase.Refresh();
|
||||
}
|
||||
|
||||
private static void CreateMaterial(string shaderName, string materialName)
|
||||
{
|
||||
Shader shader = Shader.Find(shaderName);
|
||||
if (shader == null)
|
||||
{
|
||||
Debug.LogWarning($"[RainbowDissolve] Shader not found: \"{shaderName}\". " +
|
||||
"Skipped (likely a different render pipeline or the graph hasn't imported yet).");
|
||||
return;
|
||||
}
|
||||
|
||||
string path = $"{OutputFolder}/{materialName}.mat";
|
||||
var material = new Material(shader) { name = materialName };
|
||||
AssetDatabase.CreateAsset(material, AssetDatabase.GenerateUniqueAssetPath(path));
|
||||
Debug.Log($"[RainbowDissolve] Created material {path} using shader \"{shaderName}\".", material);
|
||||
}
|
||||
|
||||
private static void EnsureFolder(string assetFolder)
|
||||
{
|
||||
if (AssetDatabase.IsValidFolder(assetFolder))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
string parent = Path.GetDirectoryName(assetFolder).Replace('\\', '/');
|
||||
string leaf = Path.GetFileName(assetFolder);
|
||||
if (!AssetDatabase.IsValidFolder(parent))
|
||||
{
|
||||
EnsureFolder(parent);
|
||||
}
|
||||
|
||||
AssetDatabase.CreateFolder(parent, leaf);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
@@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 4def4c7b0e2da0b439bcccc7108e010f
|
||||
@@ -0,0 +1,20 @@
|
||||
using System;
|
||||
using System.Linq;
|
||||
using UnityEditor;
|
||||
using UnityEngine;
|
||||
|
||||
public static class TempHeroLevelDump
|
||||
{
|
||||
[MenuItem("Tools/Temp/Dump Hero Levels")]
|
||||
public static void Run()
|
||||
{
|
||||
var guids = AssetDatabase.FindAssets("t:AllyHero_SO", new[]{"Assets/Resources/so/ally"});
|
||||
foreach (var g in guids.Take(12))
|
||||
{
|
||||
var path = AssetDatabase.GUIDToAssetPath(g);
|
||||
var so = AssetDatabase.LoadAssetAtPath<AllyHero_SO>(path);
|
||||
if (so == null) continue;
|
||||
Debug.Log($"{so.ally_heroID} | {so.ally_heroName} | exp={so.ally_currentEXP} | unlockedTier={so.ally_growthUnlockedTierIndex} | display={so.GetDisplayLevelRatingKey()} | levelStats={(so.levelStats!=null?so.levelStats.Count:0)}");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: e21797ca9148b1d42bb13ea5d550e9fa
|
||||
Reference in New Issue
Block a user