Content - Code
Create and manage your game's live content [PROF-Content-03]
Code
Beamable SDK Examples
• The following example code is available for download at GitHub.com/Beamable_SDK_Examples
Here are API highlights for ContentService.
Subscribe
private async void SetupBeamable()
{
var beamContext = BeamContext.Default;
await beamContext.OnReady;
// Fetch All
beamContext.Api.ContentService.Subscribe(clientManifest =>
{
Debug.Log($"#1. ContentService, all object count = {clientManifest.entries.Count}");
});
// Fetch Filtered
beamContext.Api.ContentService.Subscribe("items", clientManifest =>
{
Debug.Log($"#2. ContentService, filtered 'items' object count = {clientManifest.entries.Count}");
});
}
Content Subscription vs ContentLink/ContentRef
Beamable supports subscriptions to Content as well as direct references to certain pieces of content. Both of these will allow you to download content from the server, depending on the Game Maker's needs.
Subscriptions use a PlatformSubscription to dynamically read the data on the server, and fire a callback when the data is changed. However, ContentLink and ContentRef are both resolved manually when the data is needed:
using System;
using Beamable.Common.Content;
using Beamable.Common.Inventory;
using UnityEngine;
namespace Beamable.Examples.Services.ContentService
{
[Serializable]
public class ItemLink : ContentLink<ItemContent> {}
/// <summary>
/// Demonstrates <see cref="ContentService"/>.
/// </summary>
public class ContentServiceExistingExample : MonoBehaviour
{
// Fields ---------------------------------------
[SerializeField] private ItemLink _itemLink;
[SerializeField] private ItemRef _itemRef;
private ItemContent _itemContentFromLink = null;
private ItemContent _itemContentFromRef = null;
// Unity Methods --------------------------------
protected void Start()
{
Debug.Log($"Start()");
SetupBeamable();
}
// Methods --------------------------------------
private async void SetupBeamable()
{
var beamContext = BeamContext.Default;
await beamContext.OnReady;
Debug.Log($"beamContext.PlayerId = {beamContext.PlayerId}");
await _itemLink.Resolve()
.Then(content =>
{
_itemContentFromLink = content;
Debug.Log($"_itemContentFromLink.Resolve() Success! " +
$"Id = {_itemContentFromLink.Id}");
})
.Error(ex =>
{
Debug.LogError($"_itemContentFromLink.Resolve() Error!");
});
await _itemRef.Resolve()
.Then(content =>
{
_itemContentFromRef = content;
Debug.Log($"_itemContentFromRef.Resolve() Success! " +
$"Id = {_itemContentFromRef.Id}");
}).Error(ex =>
{
Debug.LogError($"_itemContentFromRef.Resolve() Error!");
});
}
}
}
Best Practice
These hints make efficent use of concepts and workflows.
If the content that your game is using is known ahead of time (e.g. there will only be subtle differences in existing pieces of content), a content reference (such as ContentLink or ContentRef) should be used.
However, if the content in your game needs to be more dynamic (e.g. the Game Maker will be pushing entirely new pieces of content, unknown to the game client), a content subscription should be used.
GetContent Method
Beamable's ContentService has another method that can pull content: GetContent. Since this method will attempt to pull several pieces of content at once, it is often inefficient, however it may be useful depending on the scope of your project.
private string contentIdToLoad;
private async void GetContent()
{
var rawContent = await _beamContext.Api.ContentService.GetContent(contentIdToLoad); //Returns as IContentObject
var content = rawContent as ItemContent;
//content can now be used as ItemContent
}
Advanced
This section contains any advanced configuration options and workflows.
ContentLink vs ContentRef
Beamable supports 2 methodologies for referencing a content object; ContentLink
and ContentRef
. While they are both very similar syntactically and need to be resolved before using, they perform differently and have different use-cases.
ContentLink
- Beamable will perform a First Frame load to resolve the reference.ContentLink
s must be present and resolvable (that is, they cannot benull
)ContentRef
- Beamable will perform a Lazy load to resolve the reference. As such, it is okay for aContentRef
to benull
as long as it is never resolved
See Content » Guide (Steps) for more info.
Best Practice
These hints make efficent use of concepts and workflows.
- DO: Use
ContentLink
in any member variable in your custom content type which references another content type. ContentLink is useful for data that needs to be loaded quickly at runtime, since it is preloaded in very early stages of the application's lifecycle.- DON'T: Use
ContentRef
by default everywhere in your project. This is supported but is considered overkill. ContentRef is useful for data that the application can afford to load on-demand (especially data that might not get loaded at all).
Content Caching
Content is cached by the client and stored both in memory and persisted to disk. When content is updated on our backend, Beamable updates a client manifest and push it to all clients to invalidate and update the cache.
See source code of Runtime/DisruptorEngine/Content/ContentCache.cs
for more info.
Content ID
The content ID is assigned at creation, and is composed of content type
and content id
. A content ID always starts with the content type.
For example, a currency content for dollars would be currency.dollars
.
Nesting
Content may be nested too, and the resulting hierarchy will be baked in to the name.
For example, to group "weekend" events under a common folder -- the content ID would be events.weekend.<user-defined-id>
.
Content Serialization
Unity's built-in features use Unity's serialization.
However, Beamable's custom content types rely instead on Beamable's custom Serialization. This serialization is strict and has limitations.
Supported Types | Unsupported Types |
---|---|
• Unity’s AssetReference • Unity’s Color • bool • double • enum • float • int • List • long • string • System.Object (and child types)• ContentRef • ContentLink | • Unity’s MonoBehaviour • Unity’s ScriptableObject • Etc... |
Excluding Fields from Serialization
The [IgnoreContentFieldAttribute]
can be applied to any field that you wish to exclude from the Content Serialization process.
[Agnostic]
[ContentType("MyCustomContent")]
public class MyCustomContent : ContentObject
{
public string Name;
public AssetReferenceSprite Icon;
[IgnoreContentFieldAttribute]
public Dictionary<string, int> KeyValuePair;
}
Content Validation
Beamable supports optional validation for each field of a ContentObject
subclass. This allows game makers to easily build tooling to facilitate team members with their content administration tasks.
See Content - Validation for more info.
Storage Location of Content Types
Each content object derives from Unity's ScriptableObject
, is an asset file written to disk, and is therefore persistent between sessions.
While all content objects must go in this location, game makers may choose to create custom subfolders within.
Development (Unity Editor)
/Assets/Beamable/Editor/content/
Deployment (On-device)
The development location is not included in the built game project. Instead, when the player loads the game, a fresh copy of all content is retrieved from the Beamable back-end and stored on-device. This allows Beamable to serve dynamic content to the game project. By default, this is a Lazy loading operation. Each of Beamable's Feature prefabs show a loading progress indicator UI automatically.
Content is stored on-device in a within Unity's Application.persistentDataPath
.
Application.persistentDataPath + $"/content/{contentId}.json";
Game Content Designer
Config data, or “Content” as it is called within Beamable, is realm-scoped and can be deployed from either...
- Unity - Via Unity's
ScriptableObject
- Google Sheets - Via Beamable's Game Content Designer
Within the context of a CI/CD pipeline, game makers can create jobs that invoke the Content deployment function against data it pulled from source control, and pass in arguments for which realm this Content should go to. This is not theoretical, this is what game makers do today in production.
Content with Microservices
This 'MyCustomContent.cs' snippet includes the [Agnostic]
attribute. To make content classes available to your microservices, add the attribute to your content classes.
See Microservices for more info.
[Agnostic]
[ContentType("MyCustomContent")]
public class MyCustomContent : ContentObject { ... }
Content Namespaces
Global Namespace
Beamable APIs that use content under the hood can only look to the global namespace.
public class Tester : MonoBehaviour
{
public CurrencyRef currency;
async void Start()
{
var ctx = BeamContext.Default;
ctx.Content.GetContent(content.Id, "tuna");
var service = (ContentService)ctx.Content;
service.SwitchDefaultManifestID("tuna");
var content = await currency.Resolve();
}
}
Updated about 1 year ago