Skip to content

Implement NodePool optimization with NOTIFY_PREDELETE for automatic node recycling#184

Open
Copilot wants to merge 3 commits intomainfrom
copilot/fix-360f912f-5155-4c9e-a75c-38097c594920
Open

Implement NodePool optimization with NOTIFY_PREDELETE for automatic node recycling#184
Copilot wants to merge 3 commits intomainfrom
copilot/fix-360f912f-5155-4c9e-a75c-38097c594920

Conversation

Copy link
Contributor

Copilot AI commented Sep 29, 2025

This PR implements automatic node recycling for NodePool using Godot's NOTIFY_PREDELETE notification system, allowing developers to use queue_free() normally while nodes are automatically returned to the pool instead of being destroyed.

Problem

Currently, NodePool requires explicit code to return nodes to the pool, making game code pool-aware and complicating memory management:

# Before: Manual pool management required
var node = pool.next()
# ... use node ...
# Developer must remember to manually return to pool
if node.get_parent():
    node.get_parent().remove_child(node)
pool.return_node(node)  # Manual cleanup

Solution

The new PooledNode base class leverages NOTIFY_PREDELETE to intercept queue_free() calls and automatically return nodes to their pool:

# After: Transparent automatic recycling
var node = pool.next()  # Returns a PooledNode
# ... use node ...
node.queue_free()  # Automatically returns to pool!

Key Features

Transparent Operation: Game code can use queue_free() as normal without pool awareness
Automatic Recycling: Nodes are intercepted before deletion and returned to pool
Memory Optimization: Reduces allocations and garbage collection pressure
Backward Compatible: Existing NodePool usage continues to work unchanged
Robust Error Handling: Prevents double-returns and handles edge cases safely

Implementation Details

  1. PooledNode Base Class: Overrides _notification() to catch NOTIFY_PREDELETE, calls cancel_free(), and returns to pool
  2. Enhanced NodePool: Sets owner_pool references and provides return_to_pool() method with proper state management
  3. State Reset System: Calls reset_for_pool() when nodes are returned to ensure clean reuse
  4. Pool Limit Management: Maintains size limits by replacing oldest nodes when full

Usage Example

# Create a pooled decal system
class_name PooledBulletHole extends PooledNode

@export var lifespan: float = 30.0
var timer: Timer

func _ready():
    timer = Timer.new()
    timer.wait_time = lifespan
    timer.timeout.connect(queue_free)  # Will auto-return to pool
    add_child(timer)

func setup_hole(position: Vector3, texture: Texture2D):
    global_position = position
    # Setup decal properties...
    timer.start()

func reset_for_pool():
    # Clean state when returned to pool
    timer.stop()
    global_position = Vector3.ZERO

# Usage in game code
var pool = NodePool.new()
pool.target_node = PooledBulletHole.new()

func create_bullet_hole(hit_pos: Vector3):
    var hole = pool.next()  # Gets pooled node
    hole.setup_hole(hit_pos, bullet_texture)
    # Hole automatically returns to pool after lifespan - no cleanup needed!

Benefits for Game Development

  • Simplified Code: No manual pool management in game logic
  • Performance: Reduced memory allocations for frequently created/destroyed objects
  • Maintainability: Pool behavior is encapsulated in the node class
  • Flexibility: Works with timers, collision detection, or manual queue_free() calls
  • Debug Friendly: Nodes behave normally but get recycled transparently

This optimization is particularly valuable for systems like bullet holes, particle effects, projectiles, and UI elements that are frequently created and destroyed during gameplay.

Testing

Includes comprehensive unit tests and integration tests to verify automatic recycling, pool limits, state reset, and edge case handling. The implementation has been designed to be robust and fail gracefully.

Original prompt

This section details on the original issue you should resolve

<issue_title>Prevent NodePool nodes being frees</issue_title>
<issue_description># Node Pool Optimization using NOTIFY_PREDELETE

Description

We can optimize our node pool implementation by leveraging Godot's notification system to automatically catch nodes before they're freed.

Implementation Details

Instead of requiring explicit code to return nodes to the pool, we can:

  1. Override the _notification(what) method in pooled nodes
  2. Check for what == NOTIFY_PREDELETE and is_queued_for_deletion()
  3. Call cancel_free() and return the node to the pool

Benefits

  • Game code doesn't need to be aware of the pool
  • Developers can use queue_free() as normal
  • Nodes are automatically recycled instead of being destroyed
  • Reduces memory allocations and improves performance

Technical Notes

This pattern allows for transparent pooling without changing existing game code that uses queue_free().</issue_description>

Comments on the Issue (you are @copilot in this section)

Fixes #183

💡 You can make Copilot smarter by setting up custom instructions, customizing its development environment and configuring Model Context Protocol (MCP) servers. Learn more Copilot coding agent tips in the docs.

Copilot AI and others added 2 commits September 29, 2025 14:13
…ode recycling

Co-authored-by: krazyjakee <623463+krazyjakee@users.noreply.github.com>
… and comprehensive documentation

Co-authored-by: krazyjakee <623463+krazyjakee@users.noreply.github.com>
Copilot AI changed the title [WIP] Prevent NodePool nodes being frees Implement NodePool optimization with NOTIFY_PREDELETE for automatic node recycling Sep 29, 2025
Copilot AI requested a review from krazyjakee September 29, 2025 14:19
@krazyjakee krazyjakee marked this pull request as ready for review October 6, 2025 10:21
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Prevent NodePool nodes being frees

2 participants