Sarah

This is a broad overview of my main projects

My projects were made to help me understand and learn Unreal Engine.


Click a tab to check it!

Work Experience

A little presentation on my work experience

XIX3D

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/

PrecisionOS

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/

Maik

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.

Maik

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

Practice Jam

First big project. This is meant to be a full game project to expand my knowledge of UE

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

Level 1

This is the first level.

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

This is the second level.

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

Level 3

This is the third level.

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

Extent Editor

Some tools to extend the editor through assets and plugins.

Custom Assets

C++

New asset to use in Blueprint

UE Plugin

Slate

Scripted Actions

Custom Asset

https://dev.epicgames.com/community/learning/tutorials/vyKB/unreal-engine-creating-a-custom-asset-type-with-its-own-editor-in-c

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 :)

Create a Tool in the Editor

This is a tool that deletes selected content

Add an argument from the main cpp to our tool so that it can use it.

            TSharedRef FSuperManagerModule::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;

              .
              .
              .
            }
          

Scripted Asset Action

Create a module that extended the content browser for assets. We use that menu to add the new actions on assets.

little side note, now with UE5 you need a BP of the UAssetActionUtility actor for the scripted actions to show :P


Here's what it looks like


This is to get all assets selected in Editor in c++
            TArray SelectedAssetsData = 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

Localisation Plugin

A plugin I made during my time in my previous position.

*published with permission

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

Plugin Presentation

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.

How it was made

This interesting bits on string datas and the parser


The FFileHelper loads the file into a manager TArray. From that Array, I parsed each FString into 3 columns (Key SourceString Comment). This gives me a full row that I can use and split to make sure the data in the array is saved correctly.

            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");
          

Build a tower

A game I made where you need to build the highest possible tower by dropping boxes

Camera Movements

Multicast Events

Material & design

C++

A little presentation of the game logic

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);
          }
        

A little bit of presentation

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.

Bunny hunt

A prototype game I'm making where you play a bunny that needs to feed its family. Mind the farmer family that also need to eat!

Camera Movements

Behaviour Tree

AI

Material & design

Multicast Events

C++

A little presentation of the game logic

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);
          }
        

A little bit of presentation

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

Automatic Tests

Automatic unit tests for better coverage of code and maintenance of functionalities.

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

Automatic Test

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

What are the tests made of?

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

C++ Practice

No real purpose other than mini C++ snippets of code to improve some skills here and there

Binary Search


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;
          }
        

Merge Linked List


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::vector SaveValues;
              

              //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;
          }
        

Indexes of Subarray Sum


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::vector SubarraySumV2(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;
            }
        

Power Of Numbers - Binary Exponentiation


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;
            }
          }
        

Kth Smallest


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;
            }
        

Majority Element


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;
          }
        

Reverse Sub Array


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::vector reverseSubArray(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;
          }