One of my all time favorite games is Amnesia: The Dark Descent, which is not really surprising coming from someone working on a first person horror game. I could go on and on about that game and all of the great design choices it made to really pull the player into its world. I could… but I won’t. What I will talk about though is the way that game forces the player to manually interact with its world. In most games when the player is presented with a openable container, the act of opening it is either triggered by running into it or by simply pressing a button. In Amnesia though, it’s done by first grabbing the door with a click and then slowly moving the mouse to move the door. It’s a very small change that I found really drew me into the game. The player is forced to use his/her hand to actually do the work of opening doors and drawers. It allows cases where you partially open a door to peek at a monster and helps maintain player immersion.
I decided to implement a similar system in MMN. I looked around for a good tutorial to implement this system but being as picky as I am, I managed to find something I didn’t like about each one of them. So once I figured out how to get it working, I decided to write my own.
How to implement a physics-based door and drawer system
Note: First of all, let me give fair warning that all the screens in this post come directly from MMN and thus may contain code that is unnecessary for this example. I’ll do my best to explain the important parts and cut out the unnecessary ones.
The best way to implement a system like this is to think about the real world counterpart. If I want to open a door/drawer my first step is to look at the door/drawer, my second step is to grab the door/drawer, my third step is to apply a force to the door/drawer and my last step is to let go of the door/drawer. So those four sections of code for interacting with the door plus the initial door setup makes only 5 small steps toward implementing the system. Simple right? Let’s look at each of them individually.
Note: I’m sick of writing door/drawer so from now on doors and drawers will be referred to as D&D. I hope this doesn’t cause any confusion.
Step #1 – D&D Setup:
First of all we want these D&Ds to act as realistically as possible, which means we don’t want them running pre-canned animations. We want them to be physics-based, which means we’ll enable physics on the parts of the mesh we want to move. Once you’ve done that, you’ll notice immediately how your door just fell over and your drawer fell out of its shelf. That’s because while we want some forces to be able to manipulate these objects, we don’t want all of them to. We need to constrain the way these things can physically move and to do that we use a Physics Constraint (RTFM).
Physics constraints are pretty simple. You just give them a component and a set of rules, and they make sure the component doesn’t break those rules. For the door I used a physics constraint like a set of hinges. I’ve seen a lot of people use two but I’ve found only one is necessary. Just put it on the door in line with where hinges would go and set the component name to the name of the mesh you want to move.
For doors the physics constraint is pretty straight forward. You want to only allow rotational movement around the z-axis so the settings look something like this.
The drawer on the other hand moves laterally so I set my physics component in the direct middle of the drawer and gave it limited linear movement. Dialing in the linear limit is unfortunately just an exercise or trial and error.
One more thing that I chose to do to each of these was to tag the mesh I wanted to be able to move as “slidable”.
Step #2 – Looking at the D&D:
Note: All code below this point is on the player character class.
So I’m not going into the look control code for MMN. It’s pretty much just the out-of-the-box FPS blueprint that comes with Unreal, but when a player tries to interact with the world I need to know what they are looking at. I’m always very cautious about putting any code in an update event because it gets called constantly, but Milo’s UI updates depending on what he’s looking at. So the following code runs in my game every tick but if you don’t need the information all the time it would be much more efficient to move it to the mouse click event before executing the code in step three.
So this is my entire update code. I’ll walk you through it bit by bit. A lot of it is only necessary for MMN’s UI, but the main idea is I’m seeing where the player is looking, checking to see if what they are looking at is important, and if it is I’m storing it.
It all starts with a simple line trace. I just take the camera, draw a line from it to some arbitrary distance and see if I hit anything of interest.
Note: The values it’s storing at the end (with the exception of hit location) are local and temporary. I have an actual variable I plan to store the values in but I only want to do that if I’m sure these values are important/valid.
You may notice a strange node called “GetStaticMeshParent”. I’ve found it easiest to put a box collider as a child of the mesh I’m attempting to move to get the line trace hit. “GetStaticMeshParent” is a function I wrote that takes the collider and returns its parent wich is really the component that I’m after. I’ve included it below.
Now that I’ve done the trace I need to see if I hit anything. If not, I should clear all the values from the last time I ran this method. If I did, I should check to see if what I hit was the same thing I hit last time (if it is I don’t need to update because all the values are already stored).
EDIT: Here is my code for the clear Interaction target function. I forgot to add this originally.
Now I check to see if what I hit has all the proper tags and interfaces.
Note: The interface check here is for updating UIs – probably not necessary for your code.
Note part 2: “Does this an Interactable actor?”… sorry… I didn’t proofread that comment before I took the screenshot. It’s supposed to say “Is that an interactable actor and does the component have the proper tags?”
If it passed all those tests, I store the values. Ignore the execution lines that go out of frame.
Step #3 – Grabbing the D&D:
So now the D&D’s are set up, and we know what we want to grab. Next all we need to do is grab it. To grab we’re going to use a Physics Handle (RTFM). The physics handle allows us to move an object through space, in this case a D&D. It’s a component attached to my Player Character. I initialize my D&D grab with a left mouse down but you can bind this to any event.
Note: The “Hide Hand” code here should be ignored.
Here I grabbed the component we stored earlier at the location we stored earlier using the Physics Handle component on my character.
Step #4 – Applying a force to the D&D:
Applying force needs to be done directly in proportion to the mouse movement, so it’s best to bind it to that event. Notice that I prevent mouse information from changing look direction while I’m sliding something.
The actual slide code is pretty simple. I just take my Physics Handle and move its target along the forward vector from the camera component.
Step #5 – Letting go of the D&D:
To release the D&D just call “Release Component” on your Physics Handle….. In retrospect I’m not sure that one needed a whole bullet point.
Anyway I hope that helps. If you have any questions feel free to leave a comment and I’ll do my best to answer it.