chunk 1: core gameplay scripts scenes runtime assets
This commit is contained in:
292
Assets/Editor/AssetBundleBuilder.cs
Normal file
292
Assets/Editor/AssetBundleBuilder.cs
Normal file
@@ -0,0 +1,292 @@
|
||||
using UnityEditor;
|
||||
using UnityEngine;
|
||||
using System.IO;
|
||||
using System.Collections.Generic;
|
||||
using System.Security.Cryptography;
|
||||
using System.Text;
|
||||
using CustomAssetBundleSystem;
|
||||
|
||||
/// <summary>
|
||||
/// Unity Editor tool to build AssetBundles and generate catalog.json
|
||||
/// Menu: Tools > AssetBundles > Build for Android
|
||||
/// </summary>
|
||||
public class AssetBundleBuilder : EditorWindow
|
||||
{
|
||||
private static string outputPath = "ServerData/AssetBundles/Android";
|
||||
private static BuildTarget buildTarget = BuildTarget.Android;
|
||||
|
||||
[MenuItem("Tools/AssetBundles/Build for Android")]
|
||||
public static void BuildAssetBundles()
|
||||
{
|
||||
Debug.Log("=== Custom AssetBundle Builder ===");
|
||||
|
||||
// Create output directory
|
||||
string fullOutputPath = Path.Combine(Application.dataPath, "..", outputPath);
|
||||
if (!Directory.Exists(fullOutputPath))
|
||||
{
|
||||
Directory.CreateDirectory(fullOutputPath);
|
||||
Debug.Log($"Created output directory: {fullOutputPath}");
|
||||
}
|
||||
|
||||
// Build options
|
||||
BuildAssetBundleOptions options = BuildAssetBundleOptions.ChunkBasedCompression |
|
||||
BuildAssetBundleOptions.StrictMode;
|
||||
|
||||
// Build the bundles
|
||||
Debug.Log($"Building AssetBundles to: {fullOutputPath}");
|
||||
Debug.Log($"Target platform: {buildTarget}");
|
||||
|
||||
AssetBundleManifest manifest = BuildPipeline.BuildAssetBundles(
|
||||
fullOutputPath,
|
||||
options,
|
||||
buildTarget
|
||||
);
|
||||
|
||||
if (manifest == null)
|
||||
{
|
||||
Debug.LogError("✗ AssetBundle build failed!");
|
||||
EditorUtility.DisplayDialog("Build Failed", "AssetBundle build failed! Check console for errors.", "OK");
|
||||
return;
|
||||
}
|
||||
|
||||
Debug.Log("✓ AssetBundles built successfully!");
|
||||
|
||||
// Generate the JSON catalog
|
||||
GenerateCatalog(fullOutputPath, manifest);
|
||||
|
||||
Debug.Log("=== Build Complete ===");
|
||||
EditorUtility.DisplayDialog("Build Complete",
|
||||
$"AssetBundles built successfully!\n\nOutput: {fullOutputPath}\n\nNext: Upload to VPS",
|
||||
"OK");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Generate catalog.json from built AssetBundles
|
||||
/// </summary>
|
||||
private static void GenerateCatalog(string outputPath, AssetBundleManifest manifest)
|
||||
{
|
||||
Debug.Log("Generating catalog.json...");
|
||||
|
||||
var catalog = new RemoteAssetCatalog
|
||||
{
|
||||
version = Application.version,
|
||||
timestamp = System.DateTimeOffset.UtcNow.ToUnixTimeSeconds(),
|
||||
baseUrl = "https://assetbundle.uncaptured.co",
|
||||
bundles = new List<RemoteAssetCatalog.BundleEntry>()
|
||||
};
|
||||
|
||||
// Get all bundle names
|
||||
string[] allBundles = manifest.GetAllAssetBundles();
|
||||
|
||||
foreach (string bundleName in allBundles)
|
||||
{
|
||||
string bundlePath = Path.Combine(outputPath, bundleName);
|
||||
|
||||
if (!File.Exists(bundlePath))
|
||||
{
|
||||
Debug.LogWarning($"Bundle file not found: {bundlePath}");
|
||||
continue;
|
||||
}
|
||||
|
||||
// Get file info
|
||||
FileInfo fileInfo = new FileInfo(bundlePath);
|
||||
|
||||
// Calculate hash
|
||||
string hash = CalculateMD5(bundlePath);
|
||||
|
||||
// Get scenes in this bundle
|
||||
var scenePaths = AssetDatabase.GetAssetPathsFromAssetBundle(bundleName);
|
||||
var sceneNames = new List<string>();
|
||||
|
||||
foreach (var scenePath in scenePaths)
|
||||
{
|
||||
if (scenePath.EndsWith(".unity"))
|
||||
{
|
||||
string sceneName = Path.GetFileNameWithoutExtension(scenePath);
|
||||
sceneNames.Add(sceneName);
|
||||
}
|
||||
}
|
||||
|
||||
// Get dependencies
|
||||
var dependencies = new List<string>();
|
||||
string[] bundleDeps = manifest.GetAllDependencies(bundleName);
|
||||
dependencies.AddRange(bundleDeps);
|
||||
|
||||
// Create bundle entry
|
||||
var entry = new RemoteAssetCatalog.BundleEntry
|
||||
{
|
||||
bundleName = Path.GetFileNameWithoutExtension(bundleName),
|
||||
fileName = bundleName,
|
||||
hash = hash,
|
||||
size = fileInfo.Length,
|
||||
scenes = sceneNames,
|
||||
dependencies = dependencies
|
||||
};
|
||||
|
||||
catalog.bundles.Add(entry);
|
||||
|
||||
Debug.Log($" ✓ Added bundle: {bundleName}");
|
||||
Debug.Log($" - Size: {fileInfo.Length / 1024f / 1024f:F2} MB");
|
||||
Debug.Log($" - Hash: {hash}");
|
||||
Debug.Log($" - Scenes: {sceneNames.Count}");
|
||||
Debug.Log($" - Dependencies: {dependencies.Count}");
|
||||
}
|
||||
|
||||
// Save catalog as JSON
|
||||
string catalogPath = Path.Combine(outputPath, "catalog.json");
|
||||
string json = JsonUtility.ToJson(catalog, true);
|
||||
File.WriteAllText(catalogPath, json);
|
||||
|
||||
Debug.Log($"✓ Catalog generated: {catalogPath}");
|
||||
Debug.Log($" Total bundles: {catalog.bundles.Count}");
|
||||
Debug.Log($" Catalog size: {json.Length / 1024f:F2} KB");
|
||||
|
||||
// Also save a pretty-printed version for debugging
|
||||
string prettyPath = Path.Combine(outputPath, "catalog_readable.json");
|
||||
File.WriteAllText(prettyPath, json);
|
||||
Debug.Log($"✓ Readable catalog: {prettyPath}");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Calculate MD5 hash of a file
|
||||
/// </summary>
|
||||
private static string CalculateMD5(string filePath)
|
||||
{
|
||||
using (var md5 = MD5.Create())
|
||||
using (var stream = File.OpenRead(filePath))
|
||||
{
|
||||
byte[] hash = md5.ComputeHash(stream);
|
||||
StringBuilder sb = new StringBuilder();
|
||||
foreach (byte b in hash)
|
||||
{
|
||||
sb.Append(b.ToString("x2"));
|
||||
}
|
||||
return sb.ToString();
|
||||
}
|
||||
}
|
||||
|
||||
[MenuItem("Tools/AssetBundles/Open Output Folder")]
|
||||
public static void OpenOutputFolder()
|
||||
{
|
||||
string fullPath = Path.Combine(Application.dataPath, "..", outputPath);
|
||||
if (Directory.Exists(fullPath))
|
||||
{
|
||||
EditorUtility.RevealInFinder(fullPath);
|
||||
}
|
||||
else
|
||||
{
|
||||
Debug.LogWarning($"Output folder doesn't exist: {fullPath}");
|
||||
}
|
||||
}
|
||||
|
||||
[MenuItem("Tools/AssetBundles/Clear Cache")]
|
||||
public static void ClearCache()
|
||||
{
|
||||
if (EditorUtility.DisplayDialog("Clear AssetBundle Cache?",
|
||||
"This will delete all cached AssetBundles. Continue?",
|
||||
"Yes", "Cancel"))
|
||||
{
|
||||
Caching.ClearCache();
|
||||
Debug.Log("✓ AssetBundle cache cleared");
|
||||
}
|
||||
}
|
||||
|
||||
[MenuItem("Tools/AssetBundles/Settings")]
|
||||
public static void ShowSettings()
|
||||
{
|
||||
var window = GetWindow<AssetBundleBuilder>("AssetBundle Builder");
|
||||
window.Show();
|
||||
}
|
||||
|
||||
// Editor Window GUI
|
||||
void OnGUI()
|
||||
{
|
||||
GUILayout.Label("Custom AssetBundle Builder", EditorStyles.boldLabel);
|
||||
GUILayout.Space(10);
|
||||
|
||||
EditorGUILayout.HelpBox(
|
||||
"This tool builds AssetBundles and generates a JSON catalog for remote loading.\n\n" +
|
||||
"1. Mark your scenes/assets with AssetBundle names\n" +
|
||||
"2. Click 'Build for Android'\n" +
|
||||
"3. Upload ServerData/AssetBundles/Android/ to your VPS",
|
||||
MessageType.Info);
|
||||
|
||||
GUILayout.Space(10);
|
||||
|
||||
// Output path
|
||||
GUILayout.Label("Output Path:", EditorStyles.boldLabel);
|
||||
outputPath = EditorGUILayout.TextField(outputPath);
|
||||
|
||||
GUILayout.Space(5);
|
||||
|
||||
// Build target
|
||||
GUILayout.Label("Build Target:", EditorStyles.boldLabel);
|
||||
buildTarget = (BuildTarget)EditorGUILayout.EnumPopup(buildTarget);
|
||||
|
||||
GUILayout.Space(10);
|
||||
|
||||
// Build button
|
||||
if (GUILayout.Button("Build AssetBundles", GUILayout.Height(40)))
|
||||
{
|
||||
BuildAssetBundles();
|
||||
}
|
||||
|
||||
GUILayout.Space(5);
|
||||
|
||||
// Utility buttons
|
||||
using (new EditorGUILayout.HorizontalScope())
|
||||
{
|
||||
if (GUILayout.Button("Open Output Folder"))
|
||||
{
|
||||
OpenOutputFolder();
|
||||
}
|
||||
|
||||
if (GUILayout.Button("Clear Cache"))
|
||||
{
|
||||
ClearCache();
|
||||
}
|
||||
}
|
||||
|
||||
GUILayout.Space(20);
|
||||
|
||||
// Info section
|
||||
GUILayout.Label("Current Settings:", EditorStyles.boldLabel);
|
||||
EditorGUILayout.LabelField("Unity Version:", Application.unityVersion);
|
||||
EditorGUILayout.LabelField("App Version:", Application.version);
|
||||
EditorGUILayout.LabelField("Company:", Application.companyName);
|
||||
|
||||
GUILayout.Space(10);
|
||||
|
||||
// Quick guide
|
||||
if (GUILayout.Button("Show Quick Guide"))
|
||||
{
|
||||
ShowQuickGuide();
|
||||
}
|
||||
}
|
||||
|
||||
private static void ShowQuickGuide()
|
||||
{
|
||||
EditorUtility.DisplayDialog("AssetBundle Quick Guide",
|
||||
"STEP 1: Mark Assets for Bundling\n" +
|
||||
"━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n" +
|
||||
"• Select your Game scene in Project window\n" +
|
||||
"• In Inspector, find 'AssetBundle' dropdown (bottom)\n" +
|
||||
"• Select 'New...' and name it 'gamescene'\n\n" +
|
||||
|
||||
"STEP 2: Build AssetBundles\n" +
|
||||
"━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n" +
|
||||
"• Tools → AssetBundles → Build for Android\n" +
|
||||
"• Wait for build to complete\n" +
|
||||
"• Files created in ServerData/AssetBundles/Android/\n\n" +
|
||||
|
||||
"STEP 3: Upload to VPS\n" +
|
||||
"━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n" +
|
||||
"• scp -r ServerData/AssetBundles/Android/* user@server:/var/www/AssetBundles/Android/\n\n" +
|
||||
|
||||
"STEP 4: Update Your Code\n" +
|
||||
"━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n" +
|
||||
"• Replace Addressables calls with CustomAssetBundleSceneLoader\n" +
|
||||
"• Build APK and test!",
|
||||
"Got it!");
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user