Godot: please let us utilize `_draw` from other nodes

Created on 20 Mar 2018  路  15Comments  路  Source: godotengine/godot

i have a inventory that uses draw calls. i want to extend this w/o inheritance (because there is no need to use all code again), i just need to draw more items on another control.

how to do it now:
i would have to attach a script to my stash Control node (stash.gd), and give it a variable hover_box_vector, and then update it with a signal, then use stash.gd's own _draw() to display it. (which is triggered from inventory.gd)

however, i feel it's really redundant. this hover_box_vector already exists in my inventory.gd, re-using the variable in another script doesn't make sense, but you have to.. because you cannot do this from inventory.gd:

for items in items:
   draw_texture("etc/etc") # < -- This works
for items in stash_items:
   StashNode.call("draw_texture", item.img, Vector2(item.pos.x, item.pos.y)) # < -- This errors out

You will get:

Drawing is only allowed inside NOTIFICATION_DRAW, _draw() function or 'draw' signal.
Description: Drawing is only allowed inside NOTIFICATION_DRAW, _draw() function or 'draw' signal.
C Error: Method/Function Failed.
C Source: scene\2d\canvas_item.cpp:682
C Function: CanvasItem::draw_texture

which is not fair, and only makes the dev do more work :(. not only that, but it adds more unnecessary code

archived discussion

Most helpful comment

You've got a "draw" signal too, which, once connected, allows you to draw in the selected node.

All 15 comments

I don't know anything about internals of Godot engine or GDScript, but usually such a way of painting things on a screen ('immediate mode GUI') needs to be inside a callback method that should only be called by the platform at specific times.

One possible way of dealing with the problem could be abstract the drawing call as an action or a command and queue drawing requests from outside as a list of such actions, then process them on every _draw call internally.

Why not make inventory.gd have a draw_items() function that performs the draw operations you want and then maintain a reference to an inventory instance in the stash.gd script that calls inventory.draw_items() in the _draw() callback? Unless I'm misunderstanding what you need here.

inside my Stash.gd:

extends Control

var cell_select_img
var hover_box_vector = Vector2()
var ItemBackgroundColor = Color()
var rect = Rect2()
func _ready():
    UI.Pages.Inventory_Draw.connect("draw_cell_box", self, "draw_cell_box")
    UI.Pages.Inventory_Draw.connect("draw_item_bg", self, "draw_item_bg")

func draw_cell_box(cell_select_img2, hover_box_vector2): # re-assign these.. :( whyyy
    cell_select_img = cell_select_img2
    hover_box_vector = hover_box_vector2
    update()

func draw_item_bg(rect2, ItemBackgroundColor2):
    rect = rect2
    ItemBackgroundColor = ItemBackgroundColor2
    update()

func _draw():
    draw_texture(cell_select_img, hover_box_vector)
    draw_rect(rect, ItemBackgroundColor)

And inventory.gd

emit_signal("draw_cell_box", cell_select_img, hover_box_vector)
emit_signal("draw_item_bg", rect, ItemBackgroundColor)

When I could just use these 3 lines inside inventory.gd:

StashNode.call("draw_texture", cell_select_img, hover_box_vector)
StashNode.call("draw_rect", rect, ItemBackgroundColor)
StashNode.update()

please let us call draw methods from other nodes, it would help so much

@Dar13 hello, just tried that. however, you still get:

0:00:03:0789 - Drawing is only allowed inside NOTIFICATION_DRAW, _draw() function or 'draw' signal.`, even when using a callback

"even when using a callback"

lol they on to us now

You still need to do it inside _draw() function, which was what @Dar13 meant. There's no reason not to do it in _draw() with his suggestion in this case, since now you have all the references to the nodes that need to participate in the drawing process.

callback is in _draw() as dar13 said, still errors out

Where are those emit_signal calls in inventory.gd?

I more meant something like this:

inventory.gd

func draw_items(control):
    control.call("draw_texture", your_texture)
    control.call("draw_rect", your_color)

stash.gd

onready var inventory = load(<path to inventory.gd>).instance()
# or
onready var inventory = <somehow get inventory.gd instance>

func _draw():
    inventory.draw_items(self)

Note: this is pseudo-code, don't go copy-pasting it haha

yield can halt the processing until it is drawable.

StashNode.update()
yield(StashNode,"draw")
StashNode.draw_texture(cell_select_img, hover_box_vector)
StashNode.draw_rect(rect, ItemBackgroundColor)

IMO, Dar13's approach might be better.

@Noshyaar that's a really cool trick... it does work, but unfortunately it's drawn behind the control node :(

i will try Dar13's approach, can close this if you guys want thanks for all the help so far. appreciate it

Additional info for clarity:
Drawing is a special operation. You can only do it when requested by the engine. Trying to draw when it is not drawable won't make it appears on the screen. This is a limitation in most engines.
You can use OS.can_draw to see if the system allows you to call draw functions or not.

@girng: When I was doing something like that, I had to put draw() operations on the last node in hierarchy so that they'd be drawn on top of the controls and not behind.

You can use OS.can_draw to see if the system allows you to call draw functions or not.

Damn, we forgot to move that one to Engine...

You've got a "draw" signal too, which, once connected, allows you to draw in the selected node.

@groud yeah.. correct

func _ready():
    skill_description.connect("draw",  self, "hi")

func hi():
    skill_description.call("draw_rect", Rect2(Vector2(0, 0), Vector2(skill_description.rect_size.x, 1)), border_color)

this works.. I thought I didn't need to do node.call (thought it was just returned) when using a signal... it kept confusing me

sorry for wasting everyone's time, now looking back at this issue i'm cringing at my OP

Note that you don't need to use call for it, and this would work as well:
```gdscript
func _ready():
skill_description.connect("draw", self, "hi")

func hi():
skill_description.draw_rect(Rect2(Vector2(0, 0), Vector2(skill_description.rect_size.x, 1)), border_color)
``

Was this page helpful?
0 / 5 - 0 ratings

Related issues

kirilledelman picture kirilledelman  路  3Comments

testman42 picture testman42  路  3Comments

mefihl picture mefihl  路  3Comments

nunodonato picture nunodonato  路  3Comments

bojidar-bg picture bojidar-bg  路  3Comments