Adam's Lair Forum

game development and casual madness
It is currently 2019/11/17, 01:27

All times are UTC + 1 hour [ DST ]




Post new topic Reply to topic  [ 1 post ] 
Author Message
 Post subject: Working with Resources
PostPosted: 2013/05/13, 02:18 
Site Admin
Site Admin
User avatar

Joined: 2013/05/11, 22:30
Posts: 2073
Location: Germany
Role: Professional
Introduction

Whenever you need to store, modify or access game content, Resources are the way to go. There are a lot of different Resource types and each of them encapsules a different kind of data. This article is here to clarify some of the concepts behind Dualitys Resource system and show you how to use it most effectively.

Overview

In Dualitys editing environment, the Project View is where you'll find all currently existing Resources. When starting a new project, it is typically empty and you'll fill it during development. Any kind of game content is stored as Resource file in Dualitys Data folder and is either created directly in the editor (Scene, Prefab, etc.) or imported from external source files (Pixmap, AudioData, etc.). You can easily create new Resources by right-clicking on an empty spot in the Project View and selecting the appropriate entry from the New menu.

Image


Of course, those Resources will be more or less empty. While some Resource types are perfectly editable using the Object Inspector, others might need to be edited using external programs. To open a Resource for editing, simply double-click it. This will extract its data to the Source/Media directory and then open it with the respective file formats default application. Duality will keep an eye on those files - as soon as you edit and save them, it will automatically reload their associated Resources and you can directly see your changes inside the editor.

Image


Instead of creating game content in Duality, you can also import Resources from external source files: Simply dragdrop them onto the Project View. Not all image, sound or font formats are supported, but .png, .ogg and .ttf should always work. When in doubt, just try it and see if Duality does something useful. The worst that could happen is an error message.

Image


Accessing Resources

After importing and setting up some Resources in your project, it is likely that you want to use them in some way. For small prototype projects, you might get away with dragging them around in the editor UI without ever having to access them yourself via source code: Want to assign a new Material to a SpriteRenderer? Just dragdrop the Material Resource to the Components SharedMaterial property. Most of the time, actions like this are sufficient. But what if you want to switch the current Scene, instantiate a Prefab or lookup some static data?

Basically, there are three ways of accessing Resources:

  • Request it from the ContentProvider. This will also load the Resource and make it directly available:
    Code:
    texRef = ContentProvider.RequestContent<Texture>(@"Data\Gfx\SomeObject.Texture.res");
  • Reference it directly. This will load the Resource as soon as it is accessed:
    Code:
    texRef = new ContentRef<Texture>(null, @"Data\Gfx\SomeObject.Texture.res");
  • Access it using the GameRes namespace. This is essentially a safe variant of #1 as invalid paths or wrong types will show up as compiler errors. When accessing specific Resources directly, this method is recommended:
    Code:
    texRef = GameRes.Data.Gfx.SomeObject_Texture;
  • Default Resources (i.e. the ones that are embedded directly in the Duality binaries) can be accessed using static methods on their respective class:
    Code:
    texRef = Texture.Checkerboard256;

You will notice that the type you're dealing with isn't a Resource object but rather a ContentRef<Resource> or IContentRef. Its purpose is to reference Resources in an abstract way based on their actual path. It is an indirection layer that simplifies dealing with certain use cases: While actual Resources may be unavailable, not-yet-loaded, disposed or disposed-and-then-reloaded, their ContentRefs always remain comparable and reliable. You can think of it as a smart reference that knows what it should contain and takes care of actually doing so. Find more information about the design choices behind ContentRef<T> here if you're interested.

A ContentRef is able to provide some basic information about its content without actually loading the associated Resource. Other than that, it does its best to assist without getting in the way. The following example is an overview of how to use them:

Code:
// Access a Resource
ContentRef<Pixmap> pixmapRef = GameRes.Data.Gfx.SomeObject_Pixmap;
Pixmap pixmap = pixmapRef.Res;

Console.WriteLine(pixmapRef);                    // [L] Pixmap "Data\Gfx\SomeObject"
Console.WriteLine(pixmap);                       // Pixmap "Data\Gfx\SomeObject"


// Reference type casting
pixmapRef = pixmap;                              // Implicit to ContentRef
pixmap = (Pixmap)pixmapRef;                      // Explicit from ContentRef

Console.WriteLine(pixmapRef);                    // [L] Pixmap "Data\Gfx\SomeObject"
Console.WriteLine(pixmap);                       // Pixmap "Data\Gfx\SomeObject"


// Resource type casting
ContentRef<Resource> resRef = pixmapRef.As<Resource>();
ContentRef<Sound> soundRef = resRef.As<Sound>();

Console.WriteLine(resRef);                       // [L] Pixmap "Data\Gfx\SomeObject"
Console.WriteLine(soundRef);                     // [N] Resource "null"
Console.WriteLine(resRef.Is<Pixmap>());          // True
Console.WriteLine(resRef.Is<Sound>());           // False


// Equality checking
ContentRef<Pixmap> pixmapRef2 = Pixmap.Checkerboard256;
Console.WriteLine(pixmapRef == pixmapRef2);      // False
Console.WriteLine(pixmapRef == pixmap);          // True
Console.WriteLine(soundRef != null);             // False


// Properties that don't trigger loading
Console.WriteLine(pixmapRef.Path);               // Data\Gfx\SomeObject.Pixmap.res
Console.WriteLine(pixmapRef.FullName);           // Data\Gfx\SomeObject
Console.WriteLine(pixmapRef.Name);               // SomeObject
Console.WriteLine(pixmapRef.ResType);            // Duality.Resources.Pixmap
Console.WriteLine(pixmapRef.IsExplicitNull);     // False
Console.WriteLine(pixmapRef.IsLoaded);           // True
Console.WriteLine(pixmapRef.IsDefaultContent);   // False
Console.WriteLine(pixmapRef.IsRuntimeResource);  // False


// Properties that do trigger loading
Console.WriteLine(pixmapRef.IsAvailable);        // True
Console.WriteLine(pixmapRef.Res);                // Pixmap "Data\Gfx\SomeObject"


// Interface class
IContentRef resRefInterface = resRef;
// ... access properties, Is, As, etc.


Custom Resources

In some cases, you might want to have your own kind of static or global data: Game rules, blueprints, shared states, whatever your specific game needs to store in or read from files. Long story short, here's how it works:

Code:
[Serializable]
public class YourCustomResource : Resource
{
    private float someFloatValue = 1.0f;

    /// <summary>
    /// [GET / SET] A float value that encodes some kind of information.
    /// </summary>
    public float SomeFloatValue
    {
        get { return this.someFloatValue; }
        set { this.someFloatValue = value; }
    }
}


It's quite easy. Just derive from the Resource base class and implement as needed. You will be able to create, configure and use your custom Resource type in the editor just like all the other Resources. For a quick test, right-click in the Project View and select New / YourProjectNamespace / YourCustomResource.

Best Practices

On referencing Resources:

  • Fields should always use ContentRef<T> instead of T.
  • Non-private Properties should always use ContentRef<T> instead of T.
  • Non-private Methods should always use ContentRef<T> instead of T in both parameters and return value.
  • You may safely hold direct references locally. However, do not store it anywhere except local variables.
    Code:
    protected void SomeMethod(ContentRef<Pixmap> somePixmapRef)
    {
        Pixmap directRef = somePixmapRef.Res;
        if (directRef == null) return;

        directRef.DoSomething();
        directRef.DoSomethingElse();
        directRef.DoSomethingReallyUseful();
    }
  • Accessing ContentRef<T>.Res is very cheap - except when the Resource hasn't been loaded before and loading is triggered on-demand. To manually trigger loading, use ContentRef<T>.MakeAvailable(). You can trigger loading of certain data directories using GameRes.Data.MyDirectory.MakeAvailable().
  • Always assume that content may be unavailable and ContentRef<T>.Res might return null.

On other topics:

  • Resources should always be fully contained within Dualitys content management system. This means:
    • Don't load external data on construction or initialization of a Resource. Instead, this data should be contained within the Resource itself or another Resource that is referenced via ContentRef<T>.
    • Don't refer to external file or directory paths in general.
    • Refer to other Resources only using ContentRef<T>. Don't refer to them using a string path.
  • You shouldn't need to reference GameObjects, Components or anything else that is part of a Scene. If you do, think again.
  • Flag non-persistent / temporary fields with the [NonSerialized] attribute.

_________________
Blog | GitHub | Twitter (@Adams_Lair)


Top
 Profile  
 
Display posts from previous:  Sort by  
Post new topic Reply to topic  [ 1 post ] 

All times are UTC + 1 hour [ DST ]


Who is online

Users browsing this forum: No registered users and 2 guests


You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum

Jump to:  
cron
Powered by phpBB® Forum Software © phpBB Group