Launcher fullscreen and auto-scaled (GUI matrix, lists grow with the screen), game menu fully covered and muted, no minimize

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
2026-10-01 20:06:34 +02:00
co-authored by Claude Opus 5.5
parent e844446a21
commit 0b55d270e5
4 changed files with 68 additions and 42 deletions
+5
View File
@@ -26,6 +26,11 @@
Server und Mod müssen beide 2.8 sein (Server: `git pull && docker compose up -d --build`). Server und Mod müssen beide 2.8 sein (Server: `git pull && docker compose up -d --build`).
**Launcher im Vollbild**
- Der MSCO-Launcher füllt das ganze Hauptmenü und **skaliert automatisch** mit der Auflösung; Listen wachsen mit dem
Platz (keine abgeschnittenen Knöpfe mehr). Das Spielmenü dahinter (Logo, Farbauswahl, MP3 …) ist komplett verdeckt
und gesperrt, die Menü-Musik ist stumm. MINIMIZE entfällt
**P2P: Weiterspielen oder neues Spiel** **P2P: Weiterspielen oder neues Spiel**
- In der Host-Lobby: **CONTINUE** (eigenen Spielstand weiterspielen, mit Datum) oder **NEW GAME** (frische Welt für - In der Host-Lobby: **CONTINUE** (eigenen Spielstand weiterspielen, mit Datum) oder **NEW GAME** (frische Welt für
alle). Der bisherige Spielstand wird vorher nach `MSCO_Saves/<Datum>` gesichert (die letzten 10 bleiben), mit alle). Der bisherige Spielstand wird vorher nach `MSCO_Saves/<Datum>` gesichert (die letzten 10 bleiben), mit
+45 -42
View File
@@ -12,10 +12,18 @@ namespace MSCO
{ {
enum Page { P2P, ServerBrowser, Favorites } enum Page { P2P, ServerBrowser, Favorites }
const float Width = 760f; // Vollbild, automatisch skaliert: gebaut für RefHeight x RefWidth, vergrößert/verkleinert passend zur Auflösung
const float Height = 500f; const float RefHeight = 760f;
const float SidebarWidth = 190f; const float RefWidth = 1180f;
const float StatusHeight = 36f; const float MinScale = 0.8f, MaxScale = 1.75f;
const float SidebarWidth = 240f;
const float StatusHeight = 40f;
const float MaxContentWidth = 1100f;
float vw, vh, uiScale = 1f; // virtuelle Bildschirmgröße (Einheiten vor der Skalierung)
float contentHeight = 400f; // Höhe des Inhaltsbereichs - Listen wachsen damit
/// <summary>Höhe für eine Liste: was nach "reserved" für Überschriften/Knöpfe übrig bleibt.</summary>
float ListHeight(float reserved, float min) { return Mathf.Max(min, contentHeight - reserved); }
static readonly Color Background = Hex(0x17, 0x17, 0x1b); static readonly Color Background = Hex(0x17, 0x17, 0x1b);
@@ -34,7 +42,6 @@ namespace MSCO
Page page = Page.P2P; Page page = Page.P2P;
Vector2 profileScroll; Vector2 profileScroll;
bool quitConfirm; bool quitConfirm;
bool minimized;
Vector2 playerScroll; Vector2 playerScroll;
Vector2 serverScroll; Vector2 serverScroll;
bool browserLoaded; bool browserLoaded;
@@ -65,8 +72,8 @@ namespace MSCO
/// <summary>Fläche, die das Fenster gerade belegt (GUI-Koordinaten, oben = 0).</summary> /// <summary>Fläche, die das Fenster gerade belegt (GUI-Koordinaten, oben = 0).</summary>
public Rect ScreenRect { get; private set; } public Rect ScreenRect { get; private set; }
/// <summary>Fenster offen (nicht minimiert) - dann ist das Spielmenü dahinter gesperrt.</summary> /// <summary>Der Launcher füllt im Hauptmenü immer den ganzen Bildschirm - das Spielmenü dahinter ist gesperrt.</summary>
public bool IsOpen { get { return !minimized; } } public bool IsOpen { get { return true; } }
/// <summary>Ist die Maus gerade über dem Fenster bzw. der eingeklappten Leiste?</summary> /// <summary>Ist die Maus gerade über dem Fenster bzw. der eingeklappten Leiste?</summary>
public bool ContainsMouse() public bool ContainsMouse()
@@ -78,24 +85,37 @@ namespace MSCO
public void Draw(MultiplayerManager mp) public void Draw(MultiplayerManager mp)
{ {
EnsureStyles(); EnsureStyles();
uiScale = Mathf.Clamp(Mathf.Min(Screen.height / RefHeight, Screen.width / RefWidth), MinScale, MaxScale);
vw = Screen.width / uiScale;
vh = Screen.height / uiScale;
ScreenRect = new Rect(0, 0, Screen.width, Screen.height);
Matrix4x4 previous = GUI.matrix;
GUI.matrix = Matrix4x4.TRS(Vector3.zero, Quaternion.identity, new Vector3(uiScale, uiScale, 1f));
try { DrawScaled(mp); }
finally { GUI.matrix = previous; }
}
void DrawScaled(MultiplayerManager mp)
{
// Undurchsichtiger Hintergrund über das ganze Spielmenü (Logo, Farbauswahl, Knöpfe ... sind dahinter verborgen)
if (texScreen == null) texScreen = Solid(Hex(0x0d, 0x0d, 0x10));
GUI.DrawTexture(new Rect(0, 0, vw, vh), texScreen);
bool pendingJoin = mp.PendingJoinHost != null && !mp.IsInSession; bool pendingJoin = mp.PendingJoinHost != null && !mp.IsInSession;
bool versionWarning = mp.PendingVersionWarning != null && !mp.IsInSession; bool versionWarning = mp.PendingVersionWarning != null && !mp.IsInSession;
bool disconnected = mp.DisconnectNotice != null && !mp.IsInSession; bool disconnected = mp.DisconnectNotice != null && !mp.IsInSession;
// Getrennt / Neu-Verbinden: eigenes Fenster in der Bildmitte, alles andere gesperrt // Getrennt / Neu-Verbinden: eigenes Fenster in der Bildmitte, alles andere gesperrt
if (disconnected) { DrawPrompt(mp, false); return; } if (disconnected) { DrawPrompt(mp, false); return; }
if (mp.IsReconnecting) { DrawPrompt(mp, true); return; } if (mp.IsReconnecting) { DrawPrompt(mp, true); return; }
if (pendingJoin || versionWarning) minimized = false; // JOIN-Link / Warnung: Fenster zeigen var window = new Rect(0, 0, vw, vh);
if (minimized) { DrawMinimized(mp); return; }
var window = new Rect((Screen.width - Width) / 2f, (Screen.height - Height) / 2f, Width, Height);
ScreenRect = window;
UiTheme.DrawShadow(window);
GUI.Box(window, GUIContent.none, windowBox); GUI.Box(window, GUIContent.none, windowBox);
DrawSidebar(new Rect(window.x, window.y, SidebarWidth, Height - StatusHeight)); DrawSidebar(new Rect(0, 0, SidebarWidth, vh - StatusHeight));
var content = new Rect(window.x + SidebarWidth + 32, window.y + 28, // Inhalt mittig im freien Bereich, nicht beliebig breit (lesbar bleiben)
Width - SidebarWidth - 64, Height - StatusHeight - 52); float free = vw - SidebarWidth;
float width = Mathf.Min(MaxContentWidth, free - 96f);
var content = new Rect(SidebarWidth + (free - width) / 2f, 40f, width, vh - StatusHeight - 72f);
contentHeight = content.height;
GUILayout.BeginArea(content); GUILayout.BeginArea(content);
if (versionWarning) DrawVersionWarning(mp); if (versionWarning) DrawVersionWarning(mp);
else if (pendingJoin) DrawJoinConfirm(mp); else if (pendingJoin) DrawJoinConfirm(mp);
@@ -105,7 +125,7 @@ namespace MSCO
else DrawServerBrowser(mp); else DrawServerBrowser(mp);
GUILayout.EndArea(); GUILayout.EndArea();
DrawStatusBar(mp, new Rect(window.x, window.yMax - StatusHeight, Width, StatusHeight)); DrawStatusBar(mp, new Rect(0, vh - StatusHeight, vw, StatusHeight));
} }
// ---------------------------------------------------------------- Bereiche // ---------------------------------------------------------------- Bereiche
@@ -238,7 +258,7 @@ namespace MSCO
GUI.Label(new Rect(header.x + header.width * 0.62f, header.y, 70, header.height), "TYPE", headerCellStyle); GUI.Label(new Rect(header.x + header.width * 0.62f, header.y, 70, header.height), "TYPE", headerCellStyle);
GUI.Label(new Rect(header.x + header.width * 0.76f, header.y, 50, header.height), "PING", headerCellStyle); GUI.Label(new Rect(header.x + header.width * 0.76f, header.y, 50, header.height), "PING", headerCellStyle);
serverScroll = GUILayout.BeginScrollView(serverScroll, GUILayout.Height(180)); serverScroll = GUILayout.BeginScrollView(serverScroll, GUILayout.Height(ListHeight(230f, 140f)));
if (shownError != null) GUILayout.Label(shownError, textStyle); if (shownError != null) GUILayout.Label(shownError, textStyle);
if (servers.Count == 0) if (servers.Count == 0)
GUILayout.Label(browser.Loading ? "Loading servers..." : all.Count > 0 ? "No servers match your filters." : "No servers online right now.", textStyle); GUILayout.Label(browser.Loading ? "Loading servers..." : all.Count > 0 ? "No servers match your filters." : "No servers online right now.", textStyle);
@@ -366,7 +386,7 @@ namespace MSCO
GUILayout.Space(8); GUILayout.Space(8);
List<Favorites.Favorite> favs = Favorites.All; List<Favorites.Favorite> favs = Favorites.All;
favoritesScroll = GUILayout.BeginScrollView(favoritesScroll, GUILayout.Height(250)); favoritesScroll = GUILayout.BeginScrollView(favoritesScroll, GUILayout.Height(ListHeight(190f, 160f)));
if (favs.Count == 0) if (favs.Count == 0)
GUILayout.Label("No favorites yet. Click the heart next to a server in the Server Browser, or add one by address below.", textStyle); GUILayout.Label("No favorites yet. Click the heart next to a server in the Server Browser, or add one by address below.", textStyle);
Favorites.Favorite remove = null; Favorites.Favorite remove = null;
@@ -582,7 +602,7 @@ namespace MSCO
GUILayout.BeginVertical(rowBox, GUILayout.ExpandWidth(true), GUILayout.MinHeight(150)); GUILayout.BeginVertical(rowBox, GUILayout.ExpandWidth(true), GUILayout.MinHeight(150));
GUILayout.Space(6); GUILayout.Space(6);
GUILayout.Label("PLAYERS " + s.Players + " / " + s.MaxPlayers, captionStyle); GUILayout.Label("PLAYERS " + s.Players + " / " + s.MaxPlayers, captionStyle);
profileScroll = GUILayout.BeginScrollView(profileScroll, GUILayout.Height(130)); profileScroll = GUILayout.BeginScrollView(profileScroll, GUILayout.Height(ListHeight(330f, 130f)));
if (s.PlayerNames == null || s.PlayerNames.Count == 0) if (s.PlayerNames == null || s.PlayerNames.Count == 0)
GUILayout.Label("Nobody online right now.", textStyle); GUILayout.Label("Nobody online right now.", textStyle);
else else
@@ -696,7 +716,7 @@ namespace MSCO
const float PromptWidth = 540f; const float PromptWidth = 540f;
const float PromptHeight = 270f; const float PromptHeight = 270f;
Texture2D texDim, texDanger; Texture2D texDim, texDanger, texScreen;
GUIStyle promptTextStyle; GUIStyle promptTextStyle;
/// <summary> /// <summary>
@@ -713,10 +733,9 @@ namespace MSCO
promptTextStyle.wordWrap = true; promptTextStyle.wordWrap = true;
promptTextStyle.fontSize = 15; promptTextStyle.fontSize = 15;
} }
GUI.DrawTexture(new Rect(0, 0, Screen.width, Screen.height), texDim); GUI.DrawTexture(new Rect(0, 0, vw, vh), texDim);
var r = new Rect((Screen.width - PromptWidth) / 2f, (Screen.height - PromptHeight) / 2f, PromptWidth, PromptHeight); var r = new Rect((vw - PromptWidth) / 2f, (vh - PromptHeight) / 2f, PromptWidth, PromptHeight);
ScreenRect = new Rect(0, 0, Screen.width, Screen.height); // Spielmenü dahinter komplett sperren
UiTheme.DrawShadow(r); UiTheme.DrawShadow(r);
GUI.Box(r, GUIContent.none, windowBox); GUI.Box(r, GUIContent.none, windowBox);
string title = reconnecting string title = reconnecting
@@ -809,7 +828,7 @@ namespace MSCO
GUILayout.Space(16); GUILayout.Space(16);
GUILayout.Label("PLAYERS " + mp.Players.Count + "/" + mp.SessionMaxPlayers, captionStyle); GUILayout.Label("PLAYERS " + mp.Players.Count + "/" + mp.SessionMaxPlayers, captionStyle);
playerScroll = GUILayout.BeginScrollView(playerScroll, GUILayout.Height(170)); playerScroll = GUILayout.BeginScrollView(playerScroll, GUILayout.Height(ListHeight(mp.IsHost ? 330f : 170f, 120f)));
foreach (PlayerInfo p in mp.Players) foreach (PlayerInfo p in mp.Players)
{ {
Rect row = GUILayoutUtility.GetRect(10, 32, GUILayout.ExpandWidth(true)); Rect row = GUILayoutUtility.GetRect(10, 32, GUILayout.ExpandWidth(true));
@@ -848,25 +867,9 @@ namespace MSCO
GUI.Box(r, GUIContent.none, sidebarBox); GUI.Box(r, GUIContent.none, sidebarBox);
Texture2D dot = mp.IsFullyConnected ? texOnline : mp.IsInSession || mp.IsReconnecting ? texPending : texMuted; Texture2D dot = mp.IsFullyConnected ? texOnline : mp.IsInSession || mp.IsReconnecting ? texPending : texMuted;
GUI.DrawTexture(new Rect(r.x + 18, r.y + (r.height - 8) / 2f, 8, 8), dot); GUI.DrawTexture(new Rect(r.x + 18, r.y + (r.height - 8) / 2f, 8, 8), dot);
GUI.Label(new Rect(r.x + 36, r.y, r.width - 180, r.height), mp.Status, statusStyle); GUI.Label(new Rect(r.x + 36, r.y, r.width - 72, r.height), mp.Status, statusStyle);
if (GUI.Button(new Rect(r.xMax - 118, r.y + 6, 106, r.height - 12), "MINIMIZE", tabStyle))
minimized = true;
} }
void DrawMinimized(MultiplayerManager mp)
{
var r = new Rect((Screen.width - 220) / 2f, 0, 220, 30);
ScreenRect = r;
UiTheme.DrawShadow(r);
GUI.Box(r, GUIContent.none, sidebarBox);
GUI.Box(new Rect(r.x + 10, r.yMax - 3, r.width - 20, 3), GUIContent.none, accentBox);
Texture2D dot = mp.IsFullyConnected ? texOnline : mp.IsInSession || mp.IsReconnecting ? texPending : texMuted;
Texture2D logo = Branding.LogoTexture;
if (logo != null) GUI.DrawTexture(new Rect(r.x + 8, r.y + 4, 22, 22), logo);
GUI.DrawTexture(new Rect(r.x + 36, r.y + 11, 8, 8), dot);
if (GUI.Button(new Rect(r.x + 48, r.y, r.width - 48, r.height), "MSCO MULTIPLAYER ▾", tabStyle))
minimized = false;
}
// ---------------------------------------------------------------- Hilfen // ---------------------------------------------------------------- Hilfen
+17
View File
@@ -72,6 +72,23 @@ namespace MSCO
return hidden; return hidden;
} }
/// <summary>
/// Geräusche und Musik des Spiel-Hauptmenüs stummschalten - der MSCO-Launcher deckt das ganze Menü ab.
/// Gibt die Anzahl der stummgeschalteten Quellen zurück.
/// </summary>
public static int MuteMenuAudio()
{
int muted = 0;
foreach (Object o in Object.FindObjectsOfType(typeof(AudioSource)))
{
var src = (AudioSource)o;
if (src == null || src.mute || src.transform.root.name.StartsWith("MSCO")) continue;
src.mute = true;
muted++;
}
return muted;
}
public static void StartGame() public static void StartGame()
{ {
PlayMakerFSM fsm; PlayMakerFSM fsm;
+1
View File
@@ -880,6 +880,7 @@ namespace MSCO
{ {
nextMenuButtonCheck = Time.realtimeSinceStartup + 0.5f; nextMenuButtonCheck = Time.realtimeSinceStartup + 0.5f;
menuButtonsHidden = MenuHelper.HideGameMenuButtons() > 0; menuButtonsHidden = MenuHelper.HideGameMenuButtons() > 0;
MenuHelper.MuteMenuAudio(); // Launcher im Vollbild: Menü-Musik/-Geräusche aus
} }
UpdateOverlayKeys(); UpdateOverlayKeys();
PersistPendingState(); // Kick-Grund / Neu-Verbinden über das Neuladen der Mods hinweg merken PersistPendingState(); // Kick-Grund / Neu-Verbinden über das Neuladen der Mods hinweg merken