-
Notifications
You must be signed in to change notification settings - Fork 319
logicnodes
This page shows how to create custom logic nodes in a node package. The same approach is used to edit or add new nodes to Armory itself. In this case, don't create a library as described on this page and use the blender/arm/logicnode/
folder for Python node definitions and Sources/armory/logicnode
for the node's Haxe source code.
Content:
We will make a new library to store the sources of custom logic nodes and keep them portable with no modifications to engine sources.
Locate your blend file and create a new Libraries
folder alongside it. Navigate to the Libraries
folder and create a new mynodes
folder in it to place your new node.
Next, we will create the logic node definition for Blender.
To do so, we have to create a file named blender.py
in Libraries/mynodes
folder. Armory automatically picks this file up once the library is loaded.
Define a simple node with single in/out socket like the one in the example below.
from bpy.types import Node
from arm.logicnode.arm_nodes import *
import arm.nodes_logic
# Extend from ArmLogicTreeNode so that the node is recognized as a logic node
class TestNode(ArmLogicTreeNode):
"""Test node"""
bl_idname = 'LNTestNode'
bl_label = 'Test'
# Use this as a tooltip in the add node menu.
# If `bl_description` does not exist, the docstring of this node is used instead.
bl_description = 'This is a test node'
def init(self, context):
self.add_input('ArmNodeSocketAction', 'In')
self.add_output('ArmNodeSocketAction', 'Out')
# Register this custom node into the `Action` category
add_node(TestNode, category='Action')
Restarting Blender and loading the project again, the new logic node is available for placement.
Armory provides a small API in arm_nodes.py
to ease working with logic nodes.
-
Adding input/output sockets:
def add_input(self, socket_type: str, socket_name: str, default_value: Any = None, is_var: bool = False) -> bpy.types.NodeSocket: def add_output(self, socket_type: str, socket_name: str, default_value: Any = None, is_var: bool = False) -> bpy.types.NodeSocket:
Small wrapper methods around
self.inputs.new()
andself.outputs.new()
.If a
default_value
is given, the socket will use this value if it has no connection. Ifis_var
is set toTrue
, the socket will have a small dot in the middle to show that this socket can be used for accessing a variable.Additional available socket types can be found in arm_sockets.py. Libraries can also define their own socket types.
-
Node versioning
def get_replacement_node(self, node_tree: bpy.types.NodeTree) -> arm.logicnode.arm_nodes.NodeReplacement:
Todo.
There are a bunch of static methods that allow you to register nodes and create node categories. For a in-depth overview, please look at arm_nodes.py
. On this page, only some often-used methods are documented.
-
def add_node(node_type: Type[bpy.types.Node], category: str, section: str = 'default', is_obsolete: bool = False) -> None:
Registers a logic node so that it is displayed in the add node menu.
-
node_type
: The class of the node (see example code in the Python section) -
category
: The category this node belongs in (see example code in the Python section). If the category does not exist yet, it is created. If you passPKG_AS_CATEGORY
(defined inarm_nodes.py
), the capitalized name of the Python package the node definition file is in is used as the category name. When you later rename a category, you don't have to change all calls toadd_node
when using this constant. -
section
(optional): Add this node into a sub-section of nodes in that given category. Node sections are visually grouped together in the menu. If the section does not exist yet, it is created. -
is_obsolete
(optional): Todo
-
-
def add_category(category: str, section: str = 'default', icon: str = 'BLANK1', description: str = '') -> Optional[ArmNodeCategory]:
Adds a category of nodes to the node menu and returns the
ArmNodeCategory
object if the category didn't exist yet.-
category
: The name of the category -
section
(optional): Just like node sections explained above, categories can also be grouped into visually separated sections. If the section does not exist yet, it is created. -
icon
(optional): Blender icon constant to give each node in this category a icon. The icon is also displayed in the node menu. -
description
(optional): Description of this category. This value is currently unused but might be used in the future to display tooltips.
-
-
def add_node_section(name: str, category: str) -> None:
Adds a section of nodes to the sub menu of the given category to group multiple nodes visually together. The given name only acts as an ID and is not displayed in the user inferface.
-
def add_category_section(name: str) -> None:
Adds a section of categories to the node menu to group multiple categories visually together. The given name only acts as an ID and is not displayed in the user inferface.
Before the project can be run, we need to implement the actual node logic in Haxe.
Start by creating the folder structure Sources/armory/logicnode/
in the same folder of blender.py
.
Next, create a TestNode.hx
file inside the logicnode
folder just created, and place the code from below in the file.
When the node gets executed, we let it print a 'Hello, World!' string.
package armory.logicnode;
class TestNode extends LogicNode {
public function new(tree:LogicTree) {
super(tree);
}
override function run(from: Int) {
// Logic for this node
trace("Hello, World!");
// Execute next action linked to this node, this activates the output socket at position/index 0
runOutput(0);
}
}
When implementing new logic nodes, browse the sources of existing nodes as a reference:
-
Python definitions of Armory nodes: https://github.com/armory3d/armory/tree/master/blender/arm/logicnode
-
Haxe implementation: https://github.com/armory3d/armory/tree/master/Sources/armory/logicnode