William hardy
Game programmer
TheClassifiedX
UNITED KINGDOM
RESUME
William hardy
Game programmer
UNITED KINGDOM
A games programmer primarily specializing in networking.I bring ideas to life to be enjoyed by a wide array of game enthusiasts with a keen eye for detail and user experience.
You'll find me primarily in the depths of Unreal Engine, utilizing both C++ & Blueprints.
I'm a hardworking, team-oriented programmer aiming to write clean and maintainable code whilst always having
the will to learn and experiment to better myself and those I work with.Happy to relocate.
EMPLOYMENT
GAMES PROGRAMMER | 2022 - 2023 | |||||
---|---|---|---|---|---|
The Human Show | Multiplayer Programmer | ã…¤ | 2022 - 2023 | ã…¤ | Unreal Engine Blueprints C++ Networking |
Unannounced Title | Gameplay Programmer | ã…¤ | 2022 - 2023 | ã…¤ | Unreal Engine Blueprints C++ |
Unannounced Title | Lead Programmer | ã…¤ | 2023 - 2023 | ã…¤ | Unreal Engine Blueprints C++ |
The Human Show (PC) - Upcoming Multiplayer Horror Game
• Conceptualized, designed, and implemented a plethora of networked game mechanics (Blueprint)
• Finding and debugging gameplay / engine-related bugs.
• Optimising the performance and bandwidth usage of actors and levels.
• UI & HUD
• Maintaining plugins (C++).
• Creating function / macro libraries to increase development efficiency.
• Scripting events and cutscenes.Unannounced Project - Upcoming Horror Game
• Designed and implemented game mechanics (C++)
• Optimised meshes, lighting, and classes.
• Scripted events (Blueprint)
• Game UI & HUD
• Created camera tools for trailer capture and steam assets.
• Created a plethora of function / macro libraries.
• Integrated art, sounds, and FX.
DIRECTOR | 2022 - 2023 | |||
---|---|---|---|
Careers Week | Event Management | 2022 - 2023 | Presenting Interviewing |
Silk | Programming | 2022 - 2023 | Unreal Engine Blueprints C++ |
Helping provide educational resources and materials to upcoming and current game developers, by giving them the knowledge and motivation to learn critical skills taught across a variety of platforms. While simultaneously creating video games and hosting events.Careers Week 2022 (600 audience members, 40+ Speakers)
• Organised a week-long virtual event that hosted talks, masterclasses, portfolio reviews, interviews, and Q&As.
• Interviewed speakers (Recordings have been made available on YouTube)
• Presented parts of the week.
• Marketed the event through various platforms.Silk (PC) Upcoming Horror game
• Conceptualized, Designed, and Implemented various unique game mechanics (C++)
• Led and managed a team of programmers.
• Integrated sound, art, VFX, and animations.
• Maintained and managed the team to use proper naming conventions and best practices.
GAMES PROGRAMMER
SELF-EMPLOYED
GAMES PROGRAMMER | 2019 - 2022 | |||
---|---|---|---|
Contract | Programming | 2019-2022 | Unreal Engine Blueprints C++ Networking |
I worked for a wide array of clients ranging from all industries. To help build and visualize their concepts through Unreal Engine.• Built interesting gameplay mechanics which fit the client's brief (Blueprint & C++)
• Consulted clients on improving their workflows, technical queries, scope, and optimization.
• Maintained a 5* rating throughout.
• Setup both game and Archviz interactable UI elements.
• Kept to tight deadlines on my services.
• Clear communication throughout.
SKILLS
Skill | Experience | |
---|---|---|
Unreal Engine | | | 9 Years |
Blueprints | | | 9 Years |
Networking | | | 8 Years |
C++ | | | 3 Years |
Game Design | ||
C# | ||
C | ||
Maya |
EDUCATION
Institution | Course | Date |
---|---|---|
Staffordshire University | Games Programming | 2020-2022 |
Derby College | • Pearson BTEC First Diploma in Information and Creative Technology • Pearson BTEC Advanced Diploma in Computer Games Development • AQA Technical Level IT: Scripting and App Programming | 2017-2020 |
THEME
"Running out of space"
Game Summary
Fluff Brawl Bros is a 2-player action fighting game, where players must battle it out over 4 floors, by knocking the other player down to the lower level. This is whilst the floor is constantly being destroyed by laser turrets and player abilities! Each character has their own ability to be utilised to get an edge over the other player, whether this is an area of effect, dash, double jump, etc ability. The last player standing wins!
SKILLS |
---|
• Unreal Engine 4 |
• Blueprint |
• Optimisation ( Interactive grids) |
• Design |
• Gitlab |
SOFTWARE | PRE-MADE ASSETS |
---|---|
• Unreal Engine 4 | SideFX Labs plugin for VAT shaders |
• Blender | |
• Houdini | |
• Gitlab |
Responsibilities
As the primary programmer on this project, I was tasked with implementing and polishing the core game mechanics and helping optimise both the level and classes. I had to overcome a large range of issues which would've negatively impacted the project, primarily optimisation... The amount of actors which could be interacted with is all visible at all times. Coupled with the destructive nature of the environment being affected both by the player's abilities and the endless dispersal of lasers from turrets firing at random positions constantly.Likewise, I was also tasked with various other responsibilities including:
• Bug fixing
• UI integration
• Design
• Audio integration
• Optimisation
• Level Design
Optimisation
Grid generation
Grid generation was the primary focus and ensuring it was optimised and efficient. We focussed on this specific mechanic to make we met the given theme being "Running out of space". The idea would be to have a grid, in which individual pieces could be destroyed limiting the player's moveable area, which will then it the theme is limited space.To do so I did have a few ideas which I experimented with, one being an actor with components set up individually to resemble the grid. This firstly wasn't optimised, nor very easy to expand. Let's say we wanted the grid to be the size of 10x10 we'd have to remove all the components, or if larger add more. This just wasn't a good approach.I then opted for it to automatically generate given a specific value, which would calculate the size of the rows and columns of the grid. This was great as it'd give more flexibility when it came to customising and also made it future-proof. Although spawning individual boxes just wasn't performant. Which is where I came up with using instanced versions. Doing so gave a great performance and all the benefits of the previous version.Although it was much harder to work with overall and setup due to instanced meshes not behaving like a singular component. Instead, they're more limiting. Also, this meant because it's an instance the collision just wouldn't work correctly due to it not updating upon the instance being removed. To rectify this I had to come up with a solution and quickly. I moved it to spawn the instance but without collision. This gave us the grid and removed the problems we faced with collision initially.But now we don't have any collisions? Well, that's where I then added a box collision with the extent of the instanced static mesh. Making individual collision volumes for each instance we can toggle at runtime, without having to update the entire instanced collision. Then the issue came up with how we differentiate between the collisions and the instance. This is where I used component tags to attach the instance index to the tag so we know which instance the collider is connected to. I then added each instance to an array of colliders so we can call it later on. After generating the grid we needed a solution to spawning obstacles the means of blocks and turrets. It was just the case of calling a function which would take a percentage (which would be a scalar to determine the amount which should spawn) and then remove the calculated amount from the total instance count. Making an array of bools if they should or shouldn't have an obstacle. In the end, it was just the case of randomising the array and spawning a child actor component with the selected instances transform.
vertex animation textures (VAT)
Now that's all left is the destruction. This was thought over heavily because the use of runtime physics just wouldn't be performant given the amount of constant destroying actors also the fact that we didn't have chaos destruction at the time, we needed to find a new method. Initially, we thought okay we'll just scale down the instances and call it a day... Potentially spawning an emitter at the location. Although this just wouldn't look great! So to rectify this, we came up with the solution of utilising SideFX VATs which I've covered heavily in the Eggmaggedon post I have on my portfolio. We mainly used this approach as it would allow us to bake down the complex simulation into a texture which wouldn't be calculated during runtime, saving performance and memory. To deal with this I simply just spawned a new actor where the destroyed instance would be, which would auto-play the VAT animation and have a lifetime to destroy itself.The destroying is something I'd like to talk about too when it comes to the instances themselves. We had to come up with a quick solution. The method I used was to simply remove the instance at the given index. Then disable the collision on the connected collider; spawning the replaced destructed mesh in its place. Which worked extremely well. We also use similar logic for the AOE abilities which will destroy all tiles in the vicinity of the player using.
Mechanics
Shared camera
As this was intended to be a fun couch game experience we didn't want to opt for Unreals built-in split screen feature (Which splits the viewport into 2 separate sections for each player controller. Instead, we wanted to make use of a shared camera which would update dependent on the player's distance from one another.I got the current player array which contains all player references, looped through them and got an array of all locations of each player. Then used the "max of float array" node which returns the max of the array and clamped it to the spring arm with a spring arm minimum and maximum value to stop any unexpected camera behaviour such as the camera being brought too close or too far. This only adjusted the spring arm though...To change the position of the camera and move it smoothly, I got the player array again and use the "Get Actor Array Average Location" which returns the centre point location relative to all the characters. This allows us to position the camera actors' location and lerp it using the "vinterp" node.
This results in a smooth and performant camera which works for both players!
THEME
"Make It Count"
THEME
"Delightful"
THEME
"What is hidden in snow comes forth in the thaw"
THEME
"Piccadilly"
THEME
"Flow"
THEME
"One Room"
THEME
"Heaven"
Game Summary
Eggmaggedon is a fun action-packed game based around delivering collected souls fallen from those who've perished to meteors to the heavens, whilst the player has to simultaneously dodge incoming meteor strikes. Each collected soul counts towards your combo score, increasing based on currently collected souls within a short period of time.
SKILLS |
---|
• Unreal Engine 5 |
• Blueprint |
• Optimisation ( 1000+ characters with destruction ) |
• Video editing |
• Design |
• Perforce |
SOFTWARE | PRE-MADE ASSETS |
---|---|
• Unreal Engine 5 | Synty POLYGON City pack |
• Blender | Realistic Starter VFX Pack Vol 2 (for heaven clouds) |
• Houdini | Niagara Mega VFX Pack vol. 1 (for meteor) |
• FL Studio 20 | Chameleon (Postprocess) |
Responsibilities
As the primary programmer on this project, I was tasked with implementing and polishing the core game mechanics and helping optimise both the level and classes. I had to overcome a plethora of issues, primarily optimisation... Due to the intense amount of actors present at any given time coupled with the destructive nature of the environment being affected both by the players' movement and abilities and the plentiful amount of meteors raining with no end in sight.Likewise, I was also tasked with various other responsibilities including:
• Bug fixing
• UI integration
• Design
• Audio integration
• Optimisation
• Video editing
Optimisation
vertex animation textures (VAT)
Based on the sheer amount of intended actors the game supports at runtime, it was of utmost importance to optimise in every method possible. This resulted in the team finding the most appropriate solution to negate any cost to performance which the standard of using skeletal animation for characters wouldn't suffice due to the cost of runtime calculations and memory. Instead, we opted for VATs with Houdini. Allowing for animations which would've been used and retargeted to a skeletal mesh to be baked down to a handful of textures to be interchanged at runtime. Giving the same visuals but without the bloated overhead. Although because of this and the fact we had a total of seven different characters all requiring their own animation set, it was rather limiting.
To make this function correctly for all characters whilst keeping it light, we used a data table to store all character data such as the static mesh, animation sets and pose asset which then could be called upon in the class.When the actor is spawned I got all available row names returning an array, this then was used to set an integer to a random index based on the length of the array. Which was used, for example, to set up which static mesh was to be set, which animation sets to use and which pose animation to play upon the actor going dormant (Idle, Death).The AI still made use of blackboards and behaviour trees but instead of updating a variable which would in turn be called in the animation graph, we could just simply change the current vertex animation and swap them out easily by updating the current material.
Chaos Destruction
With the need for destructible meshes, it was clear we needed a solution to find a lightweight option. Initially, the idea was to just use VATs as I've previously mentioned. Although this idea wouldn't feel or look visually seamless when it came to the angle of attack and force applied to each destructible (Would be super performant though!). This would've meant we'd need to have an extremely high amount of different VAT animations for each possible angle for each mesh. This just simply was out of scope and quite honestly unneeded.
Instead, we opted to use Chaos Destruction in Unreal, being that its main selling point is the high performance compared to the legacy physics system and runtime calculated destruction compared to VATs. For this instead of creating manually placing an array of anchor fields, geometry collections, colliders and navmesh modifiers (to stop AI navigating through), we opted to use a master class with an exposed geometry collection variable which could be modified from the editor. This allows us to place them around the level and swap out the geometry collection quickly in the detail panel whilst maintaining all the needed components including the logic. Where we use this reference pointer to be used for the rest collection in the construction script.We also automated the colliders by setting the extent of the box to match that of the geometry collections bounds. Although this isn't perfect it means we didn't need to use the static meshes collision instead we could use a simple box which is more performant than having either complex or UCX collision volumes which is more detailed than a standardized box. We could have set each static mesh base collision manually, but this would have just been time-consuming and not needed for the scope of this project.All that was needed now was to set up the spawning of the master field at the impact location which would add force and destruct the meshes. We ensured that the lifespan was enabled, called the trigger function in the master field and finally set the collider to ignore all collision channels, allowing the player to traverse through.
Entity count
Due to the sheer amount of actors being spawned in the level at any given time, it was crucial to find a solution to mitigate any decrease in performance with a large number of actors. Nanite wouldn't be useful with the implementation of VATs and also because that isn't just a solution which would stop the loss of performance anyway. I leaned towards forcing all actors to behave differently based on their current location in world space compared to the player character. Meaning, that if an actor is 6000 units away from the player at that current time and isn't visible, they'll skip the death animation, and call to be removed. This also ties into the combo system being that if an actor is this far it wouldn't make sense to spawn more actors at a distance a player wouldn't be able to get to in time based on the lifetime set for the pickup. Instead, these would only trigger if within the range of the player. Coupled that with the fact that both meteors and AI would have a much higher percentage chance of spawning nearer the player than away, making it seem visual that the entire level is filled with actors when in reality it's only where the player has a line of sight too or if in a close vicinity. Furthermore, to limit the overall actor count I added it so there is a smaller chance of a soul spawning, meaning that each AI wouldn't equate to spawning another actor upon death. This value was tweaked for balancing but overall made the game fun and challenging, compared to gaining a full combo in the first few minutes of playing.
Upon an actor taking damage and dying this would reflect in the manager which controlled the amount of current spawned actors, meaning that only a specific amount could be present at any given time, which was more reliable and performance-saving. However, we did think it'd be a good idea too for players to experiment so we through in a slider scale parameter for the AI spawn count, spawn frequency and meteor frequency which is honestly quite entertaining having thousands of AI running around! Although I wouldn't recommend you set the scalars to high, the performance isn't great at that point as you'd expect.
Mechanics
Spline based movement
Flying the player up to heaven was a fun challenge being that we wanted the player to be able to head to heaven from any location in the level, coupled with reliable movement paths towards heaven. Just simply lerping a player from one point to another would work... Although that wouldn't look good, nor would it adjust for collisions. Instead, I opted to use splines to lerp the player. However, this still possessed the same issue being that it'd look strange lerping to the initial spline point and then following the spline. So to solve this issue and create a seamless travel experience I added an extra spline point at runtime, that allowed us to have the spline adjust based on the player's current location in world space. Making for a smooth transition. To do this I simply created a new local vector array, adding initially the players' location, then adjusted the spline point type to be curved instead of linear. Then proceeded to add all spline points from the spline to the array. Which then gave me a new trajectory for the spline. I set the spline to use these new spline points.This has a new issue though, due to the initial (now index 1) spline point being updated the rotation is skewed, making for some unexpected results and making the player clip through the ground. To fix this, I simply set the index 1 tangent to be its own location multiplied by -1, and index 0 to use make a rotation using the x vector of the controllers' rotation. Which ultimately fixed the tangent issues.To wrap this up, with the updated spline path the player can simply be lerped through the spline points scaled by a timeline to reach the end location which for this is Heaven. This was also used for traversing back to earth.
Networked door system
Tutorial
Unreal Engine 5
Networking
Blueprint
2022
PROJECT FILES ARE AVAILABLE AT THE BOTTOM
CONTENTS |
---|
• Project setup |
• Player character |
• Door class |
• Optimisation |
• Conclusion |
Tutorial summary
Tired of incomplete tutorials on networked door systems? This guide covers everything from project setup to interaction systems to door setup, optimization, and potential deviations. Get the insights and steps necessary to create an optimized and multi-functional door system for multiplayer in Unreal Engine. Whether you're a seasoned developer or a beginner, this guide will help you achieve door system excellence with efficiency, versatility, and seamless functionality.
Tutorial Steps
project setup
Ready to get started on adding a customisable door system to your project? First, create a new Unreal project using the Blueprint version of the third-person character template. Then, set up your folders - a "Blueprint" folder and a "Data" folder within your content folder. Within the "Blueprint" folder, create three more folders: "Character," "Interactables," and "Interfaces."This is optional but move the pre-built BP_ThirdPersonCharacter class into the "Character" folder, and later, we'll cover the "Interactables" folder. In the "Interfaces" folder, create a new Blueprint Interface named "BPI_Interact" and define a single function called "Interact."Finally, create an interaction button in the project settings under input by creating a new action mapping "Interact" with the input being the 'E' key. With these steps completed, you'll be ready to move on to setting up the player character.
player character
In this section we'll be creating the interaction system to use the doors. To start, go to the Event Graph inside the player character and find an empty space. Right-click and select "Interact" Input Action. Then, create a new function by clicking the "+" button under the Functions tab on the left side. Rename this function to "InteractTrace".Next, we will use the LineTraceByChannel function to check if we have hit an actor. To do this, get the follow camera and its world location and forward vector from the component. Plug the world location into the start input of the trace function, which will be the starting point of the trace. Then, multiply the forward vector by a variable you need to make named "InteractTraceLength" (set to 750.0 and to use the float data type) and add the result to the world location. This will give us a new vector in world space, 750 Unreal Units away from the current camera location. Make sure the line trace uses the visibility trace channel.Check if the trace is valid by using an "Is Valid" node and on the "Out Hit" of the line trace get "Break Hit Result". Get the "Hit Actor" from the "Break Hit Result" and plug it into the IsValid node. Create a Return Node and connect it to the "IsValid" return. Drag the hit actor to the return node to create a new output (name it Hit Actor).Note: We should call this trace client-side and only verify it on the server if it hits something. It's also a good idea to double-check that the hit actor implements the interact interface to save server resources.Head back to the event graph and connect the new function to the pressed pin. On the return execution pin for the "InteractTrace" function, use an IsValid node and check if the player is a client or server using the "Switch has authority" node. If the player is the server (authority), call the "Interact" interface function on the hit actor. If the player is a client (remote), create a custom event RPC named "ServerInteract" and set it to "Run on Server" and "Reliable".From the execution pin of the new event, call the "InteractTrace" function and check if the HitActor is valid. If it is, call the "Interact" interface function on the hit actor. Now that we are the server and have authority, we don't need to use the "Switch has authority" node.Finally, call the "ServerInteract" event on the remote execution pin of the "Switch Has Authority" node in the event graph.
door class
Now for the meat and potatoes! Let's move on to why you came to this tutorial! The doors. Before we open up the class, navigate to the data folder we created at the start and create a new folder named "Curves" inside we're going to create a new float curve. To do so right click in the content browser and select miscellaneous->curve and in the pop-up box, select "CurveFloat", this will be used for opening the door. Name this "C_Door". For the keys inside the curve editor. We're going to simply just have the first key at 0,0. (Middle click to create a key) and the second at 1,1. (We'll be modifying the play rate in the blueprint so we can assign custom times to each actor we place in the level, instead of being restricted to a set time or having to create multiple curves.)Now we can head back to the event graph! From the RPC we made an earlier call for a branch node from the execution pin (Holding B and left-clicking is a neat little shortcut), now click on the "CanUse" function and check the pure option in the details panel. Drag the function into the event graph and connect the return value to the branch.
Now we can go and create the door class. Go back to the "Interactable" folder we made and right-click and select "Blueprint Class" selecting "Actor". Name this BP_Door_Master (the suffix of "Master" is used as this will be the parent class of children we can create based on this actor to create different doors using the logic from this base class.
The very first thing to do is go ahead and set up the actor replication settings. Navigate to the class defaults (the button is on the toolbar at the top) and scroll down in the details panel till you reach replication. Turn the check box called "Replicates" on we'll be coming back here in the optimisation section as we have quite a few different options we can change to improve bandwidth.
From there, head to the components of the class and create 3 new static mesh components. Naming them "SM_Door", "SM_DoorFrame" and finally "SM_DebugDoor" (The debug door will be used to visually place and pivot the door in the level or blueprint to reflect where the door's final transform will be. I feel this method beats having to input manually their transforms. In the "SM_DebugDoors" details panel we simply just have to ensure there isn't any collision by finding the collision preset and set the drop-down to "NoCollision" also check the "Hidden in Game" checkbox as this is only supposed to be used to store a transform.
We now need 2 more components,
one being a "Box Collision" which shall be used to see if a player is overlapping and an "Arrow Component" to see the direction of the actor when placed in the level.Within the "Box Collision" details panel make sure to set the boxes' extent, for me I found (296.0, 48.0,112.0) to be good values.Now to setup up the meshes. Open up the construction script, this is called before "Begin play" and outside of runtime. We're going to now drag the "SM_Door" component into the graph, dragging off from that and calling the "Set Static Mesh" function. With this, we'll right-click the "New Mesh" input and promote this to a variable named "DoorMesh". Make sure this is an instance editable in the details panel and assign it the mesh you'd like for the door. Then do the same as before for the "SM_DoorFrame" so drag it out, call the "Set Static Mesh" and promote the "New Mesh" to a variable named "DoorFrameMesh".For now, we're going to move over to the event graph (We'll be coming back here soon to set the timeline play rate and debug mesh).
Firstly navigate to the begin play node, if there isn't one right click and get one! From the execution pin call for the "Switch has Authority" node again, now create a new transform variable named "InitialDoorTransform", making sure it's set to replicate in the details panel. Drag this variable into the event graph and set it; connecting it to the remote execution pin.
The value we'll be setting this to will be the "SM_Door" relative transform. So go ahead and get the "SM_Door" component into the event graph and off of that get the relative transform and connect that to the "InitialDoorTransform" left-hand side pin. Then simply call for the force net update function after the execution pin. I'll explain this node later.Now head to the class settings (located at the top in the toolbar) and add the interface we made "BPI_Interact" to the implement interfaces section.Once done, go back to the event graph and implement the interface function by right-clicking and searching for "Event Interact". This shall be triggered by the player's character. We now need to create a new custom event RPC named "Server_ToggleDoor" making sure this is set to reliable and "Run on Server". Call this event on the execution pin of the interact event.Now we're going to need a new function to be made named "CanUse" this will be there to check if the actor can be interacted with and if the door isn't locked. Once created open it up. On the left create 2 new variables. "Can Interact" and "Locked", this doesn't need to be replicated as they aren't updated during runtime. Now to finish this function all we need to do is get both these variables inside the function, off of the "CanInteract" drag out and call the "and" node. In the second pin of the "and", get the "Locked" variable, drag out and get the "not" note. Plug this return into the second pin of the "and". The execution pin of the "and" add a return node. and plug the returned bool from the "and" node into the return node.Following this drag out and call the sequence node. Now we need a new boolean, named "IsOpen" make SURE this is set to be replicated. On the second sequence pin set the newly created variable and for the value we're setting it to, get the "IsOpen" variable and get a not node, plug this into the set pin.On the first pin of the sequence we need to call another branch and for the condition get the "IsOpen" variable and get the "not" again. Plug this into the branch. The reasoning behind needing the sequence is that we need to get the "IsOpen" value before it's set, and if we set it later it won't be as clean or it'll be too late if performed after the upcoming timeline is finished.On true, add a timeline by right-clicking and finding "Add Timeline", and name this TL_Door. Open this timeline up and set the curve to use the external float curve we created earlier by adding a floating track and selecting the external curve. Set it to replicate by selecting button three to the right of the timeline length. Now we've finished with the timeline, go ahead and in the event graph plug the false pin into the reverse pin of the timeline. Making sure the branch's true execution pin is into play (Not "Play from start").On the updated pin for the timeline, we'll need a new function! Let's create it and call it "DoorTransform" This function shall take 1 parameter named Alpha with the data type being a "float". Inside the function, get the "SM_Door" component, and "Set Relative Transform", for the new transform we need only a little bit of logic to find the correct transform.First get the "InitialDoorTransform" variable we made earlier, off of that call for the lerp function, this variable will go into the A parameter. Then get the "SM_DebugDoor" component and get the relative transform of that component, making this returned value go into the B value.
This shall give us both the initial transform and the target transform.
For Alpha, we're going to just use the Alpha input for the "DoorTransform" which we'll update outside of the scope of the function. Making the "Interp mode" to "Quat interp". Then plug the return of the "Lerp" function into the "New Transform" pin of the "Set Relative Transform". Finally, add the node after setting the transform and on the execution, the pin gets the "Force Net Update" node.Now we've finished with that function, plug that into the update execution pin of the timeline. With the float value from the timeline being plugged into the functions alpha pin.Finally, to finish off the interaction for the doors, we just have to go back to the construction script and set up the timeline and debug the door mesh.
So let's head into the construction script, last time we were in here we set the door frames mesh. Next, we'll do the timeline, go to components in the variables and get the "TL_Door" or whatever you named your timeline in the event graph and drag it in. Then drag off of it and get the "Set Play Rate" node. For the new rate, this will be super simple, all it is is getting a division node. With the first pin being set to 1.0f. The second is the door speed. For cases where we'd like to change the speed on, simply promote the pin to a variable named "DoorSpeed" this doesn't need to be replicated. Compile and set the variable to a default value of something like 0.6f. Also, make sure the variable is set to instance editable.Now we're moving on to setting up the debug mesh. Go ahead and create a new boolean variable which doesn't need to replicate named "ShowDebugDoor" and connect it to a branch, with the execution pin from the play rate going into the branch. Next up we need to drag the "SM_DebugDoor" into the graph and call for the "Set Static Mesh" function, with the "NewMesh" being the "DoorMesh" variable we set up earlier. Afterwards, this is optional but with the debug door drag off again and call the "Set Material" node. In the material pin, get a select node and with the index set that to the "Locked" variable we made. Now then in the true pin of the select, click it and find a material you'd like to use to show you it's locked. I used a generic red engine material. With the false pin use another different material, I used another generic engine material. Now, this shall make it show visually on the debug mesh if the door is set to be locked or not. This is great for when creating levels and knowing which doors can or can't be opened without having to click on each actor within the level.Now if you're not wanting an overlap event to trigger to open the door too, you can skip to the next tutorial step. Though if you'd like the extra customisation to feel free to carry on reading!So now that we've finished all that let's jump straight onto the overlapping events. Go and head over to the event graph, right-click anywhere and get both the "Event ActorBeginOverlap" and the "Event ActorEndOverlap".
Starting with the "Begin Overlap" event, off the execution node "Cast to Character" which will check if the current overlapping actor inherits from the "Character" class. Connect the "other actor" to the object of the cast. Do this also for the "EndOverlap" event. We're nearing the end finally but we do need to create a new function. Let's call this "CanOpen" set this to be pure like previously mentioned and have both an input and output of data type "bool".Within the function, drag the box collision component into the graph, off of that "Get Overlapping Actors" with the class filter being "Character", we do this so it only returns actors which inherit from the "Character" class. With the array returned by the function get the length. We then just need to get a >= node with the second pin being 1 and a > node off of the length with the second pin also being 1. Off of both the >= and > nodes call a "not" node on both. Now get a select node with the index being the function we're in's input bool which mine is named "Open", and the false of the select being the >='s return on the not node. The true select pin is the > not nodes return. (Feel free to look at the screenshots posted if you're confused!) From the select node, add an "and" bool node, which will have the first pin being select, the second being needed to be promoted to a variable named "CanOverlap" which won't be replicated but will be instance editable and thirdly the "Locked" variable we made with a not node coming off of the bool and going into the third pin of the "and". If you're finding it hard to add a pin to the "and" node so we have 3 inputs, just click the "Add pin +" button on the node itself. From there add a return node to the function and drag the returned bool from the "and" node into the return node. We made this function based on the input bool, it checks if there are any players currently being overlapped to stop the door from shutting or opening prematurely. Also to check if the door can be overlapped and isn't locked.I promise we're nearly finished here, head back to the event graph and create a new RPC custom event named "Server_OpenDoor" which will be set to "Run on Server" and also reliable. With an input of type bool named "Open". Off of the execution pin, get a branch node with the condition being the function we just made so go ahead and drag that out! For the input of the function make that the custom events output pin "Open" and the return of the function to be the branches condition. Next up is to set the "IsOpen" bool we made earlier, to be the "not" value of the custom events "Open" value. Check the screenshots if you're not sure! From there just simply hook the exiting execution pin into the branch we made before the timelines execution pin.WE'RE FINALLY DONE!You can now go and fully test it, remembering to check all variables we've made values to make sure they're set up right. With the meshes set, Door speed set, Updating the debug doors pivot and/or location. Also if it can be overlapped, interacted with or if the door is locked. Remember this can be set up both in the actor itself or in the level by clicking the actor and modifying the debug mesh component or clicking the actor and changing the default variables we made in the details panel.
Optimisation
For the optimisation, we'll be covering both performance and networking optimisation. To begin we'll be focussing on the doors class itself. Open it up! The majority of these optimisations will be tweaked to the actor itself which will reduce its overhead. First of all, in the class defaults (located in the toolbar at the top), we're going to be unchecking the "Start with Tick enabled" checkbox which is very important in all actors who don't need to utilise the tick function. Thus, preventing the actor from constantly ticking when it doesn't have to. Secondly uncheck the "Allow tick on dedicated server", although the actor doesn't have the tick enabled it doesn't hurt to uncheck this option too! We can also uncheck both the "Can be damaged" and "FInd Camera Component when View Target" as both of these won't need to be used.
Now let's have a look at the replication settings. In the replication category, there is a plethora of different options to add, remove or tweak. We'll be focussing on which changes we should change for this actor specifically. First of all, let's uncheck the "Call Pre-Replication" bool, all this does is when the game is loaded and is being played the engine will find all actors which have this box checked and is called right before replication occurs. This is only meant to be run on the server.
Next up is "Call Pre Replication for Replay" which we also don't need for the reasons above. Nothing in this actor needs to be called during the pre-replication stage.
The most important of all these settings is the net dormancy, ensuring this is set to "Dormant All" which tells it to pretty much turn-off, ignoring any data sent lowering the bandwidth needed. This is similar to how Fortnite has so many actors, instead of them constantly updating all clients, it instead only updates clients when it needs to. For us, we'll have the doors dormant up until they're interacted with. This is what the "Force net update" node deals with, manually being able to update all clients to the current state of the actor at any time, making it so that when a door interacts with clients are only updated at that specific point rather than constantly throughout.
Next up is the "Net Update Frequency", as this actor doesn't need to be updated constantly, we can lower this value significantly. This value is the frequency per second in which the actor itself is considered for replication, for a character sure this can be higher but for a dormant and less frequently used actor we should most definitely lower this value. I've set mine to a value of 50. Although it can be reduced a lot more! I'd recommend trying the network emulator in the project settings, to simulate clients on a poor connection and how they'd react to the actor. Although I haven't changed this one it is very important... Which is "Net Cull Distance Squared" which is the distance from the actor in which the actor will replicate, anything client outside of this zone shall not be updated, although if playing on a peer to peer the server will always be updated as it's the server which knows about the majority of what's happening at any given time. This value will be very project specific and shall need to be decided upon with testing. Although if you can't figure out the size, another option is "Always relevant" which I don't recommend setting, although you could set this during runtime and potentially make the "IsOpen" variable a rep notify and update the "Always relevant" bool and set a timer for to revert it to false after one millisecond.
Other options to look into for better optimisation would be the "Net Priority" (lowering this value means it's less likely to replicate to clients, whereas increasing it shall make it a higher chance of replicating. Although this is only noticeable when a client has a really poor connection. A value of .5-1) should be fine! Try experimenting with it.Another thing to bear in mind, which isn't project performance related but increasing development efficiency is variable organisation, commenting out code and hiding variables from the defaults. To organise the variables, simply click them and head to the category section, you can assign a category and if you'd like a subcategory you can use the '|' character. An example of this would be "Config|State" with config being the parent category and state being the child. You could even have children within children!
Next up is hiding variables which we don't want to be modified outside of runtime. You can do this by clicking the variable and checking the "Advanced Display" checkbox. To comment anything out, you can use the shortcut 'C' when selecting nodes, and it'll create a comment block you can edit and word to describe what each section does.
Conclusion
In conclusion, we have discussed and gone through the entire process of creating and setting up the door system. Furthermore, went over optimisations which can greatly benefit your project which will be easier to notice the larger the project is. It's always better to optimise as you go than go back and do it near the end of the development process. The doors are functioning and can be locked, overlapped, interacted with and positioned to reflect the transform which is user-defined. The speed at which the door is opened and closed can be adjusted and children can be created to make a large quantity of different unique doors to be used throughout your level.Feel free to download the project files below if you have any issues! Or would like commented code to better understand everything.Thank you for reading and hopefully, you've learnt a thing or two!
TUTORIALS
PROJECTS
Project KOI - Tattoo system
Interaction, Inventory & Character changing
Pickup & Drop items
Networked Revive
Intertia - UI Integration
Chat System
Projects
Projects
Responsibilities
The Accelerator - Win the Future
At LocalProjects, I played a pivotal role in the creation and design of "Win the Future," an immersive climate change awareness project that transports users into a fully interactive 3D world without the need for traditional VR headsets or controllers. This project was part of the broader initiative titled "The Accelerator," aimed at engaging audiences with pressing global issues through cutting-edge technology.In this project, I was responsible for multiple facets of development, blending technical expertise with creative vision. My contributions included:Design and Implementation: I was deeply involved in the conceptualization and realization of the interactive experience, ensuring that the virtual environment was both engaging and educational. Using Unreal Engine, I developed a seamless blend of visual storytelling and user interaction, leveraging both Blueprint and C++ to create robust gameplay mechanics.ZED Camera Tracking: A critical element of the project was the integration of ZED camera tracking technology, which allowed users to interact with the environment through natural hand gestures and body movements. I handled the precise calibration and integration of the camera system, ensuring accurate and responsive tracking that maintained the immersion without the need for external devices.Perforce Version Control: To maintain consistency and collaboration across the team, I managed the project’s version control using Perforce. This allowed for smooth workflow management and ensured that all team members were always working with the latest assets and code.Time Management: I also took on a leadership role in managing the project timeline, coordinating with various team members to meet critical deadlines and deliver milestones on schedule. This involved balancing the demands of creative iteration with the practicalities of development, ensuring a high-quality final product.The result was a fully immersive experience where users could explore and interact with a 3D world, engaging with content through movement and gestures, all within a physical space transformed by projectors and advanced tracking systems. "Win the Future" not only demonstrated the power of interactive storytelling in raising awareness about climate change but also pushed the boundaries of what’s possible in location-based virtual reality experiences.
Projects
Responsibilities
As a seasoned network programmer at DreadXP, I am a key player in the creation of compelling gameplay mechanics for both single and multiplayer networked games. Guided by a talented ensemble of programmers, artists, animators, designers, and sound engineers, I engage in the conceptualization, design, and implementation of novel features that bring excitement to the projects I work on.My technical expertise lies in writing efficient, readable code with a combination of Blueprint and C++, while I also embrace the challenge of exploring emerging technologies to ascertain their suitability for our current or future endeavors.In addition to my primary role, I am assigned a variety of secondary responsibilities that broaden my skill set and contribute to the overall success of the projects I work on, including but not limited to:• Fixing persistent bugs
• Maintaining plugins and tools
• Integrating UI
• Contributing to design
• Creating editor tools
• Integrating audio
• Optimizing performance
Network Programmer
Skills |
---|
Unreal Engine 5 |
Blueprint |
Perforce |
Networking |
C++ (Plugins) |
The Human Show is an 80's inspired horror experience with asymmetric multiplayer game modes, along with a full single player story mode.
PROJECTS
ABOUT ME
Hello, I'm William Hardy!
I specialize in networking for games programming, bringing imaginative concepts to life and creating thrilling experiences for users. I excel in Unreal Engine, crafting captivating environments and mentoring aspiring industry newcomers. Looking for a visionary programmer to partner with on your project or event? Reach out to me and let's make it happen!
CONTACT