Logo 3.5Cats_AndHalfAFish



June 04, 2020 Shade, Godot : hinge doors
The door problem(s).

Note : Godot 3.2.1, Blender 2.82.

I'm sure there's no connection, but ever since the lockdown started I've been feeling tired again. My brain is sluggish and my body feels exhausted.
Probably not a good time to try and implement doors in my game ...

Anyway. I forced myself to do a little bit of work each day (30 minutes or so), and it seems I have now made 'ALL THE MISTAKES' !
So. Read, and learn (or laugh) (or weep).

Door size matters.

I started by modeling a couple of doors based on the size of real doors (I actually made them a bit bigger because it's a castle) and I also textured them (because ... why not ? In hindsight, not one of my better ideas). My player model is smallish, compared to the doors ... so, everything seemed fine to me.

Closed door (in Blender).
Closed door (Blender) and player model : looking good ...

Next, I added collision shapes to the walls, floor, and doors, and exported everything to Godot. However, when I tried walking through an open door (in game), the player character got stuck ! The doorway also looked unexpectedly narrow ...

Trying to walk through open door (game engine).
Walking through an open door (Godot) ... Or not.

So, I ended up having to make the doors (and door frames and wall entrances) 0.05 Blender units (meters) wider (yes, I found that quite funny too), and I also changed the camera fov from 70 to 45 (gasp !). I'll just have to wait and see if that's an ok thing to do ; the doorways definitely look better ...

Interacting with doors.

Doors are 'interactables', so, in theory, they should be quick to implement since I already figured out how to do that. And more importantly, I wrote it up in a blog post, so I don't even have to remember how I did that (and I don't) !

The reality :

Stress ball.
Poor Stress-Panda.

I'm not going to talk about all the things I tried that failed, but, at one point I seriously considered replacing all hinge doors in the game with sliding doors :

Elevator doors.
New look.

There's a good reason 'door tutorials' only talk about sliding doors ...

Look !
This is my player character standing too close to an opening/closing door :

Bump !

In the end, I did find a tutorial on hinge doors.
But alas ... It was a tutorial about hinge doors that always swing open -away- from the player ( as you see in shooters). And that's not what I want for my game.

Back to creating my own 'hacky solution' ...

Hacky Solution.

So, this is the door structure I'm currently using in Godot :

Door structure (Godot).

If you have read my previous posts, you'll recognize the 'interactable' (IA) setup ... with some tweaks.

First, you may wonder why I haven't made the door frame (I called it 'arch') part of the wall rather than the 'door interactable'.
Well, there's a good reason for that ! Godot doesn't seem to have a 'snap' functionality for precisely placing 2 models relatively to one another. And, as I found out, if a door isn't in exactly the right place inside the door frame, light from the room behind the door comes through ... This looks quite bad in a game.

The 'wrapper' node contains all nodes that require the rotation animations (door, key, ...).

The 'push zone' (pz_1) is meant to solve the problem of the player character standing too close to a door. It consists of an Area node and a Position3d node.
When the player interacts with a door while he's standing inside a push zone area, he will be transported to the 'new_pos' before the door opens or closes.

A problem I had, was that I wanted this solution to work no matter what direction the player character was facing when he interacted with a door. He should always end up looking at the door :

Push zone diagram.

I have to admit I was struggling quite a bit with the math for this.
But then, I discovered that gdscript has a function called 'angle_to()' that returns the minimum angle between 2 vectors ! This makes it very easy to find out by how much the player character has to be rotated when he's transported to his new position.

The only problem with this function is, that it always returns a positive number, so I still don't know if I have to rotate the player clockwise or counter clockwise ...

There's probably an easy way to find out ... but, it just didn't 'come to me'.
Fortunately, the 2d version of 'angle_to()' -does- return the rotation direction ! So, I converted the 2 relevant Vector3's into Vector2's by getting rid of the Y axis info (the Y axis is the axis around which the player needs to be rotated) :

Code : update player position and rotation.
Code : update player position and rotation.

The function update_player_tf() is called from within do_hsl_click(), which handles clicks on doors :

Code : door interaction.
Code : door interaction.

Both functions are part of the script attached to the iad_x root node (see first image under 'Hacky Solution').

Note : I use this 'hack' with 2 conditions :
1) the player rotates in increments of 90 degrees (might change in the future)
2) player and door are horizontal and at the same height (meaning : I don't know what happens when a player that is on the stairs or a slope tries to open a door that opens towards him).

The last bit of code is attached to the push zone 'pz_1' node :

Code : push zone signals.
Code : push zone signals.

I now have working doors ! =)
Note : locking/unlocking doors and opening/closing doors is handled by the 'hotspot' nodes and the animation player, as described in a previous blog post ('IA').

Relevant information.

  • Online Video Cutter.
    A shout-out to my go-to website for trimming and cropping of in-game video footage.
  • 'UE4: Guide to Player Scale and World/Architecture Dimensions.' - World of Level Design.
    About player size and walls, doors, stairs, etc.
  • 'Visualizing Projections.' - @shaunlebron.
    Interesting tidbit about projections (standard, panorama, hybrid).
  • Godot documentation : import process.
    "When the MD5 checksum of the source asset changes, Godot will perform an automatic reimport of it, applying the preset configured for that specific asset."
    Note : it seems that the bug reported in Issue #27409 and Issue #25243 is still present in Godot 3.2.1.
    See vt5491 for a way to force an update of the reimported model.
  • Godot documentation : importing 3D scenes : compression.
    Meshes can be compressed to save space. However, sometimes this will result in a loss of precision (eg. if a mesh is very big, or if the separate meshes cover a large area, there could be gaps in the geometry => disable compression !)
  • Godot documentation : importing 3D scenes : tangents.
    If your model uses normal map textures, it also needs to have tangent arrays. These can be generated by Godot, but it’s better to generate them in the exporter (Blender).
  • As a project grows, it is a good idea to cleanup unused resources from time to time. To make sure that the resource is in fact 'unused', you can right click a scene, texture, etc. in the resource panel (res://) and select 'view owners'.
    A quicker way however, is to go to the 'Project' menu (next to 'Scene' and 'Debug'), click 'Tools', 'Orphan Resource Explorer' ... and you will be shown a nice overview of all resources in your project.