@@ -117,6 +117,115 @@ public static Dictionary<string, Dictionary<TextureCompressionFormat, string>> B
117117 outputPath , builds , assetBundleOptions , baseTextureFormat , textureSubtargets ) ;
118118 }
119119
120+ /// <summary>
121+ /// Run one or more AssetBundle builds for the specified device tiers.
122+ /// Notes about the <see cref="outputPath"/> parameter:
123+ /// - If a relative path is provided, the file paths in the returned AssetPackConfig will be relative paths.
124+ /// - If an absolute path is provided, the file paths in the returned object will be absolute paths.
125+ /// - AssetBundle builds for device tiers will be created in siblings of this directory. For
126+ /// example, for outputDirectory "a/b/c" and device tier HIGH, there will be a directory "a/b/c#tier_high".
127+ /// - If allowClearDirectory is false, this directory and any sibling directories must be empty or not exist,
128+ /// otherwise an exception is thrown.
129+ /// </summary>
130+ /// <param name="outputPath">The output directory for AssetBundles. See other notes above.</param>
131+ /// <param name="deliveryMode">A delivery mode to apply to every asset pack in the generated config.</param>
132+ /// <param name="deviceTierToBuilds">A dictionary from Device Tier to asset bundle builds. All device tiers
133+ /// should contains the same asset bundle names.</param>
134+ /// <param name="defaultDeviceTier">
135+ /// Default device tier to be used for standalone APKs. Set to zero if not specified.</param>
136+ /// <param name="assetBundleOptions">Options to pass to <see cref="BuildPipeline"/>.</param>
137+ /// <param name="allowClearDirectory">Allows this method to clear the contents of the output directory.</param>
138+ /// <returns>An <see cref="AssetPackConfig"/> containing file paths to all generated AssetBundles.</returns>
139+ public static AssetPackConfig BuildAssetBundlesDeviceTier (
140+ string outputPath ,
141+ AssetPackDeliveryMode deliveryMode ,
142+ Dictionary < DeviceTier , AssetBundleBuild [ ] > deviceTierToBuilds ,
143+ DeviceTier defaultDeviceTier = null ,
144+ BuildAssetBundleOptions assetBundleOptions = BuildAssetBundleOptions . UncompressedAssetBundle ,
145+ bool allowClearDirectory = false )
146+ {
147+ if ( defaultDeviceTier == null )
148+ {
149+ defaultDeviceTier = DeviceTier . From ( 0 ) ;
150+ }
151+
152+ if ( ! deviceTierToBuilds . Keys . Contains ( defaultDeviceTier ) )
153+ {
154+ throw new ArgumentException ( "Default device tier not present in deviceTierToBuilds." ) ;
155+ }
156+
157+ var nameToDeviceTierToPath = BuildAssetBundlesDeviceTier ( outputPath , deviceTierToBuilds ,
158+ assetBundleOptions , allowClearDirectory ) ;
159+ var assetPackConfig = new AssetPackConfig ( ) ;
160+ foreach ( var deviceTierToPath in nameToDeviceTierToPath . Values )
161+ {
162+ assetPackConfig . AddAssetBundles ( deviceTierToPath , deliveryMode ) ;
163+ }
164+
165+ assetPackConfig . DefaultDeviceTier = defaultDeviceTier ;
166+ return assetPackConfig ;
167+ }
168+
169+ /// <summary>
170+ /// Run one or more AssetBundle builds for each device tier.
171+ /// </summary>
172+ /// <param name="outputPath">The output directory for base AssetBundles. See the other method for notes.</param>
173+ /// <param name="deviceTierToBuilds">A dictionary from Device Tier to asset bundle builds. All device tiers
174+ /// should contains the same asset bundle names.</param>
175+ /// <param name="assetBundleOptions">Options to pass to <see cref="BuildPipeline"/>.</param>
176+ /// <param name="allowClearDirectory">Allows this method to clear the contents of the output directory.</param>
177+ /// <returns>A dictionary from AssetBundle name to DeviceTier to file outputPath.</returns>
178+ public static Dictionary < string , Dictionary < DeviceTier , string > > BuildAssetBundlesDeviceTier (
179+ string outputPath , Dictionary < DeviceTier , AssetBundleBuild [ ] > deviceTierToBuilds ,
180+ BuildAssetBundleOptions assetBundleOptions ,
181+ bool allowClearDirectory )
182+ {
183+ if ( deviceTierToBuilds == null || deviceTierToBuilds . Count == 0 )
184+ {
185+ throw new ArgumentException ( "DeviceTierToBuild parameter cannot be null or empty" ) ;
186+ }
187+
188+ if ( deviceTierToBuilds . Values . Any ( builds => builds == null || builds . Length == 0 ) )
189+ {
190+ throw new ArgumentException ( "AssetBundleBuilds value cannot be null or empty" ) ;
191+ }
192+
193+ var assetBundleNames = deviceTierToBuilds . Values . First ( ) . Select ( build => build . assetBundleName ) . ToList ( ) ;
194+
195+ if ( deviceTierToBuilds . Values . Any ( builds =>
196+ ! ( builds . Length == assetBundleNames . Count ( ) &&
197+ builds . Select ( build => build . assetBundleName ) . Except ( assetBundleNames ) . ToList ( ) . Count == 0 ) ) )
198+ {
199+ throw new ArgumentException ( "AssetBundleBuild names cannot differ across device tiers." ) ;
200+ }
201+
202+ foreach ( var assetBundleName in assetBundleNames )
203+ {
204+ if ( ! AndroidAppBundle . IsValidModuleName ( assetBundleName ) )
205+ {
206+ throw new ArgumentException ( "Invalid AssetBundle name: " + assetBundleName ) ;
207+ }
208+ }
209+
210+ if ( string . IsNullOrEmpty ( outputPath ) )
211+ {
212+ throw new ArgumentNullException ( "outputPath" ) ;
213+ }
214+
215+ CheckDirectory ( outputPath , allowClearDirectory ) ;
216+
217+ var tiers = new HashSet < DeviceTier > ( deviceTierToBuilds . Keys ) ;
218+ var paths = tiers . Select ( tier => outputPath + DeviceTierTargetingTools . GetTargetingSuffix ( tier ) ) ;
219+ foreach ( var path in paths )
220+ {
221+ CheckDirectory ( path , allowClearDirectory ) ;
222+ }
223+
224+ return BuildAssetBundlesDeviceTierInternal (
225+ outputPath , assetBundleOptions , deviceTierToBuilds , assetBundleNames ) ;
226+ }
227+
228+
120229 // The internal method assumes all parameter preconditions have already been checked.
121230 private static Dictionary < string , Dictionary < TextureCompressionFormat , string > > BuildAssetBundlesInternal (
122231 string outputPath , AssetBundleBuild [ ] builds , BuildAssetBundleOptions assetBundleOptions ,
@@ -157,6 +266,45 @@ private static Dictionary<string, Dictionary<TextureCompressionFormat, string>>
157266 }
158267 }
159268
269+ private static Dictionary < string , Dictionary < DeviceTier , string > > BuildAssetBundlesDeviceTierInternal (
270+ string outputPath , BuildAssetBundleOptions assetBundleOptions ,
271+ Dictionary < DeviceTier , AssetBundleBuild [ ] > deviceTierToBuilds , IEnumerable < string > assetBundleNames )
272+ {
273+ // Use a dictionary to capture the generated AssetBundles' file names.
274+ var nameToDeviceTierToPath = assetBundleNames . ToDictionary (
275+ bundleName => bundleName , _ => new Dictionary < DeviceTier , string > ( ) ) ;
276+ foreach ( var deviceTier in deviceTierToBuilds . Keys )
277+ {
278+ var tierBuilds = deviceTierToBuilds [ deviceTier ] ;
279+ // Build AssetBundles in the the base format's directory.
280+ BuildAssetBundles ( outputPath , tierBuilds , assetBundleOptions ) ;
281+
282+ // Then move the files to a new directory with the device tier suffix.
283+ var outputPathWithSuffix = outputPath + DeviceTierTargetingTools . GetTargetingSuffix ( deviceTier ) ;
284+ Directory . Move ( outputPath , outputPathWithSuffix ) ;
285+
286+ UpdateDictionaryDeviceTier ( nameToDeviceTierToPath , outputPathWithSuffix , deviceTier ) ;
287+ }
288+
289+ return nameToDeviceTierToPath ;
290+ }
291+
292+ private static void UpdateDictionaryDeviceTier (
293+ Dictionary < string , Dictionary < DeviceTier , string > > nameToDeviceTierToPath ,
294+ string outputPath , DeviceTier deviceTier )
295+ {
296+ foreach ( var entry in nameToDeviceTierToPath )
297+ {
298+ var filePath = Path . Combine ( outputPath , entry . Key ) ;
299+ entry . Value [ deviceTier ] = filePath ;
300+ if ( ! File . Exists ( filePath ) )
301+ {
302+ throw new InvalidOperationException ( string . Format ( "Missing AssetBundle file: " + filePath ) ) ;
303+ }
304+ }
305+ }
306+
307+
160308 private static void BuildAssetBundles (
161309 string outputPath , AssetBundleBuild [ ] builds , BuildAssetBundleOptions assetBundleOptions )
162310 {
@@ -226,4 +374,4 @@ private static void CheckDirectory(string path, bool allowClearDirectory)
226374 directoryInfo . Delete ( /* recursive= */ true ) ;
227375 }
228376 }
229- }
377+ }
0 commit comments