One, foreword

First, the great news is that Godot fans have a new home — our own Godot forum: The Godot Chinese community is now open. Here you can find the latest development resources, the latest technology trends, and any questions you have about development. Please feel free to join the forum to discuss, exchange and learn the latest technology in game development. 😀

So, back up, today’s topic is Godot’s Resource class, which is easy for beginners to overlook. For those of you who have developed Unity games, there is a useful class called ScriptableObject, which can be used to wrap data and should be useful in many situations. Is there a similar feature in Godot? Well, yes, that’s the type of Resource we’re talking about today.

There is also an introduction to Resources on the official website. We know that scenes cannot be dragged and dropped, and are also fixed. It is not reasonable to use scenes to save some common data, so we can use the Resource class. Compared with Node, it has obvious advantages. It is very flexible to use. You can also write scripts, define properties and methods, create resource files easily, and drag and drop applications directly. “OK, FINE!” I’ll cover all of this, but more importantly, I’ll use Resource today to present a new, flexible, “powerful” decoupled EventBus global event pattern. Interested? So let’s move on.

Main contents: the Resource of relevant usage profile reading time: 8 minutes Permanent link: liuqingwen me / 2020/08/17 /… Series homepage: liuqingwen. Me/introductio…

Second, the body

Resource is not mysterious, but it is easy to overlook. In fact, the scenes and nodes we usually create contain various types of Resource files. A diagram on the official website shows the relationship between some nodes and Resource resources:

The names above are familiar. There are many types of resources that can be used in game scene development. Common ones are: Image resources, collision graphics, various materials, UI themes, audio streams, gradiens, curves, and even animations created in our common AnimationPlayer nodes, as well as GDScript scripts and shader code, are all resources.

Creating and using resources is also very simple, but there are some limitations in the current Godot 3 release, which we’ll talk about in more detail.

Create and use Resource

Resources can be created in various ways, usually directly in the properties panel of the Node Node, such as New shapes of a player’s collision graphics, various animations in the animation player, New textures of the particle system, etc. These resources have one characteristic: we use them out of the box, and rarely save.

Resource files can also be created separately. If we need to create a resource that needs to be used in many places, such as a common theme resource, font resource, tile map TileSet resource, etc., we can create a separate type of resource file, save it, and easily reuse it in different scenarios. You can create a new resource file in the properties panel or node properties:

After creating a new resource file, remember to save it. The file name extension is usually.tres, but there are also.res file types. The difference is to save it in text format or binary format:

We can modify the properties of the saved resource file at any time by double-clicking the resource file. In addition, we can also create multiple copies, such as duplicate font resources, and then modify the font size properties to use in different places.

The use of resources is simple, you can directly drag and drop into the corresponding properties, or click Load from the properties drop-down list. The system has complete resources, of course, we can also customize the resource type. A Resource is still essentially a script file, and to create a custom Resource you first need to create a script that inherits from the Resource class:

# inherited from Resource indicates a Resource script
extends Resource
class_name CustomResource, 'res://CustomResource/custom_icon.svg'

Resources can also define common attributes
export var variable1 := ' '
export var variable2 := 0
#...

Resources can also define some methods
func printInfo() -> void:
    #...
Copy the code

We declare the resources in the new code above the class name of the icon (CustomResource) and resources (res: / / CustomResource custom_icon. SVG). Once created, you can find the corresponding custom resource type in the new resource list. The following figure shows the process:

Is it very simple? Start creating a pressure. :joy:

Resource-related issues and limitations

Resources are easy to create and use, but Godot 3 has a few holes in customizing resources, which I hope will be useful for beginners.

1. Do not use user-defined Resource as the variable type

When we create a CustomResource, we can define a class name for the resource class_name CustomResource, but we cannot define a resource variable of this type in our code:

var resource1 : Resource # no problem
var resource2 : CustomResource # not supported!
Copy the code

The above code runs with an error:

built-in:4 – Parse Error: Invalid export type. Only built-in and native resource types can be exported.

One way to avoid this problem is to use the parent type Resource as the type of the variable, but this would be inconvenient and inhumane to assign any type of Resource file in the export attribute. Of course, you can determine this in the code:

if resource && resource is CustomResource:
    # code...
Copy the code

However, the good news is that this issue will be addressed in Godot 4.0.

2. When using Resouce, note that the resource is a reference type

If a resource file is used by multiple nodes, this time you only need to change any attribute of the resource under one node, the result will cause the resource under other nodes to change.

For example, there is a font resource file called font_resource. Res in the game resource. When you change the font size in the resource properties, all other UI fonts that use the resource will change. Which is why beginners often this situation: to create a node, add a collision, a collision body shapes, new set up copy and rename the node, change new collision nodes of images and collision body shapes, inexplicably found node before the collision body shapes have also changed, in fact, it is for this reason. 😀

Therefore, it is not a big problem for Godot to create a Duplicate resource by right-clicking the resource file or using Make Unique to Make the specified resource Unique.

3. Use Resouce to avoid circular references

This error can occur if you create a number of custom resource files in your project and the custom resource code references other types of resources.

“scene/resources/resource_format_text.cpp:1387 – Circular reference to resource being saved found: ‘res://src/… /??????? .tres’ will be null next time it’s loaded.”

Circular references can occur in normal GD code, but they can be difficult to detect in custom resources. The solution to this problem is not to assign the Resource directly in the editor, but to determine and dynamically load the Resource at runtime, as shown in the following example:

export var resource : Resource       # Customize resources
export var resourceFilePath : String # resource path

func method() -> void:
    if resource == null:
        Load the resource file at runtime
        resource = load(resourceFilePath)
    # code...
Copy the code

This should be a rare case and will not be discussed in detail until later articles, but of course, the eagerly awaited version 4.0 will address this issue.

4. Other small questions

If you change the icon or class name in the Resource script, other code that references the Resource will report an error, such as the Resource class being corrupted or not fully loaded. Just restart the project.

Sometimes you get this little BUG:

core/script_language.cpp:244 – Condition “! global_classes.has(p_class)” is true. Returned: String()

It’s a bit inexplicable and not easy to reproduce. I guess it’s because I changed the name of the Resource script class. :joy:

Waiting for Godot 4.0 is a cure.

Creating a Resource is equivalent to a DataContainer

A classic use for creating custom Resources is as data containers. Creating a resource file is equivalent to creating a data container. These data containers generally have no other functions but store some application data independently, which is very convenient and flexible whether it is modified or used.

As an example of a real-world application scenario, in a Player or AI script, if there are a number of data attributes that generally don’t change or are just configuration parameters, It is perfectly possible to separate it out as a separate data class — this is one of the refactoring approaches advocated in refactoring – Improving the Design of Existing Code.

# class player

export var name := 'player'
export var moveSpeed := 200
export var rotateSpeed := 5
# Some other attributes...
Copy the code

This so-called separate data class in Godot can be wrapped with an inner class:

# class player

# inner class
class Data:
    var name := 'player'
    var moveSpeed := 200
    var rotateSpeed := 5
    func _init():
        pass
Copy the code

Inner classes can encapsulate data, but they are awkward to use outside the scope of a script and not easy to edit in an editor. In this case, we can use custom resource classes to solve this pain point:

extends Resource

export var name := 'player'
export var moveSpeed := 200
export var rotateSpeed := 5
Copy the code

Then create a single or multiple resource files and modify the corresponding property values in the properties panel of the editor, which is very convenient to use in other code:

export var dataResource : Resource = null

fun _ready() -> void:
    ifdataResource ! = null:print(dataResource.name, dataResource.moveSpeed, dataResource.rotateSpeed)
Copy the code

As a data container similar to a ScriptableObject, let’s look at another very useful scenario for Resource.

Create the EventBus for the global event with Resource

This is the focus of this article, and I haven’t seen anyone use it in a project yet, so let me explain

First of all, we are all familiar with the Godot signal and observer mode. Generally, in game development, we will follow the principle of signal up and call down, i.e. sending a signal to the upper level and calling the lower level directly. As the game becomes more and more complex, signals can fill the entire project, such as the information panel in a multiplayer game that needs to receive and display many different types of signals: The text message sent by the player pressing the enter key, the signal sent by the player winning a battle, the signal sent by the player quitting the game, the information pushed by the official server and so on. Because these messages happen in different scenes, it is not easy to process. I can think of several solutions:

  1. useget_node('.. /root/node_path')Is not recommended and strongly condemns, which leads to strong coupling and is extremely difficult to extend, maintain, and refactor
  2. useGlobal AutoLoad, also known as Singleton Singleton mode, effectively solves the coupling problem, but it is quite difficult to maintain and debug
  3. Use Resource to create corresponding event resources, powerful decoupling, easy to use, easy to debug, easy to expand and maintain

The second way is the recommended mode, which I used in the previous example: (Godot game development practice: Make a High Level Multiplayer API (1)), which is also covered in the GDQuest documentation: www.gdquest.com/docs/guidel… , the sample code is as follows:

This is an AutoLoad singleton
extends Node
Multiple common signals can be defined
signal new_message(content)

# Other code...
Copy the code

Other scenarios are also very simple to use:

# Sending signals in scenario 1:
GameConfig.emit_signal('new_message'.'... ')

# Receiving processing signals in Scenario 2:
GameConfig.connect('new_message', self, '_on_NewMessage_arrive')
Copy the code

But this approach has a major drawback: global references make refactoring difficult. Because singletons are equivalent to the global pattern and can be referenced anywhere, it is not easy to find these references when changing a method or attribute in a singletons during refactoring. This is why GDQuest’s recommended EventBus mode is a script file created separately with only signals and no other code.

Let’s take a look at the event pattern created using Resource. Start by creating an event resource:

# Customize resources
extends Resource
class_name EventResource, 'res://EventResource/event_icon.svg'
# Custom signal
signal custom_event(type, message)
Some attributes can be defined
export var type: ='defaultEvent'
# Custom method used to send signal packaging, can also send signals directly
func emitSignal(object) -> void:
    self.emit_signal('custom_event'.type.object)
Copy the code

Next, we can create some event resource files, such as message_event.tres trigger_event.tres. Different files can be changed, configured with different parameters, and then used in other scripts:

export var messageEvent : Resource = null
export var triggerEvent : Resource = null

You can use event resources to listen for events
func someMethod1() -> void:
    if triggerEvent && triggerEvent is EventResource:
        triggerEvent.connect('custom_event', self, '_onTriggerEventHandler')

You can also send events using event resources
func someMethod2() -> void:
    if messageEvent && messageEvent is EventResource:
            messageEvent.emitSignal(info)
Copy the code

Because these events are resource types, they can be directly dragged and dropped in node attributes, and they are optional and do not affect the operation of the whole project. In this example, the player’s attribute configuration is shown as follows:

You can see that Player1 only receives message_event, Player3 only sends trigger_event, and Player2 has no configuration.

To summarize some of the advantages of creating events using Resource:

  1. Strong decoupling! Easy to refactor without relying on other files or scripts or nodes
  2. Easy to debug, code as long as attentionnullJust reference, delete or add related events are very friendly
  3. Easy to test, modify event related attribute values very convenient, change all change
  4. It can be considered in large projects

There is no one-size-fits-all solution, and there are drawbacks, such as stacks of.res files that change the value of only one variable. The important thing is that there are no actual projects supporting this event mode, so it needs to be developed and explored. 🙂

Third, summary

Well, this article talked about a simple Resource topic, I hope to bring a little help to novice friends, to master friends develop a little light, then this article is worth it.

Remember our new home for Godot enthusiasts: Godot Chinese Community, come back and visit us often!

The Demo and related code have been uploaded to Github at github.com/spkingr/God… , continue to update, the original is not easy, I hope you like! 🙂

My blog address:liuqingwen.me, my blog will be synchronized to Tencent Cloud + community, invite you to join:Cloud.tencent.com/developer/s…, welcome to follow my wechat official account (first update + game development resources + related information) :