OutwardLightStudio/nexus

Add Editor Visualization Support for MovingHazard Nodes

Opened this issue · 0 comments

Currently, when setting up MovingHazard nodes in the editor, level designers have no visual feedback about the node's movement path, final position, rotation, or scale until they run the game. This makes it difficult to precisely position and configure these nodes. We need to implement editor visualization that shows the complete movement pattern directly in the editor viewport.

Current Behavior

MovingHazard nodes support configuration of:

  • Desired position (relative to initial position)
  • Desired rotation (in degrees)
  • Desired scale
  • Animation duration

However, these properties must be set numerically through the inspector, with no visual preview of their effects. Level designers must frequently enter play mode to verify their configurations, leading to an inefficient workflow.

Proposed Enhancement

Implement an editor visualization system for MovingHazard nodes that provides immediate visual feedback in the editor viewport. The visualization should include:

Visual Elements

  1. Movement Path Indicator

    • A solid yellow line showing the path from the initial position to the target position
    • Only visible when desired_position is non-zero
    • Updates in real-time as position values change
  2. Target State Preview

    • A semi-transparent yellow box showing the node's final position and scale
    • Colored axis indicators (red: X, green: Y, blue: Z) showing the final orientation
    • Updates dynamically with any property changes

Technical Implementation

The feature requires three main components:

  1. Core MovingHazard Script Enhancement
@tool
extends AnimatableBody3D
# Add @tool annotation to enable editor functionality
# Implement property change notifications
  1. Custom Editor Plugin
# New file: addons/moving_hazard/moving_hazard_plugin.gd
# Handles plugin initialization and cleanup
# Registers the custom gizmo
  1. Custom Gizmo Implementation
# New file: addons/moving_hazard/moving_hazard_gizmo.gd
# Implements the actual visualization rendering
# Handles transform calculations and updates

User Interface

The visualization should be toggleable through a new inspector property:

  • Property Name: "Show Debug Path"
  • Type: bool
  • Default: true
  • Updates visualization immediately when toggled

File Structure

addons/
  moving_hazard/
    moving_hazard_plugin.gd
    moving_hazard_gizmo.gd

Technical Requirements

Dependencies

  • Godot 4.x
  • EditorNode3DGizmoPlugin system
  • @tool script functionality

Performance Considerations

  • Visualizations should only be computed and rendered in the editor
  • Updates should be efficient and not impact editor performance
  • Memory usage should be minimal, cleaning up resources when nodes are removed

Error Handling

  • Properly handle edge cases like zero movement or rotation
  • Provide clear warnings in the editor for invalid configurations
  • Ensure visualization updates properly when undo/redo operations are performed

Benefits

  1. Improved Level Design Workflow

    • Immediate visual feedback for configuration changes
    • Reduced need to enter play mode for verification
    • More intuitive positioning and rotation setup
  2. Reduced Error Potential

    • Visual confirmation of movement paths
    • Clear indication of final states
    • Immediate feedback for unintended configurations
  3. Better Editor Integration

    • Consistent with Godot's built-in visualization systems
    • Professional-grade development experience
    • Enhanced productivity for level designers

Implementation Notes

The visualization system should be implemented using Godot's official EditorNode3DGizmoPlugin system rather than custom drawing or mesh instances. This ensures proper integration with the editor's rendering pipeline and maintains consistent behavior with other editor visualizations.

Future Enhancements

Once the basic visualization system is implemented, potential future enhancements could include:

  • Interactive gizmos for direct manipulation of target states
  • Animation preview in the editor
  • Custom visualization colors and styles
  • Multiple waypoint support
  • Path curve visualization for different movement patterns

Example code

# addons/moving_hazard/moving_hazard_plugin.gd
@tool
extends EditorPlugin

var gizmo_plugin = preload("res://addons/moving_hazard/moving_hazard_gizmo.gd").new()

func _enter_tree():
	add_node_3d_gizmo_plugin(gizmo_plugin)

func _exit_tree():
	remove_node_3d_gizmo_plugin(gizmo_plugin)
# addons/moving_hazard/moving_hazard_gizmo.gd
@tool
extends EditorNode3DGizmoPlugin

func _init():
	# Create a new gizmo plugin with default settings
	create_material("path", Color(1, 1, 0, 0.3))  # Semi-transparent yellow
	create_material("path_lines", Color(1, 1, 0))  # Solid yellow
	create_handle_material("handles")

func _has_gizmo(node):
	# Only show gizmo for MovingHazard nodes
	return node is Node3D and node.get_script() and node.get("show_debug_path")

func _redraw(gizmo: EditorNode3DGizmo):
	gizmo.clear()
	
	# Get the node we're drawing for
	var node = gizmo.get_node_3d()
	
	if not node.show_debug_path:
		return
		
	# Get the transforms we need
	var start_transform = node.transform
	var target_transform = start_transform * node._calculate_target_transform()
	
	# Create our visualization meshes
	var path_points = PackedVector3Array()
	path_points.push_back(Vector3.ZERO)  # Start point
	path_points.push_back(node.desired_position)  # End point
	
	# Draw the path line
	if node.desired_position != Vector3.ZERO:
		gizmo.add_lines(path_points, get_material("path_lines", gizmo))
	
	# Draw target position box
	var box_mesh = BoxMesh.new()
	box_mesh.size = node.desired_scale
	gizmo.add_mesh(box_mesh, get_material("path", gizmo), target_transform)
	
	# Draw orientation axes at target
	var axis_points = PackedVector3Array()
	var axis_colors = PackedColorArray()
	var axis_length = 0.5
	
	# X axis (red)
	axis_points.push_back(node.desired_position)
	axis_points.push_back(node.desired_position + Vector3(axis_length, 0, 0))
	axis_colors.push_back(Color(1, 0, 0))
	axis_colors.push_back(Color(1, 0, 0))
	
	# Y axis (green)
	axis_points.push_back(node.desired_position)
	axis_points.push_back(node.desired_position + Vector3(0, axis_length, 0))
	axis_colors.push_back(Color(0, 1, 0))
	axis_colors.push_back(Color(0, 1, 0))
	
	# Z axis (blue)
	axis_points.push_back(node.desired_position)
	axis_points.push_back(node.desired_position + Vector3(0, 0, axis_length))
	axis_colors.push_back(Color(0, 0, 1))
	axis_colors.push_back(Color(0, 0, 1))
	
	gizmo.add_lines(axis_points, get_material("path_lines", gizmo), axis_colors)