Skip to content

About

An immersive first-person language learning game developed in Unity, integrating interactive 3D environments with real-time vocabulary learning, allowing users to explore and interact with objects to learn their translations in a selected language. The system is designed with potential VR integration for enhanced spatial and contextual learning.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Latest commit

 

History

26 Commits

Folders and files

Repository files navigation

Photobulary — Interactive Language Learning Game

Attribution

This project was originally developed as a senior year team project in cerenkdy/SeniorProject. This repository is a mirror of that work, republished here with full commit history preserved.


A first-person Unity game where players learn vocabulary by exploring an open world, photographing objects, and completing quiz challenges. Every mechanic — photo capture, the album, the quiz, language switching — is connected through a clean, decoupled architecture.


Table of Contents

  1. Project Overview
  2. How to Play
  3. Architecture — The Big Picture
  4. Core System: ServiceLocator
  5. Language System
  6. Player & Camera System
  7. Photo Mode & Object Detection
  8. Photo Capture System
  9. Photo Album System
  10. Quiz System
  11. UI System Overview
  12. Language Selection Screen
  13. Data Flow — Full Walkthrough
  14. How to Add New Content
  15. Scene Setup & Inspector Assignments
  16. Folder Structure
  17. Controls Reference

1. Project Overview

Photobulary is a vocabulary learning game built in Unity. The core idea is simple:

Instead of flashcards, you walk around a real-looking environment and photograph objects to learn their names in a foreign language.

The game supports multiple language pairs (currently English ↔ Turkish) and can be extended to any language by adding a single JSON file — no code changes required.

Key features:

  • First-person exploration with photo capture
  • Photo album with filtering (All / Liked / Quiz Correct / Quiz Wrong)
  • Timed vocabulary quiz: find and photograph specific objects
  • Full language selection before play begins
  • Persistent progress (saved to PlayerPrefs between sessions)

2. How to Play

Normal Mode

Step What happens
Walk around the world WASD + mouse look
Right-click Enter photo mode (camera zoom + crosshair)
Aim at any object Object glows white when it can be photographed
Left-click Take the photo
Photo card appears Shows the word in source + target language
Click Save Photo saved to your album
Click Delete Photo discarded
Press Tab Open/close your photo album

Quiz Mode

Step What happens
Press T (or click the T button) Quiz setup panel opens
Pick how many words (3 / 5 / 10 / All) Select word count
Click Start Quiz begins — HUD shows in top-right, target word at bottom
Find the named object and photograph it Same photo mode as normal
Photo card appears showing ✓ Correct or ✗ Wrong Green or red banner
Click ✓ Save / ! Save / Skip Advance to next word
After all words: results screen Accuracy, duration, weak words

3. Architecture — The Big Picture

The project uses a Service Locator pattern instead of Singletons. Every manager registers itself once, and any other system can find it by type — without hard references between scripts.

ServiceLocator (static registry)
    │
    ├── LanguageManager       — all word lookups, language switching
    ├── PhotoAlbumManager     — stores / retrieves saved photos
    ├── QuizManager           — quiz state machine
    ├── QuizProgressTracker   — persists weak words across sessions
    ├── ObjectDatabase        — object categories, difficulty, points
    └── PlayerProgress        — player XP and learned words

Data flows like this:

World Object (PhotoSubject)
    → ObjectHighlighter (detects it via raycast)
        → PhotoModeController (user presses left-click)
            → PhotoCaptureManager (takes screenshot)
                → PhotoEntry (data container: image + words)
                    → PhotoCardPresenter (shows save/delete UI)
                        → PhotoAlbumManager (saves to PlayerPrefs)
                            → PhotoAlbumPresenter (displays in album)

During a quiz the path forks at PhotoModeController:

PhotoCaptureManager → PhotoEntry
    → QuizManager.EvaluateCapture() (is this the right object?)
        → PhotoCardPresenter (shows ✓/✗ + Save/Skip)
            → QuizManager.CompleteCapture() (updates score, advances word)

4. Core System: ServiceLocator

File: Assets/_Project/Scripts/Core/ServiceLocator.cs

A static dictionary that maps a C# type to a single instance of that type.

// Any manager registers itself in Awake():
ServiceLocator.Register(this);

// Any other script retrieves it safely:
if (ServiceLocator.TryGet<LanguageManager>(out var lm))
    string word = lm.GetSourceWord("cat");

Why not Singletons?

  • Singletons create tight coupling: LanguageManager.Instance.GetWord(...) breaks if the scene doesn't have one.
  • With ServiceLocator, TryGet returns false gracefully — no null reference crashes.
  • Easy to swap implementations (e.g., a mock language manager for unit tests).

Important rule: Every manager must call ServiceLocator.Register(this) in its Awake() method. Systems that depend on other managers must access them in Start() or later (after all Awake() calls have run).


5. Language System

LanguageManager

File: Assets/_Project/Scripts/Data/LanguageManager.cs

The central hub for all word lookups and language switching. It automatically loads every JSON file placed in Assets/_Project/Resources/Languages/.

Key concepts:

Term Meaning
Source language The language the player is actively practising (e.g., English)
Target language The native / translation language (e.g., Turkish)

The player selects these at game start via the Language Selection screen. The choice is saved to PlayerPrefs and restored next session.

Public API:

lm.GetWordPair("cat")        // Returns WordPair { sourceWord="Cat", targetWord="Kedi", ... }
lm.GetSourceWord("cat")      // "Cat"  (language being learned)
lm.GetTargetWord("cat")      // "Kedi" (translation)
lm.SetLanguages("en", "tr")  // Switch language pair at runtime
lm.AvailableLanguages        // Dictionary of all loaded language codes → LanguageData
lm.CurrentSource             // "en"
lm.CurrentTarget             // "tr"

Events:

lm.OnLanguageChanged += (source, target) => { /* refresh UI */ };

LanguageData & WordEntry

File: Assets/_Project/Scripts/Data/LanguageData.cs

Each JSON language file is deserialized into a LanguageData object. It stores a list of WordEntry objects and converts them to a Dictionary at runtime for fast O(1) lookup.

{
  "languageCode": "en",
  "languageName": "English",
  "nativeName": "English",
  "words": [
    {
      "objectId": "cat",
      "word": "Cat",
      "pronunciation": "kæt",
      "audioClipName": "en_cat",
      "exampleSentence": "The cat is sleeping."
    }
  ]
}

Every objectId in the JSON must exactly match the objectId set on the PhotoSubject component in the scene.


Adding a New Language

  1. Copy Assets/_Project/Resources/Languages/_template.json
  2. Fill in languageCode, languageName, nativeName, and all word entries
  3. Place the file in Assets/_Project/Resources/Languages/
  4. Done. The game loads it automatically — no code changes needed.

6. Player & Camera System

PlayerMovement

File: Assets/_Project/Scripts/Gameplay/PlayerMovement.cs

Standard first-person WASD movement using Unity's Character Controller and the new Input System.

Counter-based lock system: Multiple systems can lock movement independently. The player only moves when the lock count is zero.

playerMovement.Lock();    // +1 to lock count (e.g., quiz setup opens)
playerMovement.Unlock();  // -1 from lock count (e.g., quiz setup closes)
playerMovement.IsLocked;  // true if lock count > 0

This prevents conflicts: if both the album AND a dialogue lock movement, unlocking just the album keeps the player frozen until the dialogue also unlocks.

CameraLook

File: Assets/_Project/Scripts/Interactions/CameraLook.cs

Handles mouse-look rotation. Also has Lock() / Unlock() to freeze the camera independently of movement.

CameraZoom

File: Assets/_Project/Scripts/Systems/CameraZoom.cs

Changes the camera field-of-view to simulate a camera lens zoom when entering photo mode.


7. Photo Mode & Object Detection

PhotoModeController

File: Assets/_Project/Scripts/Gameplay/PhotoModeController.cs

The central coordinator for the photo capture flow. It owns all input actions and decides what happens after a photo is taken.

State machine:

[Exploring] ──right-click──► [PhotoMode]
                                  │ left-click (on valid target)
                                  ▼
                            [Capturing] ──► PhotoCaptureManager
                                                │
                                    ┌───────────┴───────────┐
                              Quiz active?              Normal mode?
                                  │                        │
                        EvaluateCapture()           ShowCard(entry)
                        ShowCardForQuiz()

Key responsibilities:

  • Enters/exits photo mode (locks movement, changes sensitivity, zooms camera)
  • Blocks photo mode when album or quiz setup is open
  • After capture: routes the result either to quiz flow or normal card flow
  • Listens to PhotoCardPresenter.OnCardClosed() to restore player control

ObjectHighlighter

File: Assets/_Project/Scripts/Interactions/ObjectHighlighter.cs

Raycasts from the center of the camera every frame while in photo mode. When it hits a PhotoSubject, it applies a white pulse glow effect to signal the object is capturable.

Camera center → Raycast → PhotoSubject?
    YES → SetupRenderers → StartPulse (fade in/out glow animation)
    NO  → ClearHighlight (remove glow, clear current target)

PhotoSubject

File: Assets/_Project/Scripts/Gameplay/PhotoSubject.cs

Attach this script to any world object the player should be able to photograph.

Inspector fields:

Field Purpose
objectId The key that links this object to all data (MUST match JSON entries)
previewSprite Optional UI preview image
highlightRenderer Renderer to apply glow effect to

The objectId is the single link that connects a world object to its language data, quiz questions, album filters, and point values. Getting this right is critical.

PhotoSubject.objectId = "cat"
    → LanguageManager finds "cat" → "Cat" / "Kedi"
    → ObjectDatabase finds "cat" → category, difficulty, points
    → QuizManager matches captures against "cat"
    → PhotoAlbumManager.GetUniqueObjectIds() includes "cat"

8. Photo Capture System

PhotoCaptureManager

File: Assets/_Project/Scripts/Systems/PhotoCaptureManager.cs

Handles the actual screenshot and creates a PhotoEntry data object.

Flow:

  1. TakePhoto(PhotoSubject subject) is called by PhotoModeController
  2. Waits one frame (WaitForEndOfFrame) so Unity finishes rendering
  3. Reads pixels from the screen, crops a square from the center
  4. Looks up the word pair from LanguageManager using subject.ObjectId
  5. Creates a PhotoEntry and fires OnPhotoCaptured

PhotoEntry

File: Assets/_Project/Scripts/Data/PhotoEntry.cs

A serializable data container for one photograph.

public class PhotoEntry
{
    public string objectId;    // "cat" — links to all data systems
    public string nameEN;      // "Cat" (source word at capture time)
    public string nameTR;      // "Kedi" (target word at capture time)
    public byte[] imageBytes;  // PNG bytes of the cropped screenshot
    public string capturedAt;  // "2025-05-07 14:32"
    public bool   isLiked;     // toggled by the heart button in the album
    public bool   quizCorrect; // true if saved as a correct quiz answer
}

PhotoEntry objects are JSON-serialized and stored in PlayerPrefs by PhotoAlbumManager.


9. Photo Album System

PhotoAlbumManager

File: Assets/_Project/Scripts/Gameplay/PhotoAlbumManager.cs

Stores all saved photos in memory and persists them to PlayerPrefs as JSON. Registered with ServiceLocator so any script can access it.

Key methods:

album.SavePhoto(entry)           // Add photo + save to PlayerPrefs
album.RemovePhoto(index)         // Delete by index + save
album.ToggleLike(index)          // Toggle isLiked flag + save
album.GetPhoto(index)            // Retrieve a single entry
album.GetFilteredPhotos(filter)  // Returns a filtered list (see below)
album.GetUniqueObjectIds()       // List of distinct objectIds (used by quiz)
album.Count                      // Total number of saved photos

Filter system (AlbumFilter enum):

Filter Shows
All Every saved photo
Liked Photos where isLiked == true
QuizCorrect Photos where quizCorrect == true
QuizWrong Photos whose objectId is in QuizProgressTracker.WeakWordIds

PhotoAlbumPresenter

File: Assets/_Project/Scripts/UI/UIToolkit/PhotoAlbumPresenter.cs

Renders the album as a two-page book spread using UI Toolkit. Built from PhotoAlbumScreen.uxml and PhotoAlbumScreen.uss.

Layout:

[Filter Tabs (left)]  [← Prev]  [Page Left | Spine | Page Right]  [Next →]
  • Each page holds a 2×2 grid = 4 photos per page, 8 per spread
  • Filter tabs: green ● (All), yellow ★ (Quiz Correct), pink ♥ (Liked), red ! (Quiz Wrong)
  • Clicking a card opens the full-card overlay with photo, words, and a flip-to-details animation
  • Delete (X) button on each card removes it from the album immediately
  • Like (♥) button in the full card overlay toggles the isLiked flag
  • Saves cursor/movement state correctly on open and close

10. Quiz System

The quiz system is split into three scripts for clear separation of concerns.

QuizManager

File: Assets/_Project/Scripts/Gameplay/QuizManager.cs

The core state machine. Manages the word queue, score, and timing.

Lifecycle:

StartQuiz(wordIds)
    → shuffles words, resets counters
    → fires OnQuizWordChanged (UI shows first word)
    → fires OnScoreUpdated (0 correct, 0 wrong, N remaining)

    [player takes a photo]

EvaluateCapture(entry)            ← called by PhotoModeController
    → checks entry.objectId vs current target word
    → stores result as _pendingIsCorrect
    → returns bool (correct or not)

    [photo card shown to player, player clicks Save or Skip]

CompleteCapture(saveToAlbum)      ← called by PhotoCardPresenter
    → if correct:  _correctCount++, optionally saves with quizCorrect=true
    → if wrong:    _wrongCount++, adds objectId to QuizProgressTracker
    → fires OnAnswerProcessed, OnScoreUpdated
    → if more words: BroadcastCurrentWord (UI shows next word)
    → if no more words: EndQuiz()

EndQuiz()
    → creates QuizSessionRecord
    → saves to QuizProgressTracker
    → fires OnQuizCompleted

Events (QuizUIPresenter subscribes to all of these):

OnQuizWordChanged  (string id, string displayWord)   // new target word
OnScoreUpdated     (int correct, int wrong, int remaining)
OnAnswerProcessed  (bool wasCorrect)                 // flash feedback
OnQuizCompleted    (QuizSessionRecord record)         // show results

Why EvaluateCapture + CompleteCapture instead of one method?

The old ProcessCapture did everything in one step (evaluate → save → advance). This skipped the photo card UI. The new two-step design:

  1. EvaluateCapture — judges the answer, stores it as pending
  2. Player sees the card (correct/wrong banner, save/skip choice)
  3. CompleteCapture — commits the result, advances the quiz

This lets the player decide whether to keep the photo even if the quiz moves on.


QuizUIPresenter

File: Assets/_Project/Scripts/UI/UIToolkit/QuizUIPresenter.cs

Builds and manages all quiz UI entirely in C# code (no UXML). Uses a UIDocument with a high Sort Order so it renders on top of everything.

Panels:

Panel When visible
T button (bottom-left) Always, during normal gameplay
Setup backdrop After pressing T, before quiz starts
HUD (top-right) During active quiz
Word prompt (bottom-center) During active quiz
Results backdrop After quiz ends

Setup panel flow:

  1. Calls PhotoAlbumManager.GetUniqueObjectIds() to count available words
  2. Shows word-count selector: 3 / 5 / 10 / All (N)
  3. On Start: shuffles all unique IDs, picks the selected count, calls QuizManager.StartQuiz(list)

IsUIBlocking property: Returns true while setup or results panels are open. PhotoModeController checks this to prevent right-click photo mode from activating during those states.


QuizProgressTracker

File: Assets/_Project/Scripts/Data/QuizProgressTracker.cs

Persists quiz history across game sessions using PlayerPrefs.

What it tracks:

  • WeakWordIds — objectIds the player has answered incorrectly at least once and never corrected
  • Full session history (date, word count, accuracy, duration)

How weak words work:

  • When a wrong answer is committed and the player saves it with ! Save, AddWeakWord(objectId) is called immediately
  • At the end of each quiz, RecordSession() removes correctly answered words from the weak list
  • The ! (Quiz Wrong) tab in the album uses WeakWordIds as its filter

QuizData (data models)

File: Assets/_Project/Scripts/Data/QuizData.cs

Plain serializable data classes used by the quiz system:

QuizSessionRecord  // one completed quiz session (date, counts, duration, word lists)
QuizSaveData       // the root object saved to PlayerPrefs (weak words + session history)

11. UI System Overview

All UI is built with Unity UI Toolkit (UIElements). Each screen has its own UIDocument component on its own GameObject, with a Sort Order that controls which renders on top.

Recommended Sort Order values:

UIDocument Sort Order
Game HUD / Photo Mode Overlay 0
Photo Card 5
Photo Album 5
Quiz UI 10
Language Selection 20

Why separate UIDocuments? Each system can show/hide independently without affecting others. The Sort Order guarantees the quiz panel is always above the album, and the language selection is above everything.

PhotoCardPresenter

File: Assets/_Project/Scripts/UI/UIToolkit/PhotoCardPresenter.cs

Displays the photo result card after a capture. Uses PhotoResultScreen.uxml (overlay + action buttons) and PhotoCard.uxml (the card component with front/back flip).

Normal mode:

  • Save (green) — saves photo to album + awards progress points
  • Delete (red) — discards photo

Quiz mode:

  • ✓ Save (green) — calls QuizManager.CompleteCapture(saveToAlbum: true) with correct answer
  • ! Save (orange) — calls QuizManager.CompleteCapture(saveToAlbum: true) with wrong answer, marks word as weak
  • Skip (grey) — calls QuizManager.CompleteCapture(saveToAlbum: false), no photo saved
  • X / ESC — same as Skip

The card front shows source and target words looked up live from LanguageManager. Clicking the photo flips to the back side showing pronunciation and example sentences.


12. Language Selection Screen

File: Assets/_Project/Scripts/UI/UIToolkit/LanguageSelectionPresenter.cs

Shown automatically at game start. Built entirely in C# code — no UXML needed.

Flow:

  1. Locks player movement and camera on Start()
  2. Reads all available languages from LanguageManager.AvailableLanguages
  3. Builds a button row for "I speak:" and "I am learning:"
  4. Validates that the two selected languages are different
  5. On Start Playing: calls LanguageManager.SetLanguages(native, learning)
  6. Unlocks player, hides itself, sets cursor to locked

The selected language pair is saved to PlayerPrefs automatically inside LanguageManager.SetLanguages() and restored on the next game session.

Inspector setup required:

  • Assign PlayerMovement reference
  • Assign CameraLook reference
  • UIDocument Sort Order = 20

13. Data Flow — Full Walkthrough

Here is the complete path from "player sees an object" to "photo appears in the album":

1. World
   PhotoSubject("cat") exists in the scene with objectId = "cat"

2. ObjectHighlighter (runs every frame in photo mode)
   Raycast hits the cat's collider
   → Glow effect applied to the cat's renderer
   → CurrentSubject = the cat's PhotoSubject

3. PhotoModeController (player presses left-click)
   Checks: _isPhotoMode && HasTarget && !_isCardOpen
   → Calls PhotoCaptureManager.TakePhoto(subject)

4. PhotoCaptureManager (runs coroutine)
   WaitForEndOfFrame → ReadPixels → CropCenter
   → LanguageManager.GetWordPair("cat") → { sourceWord="Cat", targetWord="Kedi" }
   → Creates PhotoEntry { objectId="cat", nameEN="Cat", nameTR="Kedi", imageBytes=... }
   → Fires OnPhotoCaptured(entry)

5. PhotoModeController.OnPhotoCaptured(entry)
   Quiz active? YES → QuizManager.EvaluateCapture(entry) → returns true/false
              NO  → PhotoCardPresenter.ShowCard(entry)

6a. Quiz path
   EvaluateCapture: "cat" == currentTarget? → stores pending result
   PhotoCardPresenter.ShowCardForQuiz(entry, isCorrect)
   → Shows card with green ✓ or red ✗ banner
   → Player clicks Save or Skip
   → CommitQuizCapture(saveToAlbum) → QuizManager.CompleteCapture(save)
     → if correct: PhotoAlbumManager.SavePhoto(entry with quizCorrect=true)
     → if wrong+save: PhotoAlbumManager.SavePhoto(entry) + QuizProgressTracker.AddWeakWord("cat")
     → Score updated → Next word broadcast
   → PhotoModeController.OnCardClosed() → player regains control

6b. Normal path
   PhotoCardPresenter.ShowCard(entry) → player sees photo card
   Save → PhotoAlbumManager.SavePhoto(entry) + PlayerProgress.MarkWordLearned("cat")
   Delete → discard
   → PhotoModeController.OnCardClosed() → player regains control

7. PhotoAlbumManager
   photo appended to _savedPhotos
   Serialized to JSON → PlayerPrefs.SetString("photo_album", json)

8. PhotoAlbumPresenter (when Tab is pressed)
   Loads from PhotoAlbumManager
   Renders 2×2 grid per page in book-spread layout
   Filter tabs let player browse by All / Liked / Correct / Wrong

14. How to Add New Content

Adding a New Photographable Object

  1. Place your 3D model in the scene
  2. Add the PhotoSubject component
  3. Set objectId to a unique lowercase string (e.g., "bicycle")
  4. Make sure the same objectId exists in every language JSON file:
{ "objectId": "bicycle", "word": "Bicycle", "pronunciation": "ˈbaɪsɪkəl", "exampleSentence": "I ride a bicycle." }
  1. Add the object to the correct physics layer so ObjectHighlighter can raycast it
  2. That's it — the photo, album, and quiz systems will all recognize it automatically

Adding a New Language

  1. Duplicate Assets/_Project/Resources/Languages/en.json
  2. Change languageCode, languageName, nativeName
  3. Translate every word entry
  4. Save as Assets/_Project/Resources/Languages/de.json (for German, for example)
  5. Press Play — the language appears automatically in the Language Selection screen

15. Scene Setup & Inspector Assignments

Required GameObjects

GameObject Components
GameSystems PersistentSystems, LanguageManager, PhotoAlbumManager, PlayerProgress, ObjectDatabase
QuizManager QuizManager, QuizProgressTracker
Player PlayerMovement, CameraLook
Player/Camera CameraZoom, ObjectHighlighter, PhotoCaptureManager
PhotoModeController PhotoModeController
UI GameObjects (one per screen) UIDocument + matching Presenter script

UIDocument Sort Orders

GameObject Script Sort Order
PhotoCardUI PhotoCardPresenter 5
PhotoAlbumUI PhotoAlbumPresenter 5
QuizUI QuizUIPresenter 10
LanguageSelectionUI LanguageSelectionPresenter 20

PhotoModeController Inspector

Field Assign
Player Movement PlayerMovement on the Player
Camera Look CameraLook on the Player
Camera Zoom CameraZoom on the Camera
Highlighter ObjectHighlighter on the Camera
Capture Manager PhotoCaptureManager on the Camera
Camera Screen CameraScreenController on a UI GameObject
Photo Card UI PhotoCardPresenter
Album Presenter PhotoAlbumPresenter
Photo Mode Action Right Mouse Button action
Capture Action Left Mouse Button action
Exit Action Escape key action
Album Action Tab key action
Quiz Action T key action

QuizUIPresenter Inspector

Field Assign
Quiz Toggle Action T key InputActionReference
Player Movement PlayerMovement
Camera Look CameraLook

LanguageSelectionPresenter Inspector

Field Assign
Player Movement PlayerMovement
Camera Look CameraLook

16. Folder Structure

Assets/
└── _Project/
    ├── Scripts/
    │   ├── Core/
    │   │   ├── ServiceLocator.cs          ← global manager registry
    │   │   └── PersistentSystems.cs       ← DontDestroyOnLoad guard
    │   │
    │   ├── Data/
    │   │   ├── PhotoEntry.cs              ← one saved photo (data container)
    │   │   ├── LanguageData.cs            ← JSON structure + WordEntry + WordPair
    │   │   ├── LanguageManager.cs         ← loads JSONs, handles language switching
    │   │   ├── ObjectDefinition.cs        ← object category, difficulty, points
    │   │   ├── ObjectDatabase.cs          ← ScriptableObject lookup for objects
    │   │   ├── ObjectCategory.cs          ← category enum
    │   │   ├── PlayerProgress.cs          ← XP, learned words, save/load
    │   │   ├── MovementConfig.cs          ← ScriptableObject for walk/run speeds
    │   │   ├── QuizData.cs                ← QuizSessionRecord + QuizSaveData
    │   │   └── QuizProgressTracker.cs     ← persists weak words across sessions
    │   │
    │   ├── Gameplay/
    │   │   ├── PlayerMovement.cs          ← WASD + counter-lock system
    │   │   ├── PhotoSubject.cs            ← marks an object as photographable
    │   │   ├── PhotoAlbumManager.cs       ← stores/retrieves all saved photos
    │   │   ├── PhotoCapture.cs            ← low-level screen capture utility
    │   │   ├── PhotoModeController.cs     ← photo mode state machine (main coordinator)
    │   │   └── QuizManager.cs             ← quiz state machine
    │   │
    │   ├── Interactions/
    │   │   ├── CameraLook.cs              ← mouse-look rotation
    │   │   └── ObjectHighlighter.cs       ← raycast + glow effect
    │   │
    │   ├── Systems/
    │   │   ├── PhotoCaptureManager.cs     ← screenshot + PhotoEntry creation
    │   │   └── CameraZoom.cs              ← FOV zoom for photo mode
    │   │
    │   └── UI/UIToolkit/
    │       ├── PhotoCardPresenter.cs      ← save/delete card after capture
    │       ├── PhotoAlbumPresenter.cs     ← two-page book album UI
    │       ├── QuizUIPresenter.cs         ← setup / HUD / results quiz panels
    │       ├── LanguageSelectionPresenter.cs ← language picker at game start
    │       └── CameraScreenController.cs  ← camera viewfinder overlay
    │
    ├── UI/
    │   └── Layouts/
    │       ├── UXML/
    │       │   ├── Components/PhotoCard.uxml        ← card front/back component
    │       │   └── Screens/
    │       │       ├── PhotoResultScreen.uxml        ← save/delete overlay
    │       │       ├── PhotoAlbumScreen.uxml         ← book-spread album layout
    │       │       └── PhotoModeOverlay.uxml         ← camera crosshair overlay
    │       └── USS/
    │           ├── Common.uss
    │           ├── PhotoCard.uss
    │           ├── PhotoAlbumScreen.uss
    │           └── PhotoResultScreen.uss
    │
    └── Resources/
        └── Languages/
            ├── en.json                    ← English word list
            └── tr.json                    ← Turkish word list

17. Controls Reference

Input Action
WASD Move
Mouse Look around
Left Shift Sprint
Right Mouse Button Toggle photo mode (zoom in/out)
Left Mouse Button Capture photo (only while in photo mode and aiming at an object)
Tab Open / close photo album
T Open quiz setup
Escape Exit photo mode / close photo card

Technologies

  • Unity (6000.x) — game engine
  • C# — scripting language
  • Unity UI Toolkit — all in-game UI (UIElements, UXML, USS)
  • Unity Input System — all input handling via InputActionReference
  • Unity PlayerPrefs + JsonUtility — save/load persistence
  • Universal Render Pipeline (URP) — rendering

This project was built as part of a senior design course. All systems are designed to be modular and easy to extend — new languages, objects, and UI screens can be added without touching existing code.

About

An immersive first-person language learning game developed in Unity, integrating interactive 3D environments with real-time vocabulary learning, allowing users to explore and interact with objects to learn their translations in a selected language. The system is designed with potential VR integration for enhanced spatial and contextual learning.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages