Click a tab to check it!
A little presentation on my work experience
Some of the main skills I learned
Unreal Engine C++
Unreal Engine Plugins
Unreal Engine Tools
Junior Supervision
Unreal Engine Slate
Localization
Perforce + UGS
Documentation
XIX3D is a company that specialize in the aftermarket of cars. It has created a wonderful tool that allows car customisation. You can render your creations in a hyper realistic environment to create montages, videos or photos.
I had ownership of developing a UE tool to integrate translation files using Unreal Engine C++ Slate system. I supervised two juniors devs on features implementation. For more information, you can click on the Localization Plugin tab above. There are some pictures of what the plugin looked like for the developpers. Additionally to working on the localization, I adapted some C++ UI widgets for text going overboard with translations so text scrolls when the mouse is over. I was also taked finding the right tool to store and create internal documentation for future developpers.
company website: https://xix3d.com/
Some of the main skills I learned
Unreal Engine C++
Unreal Engine Blueprint
Unreal Engine Widgets
Unreal Engine VR
Unreal Engine Multiplayer
Line tracing
Localization
Perforce + UGS
PrecisionOS is a company that creates realistic surgery stimulations in a virtual environment for educational purposes. This makes learning surgical procedure extremely easier and cost efficient has it shows many metrics following your every steps to make sure you do the operation right. A operation can be done alone or in multiplayer with many people. This allows a very immersive experience similarly of a classroom.
I asisted in the development for realistic surgical experience in virtual reality. I worked in creating the step-by-step logic to perform accurate surgeries in the operating room environment. Setting what makes a player able move to the next step and cues on how to use the available operating instruments. The work requiered much communications with the artists and doctors for accuracies, both in the visuales and the precision. Many steps demanded much maths to calculate the metric and accuracy in real time. An example of surgery I worked on from the beginning is this module:https://www.linkedin.com/posts/precisionos_introducing-the-shoulder-approach-module-activity-7219704082825932801-rnNa?utm_source=share&utm_medium=member_desktop
company website: https://www.precisionostech.com/
Some of the main skills I learned
C#
Blazor (html)
REST API
MySQL
Gitlab CI
Docker
One of two developpers working with an external client to create a MOOP plateformed aimed at the mining industry in Canada. We created a full plateform that allowed the creation of surveys to test the knowledge of miners. They had the chance to have full reviews on their skills and where to find information to strengthen their weak areas. The owner had a detailed metric for any surveys made, on anyone. This way, they could determine if certain groups of workers needed further training.
Some of the main skills I learned
C++
Unreal Engine 4
Behavior Tree
Unreal Engine UI
Gitlab
Unreal Engine Blueprint
I was a developper working to create a shopkeeping game. The aim was to manage your customers whislt making profits from them.
A image of what the game looked like
This is a simple side scroll. The aim is to travel from beginning to end of a level till the 3rd level. This is also my first full UE project
AI Characters
Animation/Animation Graph
Behavior Tree
Climbing Movement
SideScroller - BP/C++
Money/Health Management
Save/Load
Change Level
UI
Moving plateforms
Materials
Game Design
I decided to make it cartoon-like themed with assets from the Epic market
Player walking through gathering coins from the level collecting coins
Level 2 has a steampunk-like themed. I also picked my assets from the Epic market
Some fighting with an enemy from level 2 that can cast a fireball
This cool toil wire sends the player to level 3
I made some tron-like material and added it to shapes for my last level.
When characters falls on harmful objects - affecting health
This is to show the "climbing" module added to character movement
I've created of course, a health item. They are present throughout the game
With the use of glass MAT to trick player when stepping on the blocks
Custom Assets
C++
New asset to use in Blueprint
UE Plugin
Slate
Scripted Actions
This is taking from a tutorial (link above). This is a walk-through on how to create a custom asset for Unreal Engine, in this case, an asset that represents a normal distribution
Here's what it looks like
Our new asset -> a simple UObject
class ASSETTUTORIALPLUGIN_API UNormalDistribution : public UObject UNormalDistribution::UNormalDistribution() : Mean(0.5f) , StandardDeviation(0.2f) {} float UNormalDistribution::DrawSample() { return std::normal_distribution<>(Mean, StandardDeviation)(RandomNumberGenerator); } void UNormalDistribution::LogSample() { UE_LOG(LogTemp, Log, TEXT("%f"), DrawSample()) }
Implementing a class IAssetTypeActions to register the new asset to setup the looks and behaviours in the engine
class ASSETTUTORIALPLUGINEDITOR_API FNormalDistributionAssetTypeActions : public FAssetTypeActions_Base { public: UClass* GetSupportedClass() const override; FText GetName() const override; FColor GetTypeColor() const override; uint32 GetCategories() override; void OpenAssetEditor(const TArray& InObjects, TSharedPtr EditWithinLevelEditor) override; };
Now to make the editor and the engine aware FNormalDistributionAssetTypeActions exists, in the editor module created to handle operations on the new asset for the Editor, I registered our NormalDistribution once it loads (unregister when shut down too)
#include "AssetTutorialPluginEditor.h" #define LOCTEXT_NAMESPACE "FAssetTutorialPluginEditorModule" void FAssetTutorialPluginEditorModule::StartupModule() { NormalDistributionAssetTypeActions = MakeShared(); FAssetToolsModule::GetModule().Get().RegisterAssetTypeActions(NormalDistributionAssetTypeActions.ToSharedRef()); } void FAssetTutorialPluginEditorModule::ShutdownModule() { if (!FModuleManager::Get().IsModuleLoaded("AssetTools")) return; FAssetToolsModule::GetModule().Get().UnregisterAssetTypeActions(NormalDistributionAssetTypeActions.ToSharedRef()); } #undef LOCTEXT_NAMESPACE IMPLEMENT_MODULE(FAssetTutorialPluginEditorModule, AssetTutorialPluginEditor)
This is a factory, it helps to create instances in the editor
UCLASS() class ASSETTUTORIALPLUGINEDITOR_API UNormalDistributionFactory : public UFactory { GENERATED_BODY() public: UNormalDistributionFactory(); UObject* FactoryCreateNew(UClass* Class, UObject* InParent, FName Name, EObjectFlags Flags, UObject* Context, FFeedbackContext* Warn); }
From there, we can create a SlateWidget and make an unique page/tab for it :)
Add an argument from the main cpp to our tool so that it can use it.
TSharedRefFSuperManagerModule::OnSpawnAdvanceDeletionTab(const FSpawnTabArgs& SpawnTabArgs) { //sends data to widget return SNew(SDockTab).TabRole(ETabRole::NomadTab) [ //GetAllAssetDataUnderSelectedFolder() is our list of items selected SNew(SAdvanceDeletionTab).AssetsDataToStore(GetAllAssetDataUnderSelectedFolder()) ]; }
Now, in our widget we can collect our list of items selected as an Argument and use it. We can then build our widget with all the items
void SAdvanceDeletionTab::Construct(const FArguments& InArgs) { bCanSupportFocus = true; //receives value that gets passed in arg //InArgs.AssetsDataUnderSelectedFolderArray; StoredAssetsData = InArgs._AssetsDataToStore; . . . }
Here's what it looks like
TArraySelectedAssetsData = UEditorUtilityLibrary::GetSelectedAssetData();
With this, we can do things like adding a prefix depending on the asset class type
TMapPrefixMap = { {UBlueprint::StaticClass(),TEXT("BP_")}, {UStaticMesh::StaticClass(),TEXT("SM_")}, {UMaterial::StaticClass(), TEXT("M_")} }; FString* prefixFound = PrefixMap.Find(selectedObject->GetClass()); const FString newNameWithPrefix = *prefixFound + oldName; UEditorUtilityLibrary::RenameAsset(selectedObject, newNameWithPrefix);
Here's what the scripter asset duplicate nb times looks like
The aim was to make a custom translating tool. It was meant to incorporate strings from a CSV file.
Localization
UE Plugins
Slate (lots of it)
String Table/Data assets
C++
File Management
This is a custom module to change the language of an application using csv files.
A string can be modified in the plugin, but not the key, as it could cause problems in the code
A new key can be added directly in the tool
You can import language with the tool, it would be automatically be added.
How is it looks once the language has been added
The file translated.
This interesting bits on string datas and the parser
The FFileHelper loads the file into a manager TArray
FFileHelper::LoadFileToStringArray
All strings were loaded and kept inside on StringTable uasset --> made it easier to play with it and accessable in Blueprint
FName DataAssetLocation = (TEXT("StringTable'/I/AM/PATH/sT_localization.sT_localization-en'")); FStringAsseReference AssetPath(DataAssetLocation); UObject* Asset = AssetPath.TryLoad(); UStringTable* StringTableTranslation = Cast(Asset); FName StringTableID = StringTableTranslation->GetStringTableId(); //Get a string from the string table LocalizedStringDelete = FText::FromStringTable(StringTableID, "btn_Delete");
Camera Movements
Multicast Events
Material & design
C++
Some of the C++ logic
Delegats, lots of them
DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FOnChangeHeight, float, height);
My actor that is responsible to drop boxes also does the work when a box hits the water and needs to be destroy.
As long as the tower height is not 0, we need to make the camera drop so it follows the actor and stays centered
void ABoxDropper::DestroyBoxActor() { if (TowerHeight > 0) { AdjustSpawnerHeight(-100.f); NbBoxes(false); OnDecreaseCameraHeight.Broadcast(-100.f); } } //This is also watched by the camera: void ACameraPosition::BeginPlay() { Super::BeginPlay(); AActor* boxDropper = UGameplayStatics::GetActorOfClass(GetWorld(), ABoxDropper::StaticClass()); if (IsValid(Cast(boxDropper))) { BoxDropper = Cast (boxDropper); BoxDropper->OnIncreaseCameraHeight.AddDynamic(this, &ACameraPosition::AdjustSpawnerHeigth); BoxDropper->OnDecreaseCameraHeight.AddDynamic(this, &ACameraPosition::AdjustSpawnerHeigth); } }
Everytime an new actor Box is created, I change it's colour to something random
if (StaticMeshComp) { float r = FMath::FRand(); float g = FMath::FRand(); float b = FMath::FRand(); UMaterialInstanceDynamic* MIColour = UMaterialInstanceDynamic::Create(StaticMeshComp->GetMaterial(0), this); MIColour->SetVectorParameterValue(FName(TEXT("Colour")), FLinearColor(r, g, b, 1.0)); StaticMeshComp->SetMaterial(0, MIColour); }
We can see the drop system in action. The camera follows box tower height and drops if we miss a fall
My actor that is responsible to drop boxes also does the work when a box needs to be destroy.
Camera Movements
Behaviour Tree
AI
Material & design
Multicast Events
C++
Some of the C++ logic
Made a subsytem to manage key inputs and logic handle across the game for interactions
GetGameInstance()->GetSubsystem()->RegisterPlayerCharacter(); GetGameInstance()->GetSubsystem ()->OnPickUpKeySequenceIsTrue.AddDynamic(this, &ASurvivalBunny_ProtoCharacter::PickupCarrot);
Function that switches the camera view from topdown to viewing the carrot to be picked
void ASurvivalBunny_ProtoCharacter::SetCameraSettings() { auto player = UGameplayStatics::GetPlayerController(GetWorld(), 0); player->SetViewTargetWithBlend(this, 0.01f, EViewTargetBlendFunction::VTBlend_Linear); }
Our rabbit family can help us gather carrots.. I do not have a rabbit mesh...yet. So the blue character seen below is my rabbit gatherer
The rabbit travels and tries to get the closest carrot around it
What happens when a rabbit gets caught in a cage (not real cage mesh) and has to be saved by the player before the farmer arrives
This is the map created for the prototype using landscape and many assets from the UE market
Carrot mesh, made with Blender
The aim was to understand Unreal Engine Automatic Test Tool and generally, make sure our functionalities were working properly.
Unreal Engine Automatic Test Tool
Automatic Key Inputs
Functional Testing
C++
Blueprint
This is the UE Test Automatation module
This is an example of running a test in a test map where the test runs to see whether a carrot can be picked properly
This is an example of running a test where the test runs into a cage (not real mesh) and breaks it with key inputs
Inside Blueprints and C++
This code generate a key press from controller when called
void AKeyboardImputTester::PressKey(FName PressedKey, float TimePressed) { UGameInstance* GameInst = GetGameInstance(); UGameViewportClient* ViewportClient = GameInst->GetGameViewportClient(); FViewport* Viewport = ViewportClient->Viewport; int32 ControllerId = 0; // or whatever controller id, could be a function param FInputKeyEventArgs Args = FInputKeyEventArgs ( Viewport, ControllerId, FKey(PressedKey), EInputEvent::IE_Pressed ); ViewportClient->InputKey(Args); }
This shows what a Blueprint for a key sequence would look like. How I emulate key inputs for H T Y M (pick a carrot sequence in game)
This is a Functional test that watches for an overlap with player actor. It make' sure that the overlapped actor is the valid type and spawns my created BP to emulate key inputs to pick the carrot up
This shows what the test would look like if it passes through the UE tool
Given a sorted array arr and an integer k, find the position(0-based indexing) at which k is present in the array using binary search
GeeksForGeeks -- Level-Easy
int Binarysearch(std::vector& arr, int k) { std::sort(arr.begin(), arr.end(), [](const int& lhs, const int& rhs) { return lhs < rhs; }); // get mid index int highEnd = arr.size() -1; int lowEnd = 0; while (highEnd >= lowEnd) { int midIndex = (highEnd + lowEnd) / 2; if (arr[midIndex] == k) { return midIndex; } else if (arr[midIndex] > k) { highEnd = midIndex -1; } else if (arr[midIndex] < k) { lowEnd = midIndex +1; } } return -1; }
Given two sorted linked lists consisting of nodes respectively. The task is to merge both lists and return the head of the merged list.
GeeksForGeeks -- Level-Medium
struct Node { int data; struct Node* next; Node(int x) { data = x; next = NULL; } }; Node* sortedMerge(Node* head1, Node* head2) { // list to gather all elements in datas std::vectorSaveValues; //while Node is not nullptr, go to next // save value Node* tempHead = head1; while (tempHead != nullptr) { SaveValues.insert(SaveValues.end(), tempHead->data); tempHead = tempHead->next; } tempHead = head2; while (tempHead != nullptr) { SaveValues.insert(SaveValues.end(), tempHead->data); tempHead = tempHead->next; } std::sort(SaveValues.begin(), SaveValues.end(), [](const int& lhs, const int& rhs) { return lhs < rhs; }); if (SaveValues.size() > 0) { Node* newHead = new Node(SaveValues[0]); Node* prevNode = newHead; for (int i = 1; i < SaveValues.size(); i++) { Node* newNode = new Node(SaveValues[i]); prevNode->next = newNode; prevNode = newNode; } return newHead; } return nullptr; }
Given an unsorted array arr of size n that contains only non negative integers, find a sub-array (continuous elements) that has sum equal to s. You mainly need to return the left and right indexes(1-based indexing) of that subarray.
GeeksForGeeks -- Level-Medium
std::vectorSubarraySumV2(std::vector arr, int n, long long s) { std::vector endSumSequence; endSumSequence.reserve(2); if (arr.size() <= 0) { endSumSequence.push_back(-1); return endSumSequence; } int startIndex = 0; int endIndex = 0; long long sum = 0; if(arr.size() > 0) sum = arr[0]; for (endIndex = 0; endIndex < arr.size();) { int currentIndex = endIndex; if ((sum == s) && (startIndex <= endIndex)) { endSumSequence.push_back(startIndex + 1); endSumSequence.push_back(endIndex + 1); return endSumSequence; } else { if (sum < s) //if our current sum is smaller than the amount we want, we can still add more { //start index remains the same //end index moves a space if not the end if (currentIndex++ != arr.size()) { endIndex = currentIndex; if (endIndex < arr.size()) sum = sum + arr[endIndex]; } } else if (sum > s) { //need to change start index startIndex = startIndex + 1; sum = sum - arr[startIndex-1]; // remove previous index from sum } else { endIndex++; if (endIndex < arr.size()) sum = sum + arr[endIndex]; } } } endSumSequence.push_back(-1); return endSumSequence; }
Given a number and its reverse. Find that number raised to the power of its own reverse
GeeksForGeeks -- Level-Medium
long long PowerOfNumbers(int N, int R) { long mod = 1000000007; if (R == 0) return 1; if (R == 1) return N; if (R % 2 == 0) { long long result = PowerOfNumbers(N, R / 2) % mod; return (result * result) % mod; } else { long long result = PowerOfNumbers(N, (R-1) / 2) % mod; return (result * ((result * N) % mod)) % mod; } }
Given an array arr[] and an integer k where k is smaller than the size of the array, the task is to find the kth smallest element in the given array. Follow up: Don't solve it using the inbuilt sort function.
GeeksForGeeks -- Level-Medium
int KthSmallest(std::vector& arr, int k) { std::priority_queue pq; // insert elements into priority queue who will kindly sort for us for (int i = 0; i < arr.size(); i++) { pq.push(arr[i]); } int result = 0; // [] doesn't exist for pq and because elements are in > order we travel backwards into the pq for k element for (int i = arr.size(); i >= k; i--) { result = pq.top(); pq.pop(); } return result; }
Given an array arr. Find the majority element in the array. If no majority exists, return -1. A majority element in an array is an element that appears strictly more than arr.size()/2 times in the array.
GeeksForGeeks -- Level-Medium
int MajorityElement(std::vector& arr) { std::map map; int result = -1; for (int i = 0; i < arr.size(); i++) { auto it = map.find(arr[i]); if (it != map.end()) // if found { it->second++; } else { map.insert(std::pair (arr[i], 1)); } } for (const auto& pair : map) { if (pair.second > (arr.size() / 2)) return pair.first; } return result; }
Given an array arr, you need to reverse a subarray of that array. The range of this subarray is given by indices L and R (1-based indexing)
GeeksForGeeks -- Level-Basic
std::vectorreverseSubArray(std::vector & arr, int l, int r) { std::vector tempVector; tempVector.reserve(r); for (int i = l-1; i <= r; i++) { int fdsa = arr[i + 1]; arr[i+1] = arr[r-1]; arr[r - 1] = fdsa; r--; // arr[i-1] = tempVector[tempVector.size() - 1]; // tempVector.pop_back(); } return arr; }