Life and Style Media - Tutorials

Version 1.4.x



Learn to create custom units starting with the basics of Inputs and Outputs, Controls and Values. This is the first part of many, where you will learn to create custom code within the Ludiq and Bolt framework.




GENERATE A UNIT


Getting a simple block generated and onto your graph is pretty easy. We will start by first generating the block, so you can see the process in action each of the ways. As the other tutorials progress, we will assume you know what you learned, and skip showing images for every part.

While these are beginner tutorials, they are not C# tutorials. You will be expected to know most syntax that exists in the language. In this tutorial, you should know how Funcs and Lambdas work, so we can achieve smaller cleaner code.


Lets begin!

First we need to create a C# script. You can create this anywhere in your project that is not in an editor folder. Since this unit is only going to be used once to show you how inputs and outputs of all varieties work, we will choose a simple name "BasicUnit" as our class. How clever! Unit is the most basic of classes to inherit from for Units, so we'll do that. It contains an abstract method called Definition, we need to add that, but we will fill it out over the next few steps. It is essentially how ports are defined.

One tricky thing I realized while making this tutorial, is there needs to be members in the class for it to find it during generating units. So lets just simply define the variables for in the inputs and outputs. We'll be essentially combining two strings to make one, without using more then a single unit.

Its as simple as using ControlInput and ControlOutput for the flow lines, and ValueInput and ValueOutput for data. The ports need to be public, and should not be serialized. Therefore you should use the Ludiq Attribute, DoNotSerialize. We will need an enter, an exit, 2 string inputs, 1 output, and a private string variable to store the value. With all that said, the code should look like this:

using
Bolt;

public class
BasicUnit
:
Unit

{
[
DoNotSerialize
]
public
ControlInput
enter;

[
DoNotSerialize
]
public
ControlOutput
exit;

[
DoNotSerialize
]
public
ValueInput
A;

[
DoNotSerialize
]
public
ValueInput
B;

[
DoNotSerialize
]
public
ValueOutput
result;

private
string
resultValue;

protected override void
Definition()
{


}
}


Now you can go up the menu at the top, click Tools > Bolt > Build Unit Options.

(You will never need to add Custom Units into the type options. It is automatically picked up.)

After you have generated, you can add your unit to the graph. For this tutorial, you will find it in the root of the fuzzy finder, at the bottom. Once added, you should have this:





Perfect! Now lets get into defining these ports. We don't need to generate anymore for this unit. Only if it is something that effects the Fuzzy Finders position, category, ect, will need regenerated.


CONTROL PORTS


To define ports you are given 4 methods, named just like the types we have variables for. These are derived from the inherited Unit type. ValueInput, ValueOutput, ControlInput, and ControlOutput.

What you are going to do, is use those in Definition, to assign the port variables. Bolt will do all the magic with those ports itself. You just need to assign them during that method. Not assigning them means they just don't show up. Everytime the unit defines, or senses a change in the unit it, it will attempt to redefine it. That is how logic can have ports visible or not.

To assign a Control input, we must give it a string key, and a Func that sends the current flow in, and gets back an output port (the output can also be null). In this case, we want to leave through the "exit" port. As soon as we enter, we do stuff, and exit.

An exit port is the simplest of all, its just a string key. This is how your code should look now:

using
Bolt;

public class
BasicUnit
:
Unit

{
[
DoNotSerialize
]
public
ControlInput
enter;

[
DoNotSerialize
]
public
ControlOutput
exit;

[
DoNotSerialize
]
public
ValueInput
A;

[
DoNotSerialize
]
public
ValueInput
B;

[
DoNotSerialize
]
public
ValueOutput
result;

private
string
resultValue;

protected override void
Definition()
{
enter = ControlInput(
"enter"
, (flow) => {
return
exit; });
exit = ControlOutput(
"exit"
);
}
}


Now save it, and your Unit should look like this:




VALUE PORTS


Values are the only other port types to learn about. Thankfully, this is also very simple. We will move on to more complex logic in the future, but for now keep it simple. A value requires a type, via a generic, or a type value. Type values are handy if you want to actually change the type of the port on the fly based on changes on the unit. These are going to be of the type string. There is also the ability to add a default value, which we will do. Its usually nice. That would be the 2nd parameter.

using
Bolt;

public class
BasicUnit
:
Unit

{
[
DoNotSerialize
]
public
ControlInput
enter;

[
DoNotSerialize
]
public
ControlOutput
exit;

[
DoNotSerialize
]
public
ValueInput
A;

[
DoNotSerialize
]
public
ValueInput
B;

[
DoNotSerialize
]
public
ValueOutput
result;

private
string
resultValue;

protected override void
Definition()
{
enter = ControlInput(
"enter"
, (flow) => {
return
exit; });
A = ValueInput<
string
>(
"a"
,
string.Empty
);
B = ValueInput<
string
>(
"b"
,
string.Empty
);
result = ValueOutput<
string
>(
"result"
);
exit = ControlOutput(
"exit"
);
}
}


Then you get this:




FINAL STAGE


Now that we got this far, our ports are layed out. We just need to combine the two strings into one when we enter. This is super simple. Inside the lambda expression on the assignment of our enter port, we just do some code equivalent to A + B.

In order for us to retrieve the values from A and B, you need to get it from the Flow. Thankfully, our method will make sure we have a flow to use from what has entered into it, right there in the lambdas input.

enter = ControlInput(
"enter"
, (flow) => { resultValue = flow.GetValue<
string
>(A) + flow.GetValue<
string
>(B);
 return
exit; });

A = ValueInput<
string
>(
"a"
,
string.Empty
);

B = ValueInput<
string
>(
"b"
,
string.Empty
);

result = ValueOutput<
string
>(
"result"
, (flow)=> {
return
resultValue; });

exit = ControlOutput(
"exit"
);


This could also be done directly inside the ValueOutput return method, but we want to see how to do Controls as well.


COMPLETE


You have now completed your first unit. Try it out by connecting it to update, play with the values. We are LIVE editing.