Game Programmer

Portfolio by Alexander Englesson

C++  ·  Gameplay Systems  ·  Engine Programming

Alexander Englesson
Game Showcase
5 Projects
Group Projects
5 team games built in Unity and custom C++ engines — platformers, action RPGs, top-down adventures, and more.
View Projects →
Specialisation
In-Game UI Editor
A fully custom, drag-and-drop HUD system built in C++ — movable frames, action bars, spellbook, tooltips, and a live edit mode.
View Specialisation →
Tools
Editor Tools
Custom editor tooling built in C++ — particle emitter previews, VFX curve editors, scene document systems, and in-engine debug overlays.
View Tools →

About Me

Hi! I'm Alexander Englesson, a game programmer based in Stockholm, Sweden, currently studying Game Programming at The Game Assembly

I specialise in C++ game development with hands-on experience building gameplay systems, engine features, HUD/UI, and VFX systems — working in teams alongside designers, artists, and technical artists.

I'm drawn to the systems side of game development — the architecture behind how things work, not just that they work. I like building tools and systems that other people on the team can rely on.

Languages: English  ·  Swedish  ·  Spanish

C++JavaUnity Gameplay ProgrammingEngine Programming HUD / UI SystemsVFX Systems Shader IntegrationPerformance Profiling

Education

The Game Assembly — Stockholm
Game Programming
Sep 2024 – Jun 2027 (ongoing)
C++ and game engine development, gameplay systems, VFX, HUD/UI, team projects with designers and artists.
Lund University — Faculty of Engineering
Electrical Engineering with Automation
Aug 2022 – Dec 2023
Mathematics, linear algebra, calculus, programming, and automation systems.

Experience

Skärgårdspadel Blidö — Stockholm Archipelago
Head Padel Coach & Camp Organiser
Summer 2021 – Present (seasonal)
Organised summer camps for 150–250 participants, coaching children and adults.

Contact

I'm currently studying at The Game Assembly and open to internships, collaborations, and game jams. Feel free to reach out!

📋 Note regarding internship contact As per the agreement between the Games Industry and The Game Assembly, neither student nor company may be in contact with one another regarding internships before April 15. Any internship offers can be made on April 27th, at the earliest.
Download CV
© 2026 Alexander Englesson  ·  Game Programmer  ·  Stockholm

Type
Engine / Tools
Team Size
Duration
Year

Overview

My Role & Responsibilities

Technical Highlights

Screenshots

© 2026 Alexander Englesson  ·  Game Programmer  ·  Stockholm
Specialisation

In-Game UI Editor

10+Draggable Frames
C++Language
TGEEngine
LiveEdit Mode
2026Year

Overview

I built a fully custom, player-configurable HUD system from scratch in C++ using the school's custom game engine (TGE). The system allows players to enter an edit mode and freely drag, resize, and toggle every UI element — player frame, target frame, party frames, buff/debuff frames, action bars, cast bar, and stance bar. Inspired by World of Warcraft's addon system, every frame is a movable object with real-time snap-to-grid and snap-to-element behaviour.

My Role & Responsibilities

I designed and implemented the entire HUD system solo — from the base Button/Frame architecture and drag-drop system, through to every individual UI widget. I built the edit mode panel with its checkbox settings, grid slider, and advanced options toggle. I also implemented the spellbook, action bar per-slot settings popups, party frame configuration, and the spell tooltip system.

HUD / UI SystemsDrag & DropEdit Mode Action BarsBuff / Debuff SystemCast Bar Party FramesSpell TooltipC++TGE

Technical Highlights

Every HUD element inherits from a shared Button base class that handles dragging, visibility, camera binding, and input registration — meaning adding a new movable frame takes minimal code. The edit mode uses a grid snap system with a configurable grid size (25–200px), and an element snap system that detects nearby frame edges and snaps to them automatically. The buff/debuff overlap resolver detects collisions between the buff and debuff frames and pushes them apart using the minimum displacement direction — preventing z-fighting and visual overlap without any manual positioning from the player.

Development Breakdown

Step 01 — Normal View
A Complete In-Game HUD

The normal gameplay view shows the full HUD in action — player and target unit frames with portraits, HP and mana bars, five party frames, two action bars with spell icons and cooldown timers, buff icons with duration countdowns, and a cast bar.

Normal View
Step 02 — Edit Mode
Live Grid, Draggable Frames & Debug Bounds

Pressing the edit mode toggle overlays a purple grid across the screen and draws debug bounding boxes around every HUD element. All frames become draggable — snap-to-grid keeps them aligned, and snap-to-elements lets frames lock to each other's edges.

Edit Mode
Step 03 — Advanced Options
Two-Mode Settings Panel

The HUD Edit Panel has two modes — compact and advanced. In compact mode it shows the most common toggles. Enabling Advanced Options expands the panel and reveals extra sections: Frames (Pet Frame, Raid Frames) and Combat (Cast Bar, Stance Bar, Pet Bar).

Advanced Options
Step 04 — Button Base Class
Shared Architecture for Every HUD Element

Every HUD element inherits from a single Button base class. It handles dragging, hover detection, child propagation, visibility, camera binding, and debug rendering. Adding a new movable frame takes almost no boilerplate code.

Button Base Class Code
Button.hC++
class Button : public InputObserver
{
public:
  void SetDraggable(bool aValue);
  void MoveBy(const Vector2f& delta);
  bool IsDragging() const;
  void SetVisible(bool v) { myIsVisible = v; }
  void AddChild(Button* child);
  Button* GetParent() const;
  virtual void SetCamera(shared_ptr<Camera> cam);
  virtual void RegisterInput() override;
  void DebugDrawBounds(const Color& color) const;
protected:
  void SetPosition2D(const Vector2f& newPos);
  void PropagateMoveToChildren(const Vector2f& delta);
  bool myIsDraggable = false;
  bool myIsDragging  = false;
  Vector2f myDragOffset = { 0.f, 0.f };
  Button* myParent = nullptr;
  vector<Button*> myChildren;
};
Step 05 — Child Propagation
Move Propagates to All Children

When a frame moves, SetPosition2D calculates the delta and calls PropagateMoveToChildren — which recursively repositions every child without touching their text layers. A player frame dragged in edit mode automatically carries its portrait, bars, and name text together as one unit.

Child Propagation
Button.cppC++
void Button::SetPosition2D(const Vector2f& aNewPos)
{
  const Vector2f delta = aNewPos - myPosition;
  myPosition = aNewPos;
  const float z = myTransform.GetPosition().z;
  mySprite.SetPosition({ myPosition.x, myPosition.y, z });
  myTransform.SetPosition({ myPosition.x, myPosition.y, z });
  for (auto& t : myTexts)
    t.SetPosition(t.GetPosition() + delta);
  PropagateMoveToChildren(delta);
}

void Button::PropagateMoveToChildren(const Vector2f& delta)
{
  for (Button* c : myChildren)
  {
    if (!c) continue;
    c->SetPosition2D_NoText(c->GetPosition() + delta);
  }
}
Step 06 — Drag & Drop
Grab-Point-Accurate Dragging

When a draggable frame receives a left-click while hovered, it enters drag mode and stores the cursor offset from the frame centre. Every subsequent hover event moves the frame to cursor minus offset — giving smooth, grab-point-accurate dragging regardless of where you click the frame.

Drag and Drop
Button.cpp — ReceiveInputC++
case InputEvent::Hover:
{
  Vector2f mousePos =
    myCamera->GetScreenToWorldCoordinatesVec2(mouseScreenPos);
  if (myIsDragging)
  {
    SetPosition2D(mousePos - myDragOffset);
    break;
  }
}
case InputEvent::MenuLClick:
{
  if (myIsDraggable && myIsHovered)
  {
    myIsDragging = true;
    myDragOffset = mousePos - myPosition;
  }
}
Step 07 — Grid Snap
Configurable Snap-to-Grid System

When snap-to-grid is enabled, every slot position is rounded to the nearest grid cell before being applied. The grid size is configurable via a live slider (25–200px, snaps to 5px increments). A single ApplySnap function handles all the rounding math.

Grid Snap
ActionBar.cppC++
Vector2f p = GetSlotPos(i);
if (myLayout.snapToGrid)
  p = ApplySnap(p);
slot->SetPosition(p);

Vector2f ActionBar::ApplySnap(const Vector2f& p) const
{
  const float g = std::max(1.f, myLayout.gridSize);
  return {
    std::round(p.x / g) * g,
    std::round(p.y / g) * g
  };
}
s.gridSize = std::round(s.gridSize / 5.f) * 5.f;
Step 08 — Action Bar Editor
Per-Bar Layout Control

Each action bar has its own edit popup — orientation, number of rows, number of icons, icon size, icon padding, and an "Always Show Buttons" toggle. Changes apply immediately. Bars can be dragged freely in edit mode and snap to each other.

Action Bar Popup
Step 09 — Spellbook & Tooltips
Spell Assignment & Rich Tooltips

Clicking an empty action bar slot opens the spellbook popup showing all available spells as icon buttons. Hovering a filled slot shows a rich tooltip: spell name, mana cost, cast time (or "Instant cast"), and a damage description. Spells have cooldowns, cast times, and trigger buffs/debuffs on the HUD when used.

Spell Tooltip

Feature Showcase

Normal View
Normal View
Edit Mode
Edit Mode
Advanced Options
Advanced Options
Normal Options
Normal Options
Button Code
Button Base Class
Spellbook
Spellbook
Tooltip
Spell Tooltip
Action Bar
Action Bar Editor
Grid Snap
Grid Snap
Grab Point
Grab-Point
Move Propagation
Move Propagation
Drag Drop
Drag & Drop
© 2026 Alexander Englesson  ·  Game Programmer  ·  Stockholm
Tools

Particle & VFX Editor

7Spawn Shapes
11Curve Channels
C++Language
PhysXCollision
LiveImGui Editor

Overview

I built a full particle and VFX authoring system in C++ integrated directly into our custom engine (TGE). The system covers everything from the data layer — a serialisable ParticleEmitterData struct with 7 spawn shapes, burst emission, sub-emitters, and world collision — through to a live ImGui editor where artists can edit over-lifetime curves for colour (R/G/B/A independently), size, velocity per axis, and rotation, and see results in-engine immediately.

My Role & Responsibilities

I designed and implemented the entire system — data architecture, serialisation, runtime particle update, ImGui editor UI, sub-emitter support, PhysX collision integration, and shader wiring. The goal was to make it production-usable: artists and designers could open the editor, tune an effect, and see it running in the scene without touching code.

C++ImGuiParticle Systems Curve EditorJSON SerialisationSub-Emitters PhysX RaycastingShader IntegrationVFX WorkflowTGE

Technical Highlights

The core design challenge was keeping the editor and runtime in perfect sync. Every over-lifetime curve is stored as a std::vector<CurveKey> inside ParticleEmitterData, serialised to JSON, and evaluated identically in both editor preview and runtime. The entire data structure is wrapped in a CopyOnWriteWrapper so edits don't cause unnecessary copies until a value actually changes. World collision is handled by casting a PhysX ray from each particle's previous position to its current one every frame — on hit, a configurable decal TGO is spawned at the contact point.

Development Breakdown

Step 01 — First Particle
One Particle, End-to-End

Before any systems existed, I proved the full pipeline end-to-end with a single hardcoded particle. The first non-obvious discovery: sprite size must be written directly into mySize on the instance data every frame — transform matrix scale has no effect on sprites.

ParticleEmitter.cppC++
// Size must go into instance data — NOT the transform
float size = 1.f;
if (!aEmitter.data.sizeOverLifetime.empty())
    size = aEmitter.data.sizeOverLifetime.front().value;

auto spriteInst = std::make_shared<Tga::Sprite3DInstanceData>();
spriteInst->mySize = { size, size }; // ← only way
spriteInst->myColor = { 1.f, 1.f, 1.f, 1.f };
spriteInst->myTransform = t;
Step 02 — Live Editor Preview
Particles Playing Inside the Editor

With one particle working, I integrated live preview into the TGE editor. A critical bug: storing only the VFXInstance shared_ptr lets the owning GameObject go out of scope, leaving an internal pointer dangling and crashing on UpdateTransform(). Storing both together in a PreviewVFXEntry solved it.

PreviewParticleSystem.hC++
struct PreviewVFXEntry
{
    std::shared_ptr<GameObject>  go;
    std::shared_ptr<VFXInstance> vfxInst;
};
Step 03 — Data Architecture
ParticleEmitterData — The Foundation

The entire system is driven by a single ParticleEmitterData struct — spawn shape, rate, burst schedules, gravity, colour ranges, direction spread, collision flags, and sub-emitters. The struct serialises fully to and from JSON.

VFXinfo.hC++
struct ParticleEmitterData
{
    std::string emitterName = "Emitter 0";
    std::vector<ParticleEmitterData> subEmitters;
    float lifetime = 1.f;
    float spawnRate = 10.f;
    float gravityScale = 0.f;
    bool  looping = true;
    enum class SpawnShape : uint8_t
        { Point, Sphere, HollowSphere, Cone, Box, Circle, Edge };
    SpawnShape spawnShape = SpawnShape::Point;
    bool collideWithWorld = false;
    bool spawnDecalOnCollision = false;
    bool dieOnCollision = true;
};
Step 04 — Curve System
11 Independent Over-Lifetime Channels

Rather than simple min/max scalars, I built 11 independent over-lifetime curve channels: colour R, G, B, A, size, velocity X/Y/Z, and rotation. Velocity curves are baked at spawn time against the particle's initial direction vector so direction spread and spawn shape interact correctly without recomputing direction every frame.

ParticleEmitter.cppC++
// Velocity baked at spawn — dir computed once from spawn shape
auto BuildVelocityCurve = [&](
    const std::vector<Tga::CurveKey>& keys,
    float dirComponent) -> std::vector<Curve>
{
    if (!keys.empty())
    {
        std::vector<Curve> out;
        for (auto& k : keys)
            out.push_back({ k.time, k.value * dirComponent });
        return out;
    }
    return {};
};
Step 05 — World Collision & Decals
Per-Particle PhysX Raycasting

When collision is enabled, every live particle casts a PhysX ray from its previous position to its current one each frame. On hit, the particle can die immediately or continue — and if Spawn Decal on Collision is enabled, a configurable TGO asset spawns at the exact contact point.

ParticleEmitter.cpp — Update()C++
physx::PxVec3 origin = { prevPos.x, prevPos.y, prevPos.z };
physx::PxVec3 delta  = { pos.x-prevPos.x,
                          pos.y-prevPos.y,
                          pos.z-prevPos.z };
if (dist > 0.001f)
{
    physx::PxRaycastBuffer hit;
    if (globalCurrentPxScene->raycast(origin, dir, dist, hit))
    {
        if (emitter.data.spawnDecalOnCollision)
            GameWorld::GetInstance().LoadAssetToScene(
                emitter.data.collisionTgoName, hitTransform, true);
        if (emitter.data.dieOnCollision)
            vfx->ForceExpire();
    }
}