A Prop Bucket is a storage place for art assets and dynamic instances. It contains a temporary "in memory" list of assets, as opposed to the Area's permanent list of assets and their instances. This information is maintained only on the client and the server is not directly aware of these instances but you can manipulate them from scripts on the client, and those scripts can be told what to do from the server via remote calls, etc.
In HeroEngine, each Area works with instances, which have been created from assets, such as graphical images and textures which are graphically rendered. Each Area has a list of assets which are available to it. These assets are loaded into memory whenever the Area is loaded or "spun up".
There are many times when you need to dynamically introduce art (or other) assets into the client at run-time without it being specified as an asset in the area (such as the components of a spell effect). HeroEngine can do this by loading these assets into a system called a Prop Bucket. A specific asset (AssetSpec) is loaded into this virtual bucket, and then instances are made of that asset. The instances are then "activated" which means that they are graphically rendered (displayed) either on a Virtual Stage (such as a popup window), or rendered in the game world itself.
For example, if a certain spell effect needs certain assets, then instead of each area being required to already contain those assets, a spell script can create a Prop Bucket and place the necessary assets within it. The first time that the spell is cast in a particular Area, it could create a Prop Bucket named "fireballBucket" and place the necessary assets within it. Then future attempts to cast the spell would also attempt to create the same Bucket, but since it was already created, would not cause any resource drain.
Note: All PropBucket functions and actions take place on the client side only.
Propbuckets can be useful for virtual stages, see also:
Create Prop Bucket
This function creates a new Prop Bucket. If the bucket already exists, the operation is aborted. There is nothing wrong with attempting to re-create a PropBucket that already exists. Buckets are not persistent -- when the game client is shut down, neither the bucket nor any of the instances created from it are saved.
Since a Prop Bucket only exists on a client, its instances are only displayed to that one user. If it is desired to display instances to multiple users, then a server-side script would call the client script on several different clients, instructing each one to create a Prop Bucket and the necessary instances, and display them to their respective users all at the same time.
CreatePropBucket(bucketname as string)
Destroy Prop Bucket
Destroying a prop bucket will delete all assets and instances which are in or were created from that bucket. Attempting to destroy a bucket that does not already exist, will generate a script error.
For optimal results, a Prop Bucket should probably not be destroyed though, unless it is genuinely a one-time-use situation. If there's any chance that its instances might be needed again that day, it's better to leave it in existence.
DestroyPropBucket(bucketname as string)
Add Asset to Bucket
This function adds a specific asset to a Prop Bucket, and returns a noderef to the added asset. An ID number is also assigned to the added asset.
AddAssetSpecToPropBucket(bucketName as string, assetSpecFQN as string) as noderef
AssetSpecFQN stands for "Asset Spec Fully Qualified Name"
bucketName does not exist, a value of 0 is returned in the noderef.
Once added, in order to determine the ID number, create an ID variable and set it equal to the noderef. For example:
newAsset as noderef newAsset = AddAssetSpecToPropBucket(FunBucket, "flower.gr2") newAssetID as ID newAssetID = newAsset // Sets the ID#, based on the noderef
- Assets load asynchonrously, so it won't be available immediately for us. See below.
- An ID number is assigned only if the node isn't already in the bucket.
- Some assetSpecs, if not yet loaded from the server, will have to be loaded when this function is called. Instances will not be able to be created from the asset until after it is fully loaded. To check if the asset has loaded or not, use the "IsAssetSpecReady" function (note that you'll need to check by ID instead of noderef).
Add Asset to Bucket With Callback
Rather than polling using IsAssetSpecReady(), it is often useful to use this alternative external function to add the asset spec to a prop bucket because it generates a callback to a method on a node you specify.
The callback is made to the method AssetSpecReady, passing in the spec as noderef and whether or not the loadFailed as boolean.
// requests the asset specification be loaded and calls back method AssetSpecReady( spec as NodeRef of Class HBSpec, loadFailed as Boolean). external function AddAssetSpecToPropBucketCB( bucketName as String, assetFQN as String, callbackNode as NodeRef) as noderef
Is Asset Spec Ready
Check if an asset (either PropBucket or area-related) is loaded. Remember, assets load asynchronously... so it can take an undetermined amount of time for the asset to be ready to use. In fact, it could be that that asset needs to be (re)downloaded from the server so the interval could be many seconds in such cases.
IsAssetSpecReady(location as string, assetID as ID) as boolean
If the asset is fully loaded, the function returns a value of TRUE.
location is either null, or the name of a propBucket. If it is blank, this function will search in the active area rather than a Prop Bucket. An invalid or unknown Prop Bucket name will generate a script error.
if (IsAssetSpecReady(FunBucket, newAssetID)) msgArea("It's ready.") else msgArea("It's not ready yet, let it cook some more.") .
Is Asset Spec Broken
In some cases, an asset spec added to a prop bucket will never be ready. The most likely reason that an asset spec would be "broken" is that the FQN specified does not exist in the repository.
if IsAssetSpecBroken( "FunBucket", newAssetID ) println("The asset spec " + newAssetID + " is broken.") else println("The asset spec " + newAssetID + " is ok.") .
Note: As an added convenience, for particle specs, the function also checks if the spec's EmitSpec is broken.
If BucketName is empty, the active area's assets will be checked rather than a PropBucket.
Remove Asset Spec From Prop Bucket
RemoveAssetSpecFromPropBucket(bucketName as string, assetID as ID)
This function will remove an asset from the specified bucket. It will also delete any instances which were created from that asset. Attempting to call this function with an invalid ID or bucket name will generate a script error.
Create Instance from Bucket
CreateInstanceFromPropBucket(bucketName as string, assetSpec as noderef) as noderef
Creates a new instance from the given asset spec as found in the given PropBucket. Returns the node reference to the newly-created instance if successful, or 0 if the bucket or asset spec could not be found.
If bucketName is null, the new instance will be created in the Area. Otherwise it will be created inside the same bucket that the asset already exists in.
Note that when an instance is created, it still isn't displayed until it is activated.
ActivateInstance(instanceID as ID, stageName as string) as boolean
For more info, see ActivateInstance()
Remove Instance from Bucket
This function deletes an instance. It also automatically deactivates or disconnects the specified instance from any parents, children, or associations, as appropriate.
RemoveInstanceFromPropBucket(bucketName as string, instance as noderef) as bool
Remove Instances from Bucket
RemoveInstancesFromPropBucket(bucketName as string, asset as noderef) as bool
Removes all instances of the specified asset from the propBucket (automatically inactivating them first, as above). Returns false if the bucket could not be found.
Note: If the asset passed in is 0, then this will remove all instances from all assets in the propbucket.
This function will load the asset "flower.gr2" into a Prop Bucket called "greenhouse", and then renders (graphically displays) a few instances of the asset before then deleting everything. Note that this script would be impractical in the game world, since the instances would be rendered and deleted too fast to see! The following code is for an example only:
function displayPrettyThings() CreatePropBucket("greenhouse") flower as noderef flower = AddAssetSpecToPropBucket("greenhouse", "flower.gr2") flowerID as ID = flower // Get the ID# of the new noderef if (IsAssetSpecReady("greenhouse", flowerID)) rose1 as noderef = CreateInstanceFromPropBucket("greenhouse", flower) rose2 as noderef = CreateInstanceFromPropBucket("greenhouse", flower) rose3 as noderef = CreateInstanceFromPropBucket("greenhouse", flower) roseID as ID = rose3 ActivateInstance(roseID, "greenhouse") // This rose would be rendered in the virtual stage screen roseID = rose2 ActivateInstance(roseID , "") // This rose would be rendered in the world/area nullNoderef as noderef RemoveInstanceFromPropBucket("greenhouse", rose1) // Remove rose1 RemoveInstancesFromPropBucket("greenhouse", nullNoderef) // Removes rose2 and rose3 RemoveAssetSpecFromPropBucket("greenhouse", flowerID) DestroyPropBucket("greenhouse") else MsgArea("Can't create flowers yet, since the flower asset isn't loaded yet.") . .
One thing that is tricky is that adding an asset to a propbucket is an asynchronous operation. Consequently, you can not actually use it until it is loaded requiring that you either poll using the IsAssetSpecReady() function until the asset loads (via a timer) or add the asset using the AddAssetSpecToPropBucketCB() external function which will perform a method callback when the asset is ready.