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
42 changes: 42 additions & 0 deletions .github/workflows/docs.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
# Build Jekyll documentation (GitHub Pages source under /docs).
# No secrets required.
name: Documentation

on:
push:
branches: [main, master]
paths:
- 'docs/**'
- '.github/workflows/docs.yml'
pull_request:
paths:
- 'docs/**'
- '.github/workflows/docs.yml'

concurrency:
group: docs-${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true

permissions:
contents: read

jobs:
jekyll-build:
runs-on: ubuntu-latest
defaults:
run:
working-directory: docs

steps:
- name: Checkout
uses: actions/checkout@v4

- name: Setup Ruby
uses: ruby/setup-ruby@v1
with:
ruby-version: '3.3'
bundler-cache: true
working-directory: docs

- name: Jekyll build
run: bundle exec jekyll build --destination ./_site
39 changes: 39 additions & 0 deletions .github/workflows/unity.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
# Unity project: compile + run Edit Mode tests (if any) via GameCI.
# Requires repo secret UNITY_LICENSE — see docs/ci.md
name: Unity

on:
push:
branches: [main, master]
paths:
- 'First Principles/**'
- '.github/workflows/unity.yml'
pull_request:
paths:
- 'First Principles/**'
- '.github/workflows/unity.yml'
workflow_dispatch:

concurrency:
group: unity-${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true

permissions:
contents: read

jobs:
test-editmode:
name: Edit Mode tests
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4

- name: Run tests
uses: game-ci/unity-test-runner@v4
env:
UNITY_LICENSE: ${{ secrets.UNITY_LICENSE }}
with:
projectPath: "First Principles"
unityVersion: 6000.4.0f1
testMode: editmode
36 changes: 36 additions & 0 deletions CREDITS.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
# Credits — *First Principles*

Use this file for **in-app parity**, **App Store Connect** (description / promotional text), and **support pages**. Keep wording accurate; avoid implying endorsement by Apple, Unity, or other third parties.

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

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

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

## Development & support

**First Principles** has been built over **about four years** of design, engineering, and iteration. If the project matters to you, **please support** the team however you can — purchasing or wishlisting on your platform, sharing the game, or following updates — so we can keep improving it. **Thank you.**

*(Use your real App Store / Steam / itch.io / support links on store pages and in promotional copy; they are not duplicated here to avoid stale URLs.)*

## Project

- **Name:** First Principles
- **License:** [Proprietary](LICENSE) — all rights reserved.

## Third-party software (summary)

| Component | Rights / notes |
|-----------|----------------|
| **Unity Engine** | © Unity Technologies. Use of Unity is subject to [Unity’s terms and policies](https://unity.com/legal). “Unity” is a trademark of Unity Technologies. |
| **TextMesh Pro** | Included with Unity; bundled resources may include fonts under OFL or other licenses—see asset folders (e.g. `Assets/TextMesh Pro/`) for license files. |
| **Outfit (font)** | Copyright © The Outfit Project Authors. Licensed under the **SIL Open Font License 1.1** — see `First Principles/Assets/Fonts/Outfit-OFL.txt`. Bundled variable font `Outfit-VariableFont_wght.ttf`; used project-wide via TextMesh Pro after running **First Principles → Fonts → Apply Outfit for all TextMesh Pro** in the editor. |

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

## Contact / support

Link your **support URL** and **privacy policy URL** (if any) in App Store Connect when you submit. Repository and issue-tracker links belong in the store listing, not inside the app, unless you add a dedicated screen.
8 changes: 8 additions & 0 deletions First Principles/Assets/Editor.meta

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

209 changes: 209 additions & 0 deletions First Principles/Assets/Editor/OutfitFontProjectSetup.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,209 @@
#if UNITY_EDITOR
using System.Collections.Generic;
using TMPro;
using UnityEditor;
using UnityEditor.SceneManagement;
using UnityEngine;
using UnityEngine.TextCore.LowLevel;

/// <summary>
/// Generates a TextMeshPro SDF asset from <b>Outfit</b> (Google Fonts / OFL), sets it as the project default,
/// and assigns it to all <see cref="TextMeshProUGUI"/> / <see cref="TextMeshPro"/> in scenes &amp; prefabs.
/// </summary>
public static class OutfitFontProjectSetup
{
public const string TtfPath = "Assets/Fonts/Outfit-VariableFont_wght.ttf";
public const string SdfPath = "Assets/Fonts/Outfit SDF.asset";
const string TmpSettingsPath = "Assets/TextMesh Pro/Resources/TMP Settings.asset";
const string LiberationFallbackPath = "Assets/TextMesh Pro/Resources/Fonts & Materials/LiberationSans SDF.asset";

[MenuItem("First Principles/Fonts/Apply Outfit for all TextMesh Pro")]
public static void GenerateAndApplyFromMenu()
{
if (!GenerateAndApplyAll())
EditorUtility.DisplayDialog("Outfit font", "Setup failed — see Console.", "OK");
else
EditorUtility.DisplayDialog("Outfit font", "Outfit is now the default TMP font and applied across scenes & prefabs.", "OK");
}

/// <summary>Unity Batchmode: <c>-executeMethod OutfitFontProjectSetup.GenerateAndApplyAllBatch</c> (close the editor if it has this project open).</summary>
public static void GenerateAndApplyAllBatch()
{
if (!GenerateAndApplyAll())
EditorApplication.Exit(1);
else
EditorApplication.Exit(0);
}

static bool GenerateAndApplyAll()
{
AssetDatabase.Refresh();

var outfit = GetOrCreateOutfitSdfAsset();
if (outfit == null)
{
Debug.LogError("[Outfit] Could not create TMP font asset. Is the TTF at " + TtfPath + "?");
return false;
}

if (!AssignTmpSettingsDefault(outfit))
return false;

RetargetAllTmpComponents(outfit);

AssetDatabase.SaveAssets();
AssetDatabase.Refresh();
Debug.Log("[Outfit] Font setup complete.");
return true;
}

public static TMP_FontAsset GetOrCreateOutfitSdfAsset()
{
var existing = AssetDatabase.LoadAssetAtPath<TMP_FontAsset>(SdfPath);
if (existing != null)
return existing;

var font = AssetDatabase.LoadAssetAtPath<Font>(TtfPath);
if (font == null)
{
Debug.LogError("[Outfit] Missing font file: " + TtfPath);
return null;
}

// Dynamic atlas — fills glyphs as needed; includeFontData must be on the TTF import.
var asset = TMP_FontAsset.CreateFontAsset(
font,
90,
9,
GlyphRenderMode.SDFAA,
1024,
1024,
AtlasPopulationMode.Dynamic,
true);

if (asset == null)
{
Debug.LogError("[Outfit] TMP_FontAsset.CreateFontAsset returned null.");
return null;
}

asset.name = "Outfit SDF";
AssetDatabase.CreateAsset(asset, SdfPath);

var liberation = AssetDatabase.LoadAssetAtPath<TMP_FontAsset>(LiberationFallbackPath);
if (liberation != null)
{
if (asset.fallbackFontAssetTable == null)
asset.fallbackFontAssetTable = new List<TMP_FontAsset>();
if (!asset.fallbackFontAssetTable.Contains(liberation))
asset.fallbackFontAssetTable.Add(liberation);
EditorUtility.SetDirty(asset);
}

AssetDatabase.SaveAssets();
return asset;
}

static bool AssignTmpSettingsDefault(TMP_FontAsset outfit)
{
var settings = AssetDatabase.LoadAssetAtPath<TMP_Settings>(TmpSettingsPath);
if (settings == null)
{
Debug.LogError("[Outfit] TMP Settings not found at " + TmpSettingsPath);
return false;
}

var so = new SerializedObject(settings);
so.FindProperty("m_defaultFontAsset").objectReferenceValue = outfit;

var fallback = so.FindProperty("m_fallbackFontAssets");
if (fallback != null && fallback.isArray)
{
var liberation = AssetDatabase.LoadAssetAtPath<TMP_FontAsset>(LiberationFallbackPath);
if (liberation != null && liberation != outfit)
{
bool hasLib = false;
for (int i = 0; i < fallback.arraySize; i++)
{
if (fallback.GetArrayElementAtIndex(i).objectReferenceValue == liberation)
hasLib = true;
}

if (!hasLib)
{
int i = fallback.arraySize;
fallback.InsertArrayElementAtIndex(i);
fallback.GetArrayElementAtIndex(i).objectReferenceValue = liberation;
}
}
}

so.ApplyModifiedPropertiesWithoutUndo();
EditorUtility.SetDirty(settings);
return true;
}

static void RetargetAllTmpComponents(TMP_FontAsset outfit)
{
foreach (var guid in AssetDatabase.FindAssets("t:Scene", new[] { "Assets/Scenes" }))
{
string path = AssetDatabase.GUIDToAssetPath(guid);
var scene = EditorSceneManager.OpenScene(path, OpenSceneMode.Single);
bool dirty = false;

foreach (var root in scene.GetRootGameObjects())
{
foreach (var tmp in root.GetComponentsInChildren<TextMeshProUGUI>(true))
{
Undo.RecordObject(tmp, "Outfit font");
tmp.font = outfit;
EditorUtility.SetDirty(tmp);
dirty = true;
}

foreach (var tmp3d in root.GetComponentsInChildren<TextMeshPro>(true))
{
Undo.RecordObject(tmp3d, "Outfit font");
tmp3d.font = outfit;
EditorUtility.SetDirty(tmp3d);
dirty = true;
}
}

if (dirty)
EditorSceneManager.MarkSceneDirty(scene);

EditorSceneManager.SaveScene(scene);
}

foreach (var guid in AssetDatabase.FindAssets("t:Prefab", new[] { "Assets" }))
{
string path = AssetDatabase.GUIDToAssetPath(guid);
if (string.IsNullOrEmpty(path) || path.Contains("PackageCache"))
continue;

var root = PrefabUtility.LoadPrefabContents(path);
try
{
foreach (var tmp in root.GetComponentsInChildren<TextMeshProUGUI>(true))
{
tmp.font = outfit;
EditorUtility.SetDirty(tmp);
}

foreach (var tmp3d in root.GetComponentsInChildren<TextMeshPro>(true))
{
tmp3d.font = outfit;
EditorUtility.SetDirty(tmp3d);
}

PrefabUtility.SaveAsPrefabAsset(root, path);
}
finally
{
PrefabUtility.UnloadPrefabContents(root);
}
}
}
}
#endif
11 changes: 11 additions & 0 deletions First Principles/Assets/Editor/OutfitFontProjectSetup.cs.meta

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

Loading
Loading