connect-the-dots / docs / architecture-spatializer.md
architecture-spatializer.md
Raw

Optional Spatializer Architecture (Option A)

Intent

Keep all 2D levels and logic unchanged while enabling 3D/4D presentation via an optional spatializer. This matches the current Sphere behavior (projected 2D gameplay) and avoids forcing extra layers on 2D content.

Core idea

  • Rules remain in existing BaseLevel subclasses (Standard/Clock/DarkMode/etc).
  • Spatializer exists only when Dimension > 2.
  • The spatializer owns all spatial presentation logic:
    • projection/unprojection
    • visibility/backface logic
    • line projection
    • overlays (equator)
    • rotation/axis input
    • menu preview reset behavior
    • optional feature modules (see "Core + Modifiers inside the Spatializer")

Data model (minimal)

LevelDataSO
  LevelType = Standard | Clock | DarkMode | ...
  Dimension = 2 | 3 | 4
  BoundaryType = Rect | Circle | Custom | ...

The spatializer can validate boundary compatibility (e.g., Sphere expects Circle).

Responsibilities

LevelMgr

  • Spawns dots/lines/obstacles as it does today.
  • Creates rule instance from LevelType.
  • Creates spatializer only if Dimension > 2.
  • Ticks and disposes the spatializer alongside rule logic.

Rules (BaseLevel subclasses)

  • Own progression, win conditions, UI/audio effects.
  • No projection or dimension logic.

Spatializer (new)

  • Caches dot/line local positions.
  • Updates projected Line.Pts each tick.
  • Adjusts dot visibility/colliders based on depth.
  • Manages overlays (equator) and axis hints.
  • Handles spatial rotation input.

Core + Modifiers inside the Spatializer (optional)

You can treat each spatializer as a preset that includes:

  • Core behaviors that are intrinsic to the space (always on):
    • projection/unprojection
    • rotation state and input
    • line projection + local caches
  • Modifiers that are optional and can be toggled per spatializer:
    • equator overlay
    • backface visibility tweaks
    • axis hint / reveal messaging
    • menu preview reset tween

This keeps the "optional spatializer" model while letting each spatializer compose small feature modules internally without exposing modifiers to 2D levels.

Mapping SphereLevel -> Spatializer

Move these from SphereLevel.cs into SphereSpatializer:

  • Projection: ProjectWorldToLocal, ProjectLocalToWorld
  • Dot visibility/backface: UpdateDots, GetBackfaceHidePos
  • Line projection: UpdateLines, ApplyProjectedLinePts
  • Rotation input: UpdateRotation, HandleAxisInput, ShowAxisMessage
  • Equator overlay: EnsureEquatorLine, RefreshEquatorStyle, UpdateEquatorLine, ShowEquator/HideEquator
  • Menu preview reset: ResetMenuPreview, tween handling
  • Init/cache: PrimeSphere, CacheDots, SyncLns

Effects and collisions

  • Collisions remain 2D using projected points.
  • Arrow effects continue to use Line.Pts after connection (no change needed).
  • Obstacles remain 2D overlays in projected space.

Flow diagram

LoadLevelVisuals
  -> spawn boundary/dots/obstacles (unchanged)
  -> rules = LevelFactory.Create(LevelType)
  -> if Dimension > 2: spatializer = SpatializerFactory.Create(Dimension)
       -> spatializer.Init(LevelMgr, LevelDataSO)
  -> rules.Init(LevelMgr, LevelDataSO)
  -> GameEvents.TriggerLevelLoaded

Update
  -> rules.Tick(dt)
  -> spatializer?.Tick(dt)

Connection
  -> LevelMgr.CreateLine(...) adds Line with 2D pts
  -> spatializer keeps Line.Pts updated (projected 2D)
  -> EffectsFactory shows arrow using Line.Pts (unchanged)

Exit
  -> spatializer?.Dispose()
  -> rules.Dispose()
  -> LevelMgr clears visuals (unchanged)

Why this is minimal and clean

  • 2D levels never see the spatializer.
  • All existing gameplay and editor flows remain valid.
  • Spatial complexity is isolated to Dimension > 2 only.