Skip to main content

The Big Drag and Drop Update

·884 words·5 mins

Stretch to Viewport
#

Last demo had an issue with resizing.

I couldn’t duplicate it on Itch.io but maybe my friend has a bigger monitor, who knows.

The outside edges of the sprite I was using to define the boundaries of the level are showing. I feel exposed!

Anyway. In order to fix this I wanted to duplicate the issue locally. Of course, I can’t just open the web export’s HTML file in the browser because it’s in sandbox mode and can’t retrieve the other game files.

I needed a simple server and Stack Overflow did not disappoint:

#!/usr/bin/env python3
from http import server # Python 3

class MyHTTPRequestHandler(server.SimpleHTTPRequestHandler):
        def end_headers(self):
                self.send_my_headers()
                server.SimpleHTTPRequestHandler.end_headers(self)

        def send_my_headers(self):
                self.send_header("Access-Control-Allow-Origin", "*")
                self.send_header("Cross-Origin-Embedder-Policy", "require-corp")
                self.send_header("Cross-Origin-Opener-Policy", "same-origin")

if __name__ == '__main__':
        server.test(HandlerClass=MyHTTPRequestHandler)

Turns out all I needed to do was change Project Settings > Display > Window > Stretch > Mode from disabled to viewport. Apparently different configurations are better for pixel art (e.g., integer scale mode) but I’m not sure which way I’m going exactly for that so I’ll leave it with fractional scaling for now.

Reducing Drag and Drop Failures
#

The biggest problem I’ve seen in play testing is all the drag and drop failures. Trying to move a mirror into a new position only to have it fail and float back to where it was before gets pretty frustrating. It’s fine once or twice but it happens a lot in the game right now.

Sometimes the player needs to rotate the mirror first and then drag it into position but not everybody is used to thinking that way. Even for those that do, it’s still a hindrance, friction from the UI.

I’m thinking I’ll create a 64x64 “mounting point” at the origin of each mirror and prism. The player can drag the mirror and placement will always succeed as long as the mounting point is not colliding with anything. If the piece itself—the mirror or the prism—is colliding with something it’ll be rendered as a red ghost and lasers will pass straight through it.

Communicating that “disabled due to collision” mode will be the unfortunate byproduct of this strategy but hopefully it’ll be an improvement overall.

This is gonna take some reworking.

The mounting point will need to be a static body as well and it’ll be on a separate collision layer so it doesn’t collide with any prisms or mirrors. I guess I better prototype this in a separate project.

When the Children Are Ready
#

Learned a thing.

I was instantiating a MyScene and making changes to its children like so:

# Instantiate and prepare the scene
var my_scene = my_scene_package.instantiate()
my_scene.do_something_to_child_nodes()

# Add the scene to the scene tree.
add_child(my_scene)

I had problems because the children weren’t ready yet. MyScene’s _ready() override had not been called yet either.

I tried using call_deferred() and that did work but it meant all my setters needed to have this defer code.

var my_child
func _ready():
    my_child = $MyChild
    
func do_something_to_child_nodes():
  my_child.foobar()

The above would give errors but the following fixed it:

func do_something_to_child_nodes():
  if my_child:
    my_child.foobar()
  else:
    call_deferred("do_something_to_child_nodes")

I tried also waiting for the ready signal but that just silently crashed the game. I’m guessing I was blocking the stack that would result in the node becoming ready so it never happened.

var my_scene = my_scene_package.instantiate()
await my_scene.ready
my_scene.do_something_to_child_nodes()
add_child(my_scene)

Well, the best solution is much much simpler. Just add the node to the tree first. Then the children are instantiated and added, ready is called, and the setters can be called no problem.

var my_scene = my_scene_package.instantiate()
add_child(my_scene)

my_scene.do_something_to_child_nodes()

Sure, a robust implementation would work without having to make sure the scene has been added. Maybe store properties in MyScene and, when ready is called, write those properties to the children. But this is way simpler and since I have control over the code that instantiates and adds MyScene to the scene tree I can make sure I do it in the correct order.

TIL

New Drag and Drop
#

Okay, I think I managed to get the functionality I’m looking for.

Here are three mirror fixtures (cyan) with their mounts (white).

Fixtures can be rotated independently of their mounts.

Fixtures can collide with each other but a collision will not cause a revert.

But they don’t collide with mounts.

Mounts collide with each other (and will cause a drag and drop revert).

One big implementation difference is that I need to update the collision status of all interactibles whenever one of them moves. I’m keeping a list of all the interactible children in the scene just for that purpose.

Container (Node2D)
- Mount (StaticBody2D)
- Fixture (StaticBody2D)

Another bad side effect of this UI is that I can’t rotate mirrors and prisms in the editor, at least, not without rotating the mounts and I want all the mounts to be at the same angle.

For now my solution is to let it look wrong in the editor but when the mirror or prism or whatever is _ready() then it reads its rotation and applies it to the fixture and then resets its rotation to zero. That lets me change rotation on the fly in the editor but keeps all the mounts square to each other.

Phew!

Integrating this code is gonna be a challenge.