Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions CREDITS.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,11 @@ Use this file for **in-app parity**, **App Store Connect** (description / promot

## Developers

**GAME GENESIS** ([itch.io](https://game-genesis.itch.io) · [Rayan Kaissi](https://github.com/rkaissi/)) × **ORCH AEROSPACE** ([orchaerospace.com](https://orchaerospace.com) · [John Wonmo Seong](https://github.com/wonmor))
**ORCH AEROSPACE** ([orchaerospace.com](https://orchaerospace.com) · [John Wonmo Seong](https://github.com/wonmor)) × **GAME GENESIS** ([itch.io](https://game-genesis.itch.io) · [Rayan Kaissi](https://github.com/rkaissi/))

**Proud graduates of Garth Webb Secondary School, Oakville.**

**Copyright © 2022-2026** Game Genesis (Rayan Kaissi), Orch Aerospace (John Wonmo Seong). All rights reserved.
**Copyright © 2022-2026** Orch Aerospace (John Wonmo Seong), Game Genesis (Rayan Kaissi). All rights reserved.

## Development & support

Expand All @@ -32,7 +32,7 @@ Use this file for **in-app parity**, **App Store Connect** (description / promot
| **Nanum Gothic (font)** | Copyright as listed in Nanum Gothic. Licensed under **SIL OFL 1.1** — see `First Principles/Assets/Resources/Fonts/LICENSE-NanumGothic-OFL.txt`. Used as a **fallback** for Korean (and Latin coverage) alongside **Noto** fonts under `Assets/Resources/Fonts/`. |
| **Noto Sans** (Arabic, Devanagari, Bengali, Nastaliq Urdu, SC, JP, KR, etc.) | Copyright © Google Inc. **SIL OFL 1.1** — see `First Principles/Assets/Resources/Fonts/LICENSE-Noto-OFL.txt`. Runtime TextMesh Pro fallbacks. |
| **Outfit (font, optional)** | Copyright © The Outfit Project Authors. **SIL OFL 1.1** — `First Principles/Assets/Fonts/Outfit-OFL.txt`. Optional alternate UI face via **Apply Outfit for all TextMesh Pro**. |
| **Nunito (font)** | Bundled SDF asset under `Assets/Fonts/` (legacy / alternate). |
| **Nunito (font)** | Bundled SDF asset under `First Principles/Assets/Resources/Fonts/` (Menu/Game + runtime-built UI load the same face via `Resources`). |

For other assets under `Assets/`, check any `LICENSE`, `*.txt`, or `Third Party Notices` files next to those assets.

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -116,4 +116,4 @@
<b><size=110%>للمزيد من القراءة</size></b>
<b>docs/math-concepts.md</b> (فهرس)، <b>docs/first-principles-business.md</b>، <b>docs/engineering-math.md</b>، وملفات الامتحانات أعلاه على GitHub Pages.

— © 2022-2026 · GAME GENESIS × ORCH AEROSPACE · First Principles
— © 2022-2026 · ORCH AEROSPACE × GAME GENESIS · First Principles
Original file line number Diff line number Diff line change
Expand Up @@ -20,4 +20,4 @@ V anglické verzi článku níže najdete kompletní glosář §1–13, aerospac
<b><size=110%>Kde číst dál</size></b>
<b>docs/math-concepts.md</b>, <b>docs/derivative-rules.md</b>, <b>docs/definite-indefinite-integrals.md</b>, <b>docs/first-principles-business.md</b>, <b>docs/engineering-math.md</b>, přípravné soubory AP / AMC / TMUA / MAT.

— © 2022-2026 · GAME GENESIS × ORCH AEROSPACE · First Principles
— © 2022-2026 · ORCH AEROSPACE × GAME GENESIS · First Principles
Original file line number Diff line number Diff line change
Expand Up @@ -155,4 +155,4 @@ BC stacks <b>series & Taylor</b>, <b>parametric / polar / vector-valued</b> moti
<b><size=110%>Where to read more</size></b>
<b>docs/math-concepts.md</b> (index), <b>docs/first-principles-business.md</b> (Musk-popularized builder lens ↔ game), <b>docs/competition-math.md</b> &amp; <b>docs/amc-10-12.md</b> (contest / MAA map), <b>docs/engineering-math.md</b> (applied circuits/oscillations), and the exam-prep files above on GitHub Pages.

— © 2022-2026 · GAME GENESIS × ORCH AEROSPACE · First Principles
— © 2022-2026 · ORCH AEROSPACE × GAME GENESIS · First Principles
Original file line number Diff line number Diff line change
Expand Up @@ -135,4 +135,4 @@ Meccanica ed E&amp;M <b>calcolo‑first</b>: \(v = dr/dt\), \(a = dv/dt\), integ
<b><size=110%>Dove leggere di più</size></b>
<b>docs/math-concepts.md</b> (indice), <b>docs/first-principles-business.md</b> (lente builder popolarizzata da Musk ↔ gioco), <b>docs/competition-math.md</b> &amp; <b>docs/amc-10-12.md</b> (mappa concorsi / MAA), <b>docs/engineering-math.md</b> (circuiti/oscillazioni applicate) e i file d’esame sopra su GitHub Pages.

— © 2022-2026 · GAME GENESIS × ORCH AEROSPACE · First Principles
— © 2022-2026 · ORCH AEROSPACE × GAME GENESIS · First Principles
Original file line number Diff line number Diff line change
Expand Up @@ -20,4 +20,4 @@ Pełny glosariusz §1–13, aerospace, „first principles” i mapa egzaminów
<b><size=110%>Gdzie czytać dalej</size></b>
<b>docs/math-concepts.md</b>, <b>docs/derivative-rules.md</b>, <b>docs/definite-indefinite-integrals.md</b>, <b>docs/first-principles-business.md</b>, <b>docs/engineering-math.md</b> oraz pliki przygotowawcze AP / AMC / TMUA / MAT.

— © 2022-2026 · GAME GENESIS × ORCH AEROSPACE · First Principles
— © 2022-2026 · ORCH AEROSPACE × GAME GENESIS · First Principles
Original file line number Diff line number Diff line change
Expand Up @@ -20,4 +20,4 @@
<b><size=110%>Где читать дальше</size></b>
<b>docs/math-concepts.md</b>, <b>docs/derivative-rules.md</b>, <b>docs/definite-indefinite-integrals.md</b>, <b>docs/first-principles-business.md</b>, <b>docs/engineering-math.md</b> и файлы подготовки AP / AMC / TMUA / MAT.

— © 2022-2026 · GAME GENESIS × ORCH AEROSPACE · First Principles
— © 2022-2026 · ORCH AEROSPACE × GAME GENESIS · First Principles
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
{"TestSuite":"","Date":0,"Player":{"Development":false,"ScreenWidth":0,"ScreenHeight":0,"ScreenRefreshRate":0,"Fullscreen":false,"Vsync":0,"AntiAliasing":0,"Batchmode":false,"RenderThreadingMode":"MultiThreaded","MtRendering":false,"GraphicsJobs":false,"GpuSkinning":false,"Platform":"","ColorSpace":"","AnisotropicFiltering":"","BlendWeights":"","GraphicsApi":"","ScriptingBackend":"Mono2x","AndroidTargetSdkVersion":"AndroidApiLevelAuto","AndroidBuildSystem":"Gradle","BuildTarget":"Android","StereoRenderingPath":"MultiPass"},"Hardware":{"OperatingSystem":"","DeviceModel":"","DeviceName":"","ProcessorType":"","ProcessorCount":0,"GraphicsDeviceName":"","SystemMemorySizeMB":0},"Editor":{"Version":"6000.4.0f1","Branch":"6000.4/staging","Changeset":"8cf496087c8f","Date":1773430047},"Dependencies":["com.unity.2d.animation@14.0.3","com.unity.2d.pixel-perfect@5.1.1","com.unity.2d.psdimporter@13.0.2","com.unity.2d.sprite@1.0.0","com.unity.2d.spriteshape@14.0.1","com.unity.2d.tilemap@1.0.0","com.unity.ai.navigation@2.0.11","com.unity.collab-proxy@2.11.4","com.unity.ide.rider@3.0.39","com.unity.ide.visualstudio@2.0.27","com.unity.ide.vscode@1.2.4","com.unity.inputsystem@1.16.0","com.unity.multiplayer.center@1.0.1","com.unity.test-framework@1.6.0","com.unity.timeline@1.8.11","com.unity.ugui@2.0.0","com.unity.modules.accessibility@1.0.0","com.unity.modules.adaptiveperformance@1.0.0","com.unity.modules.ai@1.0.0","com.unity.modules.androidjni@1.0.0","com.unity.modules.animation@1.0.0","com.unity.modules.assetbundle@1.0.0","com.unity.modules.audio@1.0.0","com.unity.modules.cloth@1.0.0","com.unity.modules.director@1.0.0","com.unity.modules.imageconversion@1.0.0","com.unity.modules.imgui@1.0.0","com.unity.modules.jsonserialize@1.0.0","com.unity.modules.particlesystem@1.0.0","com.unity.modules.physics@1.0.0","com.unity.modules.physics2d@1.0.0","com.unity.modules.screencapture@1.0.0","com.unity.modules.terrain@1.0.0","com.unity.modules.terrainphysics@1.0.0","com.unity.modules.tilemap@1.0.0","com.unity.modules.ui@1.0.0","com.unity.modules.uielements@1.0.0","com.unity.modules.umbra@1.0.0","com.unity.modules.unityanalytics@1.0.0","com.unity.modules.unitywebrequest@1.0.0","com.unity.modules.unitywebrequestassetbundle@1.0.0","com.unity.modules.unitywebrequestaudio@1.0.0","com.unity.modules.unitywebrequesttexture@1.0.0","com.unity.modules.unitywebrequestwww@1.0.0","com.unity.modules.vectorgraphics@1.0.0","com.unity.modules.vehicles@1.0.0","com.unity.modules.video@1.0.0","com.unity.modules.vr@1.0.0","com.unity.modules.wind@1.0.0","com.unity.modules.xr@1.0.0","com.unity.modules.subsystems@1.0.0","com.unity.modules.hierarchycore@1.0.0","com.unity.ext.nunit@2.0.5","com.unity.2d.common@13.0.1","com.unity.mathematics@1.3.3","com.unity.collections@6.4.0","com.unity.burst@1.8.28","com.unity.nuget.mono-cecil@1.11.6","com.unity.test-framework.performance@3.2.0"],"Results":[]}

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
{"MeasurementCount":-1}

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

47 changes: 44 additions & 3 deletions First Principles/Assets/Scripts/Functions/FunctionPlotter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,8 @@
* FunctionPlotter.cs — John Seong / First Principles
*
* Maintenance overview:
* • Each Update() calls InitPlotFunction → samples f and numeric f' over [xStart,xEnd].
* • Update() only runs a full replot when parameters change or the Lorenz attractor is animating;
* during the left→right reveal, it only refreshes vertex alpha. (Avoids sampling f/f′ every frame.)
* • Points are in “grid space”: (MapDisplayX(xPlot) + gridOrigin.x, MapDisplayY(yPlot) + gridOrigin.y).
* • To add a new curve: extend FunctionType, EvaluateFunctionY, and UpdateEquationText.
* • LevelManager sets public fields to match LevelDefinition; differentiate=true feeds DerivRendererUI.
Expand Down Expand Up @@ -93,6 +94,9 @@ public class FunctionPlotter : MonoBehaviour
private LineRendererUI lineRenderer;
private DerivRendererUI derivRenderer;

/// <summary>Last <see cref="ComputePlotInvalidationHash"/> after a full <see cref="PlotFunction"/> — skips redundant work in <see cref="Update"/>.</summary>
private int _cachedPlotParamHash = int.MinValue;

[Tooltip("Seconds for the main f and f′ curves to fade in left→right after the graph parameters change.")]
public float graphRevealDurationSeconds = 2.1f;

Expand Down Expand Up @@ -121,10 +125,45 @@ private void OnValidate()
private void Update()
{
UpdateGraphRevealAnimation();
InitPlotFunction();
RefreshGrid();

// Lorenz stage scrolls phase every frame — full resample required.
if (functionType == FunctionType.ChaosLorenzButterflyX)
{
InitPlotFunction();
return;
}

int paramHash = ComputePlotInvalidationHash();
if (paramHash != _cachedPlotParamHash)
{
InitPlotFunction();
return;
}

// Graph reveal: only push fade uniforms + remesh existing points (no ComputeGraph).
if (_graphRevealT01 < 0.999f)
{
if (lineRenderer == null)
lineRenderer = LineRendererUI.FindPrimaryCurve();
if (derivRenderer == null)
derivRenderer = FindAnyObjectByType<DerivRendererUI>();

PushGraphRevealToRenderers();
if (lineRenderer != null)
lineRenderer.SetVerticesDirty();
if (derivRenderer != null && differentiate)
derivRenderer.SetVerticesDirty();
}
}

/// <summary>When this changes, <see cref="PlotFunction"/> must run (same inputs that affect <see cref="ComputeGraph"/>).</summary>
int ComputePlotInvalidationHash() =>
HashCode.Combine(
ComputeGraphRevealParamsHash(),
differentiate,
showWindTunnelBackdrop,
equationExtraSuffix ?? "");

void UpdateGraphRevealAnimation()
{
int h = ComputeGraphRevealParamsHash();
Expand Down Expand Up @@ -410,6 +449,8 @@ private void PlotFunction(FunctionType type)
WindTunnelBackdrop.Sync(gridRt, this);
PushGraphRevealToRenderers();
}

_cachedPlotParamHash = ComputePlotInvalidationHash();
}

public void ComputeGraph(FunctionType functionType, float transA, float transK, float transC, float transD, int power, int baseN)
Expand Down
2 changes: 1 addition & 1 deletion First Principles/Assets/Scripts/Game/LevelDefinition.cs
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ public class LevelDefinition : ScriptableObject
public string levelName = "Stage";
[TextArea(4, 12)]
public string storyText = "Follow the curve. Watch the derivative.";
[Tooltip("Extra seconds the story stays readable after fading in (0 = use LevelManager default).")]
[Tooltip("Extra seconds the story stays readable after fading in (0 = use LevelManager default, ~3.25 s).")]
public float storyPauseSeconds = 0f;

[Header("Graph Parameters (FunctionPlotter)")]
Expand Down
8 changes: 6 additions & 2 deletions First Principles/Assets/Scripts/Game/LevelManager.cs
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,10 @@ public class LevelManager : MonoBehaviour

private const int GameplayStartingScore = 100;
private const int DerivativeTouchPenaltyPoints = 5;
/// <summary>How long the top story banner stays at full opacity after fading in (per-level override via <see cref="LevelDefinition.storyPauseSeconds"/>).</summary>
private const float DefaultStoryBannerReadableSeconds = 3.25f;
/// <summary>Preferred height for the top <c>StoryText</c> block so longer level copy fits above the graph.</summary>
private const float StoryBannerLayoutHeightPx = 400f;

/// <summary>Touch f′ penalty score (starts at <see cref="GameplayStartingScore"/> each level).</summary>
private int gameplayScore = GameplayStartingScore;
Expand Down Expand Up @@ -837,7 +841,7 @@ private void CreateStoryTextIfNeeded()
rt.anchorMax = new Vector2(0.96f, 1f);
rt.pivot = new Vector2(0.5f, 1f);
rt.anchoredPosition = new Vector2(0, -64f);
rt.sizeDelta = new Vector2(0f, 280f);
rt.sizeDelta = new Vector2(0f, StoryBannerLayoutHeightPx);

tmp.color = new Color(1f, 1f, 1f, 0f);

Expand Down Expand Up @@ -2857,7 +2861,7 @@ private void ApplyLevelTheme(LevelDefinition def)
for (int i = 1; i <= popN; i++)
stageTriggerXGrid.Add((i / (float)(popN + 1)) * width);

storyMiddlePauseSeconds = def.storyPauseSeconds > 0.01f ? def.storyPauseSeconds : 1.65f;
storyMiddlePauseSeconds = def.storyPauseSeconds > 0.01f ? def.storyPauseSeconds : DefaultStoryBannerReadableSeconds;

if (gridRenderer != null)
{
Expand Down
25 changes: 21 additions & 4 deletions First Principles/Assets/Scripts/Game/PlayerControllerUI2D.cs
Original file line number Diff line number Diff line change
Expand Up @@ -232,16 +232,19 @@ private void Update()
// Gravity.
velGrid.y -= gravityGridPerSec2 * dt;

Vector2 prevPos = posGrid;
Vector2 nextPos = posGrid;

// Horizontal step + collision.
nextPos.x = posGrid.x + velGrid.x * dt;
ResolveHorizontalPlatforms(ref nextPos);

// Vertical sweep should use Y from before this frame's vertical move but X after horizontal
// (matches diagonal motion; avoids false collisions when strafing along stepped platforms).
Vector2 prevForVerticalSweep = new Vector2(nextPos.x, posGrid.y);

// Vertical step + collision.
nextPos.y = posGrid.y + velGrid.y * dt;
ResolveVerticalPlatforms(prevPos, ref nextPos, ref grounded);
ResolveVerticalPlatforms(prevForVerticalSweep, ref nextPos, ref grounded);

// Hazard check.
if (OverlapsAny(world.hazards, nextPos))
Expand Down Expand Up @@ -353,8 +356,10 @@ private void UpdateDerivativeTouchAndHighlight(float dt)
private void PlayDerivativeLineHitFeedback()
{
bool usedHaptic = derivativeHitHaptic && Application.isMobilePlatform;
#if UNITY_ANDROID || UNITY_IOS
if (usedHaptic)
Handheld.Vibrate();
#endif

// Sound only where vibration isn’t used (no haptic on this platform/path).
if (usedHaptic)
Expand Down Expand Up @@ -534,7 +539,7 @@ private void ResolveHorizontalPlatforms(ref Vector2 pos)
}
}

/// <summary>Land on platform tops when falling; bonk head when rising through thin solids.</summary>
/// <summary>Land on platform tops when falling; bonk head when rising into a true overhead solid.</summary>
private void ResolveVerticalPlatforms(Vector2 prevPos, ref Vector2 pos, ref bool groundedOut)
{
groundedOut = false;
Expand All @@ -551,6 +556,12 @@ private void ResolveVerticalPlatforms(Vector2 prevPos, ref Vector2 pos, ref bool
float pxMax = pos.x + halfW;

const float eps = 0.001f;
// Graph levels use ~1-column × thin-slab platforms along the curve. The slab's bottom (yMin) sits
// under the walkable top (yMax); jumping forward along rising steps hits that underside and used to
// zero vy mid-air. Skip that "bonk" while the feet are still clearly below the step top.
const float maxLedgeSlabThicknessGrid = 0.95f;
const float maxLedgeSlabWidthGrid = 1.28f;
const float feetBelowStepTopClearanceGrid = 0.1f;

foreach (var p in world.platforms)
{
Expand All @@ -569,8 +580,14 @@ private void ResolveVerticalPlatforms(Vector2 prevPos, ref Vector2 pos, ref bool
groundedOut = true;
}
}
else // Rising into platform
else // Rising — only bonk real ceilings, not the underside lip of the next stair tread.
{
float platW = p.xMax - p.xMin;
float platH = p.yMax - p.yMin;
bool thinStairSlab = platW <= maxLedgeSlabWidthGrid && platH <= maxLedgeSlabThicknessGrid;
if (thinStairSlab && nextBottom < p.yMax - feetBelowStepTopClearanceGrid)
continue;

bool crossedBottomSurface = prevTop <= p.yMin + eps && nextTop >= p.yMin - eps;
if (crossedBottomSurface)
{
Expand Down
23 changes: 20 additions & 3 deletions First Principles/Assets/Scripts/UI/UiTypography.cs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
using System;
using TMPro;
using UnityEngine;

Expand All @@ -14,19 +15,35 @@ public static class UiTypography

public static float Scale(float px) => Mathf.Max(1f, Mathf.Round(px * GlobalScale));

/// <summary>Same asset referenced by Menu/Game scenes; must live under a <c>Resources</c> folder for <see cref="Resources.Load"/>.</summary>
const string ProjectPrimaryTmpFontResourcePath = "Fonts/Nunito-VariableFont_wght SDF";

/// <summary>
/// Assigns <see cref="TMP_Settings.defaultFontAsset"/> (e.g. Quicksand after font setup) so runtime-built UI
/// matches the project TMP default instead of whatever happens to be first in the scene.
/// Picks a TMP font for runtime-built UI (level select, overlays, etc.): uses <see cref="TMP_Settings.defaultFontAsset"/>
/// when you have replaced the stock Liberation default (e.g. Quicksand menu command); otherwise loads
/// <b>Resources/<see cref="ProjectPrimaryTmpFontResourcePath"/></b> so level select matches Menu/Game (Nunito in this repo).
/// </summary>
public static void ApplyDefaultFontAsset(TextMeshProUGUI target)
{
if (target == null)
return;
var font = TMP_Settings.defaultFontAsset;
TMP_FontAsset settingsFont = TMP_Settings.defaultFontAsset;
TMP_FontAsset fromResources = Resources.Load<TMP_FontAsset>(ProjectPrimaryTmpFontResourcePath);

TMP_FontAsset font = fromResources;
if (settingsFont != null && !IsLikelyStockLiberationSans(settingsFont))
font = settingsFont;
if (font == null)
font = settingsFont;
if (font == null)
return;

target.font = font;
if (font.material != null)
target.fontSharedMaterial = font.material;
}

static bool IsLikelyStockLiberationSans(TMP_FontAsset f) =>
f != null && f.name != null &&
f.name.IndexOf("Liberation", StringComparison.OrdinalIgnoreCase) >= 0;
}
Original file line number Diff line number Diff line change
Expand Up @@ -1342,6 +1342,33 @@ MonoBehaviour:
- {fileID: 0}
- {fileID: 0}
- {fileID: 0}
- {fileID: 0}
- {fileID: 0}
- {fileID: 0}
- {fileID: 0}
- {fileID: 0}
- {fileID: 0}
- {fileID: 0}
- {fileID: 0}
- {fileID: 0}
- {fileID: 0}
- {fileID: 0}
- {fileID: 0}
- {fileID: 0}
- {fileID: 0}
- {fileID: 0}
- {fileID: 0}
- {fileID: 0}
- {fileID: 0}
- {fileID: 0}
- {fileID: 0}
- {fileID: 0}
- {fileID: 0}
- {fileID: 0}
- {fileID: 0}
- {fileID: 0}
- {fileID: 0}
- {fileID: 0}
m_FontWeightTable:
- regularTypeface: {fileID: 0}
italicTypeface: {fileID: 0}
Expand Down
Loading
Loading