Last active
May 20, 2021 18:48
-
-
Save bitwes/98c4f2d5bb9b964fb8cbde7bc3eb35ae to your computer and use it in GitHub Desktop.
Place this in a new Control Node that has a (0, 0) rect_size. Set properties. Custom node outlines can be added in the array. Colors for each custom is kept in the other array.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# ------------------------------------------------------------------------------ | |
# Usage | |
# Create a Control node in the scene you want to paint boxes for and set the | |
# script for the Control to this. | |
# | |
# Draw Stoppers, Ignores, Passsers: | |
# When enabled these options will draw rectangles for any object that contains | |
# the mouse when you click. Stoppers, Ignores, Passers refers to the | |
# value of mouse_filter. | |
# | |
# Disabled - Disables all drawing of boxes and disables mouse input processing. | |
# | |
# Draw Customs - This allows you to set node paths to always draw an object's | |
# rectangle. You can use Custom Colors to specify a color for each Draw | |
# Custom node you've setup. | |
# | |
# Custom Colors - Colors to be used with the corosponding index in Draw Customs. | |
# If Custom Colors does not have enough colors then a default color will be | |
# used. If Custom Colors has too many colors nobody really caares and so | |
# nothing special happens. Not evena print statement. It just doesn't | |
# matter one bit. | |
# | |
# Background | |
# I tried to make this object the one that draws all the boxes. I couldn't | |
# figure out how to resolve screen stretch mode scales. The boxes were always | |
# a little off if there was any stretching enabled. I changed the approach to | |
# making each of the objects themselves draw the box by connecting to the | |
# draw signal. It feels a little janky but does not appear to have any scaling | |
# issues. | |
# ------------------------------------------------------------------------------ | |
extends Control | |
var _stoppers = [] | |
var _ignores = [] | |
var _passers = [] | |
var _string_font = null | |
# input handling related methods | |
var _look_for = ["_input", "_gui_input", "_unhandled_input"] | |
# occasionally godot seems to hang on generating the text. I appear to have | |
# worked around this issue by using this dictionary to hold the texts that | |
# have already been made. IDK why. | |
var _obj_text = {} | |
export var draw_stoppers = true | |
export var draw_passers = true | |
export var draw_ignores = false | |
export var disabled = false | |
export var draw_customs = true | |
export(Array, NodePath) var customs = [] | |
export(Array, Color) var custom_colors = [] | |
# This dictionary helps prevent shape names from being printed on top of | |
# each other. This is cleared prior to every draw and populated as shape | |
# boxes are drawn. Only helps when boxes have the same position. | |
# key = the position that draw_string was called with | |
# value = the width of the text that was drawn at that location plus a spacer | |
var _drawn_name_locations = {} | |
const INDENT = "| " | |
var _screen_scale = Vector2(1, 1) | |
func _ready(): | |
# get the default font. | |
var label = Label.new() | |
_string_font = label.get_font("") | |
label.free() | |
set_disabled(disabled) | |
_setup_customs() | |
func _setup_customs(): | |
if(!draw_customs): | |
return | |
for i in range(customs.size()): | |
var c = Color(0, 0, 1) | |
if(i < custom_colors.size()): | |
c = custom_colors[i] | |
var cust = get_node(customs[i]) | |
_connect_draw(cust, c, 3) | |
func _connect_draw(node, c, width): | |
var did_conn = false | |
if(node.has_signal("draw") and !node.is_connected("draw", self, "_on_obj_draw")): | |
node.connect("draw", self, "_on_obj_draw", [node, c, width]) | |
node.update() | |
did_conn = true | |
elif(!node.has_signal("draw")): | |
print(node, " missing draw signal") | |
return did_conn | |
func _on_obj_draw(obj, c, width): | |
var the_rect = obj.get_global_rect() | |
the_rect.position -= obj.rect_global_position | |
obj.draw_rect(the_rect, c, false, width) | |
if(_string_font != null): | |
# Easier to read non-transparent text | |
var txt_color = Color(c) | |
txt_color.a = 1 | |
var pos = Vector2(10, 10) | |
var text = str("[", obj.name, "]") | |
obj.draw_string(_string_font, pos, text, txt_color) | |
# func _draw_box(object, c, width): | |
# var r = object.get_global_rect() | |
# r.size = r.size * _screen_scale | |
# r.position -= get_global_position() | |
# var p = object.get_global_position() - get_global_position() | |
# draw_rect(r, c, false, width) | |
# if(_string_font != null): | |
# # Easier to read non-transparent text | |
# var txt_color = Color(c) | |
# txt_color.a = 1 | |
# var pos = p + Vector2(2, 10) | |
# while(_drawn_name_locations.has(pos)): | |
# pos.x += _drawn_name_locations[pos] | |
# var text = str("[", object.name, "]") | |
# draw_string(_string_font, pos, text, txt_color) | |
# _drawn_name_locations[pos] = _string_font.get_string_size(text).x + 5 | |
# func _draw_boxes(objects, c, width): | |
# for n in objects: | |
# _draw_box(n, c, width) | |
# func _draw_customs(): | |
# var nodes = [] | |
# for i in customs.size(): | |
# var c = Color(0, 0, 1) | |
# if(i < custom_colors.size()): | |
# c = custom_colors[i] | |
# _draw_box(get_node(customs[i]), c, 2) | |
# func _draw(): | |
# return | |
# _drawn_name_locations.clear() | |
# if(draw_ignores): | |
# _draw_boxes(_ignores, Color(0, 1, 0, .25), 9) | |
# if(draw_passers): | |
# _draw_boxes(_passers, Color(1, 1, 0, .25), 6) | |
# if(draw_stoppers): | |
# _draw_boxes(_stoppers, Color(1, 0, 0, .25), 3) | |
# if(draw_customs): | |
# _draw_customs() | |
func _make_list_of_local_methods(thing): | |
var to_return = '' | |
var methods = thing.get_method_list() | |
for method in methods: | |
# 65 indicates it is a local method (overload). | |
if(_look_for.has(method.name) and method.flags == 65): | |
to_return += str(method.name, " ") | |
return str("[", to_return, "]") | |
func _node2str(thing): | |
var to_return = '' | |
if(_obj_text.has(thing)): | |
to_return = _obj_text[thing] | |
else: | |
to_return = thing.name | |
to_return = str(_make_mouse_filter(thing), to_return) | |
if(thing.get_script() != null): | |
to_return += str("(", thing.get_script().resource_path, ")") | |
to_return += _make_list_of_local_methods(thing) | |
_obj_text[thing] = to_return | |
return to_return | |
func _make_mouse_filter(node): | |
var to_return = '<' | |
if(node.has_method("get_mouse_filter")): | |
var filter = node.get_mouse_filter() | |
var letter = "" | |
if(filter == 0): | |
letter = "S" | |
elif(filter == 1): | |
letter = "P" | |
elif(filter == 2): | |
letter = "I" | |
to_return += letter | |
to_return += '>' | |
return to_return | |
func _print_stoppers(): | |
print(_stoppers.size(), " Stopper(s)") | |
for stopper in _stoppers: | |
print("* ", _node2str(stopper)) | |
func _print_passers(): | |
print(_passers.size(), " Passer(s)") | |
for passer in _passers: | |
print("* ", _node2str(passer)) | |
func _clear_ers_array(arr): | |
for n in arr: | |
if(n.is_connected("draw", self, "_on_obj_draw")): | |
n.disconnect("draw", self, "_on_obj_draw") | |
n.update() | |
arr.clear() | |
func _add_to_ers(node): | |
var filter = node.get_mouse_filter() | |
if(filter == 0 and draw_stoppers): | |
_connect_draw(node, Color(1, 0, 0, .25), 3) | |
_stoppers.append(node) | |
elif(filter == 1 and draw_passers): | |
_connect_draw(node, Color(1, 1, 0, .25), 9) | |
_passers.append(node) | |
elif(filter == 2 and draw_ignores): | |
_connect_draw(node, Color(0, 1, 0, .25), 6) | |
_ignores.append(node) | |
func print_nodes_containing_pos(pos): | |
_clear_ers_array(_stoppers) | |
_clear_ers_array(_ignores) | |
_clear_ers_array(_passers) | |
_obj_text.clear() | |
print("------------------------------") | |
print("objects at ", pos) | |
_check_children_for_pos(get_parent(), pos) | |
_print_stoppers() | |
_print_passers() | |
print("------------------------------") | |
update() | |
func _check_children_for_pos(node, pos, indent = ''): | |
var kids = node.get_children() | |
for kid in kids: | |
if(kid.has_method("get_global_rect")): | |
# This doesn't take rotation into account. Could be better. | |
# Ingores invisible objects bc they cannot interfere with the mouse | |
if(kid.get_global_rect().has_point(pos) and kid.visible): | |
print(indent, _node2str(kid)) | |
_add_to_ers(kid) | |
_check_children_for_pos(kid, pos, indent + INDENT) | |
else: | |
_check_children_for_pos(kid, pos, indent + INDENT) | |
func _input(event): | |
if(disabled): | |
return | |
if(event is InputEventMouseButton and event.pressed): | |
#print_nodes_containing_pos(event.position) | |
call_deferred("print_nodes_containing_pos", event.position) | |
func set_disabled(value): | |
disabled = value | |
set_process_input(!disabled) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment