-
-
Notifications
You must be signed in to change notification settings - Fork 3.3k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add a small tutorial on using Resource scripts. #1768
Conversation
Thanks for this! I'll proofread - FYI, building the docs yourself locally is a great way to verify how it looks in the output, and isn't too hard: Look at the Building with Sphinx section of the README in this repository. |
1d7cfd5
to
babdcc7
Compare
@mhilbrunner I got it rendering locally (yay!), so I've fixed up a bunch of stuff with the syntax, however I can't seem to figure out how I'm supposed to make the "script classes" link work. It's supposed to link to the "Scripting Continued" page at the "Register scripts as classes" header, but all the variations I've tried (like :ref:`script classes <scripting_continued#register-scripts-as-classes>`) don't seem to work. |
|
||
- Resources can even serialize sub-Resources recursively, meaning users can design even more sophisticated data structures. | ||
|
||
- Users can save Resources as version-control-friendly text files (\*.tres). Upon exporting a game, Godot serializes resource files as binary files (\*.res) for increased compression. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Aren't *.res
files also faster to load?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, because the same amount of data is packed into a smaller space, so it doesn't take as long to parse the data as input.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I suppose I could do "for increased speed and compression" instead.
With GDScript, it only supports Resource classes at the top level. Defining | ||
a Resource subclass, i.e. an inner class, within a GDScript file will have no | ||
effect. If one attempts to save an instance of a Resource subclass, none of | ||
the scripted properties will serialize to the saved file. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This sounds like a bug to me. I've saved ShaderMaterial
s with Shader
resources inside them before, so I don't see why this should be any different.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You can have sub resources within scripts, but if you try to define an entirely new type of Resource as a subclass within another script, then Godot isn't going to identify the path for the script on that resource as "path to the file that has the Resource subclass AND somehow mark that you need to assign the constant value of the subclass Script resource as the script value for the created *.tres. You know, something like "res://file.gd#MyResource" or what-have-you.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Like, if I make a Resource object and set it's script to be not another script file, but a Resource class that's a subclass of a script file (where the script file could derive ANYTHING), it won't start serializing those script properties properly or anything.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sure, sounds good.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ah... I think that's my bad for the misunderstanding, then.
That being said, it might be worth trying to reword this paragraph, as I get the feeling it can still come off as ambiguous, not making a clear enough distinction between inner classes and subresources.
Sure, sounds good.
By the way, I deleted my first response to your reply because I realized I completely misunderstood what you were going for, and decided it'd be better to remake my comment.
.. note:: | ||
|
||
Resource scripts are similar to Unity's ScriptableObjects. However, unlike in | ||
Unity, there is no need to implement IMGUI logic to enable users to create |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In fact, there is no need to implement immediate gui or custom editor to use a "common" ScriptableObject.
The current workflow (I tested it in Unity 2018.3):
- Create your
ScriptableObject
class:
public class CustomData : ScriptableObject {
public string data;
}
- Add it to
Assets->Create
menu:
// This is equivalent to `class_name` in Godot 3.1+
[CreateAssetMenu(menuName="MyCustomStuff/CustomData")]
public class CustomData : ScriptableObject {
public string data;
}
- Create a component that will use your
ScriptableObject
:
public class MyComponent : MonoBehaviour {
public CustomData data;
}
- Create your asset using the
Assets->Create
menu; - Drag and drop it to the
data
field in the inspector.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Oh, interesting. That's news to me. Great that they added that feature! Appreciate the heads-up. I'll go ahead and take out that tidbit then.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Unity docs and changelogs are pretty confusing so I don't know in which version this started to work this way 😅
babdcc7
to
59208b8
Compare
@williamd1k0 @LikeLakers2 Updated the document to reflect all of the changes. |
64ff075
to
c4ae77b
Compare
Okay, finally figured out how to make the script classes link work (just doing an absolute link via URL rather than relative one with :ref:). |
public int health; | ||
|
||
[Export] | ||
public Resource subResource; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
public methods/properties need to be PascalCase. Update this for every property.
scripts grant all the same benefits as the base Resource object. They | ||
inherit both reference-counting memory management and automatic serialization | ||
between binary or text data (\*.res, \*.tres) and in-memory object | ||
properties. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Resource scripts grant all the same benefits as the base Resource object.
They inherit both reference-counting memory management and automatic serialization
between binary or text data (*.res, *.tres) and in-memory object properties.
This is IMO very complex. We need to avoid overwhelming users with cognitive overload :)
How about:
Resource scripts inherit all the same benefits as the base Resource object:
Automatic memory management using reference counting, automatic serialization
between binary or text data (*.res, *.tres) and in-memory object properties.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
How about...
As Resources, Resource scripts inherit the ability to freely translate between object properties and serialized text or binary data (*.tres, *.res). They also inherit the reference-counting memory management from the Reference type.
Sound better?
|
||
public class BotStatsTable : Resource | ||
{ | ||
private Godot.Dictionary<String, BotStats> stats = new Godot.Dictionary<String, BotStats>(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Use underscore prefix for private members & update other occurrences across the board http://docs.godotengine.org/en/latest/getting_started/scripting/c_sharp/c_sharp_style_guide.html#naming-conventions
public class Bot : KinematicBody | ||
{ | ||
[Export] | ||
public BotStats stats; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
public Resource subResource; | ||
|
||
[Export] | ||
public String[] strArray; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
public class BotStats : Resource | ||
{ | ||
[Export] | ||
public int health; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
c4ae77b
to
44f46a1
Compare
@mhilbrunner @exts updated to reflect the syntax changes and the rewording of the introductory paragraph. I have also added an additional warning and code segment for GDScript (not even sure if a C# example would apply for the warning since it has to do with Dictionary vs. Resource memory management and I don't know if C# GC's Dictionaries). I also fixed a few rendering errors that were preventing the examples from showing up. |
Oh, also, I realized that I've been mixing and matching my C# code examples' syntax for allocating objects, but I don't see anything in the docs to indicate what it looks like in the style guide or API differences. Which of these would be used?
And am I supposed to implement an |
[Export] | ||
public String[] Strings { get; set; } | ||
|
||
public override void _Init(int health = 0, Resource subResource = null, String[] strings = []) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
C Sharp's _Init() implementation is parameterless.
This might be best as a constructor:
public BotStats(...)
Default parameters must be compile time constants.
I suggest using String[] pStrArray = null
instead
{ | ||
Health = health; | ||
SubResource = subResource; | ||
Strings = strings; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Here you can use strArray = pStrArray ?? new String[0];
to tidy up the default parameter.
public class Bot : KinematicBody | ||
{ | ||
[Export] | ||
public BotStats Stats; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The BotStats' type is not currently displayed in the inspector.
This means that exporting this variable only presents the opportunity for users to select the wrong type.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If there isn't a way to export the type (scripted types may not be supported this way?), perhaps something where we export Resource
, but then do a runtime check that the type extends BotStats
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
BotStats is not displayed in the Create New Resource dialog either.
Its unclear to me how to create one using the editor GUI.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah, this seems like a deeper issue than I realized. godotengine/godot#22641
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
As for right now, you basically HAVE to export it as an engine type (Resource or some derivative that is still in the engine) and then check if the runtime instance is a script at runtime.
|
||
public override void _Init() | ||
{ | ||
if (!Stats) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
To perform a null check we should use:
if (stats == null)
{ | ||
if (!Stats) | ||
{ | ||
Stats = (ResourceLoader::load("BotStats.cs") as BotStats).new(10); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If we are using the constructor as suggested above we can use:
stats = new BotStats(10);
[Export] | ||
public BotStats Stats; | ||
|
||
public override void _Init() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
While testing this code I found that _Init()
isn't being called (godotengine/godot#22633).
44f46a1
to
0b488f4
Compare
Updated to reflect all changes and relevant Issues. |
public class MyNode : Node | ||
{ | ||
|
||
public class MyResource : Resource { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks pretty good now, just a couple of smaller points.
This brace would be best on the next line.
public override void _Ready() | ||
{ | ||
if (Stats != null && Stats is BotStats) { | ||
Godot.print((Stats as BotStats).Health); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Also this should be GD.Print()
.
Thanks much for your work! |
Oh, I hadn't made those last two changes yet (moving the brace and changing |
Sorry, just added the two small changes! |
I can't vouch (at all) for the C# snippets in here, so someone more familiar with Godot's C# syntax will have to proofread those. I'm also not sure if my links are executed properly and whether I can safely nest code snippets within a note or warning (as I've done here).
This should finally help people get a better idea of how useful Resource scripts can be, how to make them, and how to use them.