Tutorial:RGB Cube: Difference between revisions

From Resonite Wiki
No edit summary
pre-dinner WiP
Line 188: Line 188:
Then attach a material component to the box's Slot itself and make sure it is used in the MeshRenderer. Done!|suggestion}}
Then attach a material component to the box's Slot itself and make sure it is used in the MeshRenderer. Done!|suggestion}}


Oh, you don't know programming? That's fine! This is a tutorial. (Even experienced developers should continue reading!)
Oh, you don't know programming? That's fine! This is a tutorial. (Even experienced developers should continue reading to familiarize with the syntax!)


So, for a start, find the <code>AlbedoColor</code> property of your [[PBS_Metallic]]. Then grab the property name with the equipped ProtoFlux Tool and select the context menu <code>Source</code>.
So, for a start, find the <code>AlbedoColor</code> property of your [[PBS_Metallic]]. Then grab the property name with the equipped ProtoFlux Tool and select the context menu <code>Source</code>.
Line 226: Line 226:


On this <code>[[ProtoFlux:Write|Write]]</code> node you can see multiple inputs (left) and outputs (right) as well as something that looks close to a property:
On this <code>[[ProtoFlux:Write|Write]]</code> node you can see multiple inputs (left) and outputs (right) as well as something that looks close to a property:
* A white triangle-shaped input which identifies as <code>*<ISyncOperation></code> when pointing at it.<br/>
; A white triangle-shaped input which identifies as <code>*<ISyncOperation></code> when pointing at it
This will trigger the write node when an [[Impulses|Impulse]] comes through. Use primary-drag + secondary-click to create a [[ProtoFlux:Call Input|Call Input]] node that you can use to start execution of an impulse chain!
This will trigger the write node when an [[Impulses|Impulse]] comes through.
* An orange rectangular input which identifies as <code>Value<colorX></code><br/>
Use primary-drag + secondary-click to create a [[ProtoFlux:Call Input|Call Input]] node that you can use to start execution of an impulse chain!
This is the value that will be written into the variable targeted by the node. Create a [[ProtoFlux:Value Input|Value Input]] node the same way you created the Call Input and change it to a proper color! (Default is transparent black: Set <code>Alpha</code> to 1.0 to remove transparency!)
; An orange rectangular input which identifies as <code>Value<colorX></code><br/>
* Two triangle-shaped outputs identified as <code>OnWritten<Continuation></code> and <code>OnFail<Continuation></code><br/>
This is the value that will be written into the variable targeted by the node.
Once the Write node has done its thing, it chooses one of those outputs to continue with the next node. (usually OnWritten, unless something is wrong like no variable to write to)
Create a [[ProtoFlux:Value Input|Value Input]] node the same way you created the Call Input and change it to a proper color! (Default is transparent black: Set <code>Alpha</code> to 1.0 to remove transparency!)
* TODO
; Two triangle-shaped outputs identified as <code>OnWritten<Continuation></code> and <code>OnFail<Continuation></code><br/>
Once the Write node has done its thing, it chooses one of those outputs to continue with the next node. (usually OnWritten, unless something is wrong like “no variable to write to”)
:  Use primary-drag + secondary-click again to create an [[ProtoFlux:Impulse Display|Impulse Display]] node for each output!
; A “property” called <code>Variable</code>
:  This is the variable this node will write to.
:  Set this up by primary-dragging from the output of the Source node to the label or reference input below it! The reference input should now display <code>ValueSource`1</code> instead of <code>''null''</code>.
 
If you followed all steps your code should now look like this:
 
[[File:Manually triggered ValueWrite.png|thumb|center]]
 
Execute it by clicking the <code>Call</code> button on the Call Input node! The box should now have changed color.
 
If that didn't happen, check:
# Is the code connected like shown? Is there an arrow between the Source and the Write node when you hover over either?
# Look at the impulse outputs: If the impulse exited via <code>OnFail</code> your source may point to a material or Source node you have destroyed already. (e.g. if you had multiple cubes and deleted the wrong one)
# Click the arrow button on the source field: If nothing happens it points to an already destroyed material. Otherwise, check if the material is actually used with the MeshRenderer!
# Did you try to change the color to the same color the cube had already? Please change the input to the write node to something where you actually notice the change!
 
PS: Those are good troubleshooting tips in general. Feel free to look at them even if everything worked on the first try!
 
So, now you have a color-changing box. How can it be improved?
 
; You want to assign a random color?
:# Create a [[ProtoFlux:Random Hue ColorX|Random Hue ColorX]] node from the category <code>Math/Random</code>!
:# Connect it to the Write node's <code>Value</code> input with primary-drag from its output to the input! (or the other way around!)
; You want to trigger a color change automatically, e.g. when you hit something with the box?
:# Create the node [[ProtoFlux:On Contact Start|On Contact Start]] from the category <code>Physics/Events</code>!
:# Connect its <code>OnEvent</code> impulse output with the <code>*</code> input of the Write node! (primary-drag again...)
:# Grab the [[Component:BoxCollider|BoxCollider]] component's title and let go of it while pointing at the <code>Collider</code> property of On Contact Start!
:# Change the BoxCollider's <code>Type</code> to <code>Active</code> to make it actively detect collisions with other [[Collider|colliders]]!
; You want to play a sound on each color change?
:# Create the node [[ProtoFlux:Play One Shot|Play One Shot]] from the category <code>Audio</code>!
:# Create a connection from the Write node's <code>OnWritten</code> output to the <code>*</code> input of the Play One Shot!
:# Create an input node for the <code>Clip</code> input of the Play One Shot! (Remember: primary-drag + secondary!)
:# Get/import an audio clip and put it into the audio clip input. (e.g. primary-click with your laser while grabbing)
:#* If you don't have any at hand, use a [[Microphone]] to record a very short sound! (<1s, don't forget to reequip the ProtoFlux tool again!)
:# Go to the inspector showing your Box! On the left side (a hierarchical view) grab the item representing your box, open your context menu and select <code>Reference</code>! This spawns a [[ProtoFlux:Changeable Source|Changeable Source]] node that outputs a reference to your box.
:# Connect the Changeable Source to the <code>Root</code> input of the Play One Shot! This makes sure that the sound comes from the box and not from the code.


== Notes ==
== Notes ==

Revision as of 17:57, 10 April 2024

This article or section is a Stub. You can help the Resonite Wiki by expanding it.


This is meant as a quick jump into the Entity Component System (Slots/Components) and Protoflux. Over the course of this tutorial you will create a RGB cube from scratch and play around a bit with a few tools.

It is assumed that you already know how to navigate your Dash Menu or how to open a Context Menu.

Note: Clicking in this tutorial refers to hitting the primary action key. (i.e. left mouse button on desktop mode)

Basic Visuals

«In the beginning, there was nothing.
And Frooxius said, Let there be light.
And there was light.»[Citation needed]

But what help is light if there is nothing to see?

File:Dev Tool

So, equip a Dev Tool, open the context menu, and select Create New..., then double-click Empty Object.

You can find a Dev Tool in your Inventory within the folder Resonite Essentials/Tools
File:Dev Tool Create New.png
File:Create New Empty Object.png

You created an empty Slot - still a bit boring, right?
This is because Slots are just containers for Components and other Slots. They don't do anything on their own. (except having a name, being in a 3D hierarchy and a few other properties)

File:Empty Slot in Inspector.png

Click Attach Component and select the component Assets/Procedural Meshes/BoxMesh. (double clicks!)

File:Slot with BoxMesh.png

You added a component to the slot. They are the actual things you see, hear, touch etc.

But why don't you see the box you just created?
The reason is that while some components do things, some other components just represent data - like a BoxMesh which is just 24 vertices, connected by 12 triangles. There is no instruction telling Resonite to render this mesh.

You can fix this by adding the component Rendering/MeshRenderer. And don't cheat by clicking Setup Renderer!

The MeshRenderer component will tell the engine to actually render a mesh.
Emphasis is on a mesh! You need to specify which one with the Mesh property.
To fill it, first grab the header of the BoxMesh component.

File:Grabbed BoxMesh pointed at Mesh property.png

You are now holding a reference to the mesh which is visualized with a preview of the mesh data. Then point at the Mesh property or the MeshRenderer and let go. (alternative: click while pointing at the property)

File:Rendered Box without material.png

Success, there is something to look at!

But what is this checkerboard pattern?
The answer is simple: Resonite now knows the shape of the object but not what its surface is like. (Is it green like grass? Is it shiny? Does it glow in the dark?)

For a start just add the component Assets/Metarials/PBS_Metallic. Similar to the mesh this component just represents some data.

File:PBS Metallic in default config.png

To put it into the MeshRenderer, just click the Add button under Materials (list): and fill the new entry with a reference to the material. (grab&drop/click!)

File:MeshRenderer with BoxMesh and PBS Metallic.png

This looks very close to the default cube. You could almost touch it - but you can't!

Basic Interaction

Your creation still lacks in the interactive part of the experience.

To be able to physically interact the Physics category of components is a must-have. In this case you must choose one of the Colliders, and which of them matches a box the most if not the BoxCollider?

File:Box with BoxCollider shows laser interaction.png

Your laser now hits the box - that's it. Now that you could interact with it you need to specify how: Attach the component Transform/Interaction/Grabbable. This tells Resonite that anyone can grab this object when their laser or grab sphere touch it and they use their grab action.

You want to change the size of your box while holding it? Enable the Scalable property of the Grabbable and pick it up with two hands! (Desktop: right mouse button to “grab” + Shift + move mouse wheel)

File:Box being grabbed with two hands.png

Editing

A box like this is not very useful. You can't even hit people with it.
You can blame the shape: It's just too fat!

Go to the BoxMesh component and edit the Size property to be [0.1; 0.1; 1].

File:Box that has been slimmed down.png

Changing the size of a box by numbers is a bit lame. This is where you can use the power of Dev Tools: Use secondary action while pointing at the box to select it, open the context menu and select Gizmo Options/MeshRenderer.

File:Box with MeshRenderer gizmos.png

This is one example of the many types of Gizmos that can be interacted with via primary action.
Note that they usually have priority over other colliders, allowing you to interact with Gizmos behind your object!

Have you noticed that the selection visuals don't align with the dimensions of your box visuals?
Take a look at the BoxCollider!

File:BoxCollider with default values.png

It also has a Size. Visualize it by clicking the button Visualize Collider within the BoxCollider component!

File:Box with unmatching BoxCollider visuals.png

Now copy the values from the MeshRenderer to the BoxCollider!

File:Box with matching BoxCollider visuals.png

Ta-da! It matches the visuals again.

For the purpose of this tutorial we will assume we are not happy with the dimensions:

  • Change them!
  • And make sure that the collider matches the visuals!
  • Change it again while matching the collider!
  • Do it again!
  • And again!
  • Do it 100 times!

Okay... That probably is enough. Tedious, right?

So, here are a few tricks that you may have figured out on the way:

  • You can copy and paste text via the clipboard of the operating system of your choice. In VR there are usually dedicated keys on your keyboard while on desktop you use the shortcuts Ctrl+C and Ctrl+V.
  • You can “Grab & Drop” texts from one text field to another.
  • Grabbing the colored icon to the left of a property name and dropping it directly on a property name of a compatible type allows you to copy the full value at once. (a 3-dimensional vector here)
  • Even with all those tricks it is tedious work to copy all those values manually. Mistakes are a guarantee!

Simple Data Binding

Introducing: Transform/Drivers/ValueCopy

Configure it like this:

  • Source should be a reference to the Size of the BoxMesh. Grab the property name and drop/click it here. (on the input which shows a null value by default!)
  • Target should be a reference to the Size of the BoxCollider.
File:ValueCopy from BoxMesh.Size to BoxCollider.Size.png

Now it should do the work for you - all changes to the BoxMesh will be applied to the BoxCollider.

Take a look at the Size property of the BoxCollider:

File:Driven BoxCollider Size.png

Why is it pink?!
This color indicates that a property is driven. If you refuse to use Wiki links, here is the short version:

  • It is not an independent value anymore but instead computed from others. (Size of BoxMesh in this case)
  • As a side-effect it disables the FrooxEngine magic that communicates values across the network. Everyone computes their own version. (Warning: Links contain quite technical Wiki pages!)

Try to edit the Size of the BoxCollider now!

It doesn't work, right? This makes sense if you consider that this value is not independent anymore. Even assuming that you were temporarily changing it - which you didn't - it would be overwritten by ValueCopy immediately. (Note: the inner workings are slightly different and also a bit more complex.)

This behaviour changes when you enable WriteBack.

Now you can change the Size of either BoxMesh or BoxCollider and the other will change accordingly.

This example shows that there may be many useful components you haven't heard of before and even simple properties may change their behaviour completely. You may be overwhelmed by how many there are, so take it easy and learn it in your own pace!
At good starting point is the category Transform/Drivers which contains many useful components that are centered around driving values.

It doesn't matter if you just try stuff on your own or if you study the Wiki:
If you are stuck, ask the community! We are happy to share our experience with you.
In fact teaching others or being taught in Resonite is a nice, social experience on its own. More often than not, not only the “student” but also the “teacher” learns something new. (Bonus: There are no exams, you just learn for life!)

If you still have energy you may continue with the next chapter to be introduced to programming in Resonite. Otherwise you can save your box when you grab it, open your Dash Menu, select one of your own directories inside the Inventory and click Save Item.

Don't forget to give your project a proper (Slot) name before saving! (Too late, isn't it? :-P)

Programming

Only grabbing and scaling becomes boring after a while. Wouldn't it be nicer if your project could actually do something a little more complex?

We could make it spin, snap to other objects or just look creepy.
There are many things we have components for and a lot of things you can do with a combination of them
and if you want to do even more you can create your own logic in ProtoFlux, the visual programming language of Resonite.

File:Flux Tool

So equip your ProtoFlux Tool and start programming!

You want to continue and found out that you lost your first project?

Don't worry: You were actually tricked to create a slightly modified default cube that you can spawn with the Dev Tip via Create New.../3D Model/Box. (They have to come from somewhere!)

Use secondary action on the default box and click Open Inspector on the context menu. Then attach a material component to the box's Slot itself and make sure it is used in the MeshRenderer. Done!

Oh, you don't know programming? That's fine! This is a tutorial. (Even experienced developers should continue reading to familiarize with the syntax!)

So, for a start, find the AlbedoColor property of your PBS_Metallic. Then grab the property name with the equipped ProtoFlux Tool and select the context menu Source.

File:Source of PBS Metallic.AlbedoColor.png

You have created a Source node which is one of the bridges that connect your code with the World around it. It allows you to read the value of a property directly.
To demonstrate this, use and hold down primary action on the orange output that shows the tooltip *<colorX> on your ProtoFlux tool and tap secondary action.

File:Source with Display.png

You have now created a Display node which is displaying the value connected to its input directly. Their main use is to test your code since they are only visible when the code is visible. (Also very useful when exploring unknown nodes/code!)

Change the AlbedoColor with the inspector and observe that the Display node changes output! (Hint: You can click the colored rectangles next to the raw color value to open a color picker!)

The Source node also acts as a bridge in the other direction. To do this open the context menu of the ProtoFlux Tool and click Browse nodes.

File:Node Browser.png

You have opened the node browser which you navigate through via double-clicks of your primary action.
Go to the category Actions/Write

File:Node Browser.Actions.Write.png

ObjectWrite? ValueWrite? <T> or <C,T>?
This is a bit confusing for the start but you will get there. For now:

  • Grab the node browser and create a duplicate of it via the context menu! Put it to the side for later!
  • Navigate to the subcategory ValueWrite<T>, scroll down until you find the entry ValueWrite<colorX> and double-click it with the ProtoFlux Tool!
File:Selection of ValueWrite colorX.png

You have selected the node type ValueWrite<colorX>.
Double-click primary with the ProtoFlux Tool while not pointing at things to create one!

File:ValueWrite node for colorX.png

On this Write node you can see multiple inputs (left) and outputs (right) as well as something that looks close to a property:

A white triangle-shaped input which identifies as *<ISyncOperation> when pointing at it
This will trigger the write node when an Impulse comes through.
Use primary-drag + secondary-click to create a Call Input node that you can use to start execution of an impulse chain!
An orange rectangular input which identifies as Value<colorX>
This is the value that will be written into the variable targeted by the node.
Create a Value Input node the same way you created the Call Input and change it to a proper color! (Default is transparent black: Set Alpha to 1.0 to remove transparency!)
Two triangle-shaped outputs identified as OnWritten<Continuation> and OnFail<Continuation>
Once the Write node has done its thing, it chooses one of those outputs to continue with the next node. (usually OnWritten, unless something is wrong like “no variable to write to”)
Use primary-drag + secondary-click again to create an Impulse Display node for each output!
A “property” called Variable
This is the variable this node will write to.
Set this up by primary-dragging from the output of the Source node to the label or reference input below it! The reference input should now display ValueSource`1 instead of null.

If you followed all steps your code should now look like this:

File:Manually triggered ValueWrite.png

Execute it by clicking the Call button on the Call Input node! The box should now have changed color.

If that didn't happen, check:

  1. Is the code connected like shown? Is there an arrow between the Source and the Write node when you hover over either?
  2. Look at the impulse outputs: If the impulse exited via OnFail your source may point to a material or Source node you have destroyed already. (e.g. if you had multiple cubes and deleted the wrong one)
  3. Click the arrow button on the source field: If nothing happens it points to an already destroyed material. Otherwise, check if the material is actually used with the MeshRenderer!
  4. Did you try to change the color to the same color the cube had already? Please change the input to the write node to something where you actually notice the change!

PS: Those are good troubleshooting tips in general. Feel free to look at them even if everything worked on the first try!

So, now you have a color-changing box. How can it be improved?

You want to assign a random color?
  1. Create a Random Hue ColorX node from the category Math/Random!
  2. Connect it to the Write node's Value input with primary-drag from its output to the input! (or the other way around!)
You want to trigger a color change automatically, e.g. when you hit something with the box?
  1. Create the node On Contact Start from the category Physics/Events!
  2. Connect its OnEvent impulse output with the * input of the Write node! (primary-drag again...)
  3. Grab the BoxCollider component's title and let go of it while pointing at the Collider property of On Contact Start!
  4. Change the BoxCollider's Type to Active to make it actively detect collisions with other colliders!
You want to play a sound on each color change?
  1. Create the node Play One Shot from the category Audio!
  2. Create a connection from the Write node's OnWritten output to the * input of the Play One Shot!
  3. Create an input node for the Clip input of the Play One Shot! (Remember: primary-drag + secondary!)
  4. Get/import an audio clip and put it into the audio clip input. (e.g. primary-click with your laser while grabbing)
    • If you don't have any at hand, use a Microphone to record a very short sound! (<1s, don't forget to reequip the ProtoFlux tool again!)
  5. Go to the inspector showing your Box! On the left side (a hierarchical view) grab the item representing your box, open your context menu and select Reference! This spawns a Changeable Source node that outputs a reference to your box.
  6. Connect the Changeable Source to the Root input of the Play One Shot! This makes sure that the sound comes from the box and not from the code.

Notes

Done:

  • BoxMesh -> nothing to see here
  • MeshRenderer -> still nothing
  • Drag&Drop ref into field -> Finally, but checkerboard!
  • Material -> Looks OK!

(* Texture (something bright, have it in wiki) -> Nice but can't interact!)

  • Component:BoxCollider -> some reaction with laser but still no interaction
  • Grabbable -> interactive but a bit large
  • Scalable -> but can't stretch
  • resize with numbers -> boring
  • resize with Dev Tool -> much better, but wait, the interaction is with original box (also: hint at the multitude of Gizmos)
  • manually copy values -> Automation?!
  • Component:ValueCopy -> better
  • The simple way to do the same. (destroy ValueCopy and drag&drop)
  • WriteBack
  • hint to documentation and asking people

Planned:

  • PF: source
  • PF: write with input
  • PF: PF with random
  • PF: create on collision trigger
  • PF: drive (flashy images warning)
  • PF: T -> Hue ->
  • PF: time multiplier
  • PF: Packing (create dedicated Slot)
  • Refactoring: create parent Slot, drag&drop grabbable component
  • Proper naming
  • end of tutorial: We failed - it's not a cube!