Compare commits

...

23 Commits

Author SHA1 Message Date
Dominik G.
76a513b577 Refaktro funguje lol 2025-11-01 18:15:05 +01:00
Dominik G.
bec90a20a0 Public metody pro vlastní Menu. 2025-11-01 16:32:34 +01:00
Dominik G.
fbe588c854 Low Fix 2025-10-31 14:03:04 +01:00
Dominik G.
4c110f98c0 Nekopletní fix 2025-10-29 19:14:20 +01:00
Dominik G.
cd0ef3c73a Jmenovka 2025-10-29 16:44:55 +01:00
Dominik G.
691c80343e wtf sračka 2025-10-29 15:36:37 +01:00
Dominik G.
2c283c6623 Fix UI 2025-10-28 14:49:02 +01:00
Dominik G.
d0a16d1e44 Marakake Ficx 2025-10-28 12:09:49 +01:00
Dominik G.
e2fa430d50 Marek Fix 2025-10-28 12:04:22 +01:00
0cb9be4070 Revert "online fix"
This reverts commit 3221488a9e.
2025-10-27 15:10:17 +01:00
3221488a9e online fix 2025-10-27 15:06:26 +01:00
43ea25b9e3 online fix 2025-10-27 14:58:20 +01:00
b34e135610 online fix 2025-10-27 14:47:03 +01:00
24a09dac43 online fix 2025-10-27 14:41:22 +01:00
cf44edc19a online fix 2025-10-27 14:34:26 +01:00
3c5507dd87 online fix 2025-10-27 14:18:56 +01:00
9ded503704 online fix 2025-10-27 14:09:52 +01:00
8ea4b173a3 online fix 2025-10-27 13:54:38 +01:00
3ff9819d78 online fix 2025-10-27 13:45:26 +01:00
Dominik G.
94511923d5 Hotovo animace 2025-10-27 13:15:34 +01:00
Dominik G.
2435f0d27f Merge remote-tracking branch 'origin/master' 2025-10-27 12:59:19 +01:00
Dominik G.
352ba8a27b Animace 2+ 2025-10-27 12:58:27 +01:00
Dominik G.
501312e0c2 Animace 2+ 2025-10-27 12:57:52 +01:00
83 changed files with 130400 additions and 15274 deletions

View File

@@ -29,3 +29,8 @@ MonoBehaviour:
SourcePrefabToOverride: {fileID: 0}
SourceHashToOverride: 0
OverridingTargetPrefab: {fileID: 0}
- Override: 0
Prefab: {fileID: 1170732337855516, guid: d086cbb30c7bb4ba3a58cb2024fa0b31, type: 3}
SourcePrefabToOverride: {fileID: 0}
SourceHashToOverride: 0
OverridingTargetPrefab: {fileID: 0}

200
Editor/LobbyPanelBuilder.cs Normal file
View File

@@ -0,0 +1,200 @@
#if UNITY_EDITOR
using TMPro;
using UnityEditor;
using UnityEngine;
using UnityEngine.UI;
namespace MegaKoop.EditorTools
{
internal sealed class LobbyPanelBuilder
{
private readonly Transform _parent;
internal LobbyPanelBuilder(Transform parent)
{
_parent = parent;
}
internal GameObject Build()
{
var panelLobby = UGUIBuilderUtils.CreatePanel(_parent, "Panel_Lobby", new Vector2(1100, 820));
panelLobby.SetActive(false);
var lobbyContainer = UGUIBuilderUtils.CreateVerticalGroup(
panelLobby.transform,
"Lobby_VLayout",
16f,
TextAnchor.UpperCenter,
new RectOffset(24, 24, 24, 24));
var lobbyHeader = UGUIBuilderUtils.CreateHorizontalGroup(
lobbyContainer.transform,
"Lobby_Header",
10f,
TextAnchor.MiddleCenter,
new RectOffset(0, 0, 0, 10));
UGUIBuilderUtils.CreateText(lobbyHeader.transform, "Text_LobbyTitle", "MULTIPLAYER LOBBY", 36, TextAnchor.MiddleLeft, new Color(0.7f, 1f, 0.7f), FontStyles.Bold);
UGUIBuilderUtils.CreateText(lobbyHeader.transform, "Text_Status", "OFFLINE", 18, TextAnchor.MiddleRight, new Color(0.8f, 0.8f, 0.8f), FontStyles.Bold);
var codeGroup = UGUIBuilderUtils.CreateVerticalGroup(
lobbyContainer.transform,
"Lobby_CodeGroup",
8f,
TextAnchor.MiddleCenter,
new RectOffset(10, 10, 10, 10));
UGUIBuilderUtils.CreateText(codeGroup.transform, "Text_LobbyCodeLabel", "LOBBY CODE", 16, TextAnchor.MiddleCenter, new Color(0.8f, 0.8f, 0.8f), FontStyles.Bold);
var codeRow = UGUIBuilderUtils.CreateHorizontalGroup(
codeGroup.transform,
"Lobby_CodeRow",
10f,
TextAnchor.MiddleCenter,
new RectOffset(0, 0, 0, 0));
UGUIBuilderUtils.CreateText(codeRow.transform, "Text_LobbyCodeValue", "------", 44, TextAnchor.MiddleCenter, new Color(0.7f, 1f, 0.7f), FontStyles.Bold);
UGUIBuilderUtils.CreateMenuButton(codeRow.transform, "Button_CopyCode", "COPY");
UGUIBuilderUtils.CreateText(codeGroup.transform, "Text_LobbyCodeHint", "Share this code with friends to invite them", 12, TextAnchor.MiddleCenter, new Color(0.8f, 0.8f, 0.8f), FontStyles.Normal);
var tabs = UGUIBuilderUtils.CreateHorizontalGroup(
lobbyContainer.transform,
"Lobby_Tabs",
0f,
TextAnchor.MiddleCenter,
new RectOffset(0, 0, 0, 0));
UGUIBuilderUtils.CreateMenuButton(tabs.transform, "Button_HostTab", "HOST LOBBY");
UGUIBuilderUtils.CreateMenuButton(tabs.transform, "Button_JoinTab", "JOIN LOBBY");
var joinGroup = UGUIBuilderUtils.CreateVerticalGroup(
lobbyContainer.transform,
"Group_Join",
10f,
TextAnchor.UpperCenter,
new RectOffset(10, 10, 10, 10));
var joinRow = UGUIBuilderUtils.CreateHorizontalGroup(
joinGroup.transform,
"Join_Row",
10f,
TextAnchor.MiddleCenter,
new RectOffset(0, 0, 0, 0));
UGUIBuilderUtils.CreateInputField(joinRow.transform, "Input_LobbyCode", "Enter 6-digit code...");
UGUIBuilderUtils.CreateMenuButton(joinGroup.transform, "Button_Connect", "CONNECT");
var hostGroup = UGUIBuilderUtils.CreateVerticalGroup(
lobbyContainer.transform,
"Group_Host",
10f,
TextAnchor.UpperCenter,
new RectOffset(10, 10, 10, 10));
UGUIBuilderUtils.CreateDropdown(hostGroup.transform, "Dropdown_MaxPlayers", new[] { "2", "3", "4", "8" });
UGUIBuilderUtils.CreateToggle(hostGroup.transform, "Toggle_PublicLobby", "Public", true);
UGUIBuilderUtils.CreateMenuButton(hostGroup.transform, "Button_CreateLobby", "CREATE LOBBY");
var playersHeader = UGUIBuilderUtils.CreateHorizontalGroup(
lobbyContainer.transform,
"Players_Header",
10f,
TextAnchor.MiddleLeft,
new RectOffset(0, 0, 0, 0));
UGUIBuilderUtils.CreateText(playersHeader.transform, "Text_PlayersTitle", "PLAYERS", 20, TextAnchor.MiddleLeft, Color.white, FontStyles.Bold);
UGUIBuilderUtils.CreateText(playersHeader.transform, "Text_PlayerCount", "0/4", 18, TextAnchor.MiddleRight, new Color(0.7f, 1f, 0.7f), FontStyles.Bold);
var scroll = UGUIBuilderUtils.CreateScrollList(
lobbyContainer.transform,
out var playersContent,
"Scroll_Players",
"Viewport",
"Content_PlayersList");
UGUIBuilderUtils.CreateText(lobbyContainer.transform, "Empty_Players", "Waiting for players...", 16, TextAnchor.MiddleCenter, new Color(0.8f, 0.8f, 0.8f), FontStyles.Italic);
var template = new GameObject("PlayerItemTemplate", typeof(RectTransform), typeof(HorizontalLayoutGroup));
template.transform.SetParent(playersContent, false);
var hlg = template.GetComponent<HorizontalLayoutGroup>();
hlg.childAlignment = TextAnchor.MiddleLeft;
hlg.spacing = 12;
hlg.childControlWidth = false;
hlg.childForceExpandWidth = true;
var avatar = UGUIBuilderUtils.CreateImage(template.transform, "Image_Avatar", new Color(0.3f, 0.6f, 0.3f));
var name = UGUIBuilderUtils.CreateText(template.transform, "Text_PlayerName", "Player", 18, TextAnchor.MiddleLeft, Color.white, FontStyles.Bold);
var status = UGUIBuilderUtils.CreateText(template.transform, "Text_PlayerStatus", "NOT READY", 14, TextAnchor.MiddleRight, new Color(0.8f, 0.8f, 0.8f), FontStyles.Bold);
((RectTransform)avatar.transform).sizeDelta = new Vector2(40, 40);
template.SetActive(false);
var hostControls = UGUIBuilderUtils.CreateHorizontalGroup(
lobbyContainer.transform,
"Host_Controls",
10f,
TextAnchor.MiddleCenter,
new RectOffset(0, 0, 0, 0));
UGUIBuilderUtils.CreateMenuButton(hostControls.transform, "Button_InviteFriends", "INVITE FRIENDS");
UGUIBuilderUtils.CreateMenuButton(hostControls.transform, "Button_KickSelected", "KICK SELECTED");
var friendsOverlay = new GameObject("Panel_Friends", typeof(RectTransform), typeof(Image));
friendsOverlay.transform.SetParent(panelLobby.transform, false);
var ovRT = (RectTransform)friendsOverlay.transform;
ovRT.anchorMin = Vector2.zero;
ovRT.anchorMax = Vector2.one;
ovRT.offsetMin = Vector2.zero;
ovRT.offsetMax = Vector2.zero;
var ovImg = friendsOverlay.GetComponent<Image>();
ovImg.color = new Color(0, 0, 0, 0.55f);
friendsOverlay.SetActive(false);
var closeBg = new GameObject("Button_CloseFriendsOverlay", typeof(RectTransform), typeof(Image), typeof(Button));
closeBg.transform.SetParent(friendsOverlay.transform, false);
var closeBgRT = (RectTransform)closeBg.transform;
closeBgRT.anchorMin = Vector2.zero;
closeBgRT.anchorMax = Vector2.one;
closeBgRT.offsetMin = Vector2.zero;
closeBgRT.offsetMax = Vector2.zero;
var closeBgImg = closeBg.GetComponent<Image>();
closeBgImg.color = new Color(0, 0, 0, 0);
var friendsWindow = UGUIBuilderUtils.CreatePanel(friendsOverlay.transform, "Friends_Window", new Vector2(900, 420));
var friendsV = UGUIBuilderUtils.CreateVerticalGroup(
friendsWindow.transform,
"Friends_VLayout",
8f,
TextAnchor.UpperCenter,
new RectOffset(16, 16, 16, 16));
UGUIBuilderUtils.CreateText(friendsV.transform, "Text_FriendsTitle", "INVITE FRIENDS", 24, TextAnchor.MiddleCenter, Color.white, FontStyles.Bold);
UGUIBuilderUtils.CreateText(friendsV.transform, "Text_FriendsHint", "Select a friend to send a Steam invite.", 12, TextAnchor.MiddleCenter, new Color(0.8f, 0.8f, 0.8f), FontStyles.Normal);
UGUIBuilderUtils.CreateScrollList(friendsV.transform, out var friendsContent, "Scroll_Friends", "Viewport", "Content_FriendsList");
var vlgFriends = friendsContent.GetComponent<VerticalLayoutGroup>();
if (vlgFriends)
{
Object.DestroyImmediate(vlgFriends);
}
var gridFriends = friendsContent.gameObject.AddComponent<GridLayoutGroup>();
gridFriends.cellSize = new Vector2(72, 72);
gridFriends.spacing = new Vector2(10, 10);
gridFriends.startAxis = GridLayoutGroup.Axis.Horizontal;
var csfFriends = friendsContent.GetComponent<ContentSizeFitter>();
if (csfFriends == null)
{
csfFriends = friendsContent.gameObject.AddComponent<ContentSizeFitter>();
}
csfFriends.verticalFit = ContentSizeFitter.FitMode.PreferredSize;
UGUIBuilderUtils.CreateText(friendsV.transform, "Empty_Friends", "No friends found.", 14, TextAnchor.MiddleCenter, new Color(0.8f, 0.8f, 0.8f), FontStyles.Italic);
var friendsFooter = UGUIBuilderUtils.CreateHorizontalGroup(
friendsV.transform,
"Friends_Footer",
8f,
TextAnchor.MiddleCenter,
new RectOffset(0, 0, 0, 0));
UGUIBuilderUtils.CreateMenuButton(friendsFooter.transform, "Button_BackFromFriends", "BACK");
UGUIBuilderUtils.CreateMenuButton(lobbyContainer.transform, "Button_ToggleReady", "TOGGLE READY");
var footer = UGUIBuilderUtils.CreateHorizontalGroup(
lobbyContainer.transform,
"Lobby_Footer",
10f,
TextAnchor.MiddleCenter,
new RectOffset(0, 0, 0, 0));
UGUIBuilderUtils.CreateMenuButton(footer.transform, "Button_StartGame", "START GAME");
UGUIBuilderUtils.CreateMenuButton(footer.transform, "Button_LeaveLobby", "LEAVE LOBBY");
UGUIBuilderUtils.CreateMenuButton(footer.transform, "Button_BackFromLobby", "BACK TO MENU");
return panelLobby;
}
}
}
#endif

View File

@@ -0,0 +1,2 @@
fileFormatVersion: 2
guid: 46d584193d4545f45b0a8a344fa11093

View File

@@ -0,0 +1,40 @@
#if UNITY_EDITOR
using TMPro;
using UnityEngine;
using UnityEngine.UI;
namespace MegaKoop.EditorTools
{
internal sealed class MainMenuPanelBuilder
{
private readonly Transform _parent;
internal MainMenuPanelBuilder(Transform parent)
{
_parent = parent;
}
internal GameObject Build()
{
var panelMain = UGUIBuilderUtils.CreatePanel(_parent, "Panel_MainMenu", new Vector2(900, 800));
var mainContainer = UGUIBuilderUtils.CreateVerticalGroup(
panelMain.transform,
"Main_VLayout",
20f,
TextAnchor.MiddleCenter,
new RectOffset(30, 30, 30, 30));
UGUIBuilderUtils.CreateText(mainContainer.transform, "Text_Title", "MEGA KOOP", 70, TextAnchor.MiddleCenter, Color.white, FontStyles.Bold);
UGUIBuilderUtils.CreateText(mainContainer.transform, "Text_Subtitle", "CO-OP ADVENTURE", 20, TextAnchor.MiddleCenter, new Color(0.8f, 0.8f, 0.8f), FontStyles.Normal);
UGUIBuilderUtils.CreateSpacer(mainContainer.transform, 20f);
UGUIBuilderUtils.CreateMenuButton(mainContainer.transform, "Button_Multiplayer", "MULTIPLAYER");
UGUIBuilderUtils.CreateMenuButton(mainContainer.transform, "Button_Settings", "SETTINGS");
UGUIBuilderUtils.CreateMenuButton(mainContainer.transform, "Button_Quit", "QUIT GAME", isDanger: true);
return panelMain;
}
}
}
#endif

View File

@@ -0,0 +1,2 @@
fileFormatVersion: 2
guid: c43d178d57dd2ae4db117649aad9e5b7

View File

@@ -0,0 +1,67 @@
#if UNITY_EDITOR
using TMPro;
using UnityEngine;
using UnityEngine.UI;
namespace MegaKoop.EditorTools
{
internal sealed class SettingsPanelBuilder
{
private readonly Transform _parent;
internal SettingsPanelBuilder(Transform parent)
{
_parent = parent;
}
internal GameObject Build()
{
var panelSettings = UGUIBuilderUtils.CreatePanel(_parent, "Panel_Settings", new Vector2(900, 800));
panelSettings.SetActive(false);
var settingsContainer = UGUIBuilderUtils.CreateVerticalGroup(
panelSettings.transform,
"Settings_VLayout",
16f,
TextAnchor.UpperCenter,
new RectOffset(30, 30, 30, 30));
UGUIBuilderUtils.CreateText(settingsContainer.transform, "Text_SettingsTitle", "SETTINGS", 48, TextAnchor.MiddleCenter, Color.white, FontStyles.Bold);
UGUIBuilderUtils.CreateSpacer(settingsContainer.transform, 10f);
var gfxGroup = UGUIBuilderUtils.CreateVerticalGroup(
settingsContainer.transform,
"Graphics_Group",
10f,
TextAnchor.UpperLeft,
new RectOffset(10, 10, 10, 10));
UGUIBuilderUtils.CreateText(gfxGroup.transform, "Text_Graphics", "Graphics", 24, TextAnchor.MiddleLeft, Color.white, FontStyles.Bold);
UGUIBuilderUtils.CreateDropdown(gfxGroup.transform, "Dropdown_Quality", new[] { "Low", "Medium", "High", "Ultra" });
UGUIBuilderUtils.CreateToggle(gfxGroup.transform, "Toggle_Fullscreen", "Fullscreen", true);
UGUIBuilderUtils.CreateDropdown(gfxGroup.transform, "Dropdown_Resolution", new[] { "1280x720", "1920x1080", "2560x1440", "3840x2160" });
var audioGroup = UGUIBuilderUtils.CreateVerticalGroup(
settingsContainer.transform,
"Audio_Group",
10f,
TextAnchor.UpperLeft,
new RectOffset(10, 10, 10, 10));
UGUIBuilderUtils.CreateText(audioGroup.transform, "Text_Audio", "Audio", 24, TextAnchor.MiddleLeft, Color.white, FontStyles.Bold);
UGUIBuilderUtils.CreateLabeledSlider(audioGroup.transform, "Master Volume", "Slider_MasterVolume", 0, 100, 100);
UGUIBuilderUtils.CreateLabeledSlider(audioGroup.transform, "Music Volume", "Slider_MusicVolume", 0, 100, 80);
UGUIBuilderUtils.CreateLabeledSlider(audioGroup.transform, "SFX Volume", "Slider_SFXVolume", 0, 100, 100);
var buttonsRow = UGUIBuilderUtils.CreateHorizontalGroup(
settingsContainer.transform,
"Settings_Buttons",
10f,
TextAnchor.MiddleCenter,
new RectOffset(0, 0, 0, 0));
UGUIBuilderUtils.CreateMenuButton(buttonsRow.transform, "Button_ApplySettings", "APPLY");
UGUIBuilderUtils.CreateMenuButton(buttonsRow.transform, "Button_BackFromSettings", "BACK");
return panelSettings;
}
}
}
#endif

View File

@@ -0,0 +1,2 @@
fileFormatVersion: 2
guid: 1f247737932b5ac4f83890f357d265f6

432
Editor/UGUIBuilderUtils.cs Normal file
View File

@@ -0,0 +1,432 @@
#if UNITY_EDITOR
using System.Linq;
using TMPro;
using UnityEditor;
using UnityEngine;
using UnityEngine.UI;
namespace MegaKoop.EditorTools
{
internal static class UGUIBuilderUtils
{
internal static GameObject CreatePanel(Transform parent, string name, Vector2 size)
{
var go = new GameObject(name, typeof(RectTransform), typeof(Image));
go.transform.SetParent(parent, false);
var rt = (RectTransform)go.transform;
rt.anchorMin = rt.anchorMax = new Vector2(0.5f, 0.5f);
rt.pivot = new Vector2(0.5f, 0.5f);
rt.sizeDelta = size;
var img = go.GetComponent<Image>();
img.color = new Color(0.13f, 0.13f, 0.13f, 0.95f);
return go;
}
internal static GameObject CreateVerticalGroup(Transform parent, string name, float spacing, TextAnchor align, RectOffset padding)
{
var go = new GameObject(name, typeof(RectTransform), typeof(VerticalLayoutGroup), typeof(ContentSizeFitter));
go.transform.SetParent(parent, false);
var rt = (RectTransform)go.transform;
rt.anchorMin = new Vector2(0, 0);
rt.anchorMax = new Vector2(1, 1);
rt.offsetMin = Vector2.zero;
rt.offsetMax = Vector2.zero;
var vlg = go.GetComponent<VerticalLayoutGroup>();
vlg.spacing = spacing;
vlg.childAlignment = align;
vlg.padding = padding;
vlg.childForceExpandWidth = true;
vlg.childControlWidth = true;
vlg.childControlHeight = false;
var fitter = go.GetComponent<ContentSizeFitter>();
fitter.verticalFit = ContentSizeFitter.FitMode.PreferredSize;
fitter.horizontalFit = ContentSizeFitter.FitMode.Unconstrained;
return go;
}
internal static GameObject CreateHorizontalGroup(Transform parent, string name, float spacing, TextAnchor align, RectOffset padding)
{
var go = new GameObject(name, typeof(RectTransform), typeof(HorizontalLayoutGroup));
go.transform.SetParent(parent, false);
var rt = (RectTransform)go.transform;
rt.anchorMin = new Vector2(0, 0);
rt.anchorMax = new Vector2(1, 0);
rt.pivot = new Vector2(0.5f, 0.5f);
rt.sizeDelta = new Vector2(0, 60);
var hlg = go.GetComponent<HorizontalLayoutGroup>();
hlg.spacing = spacing;
hlg.childAlignment = align;
hlg.padding = padding;
hlg.childControlWidth = true;
hlg.childForceExpandWidth = true;
return go;
}
internal static GameObject CreateText(Transform parent, string name, string text, int fontSize, TextAnchor anchor, Color color, FontStyles fontStyle)
{
var go = new GameObject(name, typeof(RectTransform), typeof(TextMeshProUGUI), typeof(LayoutElement));
go.transform.SetParent(parent, false);
var tmp = go.GetComponent<TextMeshProUGUI>();
tmp.text = text;
tmp.fontSize = fontSize;
tmp.color = color;
tmp.alignment = MapAlignment(anchor);
tmp.fontStyle = fontStyle;
tmp.enableWordWrapping = false;
tmp.raycastTarget = false;
if (TMP_Settings.defaultFontAsset != null)
{
tmp.font = TMP_Settings.defaultFontAsset;
}
var le = go.GetComponent<LayoutElement>();
le.minHeight = Mathf.Max(24, fontSize + 12);
var rt = (RectTransform)go.transform;
rt.sizeDelta = new Vector2(0, fontSize + 20);
return go;
}
private static TextAlignmentOptions MapAlignment(TextAnchor anchor)
{
switch (anchor)
{
case TextAnchor.UpperLeft: return TextAlignmentOptions.TopLeft;
case TextAnchor.UpperCenter: return TextAlignmentOptions.Top;
case TextAnchor.UpperRight: return TextAlignmentOptions.TopRight;
case TextAnchor.MiddleLeft: return TextAlignmentOptions.MidlineLeft;
case TextAnchor.MiddleCenter: return TextAlignmentOptions.Midline;
case TextAnchor.MiddleRight: return TextAlignmentOptions.MidlineRight;
case TextAnchor.LowerLeft: return TextAlignmentOptions.BottomLeft;
case TextAnchor.LowerCenter: return TextAlignmentOptions.Bottom;
case TextAnchor.LowerRight: return TextAlignmentOptions.BottomRight;
default: return TextAlignmentOptions.Center;
}
}
internal static GameObject CreateMenuButton(Transform parent, string name, string label, bool isDanger = false)
{
var go = new GameObject(name, typeof(RectTransform), typeof(Image), typeof(Button));
go.transform.SetParent(parent, false);
var img = go.GetComponent<Image>();
img.color = isDanger ? new Color(0.5f, 0.15f, 0.15f, 0.9f) : new Color(0.25f, 0.5f, 0.25f, 0.9f);
var rt = (RectTransform)go.transform;
rt.sizeDelta = new Vector2(0, 48);
var leBtn = go.AddComponent<LayoutElement>();
leBtn.flexibleWidth = 1;
leBtn.minHeight = 48;
var text = CreateText(go.transform, "Text", label, 20, TextAnchor.MiddleCenter, Color.white, FontStyles.Bold);
var textRT = (RectTransform)text.transform;
textRT.anchorMin = new Vector2(0, 0);
textRT.anchorMax = new Vector2(1, 1);
textRT.offsetMin = Vector2.zero;
textRT.offsetMax = Vector2.zero;
return go;
}
internal static GameObject CreateInputField(Transform parent, string name, string placeholder)
{
var go = new GameObject(name, typeof(RectTransform), typeof(Image), typeof(TMP_InputField));
go.transform.SetParent(parent, false);
var img = go.GetComponent<Image>();
img.color = new Color(0.1f, 0.1f, 0.1f, 0.9f);
var rt = (RectTransform)go.transform;
rt.sizeDelta = new Vector2(600, 50);
var viewport = new GameObject("TextViewport", typeof(RectTransform));
viewport.transform.SetParent(go.transform, false);
var viewportRT = (RectTransform)viewport.transform;
viewportRT.anchorMin = new Vector2(0, 0);
viewportRT.anchorMax = new Vector2(1, 1);
viewportRT.offsetMin = new Vector2(10, 6);
viewportRT.offsetMax = new Vector2(-10, -6);
var text = CreateText(viewport.transform, "Text", string.Empty, 20, TextAnchor.MiddleLeft, Color.white, FontStyles.Normal);
var placeholderGO = CreateText(viewport.transform, "Placeholder", placeholder, 18, TextAnchor.MiddleLeft, new Color(0.7f, 0.7f, 0.7f), FontStyles.Italic);
var input = go.GetComponent<TMP_InputField>();
input.textViewport = viewportRT;
input.textComponent = text.GetComponent<TextMeshProUGUI>();
input.placeholder = placeholderGO.GetComponent<TextMeshProUGUI>();
var tRT = (RectTransform)text.transform;
tRT.anchorMin = new Vector2(0, 0);
tRT.anchorMax = new Vector2(1, 1);
tRT.offsetMin = new Vector2(10, 0);
tRT.offsetMax = new Vector2(-10, 0);
var pRT = (RectTransform)placeholderGO.transform;
pRT.anchorMin = new Vector2(0, 0);
pRT.anchorMax = new Vector2(1, 1);
pRT.offsetMin = new Vector2(10, 0);
pRT.offsetMax = new Vector2(-10, 0);
return go;
}
internal static GameObject CreateDropdown(Transform parent, string name, string[] options)
{
var go = new GameObject(name, typeof(RectTransform), typeof(Image), typeof(TMP_Dropdown));
go.transform.SetParent(parent, false);
var img = go.GetComponent<Image>();
img.color = new Color(0.2f, 0.2f, 0.2f, 0.9f);
var rt = (RectTransform)go.transform;
rt.sizeDelta = new Vector2(600, 50);
var caption = CreateText(go.transform, "Label", options.FirstOrDefault() ?? "Option", 18, TextAnchor.MiddleLeft, Color.white, FontStyles.Normal);
var arrow = CreateText(go.transform, "Arrow", "▼", 18, TextAnchor.MiddleRight, Color.white, FontStyles.Bold);
var template = new GameObject("Template", typeof(RectTransform), typeof(Image), typeof(ScrollRect));
template.transform.SetParent(go.transform, false);
var templateRT = (RectTransform)template.transform;
templateRT.anchorMin = new Vector2(0, 0);
templateRT.anchorMax = new Vector2(1, 0);
templateRT.pivot = new Vector2(0.5f, 1f);
templateRT.sizeDelta = new Vector2(0, 220);
template.SetActive(false);
var templateImg = template.GetComponent<Image>();
templateImg.color = new Color(0.1f, 0.1f, 0.1f, 0.95f);
var tplCanvas = template.GetComponent<Canvas>();
if (tplCanvas == null)
{
tplCanvas = template.AddComponent<Canvas>();
}
tplCanvas.overrideSorting = true;
tplCanvas.sortingOrder = 5000;
if (!template.GetComponent<GraphicRaycaster>())
{
template.AddComponent<GraphicRaycaster>();
}
var viewport = new GameObject("Viewport", typeof(RectTransform), typeof(Mask), typeof(Image));
viewport.transform.SetParent(template.transform, false);
var viewportRT = (RectTransform)viewport.transform;
viewportRT.anchorMin = new Vector2(0, 0);
viewportRT.anchorMax = new Vector2(1, 1);
viewportRT.offsetMin = Vector2.zero;
viewportRT.offsetMax = Vector2.zero;
var vImg = viewport.GetComponent<Image>();
vImg.color = new Color(0, 0, 0, 0.2f);
vImg.raycastTarget = false;
var mask = viewport.GetComponent<Mask>();
mask.showMaskGraphic = false;
var content = new GameObject("Content", typeof(RectTransform), typeof(VerticalLayoutGroup));
content.transform.SetParent(viewport.transform, false);
var contentRT = (RectTransform)content.transform;
contentRT.anchorMin = new Vector2(0, 1);
contentRT.anchorMax = new Vector2(1, 1);
contentRT.pivot = new Vector2(0.5f, 1f);
var vlg = content.GetComponent<VerticalLayoutGroup>();
vlg.spacing = 4;
vlg.childForceExpandWidth = true;
vlg.childControlHeight = true;
var item = new GameObject("Item", typeof(RectTransform), typeof(Toggle));
item.transform.SetParent(content.transform, false);
var itemRT = (RectTransform)item.transform;
itemRT.sizeDelta = new Vector2(0, 30);
var itemBG = new GameObject("Item Background", typeof(RectTransform), typeof(Image));
itemBG.transform.SetParent(item.transform, false);
var itemBGImg = itemBG.GetComponent<Image>();
itemBGImg.color = new Color(0.2f, 0.2f, 0.2f, 0.9f);
var itemCheck = new GameObject("Item Checkmark", typeof(RectTransform), typeof(Image));
itemCheck.transform.SetParent(itemBG.transform, false);
var itemCheckImg = itemCheck.GetComponent<Image>();
itemCheckImg.color = new Color(0.7f, 1f, 0.7f, 1f);
var itemLabelGO = CreateText(item.transform, "Item Label", "Option", 18, TextAnchor.MiddleLeft, Color.white, FontStyles.Normal);
var bgRT = (RectTransform)itemBG.transform;
bgRT.anchorMin = new Vector2(0, 0);
bgRT.anchorMax = new Vector2(1, 1);
bgRT.offsetMin = Vector2.zero;
bgRT.offsetMax = Vector2.zero;
var ckRT = (RectTransform)itemCheck.transform;
ckRT.anchorMin = new Vector2(0, 0.5f);
ckRT.anchorMax = new Vector2(0, 0.5f);
ckRT.pivot = new Vector2(0, 0.5f);
ckRT.sizeDelta = new Vector2(18, 18);
ckRT.anchoredPosition = new Vector2(6, 0);
var itemLabelRT = (RectTransform)itemLabelGO.transform;
itemLabelRT.anchorMin = new Vector2(0, 0);
itemLabelRT.anchorMax = new Vector2(1, 1);
itemLabelRT.offsetMin = new Vector2(28, 0);
itemLabelRT.offsetMax = new Vector2(-6, 0);
var toggle = item.GetComponent<Toggle>();
toggle.targetGraphic = itemBGImg;
toggle.graphic = itemCheckImg;
var dropdown = go.GetComponent<TMP_Dropdown>();
dropdown.template = templateRT;
dropdown.captionText = caption.GetComponent<TextMeshProUGUI>();
dropdown.itemText = itemLabelGO.GetComponent<TextMeshProUGUI>();
dropdown.options = options.Select(o => new TMP_Dropdown.OptionData(o)).ToList();
dropdown.alphaFadeSpeed = 0.15f;
dropdown.RefreshShownValue();
var scrollRect = template.GetComponent<ScrollRect>();
scrollRect.viewport = viewportRT;
scrollRect.content = (RectTransform)content.transform;
scrollRect.horizontal = false;
scrollRect.vertical = true;
return go;
}
internal static GameObject CreateSlider(Transform parent, string name, float min, float max, float value)
{
var go = new GameObject(name, typeof(RectTransform), typeof(Slider));
go.transform.SetParent(parent, false);
var slider = go.GetComponent<Slider>();
slider.minValue = min;
slider.maxValue = max;
slider.value = value;
slider.direction = Slider.Direction.LeftToRight;
slider.wholeNumbers = true;
var rt = (RectTransform)go.transform;
rt.sizeDelta = new Vector2(600, 30);
var background = new GameObject("Background", typeof(RectTransform), typeof(Image));
background.transform.SetParent(go.transform, false);
var bgRT = (RectTransform)background.transform;
bgRT.anchorMin = new Vector2(0, 0.25f);
bgRT.anchorMax = new Vector2(1, 0.75f);
bgRT.offsetMin = Vector2.zero;
bgRT.offsetMax = Vector2.zero;
var bgImg = background.GetComponent<Image>();
bgImg.color = new Color(0.1f, 0.1f, 0.1f, 1f);
var fillArea = new GameObject("Fill Area", typeof(RectTransform));
fillArea.transform.SetParent(go.transform, false);
var faRT = (RectTransform)fillArea.transform;
faRT.anchorMin = new Vector2(0, 0.25f);
faRT.anchorMax = new Vector2(1, 0.75f);
faRT.offsetMin = new Vector2(10, 0);
faRT.offsetMax = new Vector2(-10, 0);
var fill = new GameObject("Fill", typeof(RectTransform), typeof(Image));
fill.transform.SetParent(fillArea.transform, false);
var fillImg = fill.GetComponent<Image>();
fillImg.color = new Color(0.4f, 0.9f, 0.4f, 1f);
var fillRT = (RectTransform)fill.transform;
fillRT.anchorMin = new Vector2(0, 0);
fillRT.anchorMax = new Vector2(1, 1);
fillRT.offsetMin = Vector2.zero;
fillRT.offsetMax = Vector2.zero;
var handleArea = new GameObject("Handle Slide Area", typeof(RectTransform));
handleArea.transform.SetParent(go.transform, false);
var haRT = (RectTransform)handleArea.transform;
haRT.anchorMin = new Vector2(0, 0);
haRT.anchorMax = new Vector2(1, 1);
haRT.offsetMin = Vector2.zero;
haRT.offsetMax = Vector2.zero;
var handle = new GameObject("Handle", typeof(RectTransform), typeof(Image));
handle.transform.SetParent(handleArea.transform, false);
var handleImg = handle.GetComponent<Image>();
handleImg.color = new Color(0.8f, 1f, 0.8f, 1f);
var hRT = (RectTransform)handle.transform;
hRT.sizeDelta = new Vector2(16, 24);
hRT.anchorMin = new Vector2(0.5f, 0.5f);
hRT.anchorMax = new Vector2(0.5f, 0.5f);
hRT.anchoredPosition = Vector2.zero;
slider.fillRect = fill.GetComponent<RectTransform>();
slider.handleRect = handle.GetComponent<RectTransform>();
slider.targetGraphic = handleImg;
slider.direction = Slider.Direction.LeftToRight;
return go;
}
internal static GameObject CreateLabeledSlider(Transform parent, string label, string name, float min, float max, float value)
{
var group = CreateVerticalGroup(parent, name + "_Group", 4, TextAnchor.UpperLeft, new RectOffset(0, 0, 0, 0));
CreateText(group.transform, name + "_Label", label, 18, TextAnchor.MiddleLeft, Color.white, FontStyles.Normal);
CreateSlider(group.transform, name, min, max, value);
return group;
}
internal static GameObject CreateToggle(Transform parent, string name, string labelText, bool initial)
{
var go = new GameObject(name, typeof(RectTransform), typeof(Toggle));
go.transform.SetParent(parent, false);
var bg = CreateImage(go.transform, "Background", new Color(0.2f, 0.2f, 0.2f));
var check = CreateImage(bg.transform, "Checkmark", new Color(0.7f, 1f, 0.7f));
var label = CreateText(go.transform, "Label", labelText, 18, TextAnchor.MiddleLeft, Color.white, FontStyles.Normal);
var toggle = go.GetComponent<Toggle>();
toggle.isOn = initial;
toggle.graphic = check.GetComponent<Image>();
toggle.targetGraphic = bg.GetComponent<Image>();
var rt = (RectTransform)go.transform;
rt.sizeDelta = new Vector2(600, 40);
var bgRT = (RectTransform)bg.transform;
bgRT.anchorMin = new Vector2(0, 0.5f);
bgRT.anchorMax = new Vector2(0, 0.5f);
bgRT.pivot = new Vector2(0, 0.5f);
bgRT.sizeDelta = new Vector2(24, 24);
var checkRT = (RectTransform)check.transform;
checkRT.anchorMin = checkRT.anchorMax = new Vector2(0.5f, 0.5f);
checkRT.sizeDelta = new Vector2(16, 16);
var labelRT = (RectTransform)label.transform;
labelRT.anchorMin = new Vector2(0, 0);
labelRT.anchorMax = new Vector2(1, 1);
labelRT.offsetMin = new Vector2(34, 0);
labelRT.offsetMax = new Vector2(0, 0);
return go;
}
internal static GameObject CreateImage(Transform parent, string name, Color color)
{
var go = new GameObject(name, typeof(RectTransform), typeof(Image));
go.transform.SetParent(parent, false);
var img = go.GetComponent<Image>();
img.color = color;
return go;
}
internal static void CreateSpacer(Transform parent, float height)
{
var spacer = new GameObject("Spacer", typeof(RectTransform));
spacer.transform.SetParent(parent, false);
var rt = (RectTransform)spacer.transform;
rt.sizeDelta = new Vector2(0, height);
}
internal static ScrollRect CreateScrollList(Transform parent, out Transform content, string scrollName, string viewportName, string contentName)
{
var scrollGO = new GameObject(scrollName, typeof(RectTransform), typeof(Image), typeof(ScrollRect));
scrollGO.transform.SetParent(parent, false);
var srt = (RectTransform)scrollGO.transform;
srt.sizeDelta = new Vector2(0, 240);
var img = scrollGO.GetComponent<Image>();
img.color = new Color(0, 0, 0, 0.3f);
var viewport = new GameObject(viewportName, typeof(RectTransform), typeof(Mask), typeof(Image));
viewport.transform.SetParent(scrollGO.transform, false);
var vImg = viewport.GetComponent<Image>();
vImg.color = new Color(0, 0, 0, 0.1f);
viewport.GetComponent<Mask>().showMaskGraphic = false;
var vprt = (RectTransform)viewport.transform;
vprt.anchorMin = new Vector2(0, 0);
vprt.anchorMax = new Vector2(1, 1);
vprt.offsetMin = Vector2.zero;
vprt.offsetMax = Vector2.zero;
var contentGO = new GameObject(contentName, typeof(RectTransform), typeof(VerticalLayoutGroup));
contentGO.transform.SetParent(viewport.transform, false);
var vlg = contentGO.GetComponent<VerticalLayoutGroup>();
vlg.spacing = 8;
vlg.childAlignment = TextAnchor.UpperLeft;
vlg.childForceExpandWidth = true;
var crt = (RectTransform)contentGO.transform;
crt.anchorMin = new Vector2(0, 1);
crt.anchorMax = new Vector2(1, 1);
crt.pivot = new Vector2(0.5f, 1);
crt.offsetMin = Vector2.zero;
crt.offsetMax = Vector2.zero;
var csf = contentGO.AddComponent<ContentSizeFitter>();
csf.verticalFit = ContentSizeFitter.FitMode.PreferredSize;
content = contentGO.transform;
var scroll = scrollGO.GetComponent<ScrollRect>();
scroll.viewport = (RectTransform)viewport.transform;
scroll.content = (RectTransform)content;
scroll.horizontal = false;
scroll.vertical = true;
return scroll;
}
}
}
#endif

View File

@@ -0,0 +1,2 @@
fileFormatVersion: 2
guid: 2af2a4c51891b754d82ab14274d8146d

View File

@@ -2,8 +2,6 @@
using UnityEditor;
using UnityEngine;
using UnityEngine.UI;
using TMPro;
using System.Linq;
namespace MegaKoop.EditorTools
{
@@ -23,17 +21,92 @@ namespace MegaKoop.EditorTools
canvas = canvasGO.GetComponent<Canvas>();
if (canvas == null) canvas = canvasGO.AddComponent<Canvas>();
canvas.renderMode = RenderMode.ScreenSpaceOverlay;
canvas.sortingOrder = 100;
var scaler = canvasGO.GetComponent<CanvasScaler>();
if (scaler == null) scaler = canvasGO.AddComponent<CanvasScaler>();
scaler.uiScaleMode = CanvasScaler.ScaleMode.ScaleWithScreenSize;
scaler.referenceResolution = new Vector2(1920, 1080);
scaler.matchWidthOrHeight = 0.5f;
var raycaster = canvasGO.GetComponent<GraphicRaycaster>();
if (raycaster == null) raycaster = canvasGO.AddComponent<GraphicRaycaster>();
// Ensure EventSystem exists
if (Object.FindObjectOfType<UnityEngine.EventSystems.EventSystem>() == null)
// Ensure/upgrade EventSystem based on available modules
var existingES = Object.FindObjectOfType<UnityEngine.EventSystems.EventSystem>();
var inputSystemModuleType = System.Type.GetType("UnityEngine.InputSystem.UI.InputSystemUIInputModule, Unity.InputSystem", false);
bool wantIS = inputSystemModuleType != null;
bool wantLegacy = !wantIS;
GameObject esGO = null;
if (existingES == null)
{
var es = new GameObject("EventSystem", typeof(UnityEngine.EventSystems.EventSystem), typeof(UnityEngine.EventSystems.StandaloneInputModule));
var es = new GameObject("EventSystem", typeof(UnityEngine.EventSystems.EventSystem));
if (wantIS) es.AddComponent(inputSystemModuleType);
if (wantLegacy) es.AddComponent<UnityEngine.EventSystems.StandaloneInputModule>();
Undo.RegisterCreatedObjectUndo(es, "Create EventSystem");
esGO = es;
}
else
{
esGO = existingES.gameObject;
var sim = existingES.GetComponent<UnityEngine.EventSystems.StandaloneInputModule>();
var ism = inputSystemModuleType != null ? existingES.GetComponent(inputSystemModuleType) : null;
if (wantIS && ism == null) Undo.AddComponent(existingES.gameObject, inputSystemModuleType);
if (wantLegacy && sim == null) Undo.AddComponent<UnityEngine.EventSystems.StandaloneInputModule>(existingES.gameObject);
}
// Assign actionsAsset only if the picked InputActionAsset contains a "UI" action map
bool assignedUIActions = false;
if (wantIS && inputSystemModuleType != null && esGO != null)
{
var module = esGO.GetComponent(inputSystemModuleType);
if (module != null)
{
var prop = inputSystemModuleType.GetProperty("actionsAsset");
if (prop != null)
{
var current = prop.GetValue(module, null) as UnityEngine.Object;
if (current == null)
{
var iaaType = System.Type.GetType("UnityEngine.InputSystem.InputActionAsset, Unity.InputSystem", false);
string ChooseUIAsset()
{
var guids = AssetDatabase.FindAssets("t:InputActionAsset");
foreach (var g in guids)
{
var path = AssetDatabase.GUIDToAssetPath(g);
var asset = AssetDatabase.LoadAssetAtPath<UnityEngine.Object>(path);
if (asset != null && iaaType != null && iaaType.IsInstanceOfType(asset))
{
var map = iaaType.GetMethod("FindActionMap", new[] { typeof(string), typeof(bool) })?.Invoke(asset, new object[] { "UI", true });
if (map != null) return path;
}
}
return null;
}
var best = ChooseUIAsset();
if (!string.IsNullOrEmpty(best))
{
var asset = AssetDatabase.LoadAssetAtPath<UnityEngine.Object>(best);
prop.SetValue(module, asset, null);
var comp = module as Component; if (comp != null) EditorUtility.SetDirty(comp);
assignedUIActions = true;
}
// else: leave null to let module use its default actions
}
else { assignedUIActions = true; }
}
}
}
if (esGO != null)
{
var hasSIM = esGO.GetComponent<UnityEngine.EventSystems.StandaloneInputModule>() != null;
var hasIS = inputSystemModuleType != null && esGO.GetComponent(inputSystemModuleType) != null;
if (hasIS && !assignedUIActions && !hasSIM)
{
Undo.AddComponent<UnityEngine.EventSystems.StandaloneInputModule>(esGO);
}
Debug.Log($"[UGUIMenuBuilder] EventSystem configured. InputSystem={(hasIS ? "ON" : "OFF")} (actions={(assignedUIActions ? "OK" : "none")}), Standalone={(hasSIM ? "ON" : "OFF")}");
}
// Clean old generated panels if present to avoid duplicates
@@ -49,134 +122,9 @@ namespace MegaKoop.EditorTools
DestroyChildIfExists(canvas.transform, "Panel_Settings");
DestroyChildIfExists(canvas.transform, "Panel_Lobby");
// Root panels
var panelMain = CreatePanel(canvas.transform, "Panel_MainMenu", new Vector2(900, 800));
var panelSettings = CreatePanel(canvas.transform, "Panel_Settings", new Vector2(900, 800));
var panelLobby = CreatePanel(canvas.transform, "Panel_Lobby", new Vector2(1100, 820));
panelSettings.SetActive(false);
panelLobby.SetActive(false);
// Main Menu Content
var mainContainer = CreateVerticalGroup(panelMain.transform, "Main_VLayout", 20, TextAnchor.MiddleCenter, new RectOffset(30, 30, 30, 30));
CreateText(mainContainer.transform, "Text_Title", "MEGA KOOP", 70, TextAnchor.MiddleCenter, Color.white, FontStyles.Bold);
CreateText(mainContainer.transform, "Text_Subtitle", "CO-OP ADVENTURE", 20, TextAnchor.MiddleCenter, new Color(0.8f,0.8f,0.8f), FontStyles.Normal);
CreateSpacer(mainContainer.transform, 20);
CreateMenuButton(mainContainer.transform, "Button_Multiplayer", "MULTIPLAYER");
CreateMenuButton(mainContainer.transform, "Button_Settings", "SETTINGS");
CreateMenuButton(mainContainer.transform, "Button_Quit", "QUIT GAME", isDanger:true);
// Settings
var settingsContainer = CreateVerticalGroup(panelSettings.transform, "Settings_VLayout", 16, TextAnchor.UpperCenter, new RectOffset(30,30,30,30));
CreateText(settingsContainer.transform, "Text_SettingsTitle", "SETTINGS", 48, TextAnchor.MiddleCenter, Color.white, FontStyles.Bold);
CreateSpacer(settingsContainer.transform, 10);
var gfxGroup = CreateVerticalGroup(settingsContainer.transform, "Graphics_Group", 10, TextAnchor.UpperLeft, new RectOffset(10,10,10,10));
CreateText(gfxGroup.transform, "Text_Graphics", "Graphics", 24, TextAnchor.MiddleLeft, Color.white, FontStyles.Bold);
CreateDropdown(gfxGroup.transform, "Dropdown_Quality", new string[]{"Low","Medium","High","Ultra"});
CreateToggle(gfxGroup.transform, "Toggle_Fullscreen", "Fullscreen", true);
CreateDropdown(gfxGroup.transform, "Dropdown_Resolution", new string[]{"1280x720","1920x1080","2560x1440","3840x2160"});
var settingsButtons = CreateHorizontalGroup(settingsContainer.transform, "Settings_Buttons", 10, TextAnchor.MiddleCenter, new RectOffset(0,0,0,0));
CreateMenuButton(settingsButtons.transform, "Button_ApplySettings", "APPLY");
CreateMenuButton(settingsButtons.transform, "Button_BackFromSettings", "BACK");
// Lobby
var lobbyContainer = CreateVerticalGroup(panelLobby.transform, "Lobby_VLayout", 16, TextAnchor.UpperCenter, new RectOffset(24,24,24,24));
var lobbyHeader = CreateHorizontalGroup(lobbyContainer.transform, "Lobby_Header", 10, TextAnchor.MiddleCenter, new RectOffset(0,0,0,10));
CreateText(lobbyHeader.transform, "Text_LobbyTitle", "MULTIPLAYER LOBBY", 36, TextAnchor.MiddleLeft, new Color(0.7f,1f,0.7f), FontStyles.Bold);
CreateText(lobbyHeader.transform, "Text_Status", "OFFLINE", 18, TextAnchor.MiddleRight, new Color(0.8f,0.8f,0.8f), FontStyles.Bold);
// Lobby code area
var codeGroup = CreateVerticalGroup(lobbyContainer.transform, "Lobby_CodeGroup", 8, TextAnchor.MiddleCenter, new RectOffset(10,10,10,10));
CreateText(codeGroup.transform, "Text_LobbyCodeLabel", "LOBBY CODE", 16, TextAnchor.MiddleCenter, new Color(0.8f,0.8f,0.8f), FontStyles.Bold);
var codeRow = CreateHorizontalGroup(codeGroup.transform, "Lobby_CodeRow", 10, TextAnchor.MiddleCenter, new RectOffset(0,0,0,0));
CreateText(codeRow.transform, "Text_LobbyCodeValue", "------", 44, TextAnchor.MiddleCenter, new Color(0.7f,1f,0.7f), FontStyles.Bold);
CreateMenuButton(codeRow.transform, "Button_CopyCode", "COPY");
CreateText(codeGroup.transform, "Text_LobbyCodeHint", "Share this code with friends to invite them", 12, TextAnchor.MiddleCenter, new Color(0.8f,0.8f,0.8f), FontStyles.Normal);
// Tabs
var tabs = CreateHorizontalGroup(lobbyContainer.transform, "Lobby_Tabs", 0, TextAnchor.MiddleCenter, new RectOffset(0,0,0,0));
CreateMenuButton(tabs.transform, "Button_HostTab", "HOST LOBBY");
CreateMenuButton(tabs.transform, "Button_JoinTab", "JOIN LOBBY");
// Join content
var joinGroup = CreateVerticalGroup(lobbyContainer.transform, "Group_Join", 10, TextAnchor.UpperCenter, new RectOffset(10,10,10,10));
var joinRow = CreateHorizontalGroup(joinGroup.transform, "Join_Row", 10, TextAnchor.MiddleCenter, new RectOffset(0,0,0,0));
CreateInputField(joinRow.transform, "Input_LobbyCode", "Enter 6-digit code...");
CreateMenuButton(joinGroup.transform, "Button_Connect", "CONNECT");
// Host content
var hostGroup = CreateVerticalGroup(lobbyContainer.transform, "Group_Host", 10, TextAnchor.UpperCenter, new RectOffset(10,10,10,10));
CreateDropdown(hostGroup.transform, "Dropdown_MaxPlayers", new string[]{"2","3","4","8"});
CreateToggle(hostGroup.transform, "Toggle_PublicLobby", "Public", true);
CreateMenuButton(hostGroup.transform, "Button_CreateLobby", "CREATE LOBBY");
// Players list
var playersHeader = CreateHorizontalGroup(lobbyContainer.transform, "Players_Header", 10, TextAnchor.MiddleLeft, new RectOffset(0,0,0,0));
CreateText(playersHeader.transform, "Text_PlayersTitle", "PLAYERS", 20, TextAnchor.MiddleLeft, Color.white, FontStyles.Bold);
CreateText(playersHeader.transform, "Text_PlayerCount", "0/4", 18, TextAnchor.MiddleRight, new Color(0.7f,1f,0.7f), FontStyles.Bold);
var scroll = CreateScrollList(lobbyContainer.transform, out var content, "Scroll_Players", "Viewport", "Content_PlayersList");
var empty = CreateText(lobbyContainer.transform, "Empty_Players", "Waiting for players...", 16, TextAnchor.MiddleCenter, new Color(0.8f,0.8f,0.8f), FontStyles.Italic);
// Template for player entry
var template = new GameObject("PlayerItemTemplate", typeof(RectTransform), typeof(HorizontalLayoutGroup));
template.transform.SetParent(content, false);
var hlg = template.GetComponent<HorizontalLayoutGroup>();
hlg.childAlignment = TextAnchor.MiddleLeft; hlg.spacing = 12; hlg.childControlWidth = false; hlg.childForceExpandWidth = true;
var avatar = CreateImage(template.transform, "Image_Avatar", new Color(0.3f,0.6f,0.3f));
var name = CreateText(template.transform, "Text_PlayerName", "Player", 18, TextAnchor.MiddleLeft, Color.white, FontStyles.Bold);
var status = CreateText(template.transform, "Text_PlayerStatus", "NOT READY", 14, TextAnchor.MiddleRight, new Color(0.8f,0.8f,0.8f), FontStyles.Bold);
((RectTransform)avatar.transform).sizeDelta = new Vector2(40,40);
template.SetActive(false);
// Host controls
var hostControls = CreateHorizontalGroup(lobbyContainer.transform, "Host_Controls", 10, TextAnchor.MiddleCenter, new RectOffset(0,0,0,0));
CreateMenuButton(hostControls.transform, "Button_InviteFriends", "INVITE FRIENDS");
CreateMenuButton(hostControls.transform, "Button_KickSelected", "KICK SELECTED");
// Friends picker will be a modal overlay under Panel_Lobby (so it doesn't stretch main layout)
var friendsOverlay = new GameObject("Panel_Friends", typeof(RectTransform), typeof(Image));
friendsOverlay.transform.SetParent(panelLobby.transform, false);
var ovRT = (RectTransform)friendsOverlay.transform; ovRT.anchorMin = new Vector2(0,0); ovRT.anchorMax = new Vector2(1,1); ovRT.offsetMin = Vector2.zero; ovRT.offsetMax = Vector2.zero;
var ovImg = friendsOverlay.GetComponent<Image>(); ovImg.color = new Color(0,0,0,0.55f);
friendsOverlay.SetActive(false);
// Transparent background button to close on backdrop click
var closeBg = new GameObject("Button_CloseFriendsOverlay", typeof(RectTransform), typeof(Image), typeof(Button));
closeBg.transform.SetParent(friendsOverlay.transform, false);
var closeBgRT = (RectTransform)closeBg.transform; closeBgRT.anchorMin = new Vector2(0,0); closeBgRT.anchorMax = new Vector2(1,1); closeBgRT.offsetMin = Vector2.zero; closeBgRT.offsetMax = Vector2.zero;
var closeBgImg = closeBg.GetComponent<Image>(); closeBgImg.color = new Color(0,0,0,0);
// Center modal window
var friendsWindow = CreatePanel(friendsOverlay.transform, "Friends_Window", new Vector2(900, 420));
var friendsV = CreateVerticalGroup(friendsWindow.transform, "Friends_VLayout", 8, TextAnchor.UpperCenter, new RectOffset(16,16,16,16));
CreateText(friendsV.transform, "Text_FriendsTitle", "INVITE FRIENDS", 24, TextAnchor.MiddleCenter, Color.white, FontStyles.Bold);
CreateText(friendsV.transform, "Text_FriendsHint", "Select a friend to send a Steam invite.", 12, TextAnchor.MiddleCenter, new Color(0.8f,0.8f,0.8f), FontStyles.Normal);
var friendsScroll = CreateScrollList(friendsV.transform, out var friendsContent, "Scroll_Friends", "Viewport", "Content_FriendsList");
// Make friends content a compact grid of icons
var vlgFriends = friendsContent.GetComponent<VerticalLayoutGroup>();
if (vlgFriends) Object.DestroyImmediate(vlgFriends);
var gridFriends = friendsContent.gameObject.AddComponent<GridLayoutGroup>();
gridFriends.cellSize = new Vector2(72, 72);
gridFriends.spacing = new Vector2(10, 10);
gridFriends.startAxis = GridLayoutGroup.Axis.Horizontal;
var csfFriends = friendsContent.GetComponent<ContentSizeFitter>();
if (csfFriends == null) csfFriends = friendsContent.gameObject.AddComponent<ContentSizeFitter>();
csfFriends.verticalFit = ContentSizeFitter.FitMode.PreferredSize;
CreateText(friendsV.transform, "Empty_Friends", "No friends found.", 14, TextAnchor.MiddleCenter, new Color(0.8f,0.8f,0.8f), FontStyles.Italic);
var friendsFooter = CreateHorizontalGroup(friendsV.transform, "Friends_Footer", 8, TextAnchor.MiddleCenter, new RectOffset(0,0,0,0));
CreateMenuButton(friendsFooter.transform, "Button_BackFromFriends", "BACK");
// Ready
CreateMenuButton(lobbyContainer.transform, "Button_ToggleReady", "TOGGLE READY");
// Footer
var footer = CreateHorizontalGroup(lobbyContainer.transform, "Lobby_Footer", 10, TextAnchor.MiddleCenter, new RectOffset(0,0,0,0));
CreateMenuButton(footer.transform, "Button_StartGame", "START GAME");
CreateMenuButton(footer.transform, "Button_LeaveLobby", "LEAVE LOBBY");
CreateMenuButton(footer.transform, "Button_BackFromLobby", "BACK TO MENU");
var mainPanel = new MainMenuPanelBuilder(canvas.transform).Build();
var settingsPanel = new SettingsPanelBuilder(canvas.transform).Build();
var lobbyPanel = new LobbyPanelBuilder(canvas.transform).Build();
// Controllers
var mainCtrl = canvas.gameObject.GetComponent<MegaKoop.UI.UGUIMainMenuController>();
@@ -185,238 +133,13 @@ namespace MegaKoop.EditorTools
if (lobbyCtrl == null) lobbyCtrl = canvas.gameObject.AddComponent<MegaKoop.UI.UGUIMultiplayerLobbyController>();
// Inject panel references for reliability
mainCtrl.SetPanels(panelMain, panelSettings, panelLobby);
lobbyCtrl.SetLobbyRoot(panelLobby);
mainCtrl.SetPanels(mainPanel, settingsPanel, lobbyPanel);
lobbyCtrl.SetLobbyRoot(lobbyPanel);
Selection.activeGameObject = canvas.gameObject;
Debug.Log("[UGUIMenuBuilder] UGUI Main Menu generated successfully. You can press Play and test.");
}
private static GameObject CreatePanel(Transform parent, string name, Vector2 size)
{
var go = new GameObject(name, typeof(RectTransform), typeof(Image));
go.transform.SetParent(parent, false);
var rt = (RectTransform)go.transform;
rt.anchorMin = rt.anchorMax = new Vector2(0.5f, 0.5f);
rt.pivot = new Vector2(0.5f, 0.5f);
rt.sizeDelta = size;
var img = go.GetComponent<Image>();
img.color = new Color(0.13f, 0.13f, 0.13f, 0.95f);
return go;
}
private static GameObject CreateVerticalGroup(Transform parent, string name, float spacing, TextAnchor align, RectOffset padding)
{
var go = new GameObject(name, typeof(RectTransform), typeof(VerticalLayoutGroup), typeof(ContentSizeFitter));
go.transform.SetParent(parent, false);
var rt = (RectTransform)go.transform;
rt.anchorMin = new Vector2(0, 0); rt.anchorMax = new Vector2(1, 1); rt.offsetMin = new Vector2(0,0); rt.offsetMax = new Vector2(0,0);
var vlg = go.GetComponent<VerticalLayoutGroup>();
vlg.spacing = spacing; vlg.childAlignment = align; vlg.padding = padding; vlg.childForceExpandWidth = true; vlg.childControlWidth = true; vlg.childControlHeight = false;
var fitter = go.GetComponent<ContentSizeFitter>();
fitter.verticalFit = ContentSizeFitter.FitMode.PreferredSize; fitter.horizontalFit = ContentSizeFitter.FitMode.Unconstrained;
return go;
}
private static GameObject CreateHorizontalGroup(Transform parent, string name, float spacing, TextAnchor align, RectOffset padding)
{
var go = new GameObject(name, typeof(RectTransform), typeof(HorizontalLayoutGroup));
go.transform.SetParent(parent, false);
var rt = (RectTransform)go.transform;
rt.anchorMin = new Vector2(0, 0); rt.anchorMax = new Vector2(1, 0); rt.pivot = new Vector2(0.5f, 0.5f);
rt.sizeDelta = new Vector2(0, 60);
var hlg = go.GetComponent<HorizontalLayoutGroup>();
hlg.spacing = spacing; hlg.childAlignment = align; hlg.padding = padding; hlg.childControlWidth = true; hlg.childForceExpandWidth = true;
return go;
}
private static GameObject CreateText(Transform parent, string name, string text, int fontSize, TextAnchor anchor, Color color, FontStyles fontStyle)
{
var go = new GameObject(name, typeof(RectTransform), typeof(TextMeshProUGUI), typeof(LayoutElement));
go.transform.SetParent(parent, false);
var t = go.GetComponent<TextMeshProUGUI>();
t.text = text; t.fontSize = fontSize; t.color = color; t.alignment = MapAlignment(anchor); t.fontStyle = fontStyle;
t.enableWordWrapping = false; // avoid vertical letters
t.raycastTarget = false; // don't block parent UI clicks
if (TMP_Settings.defaultFontAsset != null) t.font = TMP_Settings.defaultFontAsset;
var le = go.GetComponent<LayoutElement>();
le.minHeight = Mathf.Max(24, fontSize + 12);
var rt = (RectTransform)go.transform; rt.sizeDelta = new Vector2(0, fontSize + 20);
return go;
}
private static TextAlignmentOptions MapAlignment(TextAnchor anchor)
{
switch (anchor)
{
case TextAnchor.UpperLeft: return TextAlignmentOptions.TopLeft;
case TextAnchor.UpperCenter: return TextAlignmentOptions.Top;
case TextAnchor.UpperRight: return TextAlignmentOptions.TopRight;
case TextAnchor.MiddleLeft: return TextAlignmentOptions.MidlineLeft;
case TextAnchor.MiddleCenter: return TextAlignmentOptions.Midline;
case TextAnchor.MiddleRight: return TextAlignmentOptions.MidlineRight;
case TextAnchor.LowerLeft: return TextAlignmentOptions.BottomLeft;
case TextAnchor.LowerCenter: return TextAlignmentOptions.Bottom;
case TextAnchor.LowerRight: return TextAlignmentOptions.BottomRight;
default: return TextAlignmentOptions.Center;
}
}
private static GameObject CreateMenuButton(Transform parent, string name, string label, bool isDanger = false)
{
var go = new GameObject(name, typeof(RectTransform), typeof(Image), typeof(Button));
go.transform.SetParent(parent, false);
var img = go.GetComponent<Image>();
img.color = isDanger ? new Color(0.5f, 0.15f, 0.15f, 0.9f) : new Color(0.25f, 0.5f, 0.25f, 0.9f);
var rt = (RectTransform)go.transform; rt.sizeDelta = new Vector2(0, 48);
var leBtn = go.AddComponent<LayoutElement>();
leBtn.flexibleWidth = 1; leBtn.minHeight = 48;
var text = CreateText(go.transform, "Text", label, 20, TextAnchor.MiddleCenter, Color.white, FontStyles.Bold);
var textRT = (RectTransform)text.transform; textRT.anchorMin = new Vector2(0,0); textRT.anchorMax = new Vector2(1,1); textRT.offsetMin = Vector2.zero; textRT.offsetMax = Vector2.zero;
return go;
}
private static GameObject CreateInputField(Transform parent, string name, string placeholder)
{
var go = new GameObject(name, typeof(RectTransform), typeof(Image), typeof(TMP_InputField));
go.transform.SetParent(parent, false);
var img = go.GetComponent<Image>(); img.color = new Color(0.1f,0.1f,0.1f,0.9f);
var rt = (RectTransform)go.transform; rt.sizeDelta = new Vector2(600, 50);
var viewport = new GameObject("TextViewport", typeof(RectTransform)); viewport.transform.SetParent(go.transform, false);
var viewportRT = (RectTransform)viewport.transform; viewportRT.anchorMin = new Vector2(0,0); viewportRT.anchorMax = new Vector2(1,1); viewportRT.offsetMin = new Vector2(10,6); viewportRT.offsetMax = new Vector2(-10,-6);
var text = CreateText(viewport.transform, "Text", string.Empty, 20, TextAnchor.MiddleLeft, Color.white, FontStyles.Normal);
var placeholderGO = CreateText(viewport.transform, "Placeholder", placeholder, 18, TextAnchor.MiddleLeft, new Color(0.7f,0.7f,0.7f), FontStyles.Italic);
var input = go.GetComponent<TMP_InputField>();
input.textViewport = viewportRT;
input.textComponent = text.GetComponent<TextMeshProUGUI>();
input.placeholder = placeholderGO.GetComponent<TextMeshProUGUI>();
var tRT = (RectTransform)text.transform; tRT.anchorMin = new Vector2(0,0); tRT.anchorMax = new Vector2(1,1); tRT.offsetMin = new Vector2(10,0); tRT.offsetMax = new Vector2(-10,0);
var pRT = (RectTransform)placeholderGO.transform; pRT.anchorMin = new Vector2(0,0); pRT.anchorMax = new Vector2(1,1); pRT.offsetMin = new Vector2(10,0); pRT.offsetMax = new Vector2(-10,0);
return go;
}
private static GameObject CreateDropdown(Transform parent, string name, string[] options)
{
var go = new GameObject(name, typeof(RectTransform), typeof(Image), typeof(TMP_Dropdown));
go.transform.SetParent(parent, false);
var img = go.GetComponent<Image>(); img.color = new Color(0.2f,0.2f,0.2f,0.9f);
var rt = (RectTransform)go.transform; rt.sizeDelta = new Vector2(600, 50);
// Caption label
var caption = CreateText(go.transform, "Label", options.FirstOrDefault() ?? "Option", 18, TextAnchor.MiddleLeft, Color.white, FontStyles.Normal);
var arrow = CreateText(go.transform, "Arrow", "▼", 18, TextAnchor.MiddleRight, Color.white, FontStyles.Bold);
// Template (disabled by default)
var template = new GameObject("Template", typeof(RectTransform), typeof(Image), typeof(ScrollRect));
template.transform.SetParent(go.transform, false);
var templateRT = (RectTransform)template.transform;
templateRT.anchorMin = new Vector2(0, 0); templateRT.anchorMax = new Vector2(1, 0); templateRT.pivot = new Vector2(0.5f, 1);
templateRT.sizeDelta = new Vector2(0, 220); // visible dropdown height
template.SetActive(false);
var templateImg = template.GetComponent<Image>(); templateImg.color = new Color(0.1f,0.1f,0.1f,0.95f);
// Viewport
var viewport = new GameObject("Viewport", typeof(RectTransform), typeof(Mask), typeof(Image));
viewport.transform.SetParent(template.transform, false);
var viewportRT = (RectTransform)viewport.transform; viewportRT.anchorMin = new Vector2(0,0); viewportRT.anchorMax = new Vector2(1,1); viewportRT.offsetMin = Vector2.zero; viewportRT.offsetMax = Vector2.zero;
var vImg = viewport.GetComponent<Image>(); vImg.color = new Color(0,0,0,0.2f);
viewport.GetComponent<Mask>().showMaskGraphic = false;
// Scroll content
var content = new GameObject("Content", typeof(RectTransform), typeof(VerticalLayoutGroup));
content.transform.SetParent(viewport.transform, false);
var contentRT = (RectTransform)content.transform; contentRT.anchorMin = new Vector2(0,1); contentRT.anchorMax = new Vector2(1,1); contentRT.pivot = new Vector2(0.5f,1);
var vlg = content.GetComponent<VerticalLayoutGroup>(); vlg.spacing = 4; vlg.childForceExpandWidth = true; vlg.childControlHeight = true;
// Item (Toggle)
var item = new GameObject("Item", typeof(RectTransform), typeof(Toggle));
item.transform.SetParent(content.transform, false);
var itemRT = (RectTransform)item.transform; itemRT.sizeDelta = new Vector2(0, 30);
var itemBG = new GameObject("Item Background", typeof(RectTransform), typeof(Image));
itemBG.transform.SetParent(item.transform, false);
var itemBGImg = itemBG.GetComponent<Image>(); itemBGImg.color = new Color(0.2f,0.2f,0.2f,0.9f);
var itemCheck = new GameObject("Item Checkmark", typeof(RectTransform), typeof(Image));
itemCheck.transform.SetParent(itemBG.transform, false);
var itemCheckImg = itemCheck.GetComponent<Image>(); itemCheckImg.color = new Color(0.7f,1f,0.7f,1f);
var itemLabelGO = CreateText(item.transform, "Item Label", "Option", 18, TextAnchor.MiddleLeft, Color.white, FontStyles.Normal);
// Position sub-elements
var bgRT = (RectTransform)itemBG.transform; bgRT.anchorMin = new Vector2(0,0); bgRT.anchorMax = new Vector2(1,1); bgRT.offsetMin = Vector2.zero; bgRT.offsetMax = Vector2.zero;
var ckRT = (RectTransform)itemCheck.transform; ckRT.anchorMin = new Vector2(0,0.5f); ckRT.anchorMax = new Vector2(0,0.5f); ckRT.pivot = new Vector2(0,0.5f); ckRT.sizeDelta = new Vector2(18,18); ckRT.anchoredPosition = new Vector2(6,0);
var itemLabelRT = (RectTransform)itemLabelGO.transform; itemLabelRT.anchorMin = new Vector2(0,0); itemLabelRT.anchorMax = new Vector2(1,1); itemLabelRT.offsetMin = new Vector2(28,0); itemLabelRT.offsetMax = new Vector2(-6,0);
// Toggle wiring
var tgl = item.GetComponent<Toggle>();
tgl.targetGraphic = itemBGImg; tgl.graphic = itemCheckImg;
// Dropdown wiring
var dd = go.GetComponent<TMP_Dropdown>();
dd.template = template.GetComponent<RectTransform>();
dd.captionText = caption.GetComponent<TextMeshProUGUI>();
dd.itemText = itemLabelGO.GetComponent<TextMeshProUGUI>();
dd.options = options.Select(o => new TMP_Dropdown.OptionData(o)).ToList();
dd.RefreshShownValue();
// ScrollRect wiring
var scr = template.GetComponent<ScrollRect>();
scr.viewport = viewport.GetComponent<RectTransform>();
scr.content = content.GetComponent<RectTransform>();
scr.horizontal = false; scr.vertical = true;
return go;
}
private static GameObject CreateToggle(Transform parent, string name, string labelText, bool initial)
{
var go = new GameObject(name, typeof(RectTransform), typeof(Toggle));
go.transform.SetParent(parent, false);
var bg = CreateImage(go.transform, "Background", new Color(0.2f,0.2f,0.2f));
var check = CreateImage(bg.transform, "Checkmark", new Color(0.7f,1f,0.7f));
var label = CreateText(go.transform, "Label", labelText, 18, TextAnchor.MiddleLeft, Color.white, FontStyles.Normal);
var t = go.GetComponent<Toggle>();
t.isOn = initial; t.graphic = check.GetComponent<Image>(); t.targetGraphic = bg.GetComponent<Image>();
var rt = (RectTransform)go.transform; rt.sizeDelta = new Vector2(600, 40);
var bgRT = (RectTransform)bg.transform; bgRT.anchorMin = new Vector2(0,0.5f); bgRT.anchorMax = new Vector2(0,0.5f); bgRT.pivot = new Vector2(0,0.5f); bgRT.sizeDelta = new Vector2(24,24);
var checkRT = (RectTransform)check.transform; checkRT.anchorMin = checkRT.anchorMax = new Vector2(0.5f,0.5f); checkRT.sizeDelta = new Vector2(16,16);
var labelRT = (RectTransform)label.transform; labelRT.anchorMin = new Vector2(0,0); labelRT.anchorMax = new Vector2(1,1); labelRT.offsetMin = new Vector2(34,0); labelRT.offsetMax = new Vector2(0,0);
return go;
}
private static GameObject CreateImage(Transform parent, string name, Color color)
{
var go = new GameObject(name, typeof(RectTransform), typeof(Image));
go.transform.SetParent(parent, false);
var img = go.GetComponent<Image>(); img.color = color;
return go;
}
private static void CreateSpacer(Transform parent, float height)
{
var sp = new GameObject("Spacer", typeof(RectTransform));
sp.transform.SetParent(parent, false);
var rt = (RectTransform)sp.transform; rt.sizeDelta = new Vector2(0, height);
}
private static ScrollRect CreateScrollList(Transform parent, out Transform content, string scrollName, string viewportName, string contentName)
{
var scrollGO = new GameObject(scrollName, typeof(RectTransform), typeof(Image), typeof(ScrollRect));
scrollGO.transform.SetParent(parent, false);
var srt = (RectTransform)scrollGO.transform; srt.sizeDelta = new Vector2(0, 240);
var img = scrollGO.GetComponent<Image>(); img.color = new Color(0,0,0,0.3f);
var viewport = new GameObject(viewportName, typeof(RectTransform), typeof(Mask), typeof(Image));
viewport.transform.SetParent(scrollGO.transform, false);
var vImg = viewport.GetComponent<Image>(); vImg.color = new Color(0,0,0,0.1f);
viewport.GetComponent<Mask>().showMaskGraphic = false;
var vprt = (RectTransform)viewport.transform; vprt.anchorMin = new Vector2(0,0); vprt.anchorMax = new Vector2(1,1); vprt.offsetMin = Vector2.zero; vprt.offsetMax = Vector2.zero;
var contentGO = new GameObject(contentName, typeof(RectTransform), typeof(VerticalLayoutGroup));
contentGO.transform.SetParent(viewport.transform, false);
var vlg = contentGO.GetComponent<VerticalLayoutGroup>(); vlg.spacing = 8; vlg.childAlignment = TextAnchor.UpperLeft; vlg.childForceExpandWidth = true;
var crt = (RectTransform)contentGO.transform; crt.anchorMin = new Vector2(0,1); crt.anchorMax = new Vector2(1,1); crt.pivot = new Vector2(0.5f,1); crt.offsetMin = new Vector2(0,0); crt.offsetMax = new Vector2(0,0);
var csf = contentGO.AddComponent<ContentSizeFitter>(); csf.verticalFit = ContentSizeFitter.FitMode.PreferredSize;
content = contentGO.transform;
var scroll = scrollGO.GetComponent<ScrollRect>(); scroll.viewport = (RectTransform)viewport.transform; scroll.content = (RectTransform)content; scroll.horizontal = false; scroll.vertical = true;
return scroll;
}
}
}
#endif

View File

@@ -13,8 +13,8 @@ MonoBehaviour:
m_Name: BossSchedule
m_EditorClassIdentifier: Assembly-CSharp::Game.Scripts.Runtime.Data.BossSchedule
events:
- TimeSinceStart: 1000
Boss: {fileID: 11400000, guid: 1bc4888fa172eb99f94756653be6c1ed, type: 2}
- TimeSinceStart: 30
Boss: {fileID: 11400000, guid: 931549bcc3a079d30b302ebd17d6ebd4, type: 2}
Count: 1
useSpawnRadiusOverride: 0
spawnRadiusOverride: 0

View File

@@ -205,6 +205,8 @@ namespace MegaKoop.Game.EditorExtensions
var move = root.AddState("Move", new Vector3(250, 150, 0));
move.motion = CreateMoveBlendTree(controller);
move.speedParameterActive = true;
move.speedParameter = "MoveSpeedNormalized";
AnimatorState crouch = null;
if (crouchIdle != null)
@@ -262,11 +264,27 @@ namespace MegaKoop.Game.EditorExtensions
}
// Jump transitions: AnyState -> Jump Begin
var anyToJumpBegin = root.AddAnyStateTransition(jumpBeginState);
anyToJumpBegin.hasExitTime = false;
anyToJumpBegin.duration = 0.05f;
anyToJumpBegin.canTransitionToSelf = false;
anyToJumpBegin.AddCondition(AnimatorConditionMode.If, 0f, "Jump");
// Jump transitions from grounded states
var idleToJump = idle.AddTransition(jumpBeginState);
idleToJump.hasExitTime = false;
idleToJump.duration = 0.05f;
idleToJump.AddCondition(AnimatorConditionMode.If, 0f, "IsJumping");
idleToJump.AddCondition(AnimatorConditionMode.IfNot, 0f, "IsGrounded");
var moveToJump = move.AddTransition(jumpBeginState);
moveToJump.hasExitTime = false;
moveToJump.duration = 0.05f;
moveToJump.AddCondition(AnimatorConditionMode.If, 0f, "IsJumping");
moveToJump.AddCondition(AnimatorConditionMode.IfNot, 0f, "IsGrounded");
if (crouch != null)
{
var crouchToJump = crouch.AddTransition(jumpBeginState);
crouchToJump.hasExitTime = false;
crouchToJump.duration = 0.05f;
crouchToJump.AddCondition(AnimatorConditionMode.If, 0f, "IsJumping");
crouchToJump.AddCondition(AnimatorConditionMode.IfNot, 0f, "IsGrounded");
}
// Jump Begin -> Jump Fall (automatic after animation)
var jumpBeginToFall = jumpBeginState.AddTransition(jumpFallState);
@@ -330,10 +348,11 @@ namespace MegaKoop.Game.EditorExtensions
controller.AddParameter("MoveX", AnimatorControllerParameterType.Float);
controller.AddParameter("MoveZ", AnimatorControllerParameterType.Float);
controller.AddParameter("Speed", AnimatorControllerParameterType.Float);
controller.AddParameter("MoveSpeedNormalized", AnimatorControllerParameterType.Float);
controller.AddParameter("IsGrounded", AnimatorControllerParameterType.Bool);
controller.AddParameter("IsCrouching", AnimatorControllerParameterType.Bool);
controller.AddParameter("IsDead", AnimatorControllerParameterType.Bool);
controller.AddParameter("Jump", AnimatorControllerParameterType.Trigger);
controller.AddParameter("IsJumping", AnimatorControllerParameterType.Bool);
}
private Motion CreateMoveBlendTree(AnimatorController controller)

8
Game/Enemy/Butcher.meta Normal file
View File

@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 48c9e67a0c0c948489f927fd1c27b2fa
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,9 @@
fileFormatVersion: 2
guid: d086cbb30c7bb4ba3a58cb2024fa0b31
timeCreated: 1526423972
licenseType: Store
NativeFormatImporter:
mainObjectFileID: 100100000
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,22 @@
%YAML 1.1
%TAG !u! tag:unity3d.com,2011:
--- !u!114 &11400000
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 0}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: 6b0e84710d3119d60af5d5373005e029, type: 3}
m_Name: ButcherDefinition
m_EditorClassIdentifier: Assembly-CSharp::Game.Scripts.Runtime.Data.EnemyDefinition
id: enemy_golem
Prefab: {fileID: 1170732337855516, guid: d086cbb30c7bb4ba3a58cb2024fa0b31, type: 3}
IsBoss: 1
BaseHP: 300
MoveSpeed: 6
Damage: 10
PrefabPivotOffset: {x: 0, y: 0, z: 0}
Tags: []

View File

@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 931549bcc3a079d30b302ebd17d6ebd4
NativeFormatImporter:
externalObjects: {}
mainObjectFileID: 11400000
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,29 @@
%YAML 1.1
%TAG !u! tag:unity3d.com,2011:
--- !u!114 &11400000
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 0}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: 1db1d1943f6165546b7a8aa42068f69b, type: 3}
m_Name: Champion_00
m_EditorClassIdentifier: Assembly-CSharp::MegaKoop.Champions.ChampionData
championName: Champion 1
championId: 0
description: Popis pro Champion 1
portrait: {fileID: 0}
championPrefab: {fileID: 7059514996416789454, guid: fe75fe22781f92b369675fdfc9657f7d, type: 3}
primaryGunName: Primary Weapon
primaryGunDescription: Primary gun description
secondaryGunName: Secondary Weapon
secondaryGunDescription: Secondary gun description
passiveName: Passive Ability
passiveDescription: Passive ability description
primaryGunIcon: {fileID: 0}
secondaryGunIcon: {fileID: 0}
passiveIcon: {fileID: 0}
themeColor: {r: 1, g: 1, b: 1, a: 1}

View File

@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: e91e178970c9ce94fa1aae63627dd287
NativeFormatImporter:
externalObjects: {}
mainObjectFileID: 11400000
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,29 @@
%YAML 1.1
%TAG !u! tag:unity3d.com,2011:
--- !u!114 &11400000
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 0}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: 1db1d1943f6165546b7a8aa42068f69b, type: 3}
m_Name: Champion_01
m_EditorClassIdentifier: Assembly-CSharp::MegaKoop.Champions.ChampionData
championName: Champion 2
championId: 1
description: Popis pro Champion 2
portrait: {fileID: 0}
championPrefab: {fileID: 2809934685114486836, guid: ae082cf2d3a36684fb23d8ec0e643150, type: 3}
primaryGunName: Primary Weapon
primaryGunDescription: Primary gun description
secondaryGunName: Secondary Weapon
secondaryGunDescription: Secondary gun description
passiveName: Passive Ability
passiveDescription: Passive ability description
primaryGunIcon: {fileID: 0}
secondaryGunIcon: {fileID: 0}
passiveIcon: {fileID: 0}
themeColor: {r: 1, g: 1, b: 1, a: 1}

View File

@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 3063583c11830974380a240794f2a205
NativeFormatImporter:
externalObjects: {}
mainObjectFileID: 11400000
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -15,10 +15,10 @@ MonoBehaviour:
displayName: Weapon
viewPrefab: {fileID: 6595277065068154610, guid: 7eba33411273ad195adc8a8253711e93, type: 3}
projectilePrefab: {fileID: -6920969466594260193, guid: 6703b124cb13a577c8aae6a4851d0274, type: 3}
projectileSpeed: 18
projectileSpeed: 15
projectileLifetime: 5
shotsPerSecond: 2
baseDamage: 50
shotsPerSecond: 1
baseDamage: 5
range: 25
projectilesPerShot: 2
spreadAngle: 0

View File

@@ -643,6 +643,7 @@ GameObject:
- component: {fileID: 7469640283978802878}
- component: {fileID: 1813583201016405249}
- component: {fileID: 3442404066554451922}
- component: {fileID: -2591875487324942466}
m_Layer: 0
m_Name: Wizard 2.0
m_TagString: Untagged
@@ -733,7 +734,14 @@ MonoBehaviour:
jumpHeight: 1.6
gravity: -20
groundedGravity: -5
jumpBufferTime: 0.1
coyoteTime: 0.1
upwardGravityMultiplier: 1
fallGravityMultiplier: 2.5
cameraTransform: {fileID: 6707832248248563092}
animator: {fileID: 9099213046038254594}
animationDamping: 0.075
crouchKey: 306
--- !u!114 &710593002191720509
MonoBehaviour:
m_ObjectHideFlags: 0
@@ -903,6 +911,7 @@ MonoBehaviour:
m_Name:
m_EditorClassIdentifier: Assembly-CSharp::MegaKoop.Game.Networking.SteamCharacterNetworkBridge
characterController: {fileID: 3919077998180469670}
animator: {fileID: 9099213046038254594}
identity: {fileID: 3262185422700989869}
rootTransform: {fileID: 3042213512511893744}
networkInputProxy: {fileID: 3442404066554451922}
@@ -939,7 +948,7 @@ MonoBehaviour:
m_EditorClassIdentifier: Assembly-CSharp::MegaKoop.Game.Networking.SteamLocalInputSender
characterNetwork: {fileID: 8235037133905039757}
sendInterval: 0.05
cameraTransform: {fileID: 0}
cameraTransform: {fileID: 6707832248248563092}
--- !u!114 &3442404066554451922
MonoBehaviour:
m_ObjectHideFlags: 0
@@ -952,6 +961,23 @@ MonoBehaviour:
m_Script: {fileID: 11500000, guid: e3118c9c432a7acbd824645749251552, type: 3}
m_Name:
m_EditorClassIdentifier: Assembly-CSharp::MegaKoop.Game.Networking.NetworkCharacterInputProxy
--- !u!114 &-2591875487324942466
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 2809934685114486836}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: 79fb1fbc20d80f546ba695c751860930, type: 3}
m_Name:
m_EditorClassIdentifier: Assembly-CSharp::MegaKoop.Game.UI.SteamNameTag
followHead: {fileID: 852694279969892930}
worldOffset: {x: 0, y: 2.5, z: 0}
billboardToCamera: 1
hideForLocal: 1
visibleDistance: 60
--- !u!1 &2999616957953547817
GameObject:
m_ObjectHideFlags: 0

View File

@@ -1859,6 +1859,7 @@ GameObject:
- component: {fileID: 7618815643042096284}
- component: {fileID: 2662658783078050447}
- component: {fileID: 2417536914360261273}
- component: {fileID: 2931281317230154068}
m_Layer: 0
m_Name: Wizard
m_TagString: Untagged
@@ -1947,8 +1948,12 @@ MonoBehaviour:
rotationSharpness: 15
airControlResponsiveness: 60
jumpHeight: 1.6
gravity: -1
gravity: -10
groundedGravity: -0.01
jumpBufferTime: 0.1
coyoteTime: 0.1
upwardGravityMultiplier: 1
fallGravityMultiplier: 10
cameraTransform: {fileID: 266386425542752718}
animator: {fileID: 3962868364137827229}
animationDamping: 0.075
@@ -2095,6 +2100,23 @@ MonoBehaviour:
SwitchTransformSpaceWhenParented: 0
Interpolate: 1
SlerpPosition: 0
--- !u!114 &2931281317230154068
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 7059514996416789454}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: 79fb1fbc20d80f546ba695c751860930, type: 3}
m_Name:
m_EditorClassIdentifier: Assembly-CSharp::MegaKoop.Game.UI.SteamNameTag
followHead: {fileID: 7928616218613849049}
worldOffset: {x: 0, y: 1.2, z: 0}
billboardToCamera: 1
hideForLocal: 1
visibleDistance: 60
--- !u!1 &7094911519708027617
GameObject:
m_ObjectHideFlags: 0

View File

@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: f9f986f0da88643469a3c8d6d1223351
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 6d7b510344abb3846bb4a3bdab6135d5
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

8
Game/Scripts/Data.meta Normal file
View File

@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 1dae8c62813124b4b992a6e90303c912
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -22,9 +22,6 @@ namespace MegaKoop.Game.Enemy
public class SteamEnemyController : MonoBehaviour
{
private static readonly List<Health> SharedHealthBuffer = new(32);
private static int nextEnemyNetworkId = StartingEnemyNetworkId;
private const int StartingEnemyNetworkId = 10000;
[Header("Movement")]
[SerializeField] private float moveSpeed = 3.5f;
@@ -69,7 +66,6 @@ namespace MegaKoop.Game.Enemy
private void Awake()
{
EnsureIdentity();
spawnPosition = transform.position;
baseMoveSpeed = moveSpeed;
@@ -119,7 +115,7 @@ namespace MegaKoop.Game.Enemy
[RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.BeforeSceneLoad)]
private static void ResetNetworkIdCounter()
{
nextEnemyNetworkId = StartingEnemyNetworkId;
NetworkIdAllocator.Reset();
}
private void EnsureIdentity()
@@ -134,14 +130,23 @@ namespace MegaKoop.Game.Enemy
return;
}
if (identity.NetworkId == 0)
if (identity.NetworkId != 0)
{
identity.SetNetworkId(nextEnemyNetworkId++);
return;
}
if (!ShouldAssignLocalNetworkId())
{
return;
}
int allocatedId = NetworkIdAllocator.AllocateEnemyId();
identity.SetNetworkId(allocatedId);
}
private void OnEnable()
{
EnsureIdentity();
networkManager = SteamCoopNetworkManager.Instance;
pooledInstance ??= GetComponent<PooledInstance>();
pendingDespawn = false;
@@ -232,6 +237,22 @@ namespace MegaKoop.Game.Enemy
return networkManager.IsHost;
}
private bool ShouldAssignLocalNetworkId()
{
var manager = SteamCoopNetworkManager.Instance;
if (manager == null)
{
return true;
}
if (!manager.IsConnected)
{
return true;
}
return manager.IsHost;
}
private void SyncNavMeshAgentState(bool simulate)
{
if (navMeshAgent == null)

View File

@@ -182,6 +182,7 @@ namespace MegaKoop.Game.Networking
var clone = Instantiate(template, spawnPosition, baseRotation, parent);
clone.name = BuildPlayerName(info, i);
AdjustSpawnToGround(clone);
ConfigureCloneForPlayer(clone, info, i);
ulong ownerSteamId = ParseSteamId(info.SteamId);
@@ -252,11 +253,30 @@ namespace MegaKoop.Game.Networking
return center + offset;
}
private void AdjustSpawnToGround(GameObject clone)
{
if (clone == null) return;
var t = clone.transform;
var cc = clone.GetComponent<CharacterController>();
Vector3 pos = t.position;
float up = 2.5f;
float down = 10f;
float radius = cc != null ? cc.radius : 0.25f;
Vector3 origin = pos + Vector3.up * up;
if (Physics.SphereCast(origin, Mathf.Max(0.05f, radius * 0.9f), Vector3.down, out RaycastHit hit, up + down, ~0, QueryTriggerInteraction.Ignore))
{
float centerY = cc != null ? cc.center.y : 0f;
float height = cc != null ? cc.height : 2f;
float targetY = hit.point.y + (height * 0.5f) - centerY + 0.02f;
t.position = new Vector3(pos.x, targetY, pos.z);
}
}
private void ConfigureCloneForPlayer(GameObject clone, LobbyPlayerInfo info, int index)
{
// Ensure network identity is deterministic across clients.
var identity = clone.GetComponent<NetworkIdentity>() ?? clone.AddComponent<NetworkIdentity>();
identity.SetNetworkId(index + 1);
identity.SetNetworkId(NetworkIdAllocator.PlayerIdStart + index);
var bridge = clone.GetComponent<SteamCharacterNetworkBridge>() ?? clone.AddComponent<SteamCharacterNetworkBridge>();
bridge.AssignOwner(ParseSteamId(info.SteamId), info.IsLocal);

View File

@@ -0,0 +1,80 @@
using UnityEngine;
namespace MegaKoop.Game.Networking
{
internal static class NetworkIdAllocator
{
public const int PlayerIdStart = 1;
public const int EnemyIdStart = 10000;
private const int EnemyIdRange = 10000;
private static int nextEnemyId = EnemyIdStart;
private static readonly System.Collections.Generic.HashSet<int> ActiveEnemyIds = new();
[RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.BeforeSceneLoad)]
private static void ResetOnLoad()
{
Reset();
}
public static void Reset()
{
nextEnemyId = EnemyIdStart;
ActiveEnemyIds.Clear();
}
public static int AllocateEnemyId()
{
if (nextEnemyId < EnemyIdStart)
{
nextEnemyId = EnemyIdStart;
}
const int maxAttempts = EnemyIdRange;
int attempts = 0;
while ((ActiveEnemyIds.Contains(nextEnemyId) || NetworkIdRegistry.IsIdRegistered(nextEnemyId) || NetworkIdRegistry.IsIdReserved(nextEnemyId)) && attempts < maxAttempts)
{
AdvanceEnemyCursor();
attempts++;
}
int allocated = nextEnemyId;
AdvanceEnemyCursor();
ActiveEnemyIds.Add(allocated);
return allocated;
}
public static bool IsPlayerId(int id) => id >= PlayerIdStart && id < EnemyIdStart;
public static bool IsEnemyId(int id) => id >= EnemyIdStart;
public static void SyncEnemyCursor(int id)
{
if (id >= EnemyIdStart && id >= nextEnemyId)
{
nextEnemyId = id + 1;
}
if (id >= EnemyIdStart)
{
ActiveEnemyIds.Add(id);
}
}
public static void ReleaseEnemyId(int id)
{
if (id >= EnemyIdStart)
{
ActiveEnemyIds.Remove(id);
}
}
private static void AdvanceEnemyCursor()
{
nextEnemyId++;
if (nextEnemyId >= EnemyIdStart + EnemyIdRange)
{
nextEnemyId = EnemyIdStart;
}
}
}
}

View File

@@ -0,0 +1,2 @@
fileFormatVersion: 2
guid: bda656b26417ac8de83785cb9d5b853d

View File

@@ -242,6 +242,7 @@ namespace MegaKoop.Game.Networking
instance.registry.Clear();
instance.steamIdToNetworkId.Clear();
instance.reservedIds.Clear();
NetworkIdAllocator.Reset();
Debug.Log($"[NetworkIdRegistry] Registry cleared. Removed {registryCount} identities, {mappingCount} Steam ID mappings, and {reservedCount} reserved IDs.");
}

View File

@@ -14,28 +14,37 @@ namespace MegaKoop.Game.Networking
{
// Note: Auto-increment removed in favor of deterministic ID generation
// IDs should be assigned via SetNetworkId() before or during Awake
if (assignOnAwake && networkId == 0)
if (networkId != 0)
{
Debug.LogWarning($"[NetworkIdentity] {name} has assignOnAwake=true but no ID assigned. " +
"Use DeterministicIdGenerator or manually call SetNetworkId().");
Register();
}
Register();
}
private void OnDestroy()
{
NetworkIdRegistry.Unregister(networkId);
if (networkId != 0)
{
if (NetworkIdAllocator.IsEnemyId(networkId))
{
NetworkIdAllocator.ReleaseEnemyId(networkId);
}
NetworkIdRegistry.Unregister(networkId);
networkId = 0;
}
}
private void Register()
{
if (networkId == 0)
{
Debug.LogWarning($"[NetworkIdentity] {name} has no network id and won't be tracked.");
return;
}
if (NetworkIdAllocator.IsEnemyId(networkId))
{
NetworkIdAllocator.SyncEnemyCursor(networkId);
}
if (!NetworkIdRegistry.TryRegister(this))
{
Debug.LogError($"[NetworkIdentity] Failed to register {name} with ID {networkId}. " +
@@ -72,11 +81,21 @@ namespace MegaKoop.Game.Networking
// Unregister old ID if it was registered
if (networkId != 0)
{
if (NetworkIdAllocator.IsEnemyId(networkId))
{
NetworkIdAllocator.ReleaseEnemyId(networkId);
}
NetworkIdRegistry.Unregister(networkId);
}
networkId = id;
if (NetworkIdAllocator.IsEnemyId(networkId))
{
NetworkIdAllocator.SyncEnemyCursor(networkId);
}
// Register with new ID
if (!NetworkIdRegistry.TryRegister(this))
{
@@ -89,5 +108,25 @@ namespace MegaKoop.Game.Networking
/// Gets the current network ID without triggering registration.
/// </summary>
public int GetNetworkId() => networkId;
/// <summary>
/// Clears the current network ID and unregisters without emitting duplicate warnings.
/// </summary>
public void ClearNetworkId()
{
if (networkId == 0)
{
return;
}
int id = networkId;
NetworkIdRegistry.Unregister(id);
if (NetworkIdAllocator.IsEnemyId(id))
{
NetworkIdAllocator.ReleaseEnemyId(id);
}
networkId = 0;
}
}
}

View File

@@ -15,7 +15,8 @@ namespace MegaKoop.Game.Networking
ProjectileImpact = 7,
GameState = 8,
EnemySpawn = 9,
EnemyDespawn = 10
EnemyDespawn = 10,
CharacterAnim = 11
}
public enum ProjectileImpactKind : byte
@@ -108,6 +109,71 @@ namespace MegaKoop.Game.Networking
}
}
public readonly struct CharacterAnimMessage
{
public readonly int NetworkId;
public readonly float MoveX;
public readonly float MoveZ;
public readonly float Speed;
public readonly float MoveSpeedNormalized;
public readonly bool IsGrounded;
public readonly bool IsCrouching;
public readonly bool IsDead;
public readonly bool IsJumping;
public CharacterAnimMessage(
int networkId,
float moveX,
float moveZ,
float speed,
float moveSpeedNormalized,
bool isGrounded,
bool isCrouching,
bool isDead,
bool isJumping)
{
NetworkId = networkId;
MoveX = moveX;
MoveZ = moveZ;
Speed = speed;
MoveSpeedNormalized = moveSpeedNormalized;
IsGrounded = isGrounded;
IsCrouching = isCrouching;
IsDead = isDead;
IsJumping = isJumping;
}
public static byte[] Serialize(CharacterAnimMessage message)
{
using var writer = new NetworkWriter();
writer.Write(message.NetworkId);
writer.Write(message.MoveX);
writer.Write(message.MoveZ);
writer.Write(message.Speed);
writer.Write(message.MoveSpeedNormalized);
writer.Write(message.IsGrounded);
writer.Write(message.IsCrouching);
writer.Write(message.IsDead);
writer.Write(message.IsJumping);
return writer.ToArray();
}
public static CharacterAnimMessage Deserialize(byte[] buffer)
{
using var reader = new NetworkReader(buffer);
int id = reader.ReadInt();
float moveX = reader.ReadFloat();
float moveZ = reader.ReadFloat();
float speed = reader.ReadFloat();
float moveSpeedNormalized = reader.ReadFloat();
bool isGrounded = reader.ReadBool();
bool isCrouching = reader.ReadBool();
bool isDead = reader.ReadBool();
bool isJumping = reader.ReadBool();
return new CharacterAnimMessage(id, moveX, moveZ, speed, moveSpeedNormalized, isGrounded, isCrouching, isDead, isJumping);
}
}
public readonly struct WeaponFireMessage
{
public readonly int NetworkId;

View File

@@ -1,5 +1,6 @@
using Steamworks;
using UnityEngine;
using MegaKoop.Game.UI;
namespace MegaKoop.Game.Networking
{
@@ -8,6 +9,7 @@ namespace MegaKoop.Game.Networking
{
[Header("References")]
[SerializeField] private ThirdPersonCharacterController characterController;
[SerializeField] private Animator animator;
[SerializeField] private NetworkIdentity identity;
[SerializeField] private Transform rootTransform;
[SerializeField] private NetworkCharacterInputProxy networkInputProxy;
@@ -29,12 +31,14 @@ namespace MegaKoop.Game.Networking
private Vector3 remoteTargetVelocity;
private bool haveRemoteState;
private bool localOverrideSet;
private int expectedNetworkId;
public void AssignOwner(ulong steamId, bool localPlayer)
{
ownerSteamId = steamId;
isLocalPlayer = localPlayer;
localOverrideSet = true;
CaptureExpectedNetworkId();
UpdateAuthority();
ConfigureController();
}
@@ -70,13 +74,29 @@ namespace MegaKoop.Game.Networking
}
}
if (animator == null)
{
animator = GetComponent<Animator>();
if (animator != null)
{
animator.cullingMode = AnimatorCullingMode.AlwaysAnimate;
}
}
remoteTargetPosition = rootTransform.position;
remoteTargetRotation = rootTransform.rotation;
var nameTag = GetComponent<SteamNameTag>();
if (nameTag == null)
{
nameTag = gameObject.AddComponent<SteamNameTag>();
}
nameTag.SetFollowHead(null, this);
}
private void Start()
{
networkManager = SteamCoopNetworkManager.Instance;
CaptureExpectedNetworkId();
UpdateAuthority();
ConfigureController();
}
@@ -84,6 +104,9 @@ namespace MegaKoop.Game.Networking
private void OnEnable()
{
networkManager = SteamCoopNetworkManager.Instance;
CaptureExpectedNetworkId();
UpdateAuthority();
ConfigureController();
RegisterHandlers();
}
@@ -136,6 +159,7 @@ namespace MegaKoop.Game.Networking
networkManager.RegisterHandler(NetworkMessageType.PlayerInput, HandlePlayerInputMessage);
networkManager.RegisterHandler(NetworkMessageType.CharacterTransform, HandleCharacterTransformMessage);
networkManager.RegisterHandler(NetworkMessageType.CharacterAnim, HandleCharacterAnimMessage);
isRegistered = true;
}
@@ -148,6 +172,7 @@ namespace MegaKoop.Game.Networking
networkManager.UnregisterHandler(NetworkMessageType.PlayerInput, HandlePlayerInputMessage);
networkManager.UnregisterHandler(NetworkMessageType.CharacterTransform, HandleCharacterTransformMessage);
networkManager.UnregisterHandler(NetworkMessageType.CharacterAnim, HandleCharacterAnimMessage);
isRegistered = false;
}
@@ -188,6 +213,8 @@ namespace MegaKoop.Game.Networking
if (unityController != null)
{
unityController.enabled = true;
unityController.detectCollisions = true;
unityController.enableOverlapRecovery = true;
}
}
else
@@ -197,7 +224,9 @@ namespace MegaKoop.Game.Networking
var unityController = characterController.GetComponent<UnityEngine.CharacterController>();
if (unityController != null)
{
unityController.enabled = false;
unityController.enabled = true;
unityController.detectCollisions = true;
unityController.enableOverlapRecovery = false;
}
}
}
@@ -209,12 +238,54 @@ namespace MegaKoop.Game.Networking
return;
}
if (identity.NetworkId == 0)
{
return;
}
var unityController = GetComponent<CharacterController>();
Vector3 velocity = unityController != null ? unityController.velocity : Vector3.zero;
SteamCharacterStateCache.ReportLocalState(identity.NetworkId, rootTransform.position, rootTransform.rotation, velocity);
var message = new CharacterTransformMessage(identity.NetworkId, rootTransform.position, rootTransform.rotation, velocity);
byte[] payload = CharacterTransformMessage.Serialize(message);
networkManager.SendToAll(NetworkMessageType.CharacterTransform, payload, EP2PSend.k_EP2PSendUnreliableNoDelay);
BroadcastAnimatorParameters();
}
private void BroadcastAnimatorParameters()
{
if (networkManager == null || identity == null || animator == null)
{
return;
}
if (identity.NetworkId == 0)
{
return;
}
float moveX = animator.GetFloat("MoveX");
float moveZ = animator.GetFloat("MoveZ");
float speed = animator.GetFloat("Speed");
float moveSpeedNorm = animator.GetFloat("MoveSpeedNormalized");
bool isGround = animator.GetBool("IsGrounded");
bool isCrouch = animator.GetBool("IsCrouching");
bool isDeadFlag = animator.GetBool("IsDead");
bool isJump = animator.GetBool("IsJumping");
var animMsg = new CharacterAnimMessage(
identity.NetworkId,
moveX,
moveZ,
speed,
moveSpeedNorm,
isGround,
isCrouch,
isDeadFlag,
isJump);
byte[] animPayload = CharacterAnimMessage.Serialize(animMsg);
networkManager.SendToAll(NetworkMessageType.CharacterAnim, animPayload, EP2PSend.k_EP2PSendUnreliableNoDelay);
}
private void HandlePlayerInputMessage(NetworkMessage message)
@@ -241,24 +312,9 @@ namespace MegaKoop.Game.Networking
}
CharacterTransformMessage transformMessage = CharacterTransformMessage.Deserialize(message.Payload);
if (identity != null && transformMessage.NetworkId != identity.NetworkId)
if (!EnsureMatchingNetworkId(transformMessage.NetworkId, message.Sender))
{
if (ownerSteamId != 0 && message.Sender != ownerSteamId)
{
return;
}
var existing = NetworkIdRegistry.GetById(transformMessage.NetworkId);
if (existing != null && existing != identity)
{
return;
}
identity.SetNetworkId(transformMessage.NetworkId);
if (identity.NetworkId != transformMessage.NetworkId)
{
return;
}
return;
}
remoteTargetPosition = transformMessage.Position;
@@ -267,6 +323,125 @@ namespace MegaKoop.Game.Networking
haveRemoteState = true;
}
private void HandleCharacterAnimMessage(NetworkMessage message)
{
if (isAuthority)
{
return;
}
CharacterAnimMessage anim = CharacterAnimMessage.Deserialize(message.Payload);
if (!EnsureMatchingNetworkId(anim.NetworkId, message.Sender))
{
return;
}
if (animator == null)
{
animator = GetComponent<Animator>();
if (animator == null)
{
return;
}
}
animator.SetFloat("MoveX", anim.MoveX);
animator.SetFloat("MoveZ", anim.MoveZ);
animator.SetFloat("Speed", anim.Speed);
animator.SetFloat("MoveSpeedNormalized", anim.MoveSpeedNormalized);
animator.SetBool("IsGrounded", anim.IsGrounded);
animator.SetBool("IsCrouching", anim.IsCrouching);
animator.SetBool("IsDead", anim.IsDead);
animator.SetBool("IsJumping", anim.IsJumping);
}
private void CaptureExpectedNetworkId()
{
if (identity == null)
{
identity = GetComponent<NetworkIdentity>();
}
if (identity == null)
{
return;
}
if (identity.NetworkId != 0)
{
expectedNetworkId = identity.NetworkId;
if (ownerSteamId != 0)
{
var mapped = NetworkIdRegistry.GetNetworkIdForSteamId(ownerSteamId);
if (mapped != expectedNetworkId)
{
NetworkIdRegistry.MapSteamIdToNetworkId(ownerSteamId, expectedNetworkId);
}
}
}
}
private bool EnsureMatchingNetworkId(int incomingId, ulong sender)
{
if (identity == null)
{
return false;
}
if (incomingId == 0)
{
return false;
}
int currentId = identity.NetworkId;
if (currentId == incomingId)
{
return true;
}
if (currentId != 0)
{
return false;
}
if (ownerSteamId != 0 && sender != ownerSteamId)
{
return false;
}
int mappedId = ownerSteamId != 0 ? NetworkIdRegistry.GetNetworkIdForSteamId(ownerSteamId) : expectedNetworkId;
if (mappedId != 0)
{
identity.SetNetworkId(mappedId);
if (identity.NetworkId == mappedId)
{
expectedNetworkId = mappedId;
return mappedId == incomingId;
}
return false;
}
if (!NetworkIdAllocator.IsPlayerId(incomingId))
{
return false;
}
identity.SetNetworkId(incomingId);
if (identity.NetworkId != incomingId)
{
return false;
}
expectedNetworkId = incomingId;
if (ownerSteamId != 0)
{
NetworkIdRegistry.MapSteamIdToNetworkId(ownerSteamId, incomingId);
}
return true;
}
public void SendLocalInput(Vector2 moveInput, bool jump)
{
if (networkManager == null || identity == null || !isAuthority)
@@ -279,6 +454,11 @@ namespace MegaKoop.Game.Networking
return;
}
if (identity.NetworkId == 0)
{
return;
}
networkManager.SendToAll(NetworkMessageType.PlayerInput, PlayerInputMessage.Serialize(new PlayerInputMessage(identity.NetworkId, moveInput, jump)), EP2PSend.k_EP2PSendUnreliableNoDelay);
}
}

View File

@@ -210,7 +210,7 @@ namespace MegaKoop.Game.Networking
EnemyDefinitionRegistry.Register(definition);
}
if (!DetermineAuthority() || networkManager == null)
if (!IsHostClient() || networkManager == null)
{
if (hasPendingRemoteSpawn)
{
@@ -241,7 +241,7 @@ namespace MegaKoop.Game.Networking
private void HandleInstanceDespawned(GameObject instance, EnemyDefinition definition)
{
if (!DetermineAuthority() || networkManager == null)
if (!IsHostClient() || networkManager == null)
{
return;
}
@@ -265,7 +265,7 @@ namespace MegaKoop.Game.Networking
private void HandleEnemySpawnMessage(NetworkMessage message)
{
if (DetermineAuthority())
if (IsHostClient())
{
return;
}
@@ -279,6 +279,19 @@ namespace MegaKoop.Game.Networking
hasPendingRemoteSpawn = false;
return;
}
EnemyDefinitionRegistry.Register(definition);
if (pendingRemoteSpawn.NetworkId != 0 && NetworkIdentity.TryGet(pendingRemoteSpawn.NetworkId, out var existingIdentity) && existingIdentity != null)
{
var existingInstance = existingIdentity.gameObject;
if (existingInstance != null)
{
existingInstance.SetActive(true);
existingInstance.transform.SetPositionAndRotation(pendingRemoteSpawn.Position + definition.PrefabPivotOffset, pendingRemoteSpawn.Rotation);
hasPendingRemoteSpawn = false;
}
return;
}
bool success = spawner != null && spawner.TrySpawn(definition, pendingRemoteSpawn.Position);
if (success)
@@ -302,7 +315,7 @@ namespace MegaKoop.Game.Networking
private void HandleEnemyDespawnMessage(NetworkMessage message)
{
if (DetermineAuthority())
if (IsHostClient())
{
return;
}
@@ -351,5 +364,11 @@ namespace MegaKoop.Game.Networking
identity.SetNetworkId(pendingRemoteSpawn.NetworkId);
}
}
private bool IsHostClient()
{
RefreshNetworkManager();
return networkManager != null && networkManager.IsHost;
}
}
}

View File

@@ -31,6 +31,7 @@ namespace MegaKoop.Game.Networking
private Quaternion remoteTargetRotation;
private Vector3 remoteTargetVelocity;
private bool haveRemoteState;
private SteamCharacterNetworkBridge characterBridge;
private void Awake()
{
@@ -54,6 +55,13 @@ namespace MegaKoop.Game.Networking
trackedNavMeshAgent = GetComponent<UnityEngine.AI.NavMeshAgent>();
}
characterBridge = GetComponent<SteamCharacterNetworkBridge>();
if (characterBridge != null)
{
enabled = false;
return;
}
remoteTargetPosition = targetTransform.position;
remoteTargetRotation = targetTransform.rotation;
remoteTargetVelocity = Vector3.zero;
@@ -62,6 +70,12 @@ namespace MegaKoop.Game.Networking
private void OnEnable()
{
characterBridge = GetComponent<SteamCharacterNetworkBridge>();
if (characterBridge != null)
{
enabled = false;
return;
}
EnsureNetworkManager();
RegisterHandlers();
}

View File

@@ -126,6 +126,11 @@ namespace MegaKoop.Game.Networking
}
ProjectileSpawnMessage spawnMessage = ProjectileSpawnMessage.Deserialize(message.Payload);
if (identity.NetworkId == 0 && spawnMessage.NetworkId != 0)
{
identity.SetNetworkId(spawnMessage.NetworkId);
}
if (spawnMessage.NetworkId != identity.NetworkId)
{
return;

View File

@@ -4,6 +4,7 @@ using Game.Scripts.Runtime.Abstractions;
using Game.Scripts.Runtime.Data;
using UnityEngine;
using Unity.Netcode;
using MegaKoop.Game.Networking;
namespace Game.Scripts.Runtime.Pooling
{
@@ -144,6 +145,8 @@ namespace Game.Scripts.Runtime.Pooling
{
Destroy(netObj);
}
var identity = instance.GetComponent<NetworkIdentity>();
identity?.ClearNetworkId();
bucket.Available.Enqueue(instance);
item.Handle?.NotifyDespawned();
InstanceDespawned?.Invoke(instance, item.Definition);
@@ -183,6 +186,9 @@ namespace Game.Scripts.Runtime.Pooling
go.name = $"{definition.Prefab.name}_Pooled";
go.SetActive(false);
var identity = go.GetComponent<NetworkIdentity>();
identity?.ClearNetworkId();
var handle = go.GetComponent<PooledInstance>();
if (handle == null)
{

View File

@@ -17,6 +17,10 @@ namespace MegaKoop.Game
[SerializeField] private float jumpHeight = 1.6f;
[SerializeField] private float gravity = -20f;
[SerializeField] private float groundedGravity = -5f;
[SerializeField, Range(0f, 0.3f)] private float jumpBufferTime = 0.1f;
[SerializeField, Range(0f, 0.3f)] private float coyoteTime = 0.1f;
[SerializeField, Min(0.5f)] private float upwardGravityMultiplier = 1f;
[SerializeField, Min(1f)] private float fallGravityMultiplier = 2.5f;
[Header("Camera Reference")]
[SerializeField] private Transform cameraTransform;
@@ -32,16 +36,20 @@ namespace MegaKoop.Game
private bool isGrounded;
private bool lastGrounded;
private bool isDead;
private bool isJumping;
private MegaKoop.Game.Networking.ICharacterInputSource inputSource;
private float lastJumpPressedTime = float.NegativeInfinity;
private float lastTimeGrounded = float.NegativeInfinity;
// Animator parameter hashes
private int hashMoveX;
private int hashMoveZ;
private int hashSpeed;
private int hashMoveSpeedNormalized;
private int hashIsGrounded;
private int hashIsCrouching;
private int hashIsDead;
private int hashJump;
private int hashIsJumping;
private bool animatorHashesInitialized;
private void Reset()
@@ -78,6 +86,7 @@ namespace MegaKoop.Game
{
EnsureAnimatorReference();
InitializeAnimatorHashes();
SnapToGroundImmediate();
}
private void Update()
@@ -86,6 +95,8 @@ namespace MegaKoop.Game
InitializeAnimatorHashes();
Vector2 moveInput = ReadMovementInput();
TrackJumpInput();
Vector3 desiredMove = CalculateDesiredMove(moveInput);
bool hasMoveInput = desiredMove.sqrMagnitude > 0f;
@@ -110,6 +121,31 @@ namespace MegaKoop.Game
UpdateAnimator();
}
private void SnapToGroundImmediate()
{
if (characterController == null)
{
characterController = GetComponent<UnityEngine.CharacterController>();
}
Vector3 pos = transform.position;
float up = 2.5f;
float down = 10f;
float radius = characterController != null ? characterController.radius : 0.25f;
Vector3 origin = pos + Vector3.up * up;
if (Physics.SphereCast(origin, Mathf.Max(0.05f, radius * 0.9f), Vector3.down, out RaycastHit hit, up + down, ~0, QueryTriggerInteraction.Ignore))
{
float centerY = characterController != null ? characterController.center.y : 0f;
float height = characterController != null ? characterController.height : 2f;
float bottomToCenter = Mathf.Max(radius, height * 0.5f) - radius;
float targetY = hit.point.y + bottomToCenter - centerY + 0.02f;
transform.position = new Vector3(pos.x, targetY, pos.z);
isGrounded = true;
verticalVelocity = groundedGravity;
lastTimeGrounded = Time.time;
}
}
public void SetInputSource(MegaKoop.Game.Networking.ICharacterInputSource source)
{
inputSource = source;
@@ -192,39 +228,65 @@ namespace MegaKoop.Game
private void UpdateGroundedStateBeforeGravity()
{
if (isGrounded && verticalVelocity < 0f)
if (isGrounded)
{
verticalVelocity = groundedGravity;
lastTimeGrounded = Time.time;
}
}
private void HandleJumpInput()
{
if (!isGrounded)
bool bufferedJump = Time.time - lastJumpPressedTime <= jumpBufferTime;
bool coyoteAvailable = Time.time - lastTimeGrounded <= coyoteTime;
if (!bufferedJump)
{
return;
}
if (ShouldJumpThisFrame())
if (isGrounded || coyoteAvailable)
{
verticalVelocity = Mathf.Sqrt(jumpHeight * -2f * gravity);
isGrounded = false;
isJumping = true;
lastJumpPressedTime = float.NegativeInfinity;
}
}
private bool ShouldJumpThisFrame()
private void TrackJumpInput()
{
bool jumpPressed = false;
if (inputSource != null)
{
return inputSource.JumpPressed;
jumpPressed = inputSource.JumpPressed;
}
else if (Input.GetButtonDown("Jump"))
{
jumpPressed = true;
}
return Input.GetButtonDown("Jump");
if (jumpPressed)
{
lastJumpPressedTime = Time.time;
}
}
private void ApplyGravity()
{
verticalVelocity += gravity * Time.deltaTime;
float gravityMultiplier = verticalVelocity > 0f ? upwardGravityMultiplier : fallGravityMultiplier;
float currentGravity = gravity * gravityMultiplier;
if (isGrounded && verticalVelocity < 0f)
{
verticalVelocity = groundedGravity;
lastTimeGrounded = Time.time;
isJumping = false;
}
else
{
verticalVelocity += currentGravity * Time.deltaTime;
}
}
private void UpdateAnimator()
@@ -241,24 +303,21 @@ namespace MegaKoop.Game
float denom = Mathf.Max(0.01f, moveSpeed);
float moveX = Mathf.Clamp(localVelocity.x / denom, -1f, 1f);
float moveZ = Mathf.Clamp(localVelocity.z / denom, -1f, 1f);
float normalizedSpeed = Mathf.Clamp01(speed / moveSpeed);
// Update animator parameters
animator.SetFloat(hashSpeed, speed);
animator.SetFloat(hashMoveSpeedNormalized, normalizedSpeed);
animator.SetFloat(hashMoveX, moveX, animationDamping, Time.deltaTime);
animator.SetFloat(hashMoveZ, moveZ, animationDamping, Time.deltaTime);
animator.SetBool(hashIsGrounded, isGrounded);
animator.SetBool(hashIsJumping, isJumping);
// Crouch input (currently only supports local input, can be extended via inputSource)
bool isCrouching = !isDead && Input.GetKey(crouchKey);
animator.SetBool(hashIsCrouching, isCrouching);
// Jump trigger - when leaving ground with upward velocity
if (lastGrounded && !isGrounded && verticalVelocity > 0.1f)
{
animator.ResetTrigger(hashJump);
animator.SetTrigger(hashJump);
}
animator.SetBool(hashIsDead, isDead);
lastGrounded = isGrounded;
}
@@ -283,6 +342,10 @@ namespace MegaKoop.Game
gravity = Mathf.Min(-0.01f, gravity);
groundedGravity = Mathf.Clamp(groundedGravity, gravity, 0f);
animationDamping = Mathf.Max(0f, animationDamping);
jumpBufferTime = Mathf.Clamp(jumpBufferTime, 0f, 0.3f);
coyoteTime = Mathf.Clamp(coyoteTime, 0f, 0.3f);
upwardGravityMultiplier = Mathf.Max(0.5f, upwardGravityMultiplier);
fallGravityMultiplier = Mathf.Max(1f, fallGravityMultiplier);
EnsureAnimatorReference();
InitializeAnimatorHashes();
}
@@ -309,7 +372,8 @@ namespace MegaKoop.Game
hashIsGrounded = Animator.StringToHash("IsGrounded");
hashIsCrouching = Animator.StringToHash("IsCrouching");
hashIsDead = Animator.StringToHash("IsDead");
hashJump = Animator.StringToHash("Jump");
hashIsJumping = Animator.StringToHash("IsJumping");
hashMoveSpeedNormalized = Animator.StringToHash("MoveSpeedNormalized");
animatorHashesInitialized = true;
}
}

View File

@@ -0,0 +1,250 @@
using UnityEngine;
using TMPro;
using Steamworks;
using MegaKoop.Game.Networking;
namespace MegaKoop.Game.UI
{
[DefaultExecutionOrder(10000)]
[DisallowMultipleComponent]
public class SteamNameTag : MonoBehaviour
{
[SerializeField] private Transform followHead;
[SerializeField] private Vector3 worldOffset = new Vector3(0f, 2.5f, 0f);
[SerializeField] private bool billboardToCamera = true;
[SerializeField] private bool hideForLocal = true;
[SerializeField] private float visibleDistance = 60f;
private TextMeshPro text;
private Transform tagTransform;
private Transform camTransform;
private SteamCharacterNetworkBridge bridge;
private Callback<PersonaStateChange_t> personaChanged;
private ulong lastSteamId;
private float camFindTimer;
private Coroutine ensureActiveRoutine;
private void Awake()
{
// Reuse existing NameTag child if present (prevents duplicates when cloning templates)
var existing = transform.Find("NameTag");
GameObject go;
if (existing != null)
{
tagTransform = existing;
go = existing.gameObject;
}
else
{
go = new GameObject("NameTag");
go.layer = gameObject.layer;
tagTransform = go.transform;
tagTransform.SetParent(transform, worldPositionStays: false);
}
if (!go.activeSelf) go.SetActive(true);
text = go.GetComponent<TextMeshPro>();
if (text == null)
{
text = go.AddComponent<TextMeshPro>();
}
text.alignment = TextAlignmentOptions.Center;
text.color = Color.white;
text.fontSize = 2.5f;
text.enableAutoSizing = true;
text.fontSizeMin = 1.5f;
text.fontSizeMax = 4.5f;
text.text = GetObjectDisplayName();
if (!text.gameObject.activeSelf) text.gameObject.SetActive(true);
text.enabled = true;
bridge = GetComponent<SteamCharacterNetworkBridge>();
if (followHead == null)
{
followHead = FindChildRecursive(transform, "Head");
if (followHead == null)
{
followHead = transform;
worldOffset = new Vector3(0f, 2.5f, 0f);
}
}
}
public void SetFollowHead(Transform head, SteamCharacterNetworkBridge b)
{
if (head != null) followHead = head;
if (b != null) bridge = b;
UpdateDisplayName(true);
}
private void OnEnable()
{
camTransform = Camera.main != null ? Camera.main.transform : null;
TryCreatePersonaCallback();
if (tagTransform != null && !tagTransform.gameObject.activeSelf)
{
tagTransform.gameObject.SetActive(true);
}
if (text != null)
{
if (!text.gameObject.activeSelf) text.gameObject.SetActive(true);
text.enabled = true;
}
if (ensureActiveRoutine == null)
{
ensureActiveRoutine = StartCoroutine(EnsureActiveLoop());
}
UpdateDisplayName(true);
}
private void OnDisable()
{
personaChanged = null;
if (ensureActiveRoutine != null)
{
StopCoroutine(ensureActiveRoutine);
ensureActiveRoutine = null;
}
}
private void TryCreatePersonaCallback()
{
if (personaChanged == null)
{
personaChanged = Callback<PersonaStateChange_t>.Create(OnPersonaChanged);
}
}
private System.Collections.IEnumerator EnsureActiveLoop()
{
var waiter = new WaitForEndOfFrame();
while (isActiveAndEnabled)
{
if (tagTransform != null && !tagTransform.gameObject.activeSelf)
{
tagTransform.gameObject.SetActive(true);
}
if (text != null)
{
if (!text.gameObject.activeSelf) text.gameObject.SetActive(true);
text.enabled = true;
}
yield return waiter;
}
}
private void OnPersonaChanged(PersonaStateChange_t cb)
{
if (bridge == null) return;
if (cb.m_ulSteamID == bridge.OwnerSteamId)
{
UpdateDisplayName(true);
}
}
private void LateUpdate()
{
camFindTimer -= Time.unscaledDeltaTime;
if ((camTransform == null || !camTransform.gameObject.activeInHierarchy) && camFindTimer <= 0f)
{
var cam = Camera.main;
camTransform = cam != null ? cam.transform : null;
camFindTimer = 1f;
}
// Never deactivate the NameTag GameObject; only toggle text rendering
if (tagTransform != null && !tagTransform.gameObject.activeSelf)
{
tagTransform.gameObject.SetActive(true);
}
Vector3 basePos = followHead != null ? followHead.position : transform.position;
tagTransform.position = basePos + worldOffset;
if (billboardToCamera && camTransform != null)
{
Vector3 dir = tagTransform.position - camTransform.position;
if (dir.sqrMagnitude > 0.001f)
{
tagTransform.rotation = Quaternion.LookRotation(dir, Vector3.up);
}
}
if (text != null)
{
if (!text.gameObject.activeSelf) text.gameObject.SetActive(true);
text.enabled = true;
}
UpdateDisplayName(false);
}
private void UpdateDisplayName(bool force)
{
if (text == null) return;
ulong sid = 0;
if (bridge != null) sid = bridge.OwnerSteamId;
if (!force && sid == lastSteamId && !string.IsNullOrEmpty(text.text)) return;
lastSteamId = sid;
string display = null;
if (sid != 0)
{
display = ResolvePersonaName(sid);
}
if (string.IsNullOrWhiteSpace(display))
{
display = GetObjectDisplayName();
}
if (!string.Equals(text.text, display))
{
text.text = display;
}
}
private string ResolvePersonaName(ulong sid)
{
if (!SteamBootstrap.IsInitialized)
{
return null;
}
ulong local = SteamUser.GetSteamID().m_SteamID;
if (sid == local)
{
return SteamFriends.GetPersonaName();
}
var csid = new CSteamID(sid);
string name = SteamFriends.GetFriendPersonaName(csid);
if (string.IsNullOrEmpty(name) || name == "???")
{
SteamFriends.RequestUserInformation(csid, true);
}
return name;
}
private string GetObjectDisplayName()
{
string n = transform.root != null ? transform.root.name : gameObject.name;
if (n.StartsWith("Wizard_"))
{
return n.Substring("Wizard_".Length);
}
return n;
}
private static Transform FindChildRecursive(Transform root, string name)
{
if (root == null) return null;
foreach (Transform c in root)
{
if (c.name == name) return c;
var r = FindChildRecursive(c, name);
if (r != null) return r;
}
return null;
}
}
}

View File

@@ -0,0 +1,2 @@
fileFormatVersion: 2
guid: 79fb1fbc20d80f546ba695c751860930

View File

@@ -12,6 +12,8 @@ GameObject:
- component: {fileID: 3684779780933677124}
- component: {fileID: 938331400041015936}
- component: {fileID: 8192567680868647748}
- component: {fileID: 4756281759482734665}
- component: {fileID: 8304926713945689473}
m_Layer: 0
m_Name: SpawnController
m_TagString: Untagged
@@ -92,3 +94,35 @@ MonoBehaviour:
poolRoot: {fileID: 0}
markAsDontDestroyOnLoad: 1
defaultHardCap: 64
--- !u!114 &4756281759482734665
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 3072068626087694729}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: 5137a566078d27c81957d1c4040e32eb, type: 3}
m_Name:
m_EditorClassIdentifier:
spawner: {fileID: 938331400041015936}
gameController: {fileID: 3684779780933677124}
pooler: {fileID: 8192567680868647748}
additionalDefinitions: []
autoFindReferences: 0
--- !u!114 &8304926713945689473
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 3072068626087694729}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: a4ee45b08060ff11b9c4de6b6ab9d671, type: 3}
m_Name:
m_EditorClassIdentifier:
gameController: {fileID: 3684779780933677124}
enemySpawner: {fileID: 938331400041015936}
heartbeatInterval: 0.5

View File

@@ -14,7 +14,7 @@ MonoBehaviour:
m_EditorClassIdentifier: Assembly-CSharp::Game.Scripts.Runtime.Data.SpawnerConfig
MinSpawnRadius: 8
MaxSpawnRadius: 24
SpawnInterval: 10
SpawnInterval: 5
SpawnRateOverTime:
serializedVersion: 2
m_Curve:
@@ -39,7 +39,7 @@ MonoBehaviour:
m_PreInfinity: 2
m_PostInfinity: 2
m_RotationOrder: 4
MaxConcurrent: 20
MaxConcurrent: 50
MaxPlacementAttempts: 6
PrewarmPerType: 4
PoolHardCapPerType: 60

View File

@@ -1,6 +1,34 @@
%YAML 1.1
%TAG !u! tag:unity3d.com,2011:
--- !u!1101 &-8653239217332838315
--- !u!1102 &-9159441798702175618
AnimatorState:
serializedVersion: 6
m_ObjectHideFlags: 1
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_Name: Jump Land
m_Speed: 1
m_CycleOffset: 0
m_Transitions:
- {fileID: 3313327580545924896}
- {fileID: 1851180482452469980}
m_StateMachineBehaviours: []
m_Position: {x: 50, y: 50, z: 0}
m_IKOnFeet: 0
m_WriteDefaultValues: 1
m_Mirror: 0
m_SpeedParameterActive: 0
m_MirrorParameterActive: 0
m_CycleOffsetParameterActive: 0
m_TimeParameterActive: 0
m_Motion: {fileID: 3094330708855449807, guid: c969c57136eab8b48b882fdc45e975c4, type: 3}
m_Tag:
m_SpeedParameter:
m_MirrorParameter:
m_CycleOffsetParameter:
m_TimeParameter:
--- !u!1101 &-5633800869693003173
AnimatorStateTransition:
m_ObjectHideFlags: 1
m_CorrespondingSourceObject: {fileID: 0}
@@ -15,7 +43,7 @@ AnimatorStateTransition:
m_ConditionEvent: Speed
m_EventTreshold: 0.1
m_DstStateMachine: {fileID: 0}
m_DstState: {fileID: 2133809347797768411}
m_DstState: {fileID: 5754690394921863263}
m_Solo: 0
m_Mute: 0
m_IsExit: 0
@@ -28,34 +56,116 @@ AnimatorStateTransition:
m_InterruptionSource: 0
m_OrderedInterruption: 1
m_CanTransitionToSelf: 1
--- !u!1102 &-8501351176590210390
--- !u!1101 &-5481894409494695167
AnimatorStateTransition:
m_ObjectHideFlags: 1
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_Name:
m_Conditions:
- m_ConditionMode: 2
m_ConditionEvent: IsCrouching
m_EventTreshold: 0
- m_ConditionMode: 3
m_ConditionEvent: Speed
m_EventTreshold: 0.1
m_DstStateMachine: {fileID: 0}
m_DstState: {fileID: -4140020287260570153}
m_Solo: 0
m_Mute: 0
m_IsExit: 0
serializedVersion: 3
m_TransitionDuration: 0.1
m_TransitionOffset: 0
m_ExitTime: 0.9
m_HasExitTime: 0
m_HasFixedDuration: 1
m_InterruptionSource: 0
m_OrderedInterruption: 1
m_CanTransitionToSelf: 1
--- !u!1101 &-4182145420113595913
AnimatorStateTransition:
m_ObjectHideFlags: 1
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_Name:
m_Conditions:
- m_ConditionMode: 4
m_ConditionEvent: Speed
m_EventTreshold: 0.1
m_DstStateMachine: {fileID: 0}
m_DstState: {fileID: 5754690394921863263}
m_Solo: 0
m_Mute: 0
m_IsExit: 0
serializedVersion: 3
m_TransitionDuration: 0.1
m_TransitionOffset: 0
m_ExitTime: 0.9
m_HasExitTime: 0
m_HasFixedDuration: 1
m_InterruptionSource: 0
m_OrderedInterruption: 1
m_CanTransitionToSelf: 1
--- !u!1102 &-4140020287260570153
AnimatorState:
serializedVersion: 6
m_ObjectHideFlags: 1
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_Name: Jump Begin
m_Name: Move
m_Speed: 1
m_CycleOffset: 0
m_Transitions:
- {fileID: 6106028750639478370}
- {fileID: -4182145420113595913}
- {fileID: 3230187453778429532}
m_StateMachineBehaviours: []
m_Position: {x: 50, y: 50, z: 0}
m_IKOnFeet: 0
m_WriteDefaultValues: 1
m_Mirror: 0
m_SpeedParameterActive: 0
m_SpeedParameterActive: 1
m_MirrorParameterActive: 0
m_CycleOffsetParameterActive: 0
m_TimeParameterActive: 0
m_Motion: {fileID: 3094330708855449807, guid: b1844fbe628f5bf4ab29e6c68912a708, type: 3}
m_Motion: {fileID: 1645608330671036843}
m_Tag:
m_SpeedParameter:
m_SpeedParameter: MoveSpeedNormalized
m_MirrorParameter:
m_CycleOffsetParameter:
m_TimeParameter:
--- !u!1102 &-6354293237005665956
--- !u!1101 &-2863568448335919091
AnimatorStateTransition:
m_ObjectHideFlags: 1
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_Name:
m_Conditions:
- m_ConditionMode: 1
m_ConditionEvent: IsJumping
m_EventTreshold: 0
- m_ConditionMode: 2
m_ConditionEvent: IsGrounded
m_EventTreshold: 0
m_DstStateMachine: {fileID: 0}
m_DstState: {fileID: 7993235720092425232}
m_Solo: 0
m_Mute: 0
m_IsExit: 0
serializedVersion: 3
m_TransitionDuration: 0.05
m_TransitionOffset: 0
m_ExitTime: 0.9
m_HasExitTime: 0
m_HasFixedDuration: 1
m_InterruptionSource: 0
m_OrderedInterruption: 1
m_CanTransitionToSelf: 1
--- !u!1102 &-1553988547846161377
AnimatorState:
serializedVersion: 6
m_ObjectHideFlags: 1
@@ -81,43 +191,20 @@ AnimatorState:
m_MirrorParameter:
m_CycleOffsetParameter:
m_TimeParameter:
--- !u!1101 &-4096702130486027139
AnimatorStateTransition:
m_ObjectHideFlags: 1
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_Name:
m_Conditions:
- m_ConditionMode: 4
m_ConditionEvent: Speed
m_EventTreshold: 0.1
m_DstStateMachine: {fileID: 0}
m_DstState: {fileID: 2133809347797768411}
m_Solo: 0
m_Mute: 0
m_IsExit: 0
serializedVersion: 3
m_TransitionDuration: 0.1
m_TransitionOffset: 0
m_ExitTime: 0.9
m_HasExitTime: 0
m_HasFixedDuration: 1
m_InterruptionSource: 0
m_OrderedInterruption: 1
m_CanTransitionToSelf: 1
--- !u!1102 &-3837842955075777776
--- !u!1102 &-1281127197651375096
AnimatorState:
serializedVersion: 6
m_ObjectHideFlags: 1
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_Name: Move
m_Name: Crouch
m_Speed: 1
m_CycleOffset: 0
m_Transitions:
- {fileID: -4096702130486027139}
- {fileID: -5633800869693003173}
- {fileID: -5481894409494695167}
- {fileID: -2863568448335919091}
m_StateMachineBehaviours: []
m_Position: {x: 50, y: 50, z: 0}
m_IKOnFeet: 0
@@ -127,12 +214,37 @@ AnimatorState:
m_MirrorParameterActive: 0
m_CycleOffsetParameterActive: 0
m_TimeParameterActive: 0
m_Motion: {fileID: 7292886750186029930}
m_Motion: {fileID: 3094330708855449807, guid: aa30e50360fde394fb96e9e6c0ba8e18, type: 3}
m_Tag:
m_SpeedParameter:
m_MirrorParameter:
m_CycleOffsetParameter:
m_TimeParameter:
--- !u!1101 &-1175380347819888189
AnimatorStateTransition:
m_ObjectHideFlags: 1
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_Name:
m_Conditions:
- m_ConditionMode: 1
m_ConditionEvent: IsGrounded
m_EventTreshold: 0
m_DstStateMachine: {fileID: 0}
m_DstState: {fileID: -9159441798702175618}
m_Solo: 0
m_Mute: 0
m_IsExit: 0
serializedVersion: 3
m_TransitionDuration: 0.05
m_TransitionOffset: 0
m_ExitTime: 0.9
m_HasExitTime: 0
m_HasFixedDuration: 1
m_InterruptionSource: 0
m_OrderedInterruption: 1
m_CanTransitionToSelf: 1
--- !u!91 &9100000
AnimatorController:
m_ObjectHideFlags: 0
@@ -160,6 +272,12 @@ AnimatorController:
m_DefaultInt: 0
m_DefaultBool: 0
m_Controller: {fileID: 9100000}
- m_Name: MoveSpeedNormalized
m_Type: 1
m_DefaultFloat: 0
m_DefaultInt: 0
m_DefaultBool: 0
m_Controller: {fileID: 9100000}
- m_Name: IsGrounded
m_Type: 4
m_DefaultFloat: 0
@@ -178,8 +296,8 @@ AnimatorController:
m_DefaultInt: 0
m_DefaultBool: 0
m_Controller: {fileID: 9100000}
- m_Name: Jump
m_Type: 9
- m_Name: IsJumping
m_Type: 4
m_DefaultFloat: 0
m_DefaultInt: 0
m_DefaultBool: 0
@@ -187,7 +305,7 @@ AnimatorController:
m_AnimatorLayers:
- serializedVersion: 5
m_Name: Base Layer
m_StateMachine: {fileID: 456980109897493105}
m_StateMachine: {fileID: 8917191963327386668}
m_Mask: {fileID: 0}
m_Motions: []
m_Behaviours: []
@@ -197,105 +315,7 @@ AnimatorController:
m_IKPass: 0
m_SyncedLayerAffectsTiming: 0
m_Controller: {fileID: 9100000}
--- !u!1102 &429358291008205178
AnimatorState:
serializedVersion: 6
m_ObjectHideFlags: 1
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_Name: Jump Land
m_Speed: 1
m_CycleOffset: 0
m_Transitions:
- {fileID: 6017886512958357952}
- {fileID: 9219489292818365840}
m_StateMachineBehaviours: []
m_Position: {x: 50, y: 50, z: 0}
m_IKOnFeet: 0
m_WriteDefaultValues: 1
m_Mirror: 0
m_SpeedParameterActive: 0
m_MirrorParameterActive: 0
m_CycleOffsetParameterActive: 0
m_TimeParameterActive: 0
m_Motion: {fileID: 3094330708855449807, guid: c969c57136eab8b48b882fdc45e975c4, type: 3}
m_Tag:
m_SpeedParameter:
m_MirrorParameter:
m_CycleOffsetParameter:
m_TimeParameter:
--- !u!1107 &456980109897493105
AnimatorStateMachine:
serializedVersion: 6
m_ObjectHideFlags: 1
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_Name: Base Layer
m_ChildStates:
- serializedVersion: 1
m_State: {fileID: 2133809347797768411}
m_Position: {x: 250, y: 50, z: 0}
- serializedVersion: 1
m_State: {fileID: -3837842955075777776}
m_Position: {x: 250, y: 150, z: 0}
- serializedVersion: 1
m_State: {fileID: 3657533443322484474}
m_Position: {x: 250, y: 250, z: 0}
- serializedVersion: 1
m_State: {fileID: -8501351176590210390}
m_Position: {x: 500, y: 50, z: 0}
- serializedVersion: 1
m_State: {fileID: 4880998032211746345}
m_Position: {x: 500, y: 150, z: 0}
- serializedVersion: 1
m_State: {fileID: 429358291008205178}
m_Position: {x: 500, y: 250, z: 0}
- serializedVersion: 1
m_State: {fileID: -6354293237005665956}
m_Position: {x: 250, y: 350, z: 0}
m_ChildStateMachines: []
m_AnyStateTransitions:
- {fileID: 9054913250047092416}
- {fileID: 7541083490971539274}
- {fileID: 8366131237598160117}
m_EntryTransitions: []
m_StateMachineTransitions: {}
m_StateMachineBehaviours: []
m_AnyStatePosition: {x: 50, y: 20, z: 0}
m_EntryPosition: {x: 50, y: 120, z: 0}
m_ExitPosition: {x: 800, y: 120, z: 0}
m_ParentStateMachinePosition: {x: 800, y: 20, z: 0}
m_DefaultState: {fileID: 2133809347797768411}
--- !u!1102 &2133809347797768411
AnimatorState:
serializedVersion: 6
m_ObjectHideFlags: 1
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_Name: Idle
m_Speed: 1
m_CycleOffset: 0
m_Transitions:
- {fileID: 9167710540657582094}
m_StateMachineBehaviours: []
m_Position: {x: 50, y: 50, z: 0}
m_IKOnFeet: 0
m_WriteDefaultValues: 1
m_Mirror: 0
m_SpeedParameterActive: 0
m_MirrorParameterActive: 0
m_CycleOffsetParameterActive: 0
m_TimeParameterActive: 0
m_Motion: {fileID: -2576967968662016515, guid: 56fd86b76fc74d24d83522069f5deb9b, type: 3}
m_Tag:
m_SpeedParameter:
m_MirrorParameter:
m_CycleOffsetParameter:
m_TimeParameter:
--- !u!1101 &3448741829527053982
--- !u!1101 &254728727500707171
AnimatorStateTransition:
m_ObjectHideFlags: 1
m_CorrespondingSourceObject: {fileID: 0}
@@ -304,10 +324,38 @@ AnimatorStateTransition:
m_Name:
m_Conditions:
- m_ConditionMode: 1
m_ConditionEvent: IsCrouching
m_EventTreshold: 0
m_DstStateMachine: {fileID: 0}
m_DstState: {fileID: -1281127197651375096}
m_Solo: 0
m_Mute: 0
m_IsExit: 0
serializedVersion: 3
m_TransitionDuration: 0.1
m_TransitionOffset: 0
m_ExitTime: 0.75
m_HasExitTime: 0
m_HasFixedDuration: 1
m_InterruptionSource: 0
m_OrderedInterruption: 1
m_CanTransitionToSelf: 1
--- !u!1101 &461524094991714154
AnimatorStateTransition:
m_ObjectHideFlags: 1
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_Name:
m_Conditions:
- m_ConditionMode: 1
m_ConditionEvent: IsJumping
m_EventTreshold: 0
- m_ConditionMode: 2
m_ConditionEvent: IsGrounded
m_EventTreshold: 0
m_DstStateMachine: {fileID: 0}
m_DstState: {fileID: 429358291008205178}
m_DstState: {fileID: 7993235720092425232}
m_Solo: 0
m_Mute: 0
m_IsExit: 0
@@ -320,35 +368,7 @@ AnimatorStateTransition:
m_InterruptionSource: 0
m_OrderedInterruption: 1
m_CanTransitionToSelf: 1
--- !u!1102 &3657533443322484474
AnimatorState:
serializedVersion: 6
m_ObjectHideFlags: 1
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_Name: Crouch
m_Speed: 1
m_CycleOffset: 0
m_Transitions:
- {fileID: -8653239217332838315}
- {fileID: 5152896216695493134}
m_StateMachineBehaviours: []
m_Position: {x: 50, y: 50, z: 0}
m_IKOnFeet: 0
m_WriteDefaultValues: 1
m_Mirror: 0
m_SpeedParameterActive: 0
m_MirrorParameterActive: 0
m_CycleOffsetParameterActive: 0
m_TimeParameterActive: 0
m_Motion: {fileID: 3094330708855449807, guid: aa30e50360fde394fb96e9e6c0ba8e18, type: 3}
m_Tag:
m_SpeedParameter:
m_MirrorParameter:
m_CycleOffsetParameter:
m_TimeParameter:
--- !u!1102 &4880998032211746345
--- !u!1102 &861509553362583957
AnimatorState:
serializedVersion: 6
m_ObjectHideFlags: 1
@@ -359,7 +379,7 @@ AnimatorState:
m_Speed: 1
m_CycleOffset: 0
m_Transitions:
- {fileID: 3448741829527053982}
- {fileID: -1175380347819888189}
m_StateMachineBehaviours: []
m_Position: {x: 50, y: 50, z: 0}
m_IKOnFeet: 0
@@ -375,82 +395,7 @@ AnimatorState:
m_MirrorParameter:
m_CycleOffsetParameter:
m_TimeParameter:
--- !u!1101 &5152896216695493134
AnimatorStateTransition:
m_ObjectHideFlags: 1
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_Name:
m_Conditions:
- m_ConditionMode: 2
m_ConditionEvent: IsCrouching
m_EventTreshold: 0
- m_ConditionMode: 3
m_ConditionEvent: Speed
m_EventTreshold: 0.1
m_DstStateMachine: {fileID: 0}
m_DstState: {fileID: -3837842955075777776}
m_Solo: 0
m_Mute: 0
m_IsExit: 0
serializedVersion: 3
m_TransitionDuration: 0.1
m_TransitionOffset: 0
m_ExitTime: 0.9
m_HasExitTime: 0
m_HasFixedDuration: 1
m_InterruptionSource: 0
m_OrderedInterruption: 1
m_CanTransitionToSelf: 1
--- !u!1101 &6017886512958357952
AnimatorStateTransition:
m_ObjectHideFlags: 1
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_Name:
m_Conditions:
- m_ConditionMode: 4
m_ConditionEvent: Speed
m_EventTreshold: 0.1
m_DstStateMachine: {fileID: 0}
m_DstState: {fileID: 2133809347797768411}
m_Solo: 0
m_Mute: 0
m_IsExit: 0
serializedVersion: 3
m_TransitionDuration: 0.1
m_TransitionOffset: 0
m_ExitTime: 0.7
m_HasExitTime: 1
m_HasFixedDuration: 1
m_InterruptionSource: 0
m_OrderedInterruption: 1
m_CanTransitionToSelf: 1
--- !u!1101 &6106028750639478370
AnimatorStateTransition:
m_ObjectHideFlags: 1
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_Name:
m_Conditions: []
m_DstStateMachine: {fileID: 0}
m_DstState: {fileID: 4880998032211746345}
m_Solo: 0
m_Mute: 0
m_IsExit: 0
serializedVersion: 3
m_TransitionDuration: 0.05
m_TransitionOffset: 0
m_ExitTime: 0.8
m_HasExitTime: 1
m_HasFixedDuration: 1
m_InterruptionSource: 0
m_OrderedInterruption: 1
m_CanTransitionToSelf: 1
--- !u!206 &7292886750186029930
--- !u!206 &1645608330671036843
BlendTree:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
@@ -459,7 +404,7 @@ BlendTree:
m_Name: MoveTree
m_Childs:
- serializedVersion: 2
m_Motion: {fileID: 3094330708855449807, guid: c133e3c197c12e04a9dd23bd0966910f, type: 3}
m_Motion: {fileID: 3094330708855449807, guid: 6deac83e30d8acd4cbb8c7d8a11545bd, type: 3}
m_Threshold: 0
m_Position: {x: 0, y: 1}
m_TimeScale: 1
@@ -467,7 +412,7 @@ BlendTree:
m_DirectBlendParameter:
m_Mirror: 0
- serializedVersion: 2
m_Motion: {fileID: 3094330708855449807, guid: f1f1135ca9cfa8c47bf81718bb0d6873, type: 3}
m_Motion: {fileID: 3094330708855449807, guid: a8e6c7cf678a13541a726c2ae9ec00e3, type: 3}
m_Threshold: 0
m_Position: {x: 0, y: -1}
m_TimeScale: 1
@@ -475,7 +420,7 @@ BlendTree:
m_DirectBlendParameter:
m_Mirror: 0
- serializedVersion: 2
m_Motion: {fileID: 3094330708855449807, guid: b702e254d5e77904da0429cfcbc77709, type: 3}
m_Motion: {fileID: 3094330708855449807, guid: 9f9dbe8815370164d8a2214328af4f13, type: 3}
m_Threshold: 0
m_Position: {x: -1, y: 0}
m_TimeScale: 1
@@ -483,7 +428,7 @@ BlendTree:
m_DirectBlendParameter:
m_Mirror: 0
- serializedVersion: 2
m_Motion: {fileID: 3094330708855449807, guid: 2cd37dc84c089ac4981cd6d36abd33eb, type: 3}
m_Motion: {fileID: 3094330708855449807, guid: 9f9dbe8815370164d8a2214328af4f13, type: 3}
m_Threshold: 0
m_Position: {x: 1, y: 0}
m_TimeScale: 1
@@ -491,7 +436,7 @@ BlendTree:
m_DirectBlendParameter:
m_Mirror: 0
- serializedVersion: 2
m_Motion: {fileID: 3094330708855449807, guid: 3a4cf5e04ded562489d1c3b2da8c2d7a, type: 3}
m_Motion: {fileID: 3094330708855449807, guid: 2d491dc6ab8cc5045919954fe2601203, type: 3}
m_Threshold: 0
m_Position: {x: -0.707, y: 0.707}
m_TimeScale: 1
@@ -499,7 +444,7 @@ BlendTree:
m_DirectBlendParameter:
m_Mirror: 0
- serializedVersion: 2
m_Motion: {fileID: 3094330708855449807, guid: dd9cde7e792f5f946b091935d7903296, type: 3}
m_Motion: {fileID: 3094330708855449807, guid: 152af2cd00aaae34e816ebb8deb4b68e, type: 3}
m_Threshold: 0
m_Position: {x: 0.707, y: 0.707}
m_TimeScale: 1
@@ -507,7 +452,7 @@ BlendTree:
m_DirectBlendParameter:
m_Mirror: 0
- serializedVersion: 2
m_Motion: {fileID: 3094330708855449807, guid: 5dcc71b9770f2554e8fd3ea0d6c1e1f4, type: 3}
m_Motion: {fileID: 3094330708855449807, guid: 02fe127be7c987640bef36b78efaf2cf, type: 3}
m_Threshold: 0
m_Position: {x: -0.707, y: -0.707}
m_TimeScale: 1
@@ -515,7 +460,7 @@ BlendTree:
m_DirectBlendParameter:
m_Mirror: 0
- serializedVersion: 2
m_Motion: {fileID: 3094330708855449807, guid: 55d43189338018e4c9e0c2ce1f608563, type: 3}
m_Motion: {fileID: 3094330708855449807, guid: ca7bf50d255ff5749a3a9ff602076af8, type: 3}
m_Threshold: 0
m_Position: {x: 0.707, y: -0.707}
m_TimeScale: 1
@@ -529,82 +474,7 @@ BlendTree:
m_UseAutomaticThresholds: 0
m_NormalizedBlendValues: 0
m_BlendType: 3
--- !u!1101 &7541083490971539274
AnimatorStateTransition:
m_ObjectHideFlags: 1
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_Name:
m_Conditions:
- m_ConditionMode: 1
m_ConditionEvent: Jump
m_EventTreshold: 0
m_DstStateMachine: {fileID: 0}
m_DstState: {fileID: -8501351176590210390}
m_Solo: 0
m_Mute: 0
m_IsExit: 0
serializedVersion: 3
m_TransitionDuration: 0.05
m_TransitionOffset: 0
m_ExitTime: 0.75
m_HasExitTime: 0
m_HasFixedDuration: 1
m_InterruptionSource: 0
m_OrderedInterruption: 1
m_CanTransitionToSelf: 0
--- !u!1101 &8366131237598160117
AnimatorStateTransition:
m_ObjectHideFlags: 1
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_Name:
m_Conditions:
- m_ConditionMode: 1
m_ConditionEvent: IsDead
m_EventTreshold: 0
m_DstStateMachine: {fileID: 0}
m_DstState: {fileID: -6354293237005665956}
m_Solo: 0
m_Mute: 0
m_IsExit: 0
serializedVersion: 3
m_TransitionDuration: 0.2
m_TransitionOffset: 0
m_ExitTime: 0.75
m_HasExitTime: 0
m_HasFixedDuration: 1
m_InterruptionSource: 0
m_OrderedInterruption: 1
m_CanTransitionToSelf: 1
--- !u!1101 &9054913250047092416
AnimatorStateTransition:
m_ObjectHideFlags: 1
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_Name:
m_Conditions:
- m_ConditionMode: 1
m_ConditionEvent: IsCrouching
m_EventTreshold: 0
m_DstStateMachine: {fileID: 0}
m_DstState: {fileID: 3657533443322484474}
m_Solo: 0
m_Mute: 0
m_IsExit: 0
serializedVersion: 3
m_TransitionDuration: 0.1
m_TransitionOffset: 0
m_ExitTime: 0.75
m_HasExitTime: 0
m_HasFixedDuration: 1
m_InterruptionSource: 0
m_OrderedInterruption: 1
m_CanTransitionToSelf: 1
--- !u!1101 &9167710540657582094
--- !u!1101 &1851180482452469980
AnimatorStateTransition:
m_ObjectHideFlags: 1
m_CorrespondingSourceObject: {fileID: 0}
@@ -616,32 +486,7 @@ AnimatorStateTransition:
m_ConditionEvent: Speed
m_EventTreshold: 0.1
m_DstStateMachine: {fileID: 0}
m_DstState: {fileID: -3837842955075777776}
m_Solo: 0
m_Mute: 0
m_IsExit: 0
serializedVersion: 3
m_TransitionDuration: 0.1
m_TransitionOffset: 0
m_ExitTime: 0.9
m_HasExitTime: 0
m_HasFixedDuration: 1
m_InterruptionSource: 0
m_OrderedInterruption: 1
m_CanTransitionToSelf: 1
--- !u!1101 &9219489292818365840
AnimatorStateTransition:
m_ObjectHideFlags: 1
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_Name:
m_Conditions:
- m_ConditionMode: 3
m_ConditionEvent: Speed
m_EventTreshold: 0.1
m_DstStateMachine: {fileID: 0}
m_DstState: {fileID: -3837842955075777776}
m_DstState: {fileID: -4140020287260570153}
m_Solo: 0
m_Mute: 0
m_IsExit: 0
@@ -654,3 +499,225 @@ AnimatorStateTransition:
m_InterruptionSource: 0
m_OrderedInterruption: 1
m_CanTransitionToSelf: 1
--- !u!1101 &3230187453778429532
AnimatorStateTransition:
m_ObjectHideFlags: 1
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_Name:
m_Conditions:
- m_ConditionMode: 1
m_ConditionEvent: IsJumping
m_EventTreshold: 0
- m_ConditionMode: 2
m_ConditionEvent: IsGrounded
m_EventTreshold: 0
m_DstStateMachine: {fileID: 0}
m_DstState: {fileID: 7993235720092425232}
m_Solo: 0
m_Mute: 0
m_IsExit: 0
serializedVersion: 3
m_TransitionDuration: 0.05
m_TransitionOffset: 0
m_ExitTime: 0.9
m_HasExitTime: 0
m_HasFixedDuration: 1
m_InterruptionSource: 0
m_OrderedInterruption: 1
m_CanTransitionToSelf: 1
--- !u!1101 &3313327580545924896
AnimatorStateTransition:
m_ObjectHideFlags: 1
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_Name:
m_Conditions:
- m_ConditionMode: 4
m_ConditionEvent: Speed
m_EventTreshold: 0.1
m_DstStateMachine: {fileID: 0}
m_DstState: {fileID: 5754690394921863263}
m_Solo: 0
m_Mute: 0
m_IsExit: 0
serializedVersion: 3
m_TransitionDuration: 0.1
m_TransitionOffset: 0
m_ExitTime: 0.7
m_HasExitTime: 1
m_HasFixedDuration: 1
m_InterruptionSource: 0
m_OrderedInterruption: 1
m_CanTransitionToSelf: 1
--- !u!1102 &5754690394921863263
AnimatorState:
serializedVersion: 6
m_ObjectHideFlags: 1
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_Name: Idle
m_Speed: 1
m_CycleOffset: 0
m_Transitions:
- {fileID: 6463146362643610033}
- {fileID: 461524094991714154}
m_StateMachineBehaviours: []
m_Position: {x: 50, y: 50, z: 0}
m_IKOnFeet: 0
m_WriteDefaultValues: 1
m_Mirror: 0
m_SpeedParameterActive: 0
m_MirrorParameterActive: 0
m_CycleOffsetParameterActive: 0
m_TimeParameterActive: 0
m_Motion: {fileID: -2576967968662016515, guid: 56fd86b76fc74d24d83522069f5deb9b, type: 3}
m_Tag:
m_SpeedParameter:
m_MirrorParameter:
m_CycleOffsetParameter:
m_TimeParameter:
--- !u!1101 &6463146362643610033
AnimatorStateTransition:
m_ObjectHideFlags: 1
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_Name:
m_Conditions:
- m_ConditionMode: 3
m_ConditionEvent: Speed
m_EventTreshold: 0.1
m_DstStateMachine: {fileID: 0}
m_DstState: {fileID: -4140020287260570153}
m_Solo: 0
m_Mute: 0
m_IsExit: 0
serializedVersion: 3
m_TransitionDuration: 0.1
m_TransitionOffset: 0
m_ExitTime: 0.9
m_HasExitTime: 0
m_HasFixedDuration: 1
m_InterruptionSource: 0
m_OrderedInterruption: 1
m_CanTransitionToSelf: 1
--- !u!1102 &7993235720092425232
AnimatorState:
serializedVersion: 6
m_ObjectHideFlags: 1
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_Name: Jump Begin
m_Speed: 1
m_CycleOffset: 0
m_Transitions:
- {fileID: 8546449291118487461}
m_StateMachineBehaviours: []
m_Position: {x: 50, y: 50, z: 0}
m_IKOnFeet: 0
m_WriteDefaultValues: 1
m_Mirror: 0
m_SpeedParameterActive: 0
m_MirrorParameterActive: 0
m_CycleOffsetParameterActive: 0
m_TimeParameterActive: 0
m_Motion: {fileID: 3094330708855449807, guid: b1844fbe628f5bf4ab29e6c68912a708, type: 3}
m_Tag:
m_SpeedParameter:
m_MirrorParameter:
m_CycleOffsetParameter:
m_TimeParameter:
--- !u!1101 &8415669427978378661
AnimatorStateTransition:
m_ObjectHideFlags: 1
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_Name:
m_Conditions:
- m_ConditionMode: 1
m_ConditionEvent: IsDead
m_EventTreshold: 0
m_DstStateMachine: {fileID: 0}
m_DstState: {fileID: -1553988547846161377}
m_Solo: 0
m_Mute: 0
m_IsExit: 0
serializedVersion: 3
m_TransitionDuration: 0.2
m_TransitionOffset: 0
m_ExitTime: 0.75
m_HasExitTime: 0
m_HasFixedDuration: 1
m_InterruptionSource: 0
m_OrderedInterruption: 1
m_CanTransitionToSelf: 1
--- !u!1101 &8546449291118487461
AnimatorStateTransition:
m_ObjectHideFlags: 1
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_Name:
m_Conditions: []
m_DstStateMachine: {fileID: 0}
m_DstState: {fileID: 861509553362583957}
m_Solo: 0
m_Mute: 0
m_IsExit: 0
serializedVersion: 3
m_TransitionDuration: 0.05
m_TransitionOffset: 0
m_ExitTime: 0.8
m_HasExitTime: 1
m_HasFixedDuration: 1
m_InterruptionSource: 0
m_OrderedInterruption: 1
m_CanTransitionToSelf: 1
--- !u!1107 &8917191963327386668
AnimatorStateMachine:
serializedVersion: 6
m_ObjectHideFlags: 1
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_Name: Base Layer
m_ChildStates:
- serializedVersion: 1
m_State: {fileID: 5754690394921863263}
m_Position: {x: 250, y: 50, z: 0}
- serializedVersion: 1
m_State: {fileID: -4140020287260570153}
m_Position: {x: 250, y: 150, z: 0}
- serializedVersion: 1
m_State: {fileID: -1281127197651375096}
m_Position: {x: 250, y: 250, z: 0}
- serializedVersion: 1
m_State: {fileID: 7993235720092425232}
m_Position: {x: 500, y: 50, z: 0}
- serializedVersion: 1
m_State: {fileID: 861509553362583957}
m_Position: {x: 500, y: 150, z: 0}
- serializedVersion: 1
m_State: {fileID: -9159441798702175618}
m_Position: {x: 500, y: 250, z: 0}
- serializedVersion: 1
m_State: {fileID: -1553988547846161377}
m_Position: {x: 250, y: 350, z: 0}
m_ChildStateMachines: []
m_AnyStateTransitions:
- {fileID: 254728727500707171}
- {fileID: 8415669427978378661}
m_EntryTransitions: []
m_StateMachineTransitions: {}
m_StateMachineBehaviours: []
m_AnyStatePosition: {x: 50, y: 20, z: 0}
m_EntryPosition: {x: 50, y: 120, z: 0}
m_ExitPosition: {x: 800, y: 120, z: 0}
m_ParentStateMachinePosition: {x: 800, y: 20, z: 0}
m_DefaultState: {fileID: 5754690394921863263}

8
MegaKoop.meta Normal file
View File

@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: d0da2da2780d0484dbb4b0d6698bab27
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 486c8daa3c35a794ab6d0d0926b1cdbe
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

View File

@@ -0,0 +1,130 @@
fileFormatVersion: 2
guid: e12555a05d95f214ba3f917c40371b0c
TextureImporter:
internalIDToNameTable: []
externalObjects: {}
serializedVersion: 13
mipmaps:
mipMapMode: 0
enableMipMap: 0
sRGBTexture: 1
linearTexture: 0
fadeOut: 0
borderMipMap: 0
mipMapsPreserveCoverage: 0
alphaTestReferenceValue: 0.5
mipMapFadeDistanceStart: 1
mipMapFadeDistanceEnd: 3
bumpmap:
convertToNormalMap: 0
externalNormalMap: 0
heightScale: 0.25
normalMapFilter: 0
flipGreenChannel: 0
isReadable: 0
streamingMipmaps: 0
streamingMipmapsPriority: 0
vTOnly: 0
ignoreMipmapLimit: 0
grayScaleToAlpha: 0
generateCubemap: 6
cubemapConvolution: 0
seamlessCubemap: 0
textureFormat: 1
maxTextureSize: 2048
textureSettings:
serializedVersion: 2
filterMode: 1
aniso: 1
mipBias: 0
wrapU: 1
wrapV: 1
wrapW: 1
nPOTScale: 0
lightmap: 0
compressionQuality: 50
spriteMode: 2
spriteExtrude: 1
spriteMeshType: 1
alignment: 0
spritePivot: {x: 0.5, y: 0.5}
spritePixelsToUnits: 100
spriteBorder: {x: 32, y: 32, z: 32, w: 32}
spriteGenerateFallbackPhysicsShape: 1
alphaUsage: 1
alphaIsTransparency: 1
spriteTessellationDetail: -1
textureType: 8
textureShape: 1
singleChannelComponent: 0
flipbookRows: 1
flipbookColumns: 1
maxTextureSizeSet: 0
compressionQualitySet: 0
textureFormatSet: 0
ignorePngGamma: 0
applyGammaDecoding: 0
swizzle: 50462976
cookieLightType: 0
platformSettings:
- serializedVersion: 4
buildTarget: DefaultTexturePlatform
maxTextureSize: 2048
resizeAlgorithm: 0
textureFormat: -1
textureCompression: 1
compressionQuality: 50
crunchedCompression: 0
allowsAlphaSplitting: 0
overridden: 0
ignorePlatformSupport: 0
androidETC2FallbackOverride: 0
forceMaximumCompressionQuality_BC6H_BC7: 0
- serializedVersion: 4
buildTarget: Standalone
maxTextureSize: 2048
resizeAlgorithm: 0
textureFormat: -1
textureCompression: 1
compressionQuality: 50
crunchedCompression: 0
allowsAlphaSplitting: 0
overridden: 0
ignorePlatformSupport: 0
androidETC2FallbackOverride: 0
forceMaximumCompressionQuality_BC6H_BC7: 0
- serializedVersion: 4
buildTarget: Android
maxTextureSize: 2048
resizeAlgorithm: 0
textureFormat: -1
textureCompression: 1
compressionQuality: 50
crunchedCompression: 0
allowsAlphaSplitting: 0
overridden: 0
ignorePlatformSupport: 0
androidETC2FallbackOverride: 0
forceMaximumCompressionQuality_BC6H_BC7: 0
spriteSheet:
serializedVersion: 2
sprites: []
outline: []
customData:
physicsShape: []
bones: []
spriteID:
internalID: 0
vertices: []
indices:
edges: []
weights: []
secondaryTextures: []
spriteCustomMetadata:
entries: []
nameFileIdTable: {}
mipmapLimitGroupName:
pSDRemoveMatte: 0
userData:
assetBundleName:
assetBundleVariant:

Binary file not shown.

After

Width:  |  Height:  |  Size: 430 B

View File

@@ -0,0 +1,130 @@
fileFormatVersion: 2
guid: 34eb234f95e13684ea08cfe7cd06a207
TextureImporter:
internalIDToNameTable: []
externalObjects: {}
serializedVersion: 13
mipmaps:
mipMapMode: 0
enableMipMap: 0
sRGBTexture: 1
linearTexture: 0
fadeOut: 0
borderMipMap: 0
mipMapsPreserveCoverage: 0
alphaTestReferenceValue: 0.5
mipMapFadeDistanceStart: 1
mipMapFadeDistanceEnd: 3
bumpmap:
convertToNormalMap: 0
externalNormalMap: 0
heightScale: 0.25
normalMapFilter: 0
flipGreenChannel: 0
isReadable: 0
streamingMipmaps: 0
streamingMipmapsPriority: 0
vTOnly: 0
ignoreMipmapLimit: 0
grayScaleToAlpha: 0
generateCubemap: 6
cubemapConvolution: 0
seamlessCubemap: 0
textureFormat: 1
maxTextureSize: 2048
textureSettings:
serializedVersion: 2
filterMode: 1
aniso: 1
mipBias: 0
wrapU: 1
wrapV: 1
wrapW: 1
nPOTScale: 0
lightmap: 0
compressionQuality: 50
spriteMode: 2
spriteExtrude: 1
spriteMeshType: 1
alignment: 0
spritePivot: {x: 0.5, y: 0.5}
spritePixelsToUnits: 100
spriteBorder: {x: 16, y: 16, z: 16, w: 16}
spriteGenerateFallbackPhysicsShape: 1
alphaUsage: 1
alphaIsTransparency: 1
spriteTessellationDetail: -1
textureType: 8
textureShape: 1
singleChannelComponent: 0
flipbookRows: 1
flipbookColumns: 1
maxTextureSizeSet: 0
compressionQualitySet: 0
textureFormatSet: 0
ignorePngGamma: 0
applyGammaDecoding: 0
swizzle: 50462976
cookieLightType: 0
platformSettings:
- serializedVersion: 4
buildTarget: DefaultTexturePlatform
maxTextureSize: 2048
resizeAlgorithm: 0
textureFormat: -1
textureCompression: 1
compressionQuality: 50
crunchedCompression: 0
allowsAlphaSplitting: 0
overridden: 0
ignorePlatformSupport: 0
androidETC2FallbackOverride: 0
forceMaximumCompressionQuality_BC6H_BC7: 0
- serializedVersion: 4
buildTarget: Standalone
maxTextureSize: 2048
resizeAlgorithm: 0
textureFormat: -1
textureCompression: 1
compressionQuality: 50
crunchedCompression: 0
allowsAlphaSplitting: 0
overridden: 0
ignorePlatformSupport: 0
androidETC2FallbackOverride: 0
forceMaximumCompressionQuality_BC6H_BC7: 0
- serializedVersion: 4
buildTarget: Android
maxTextureSize: 2048
resizeAlgorithm: 0
textureFormat: -1
textureCompression: 1
compressionQuality: 50
crunchedCompression: 0
allowsAlphaSplitting: 0
overridden: 0
ignorePlatformSupport: 0
androidETC2FallbackOverride: 0
forceMaximumCompressionQuality_BC6H_BC7: 0
spriteSheet:
serializedVersion: 2
sprites: []
outline: []
customData:
physicsShape: []
bones: []
spriteID:
internalID: 0
vertices: []
indices:
edges: []
weights: []
secondaryTextures: []
spriteCustomMetadata:
entries: []
nameFileIdTable: {}
mipmapLimitGroupName:
pSDRemoveMatte: 0
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -378,9 +378,36 @@ namespace MegaKoop.Steam
private void OnRichPresenceJoinRequestedCb(GameRichPresenceJoinRequested_t cb)
{
// Some Steamworks.NET versions expose only m_rgchConnect here.
// We cannot reliably parse lobby id across versions; log and rely on GameLobbyJoinRequested_t for lobby joins.
Debug.Log("[SteamLobbyService] RichPresence join requested - handle via GameLobbyJoinRequested or custom connect string.");
// Parse steam://joinlobby/<appId>/<lobbyId>/<steamId> and join the lobby
string conn = cb.m_rgchConnect;
if (!string.IsNullOrEmpty(conn))
{
try
{
var s = conn.Trim();
const string prefix = "steam://joinlobby/";
if (s.StartsWith(prefix, StringComparison.OrdinalIgnoreCase))
{
var rest = s.Substring(prefix.Length);
var parts = rest.Split('/');
// Expected: [appId, lobbyId, steamId]
if (parts.Length >= 2)
{
if (ulong.TryParse(parts[1], out var lobbyId))
{
var lobby = new CSteamID(lobbyId);
SteamMatchmaking.JoinLobby(lobby);
return;
}
}
}
}
catch (Exception ex)
{
Debug.LogWarning($"[SteamLobbyService] Failed to parse rich presence connect '{conn}': {ex.Message}");
}
}
Debug.LogWarning($"[SteamLobbyService] RichPresence join requested but could not parse connect '{conn}'.");
}
#endregion

8
Resources.meta Normal file
View File

@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: f3823f2d99b808149beac04887adcd94
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,40 @@
%YAML 1.1
%TAG !u! tag:unity3d.com,2011:
--- !u!114 &11400000
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 0}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: 2844d064cf51ea940a481b3b76c10ff6, type: 3}
m_Name: ChampionDatabase
m_EditorClassIdentifier: Assembly-CSharp::MegaKoop.Champions.ChampionDatabase
champions:
- {fileID: 11400000, guid: e91e178970c9ce94fa1aae63627dd287, type: 2}
- {fileID: 11400000, guid: 3063583c11830974380a240794f2a205, type: 2}
- {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}

View File

@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: e37c7626379bc4d4d8201c2e0a952d71
NativeFormatImporter:
externalObjects: {}
mainObjectFileID: 11400000
userData:
assetBundleName:
assetBundleVariant:

8
Resources/UI.meta Normal file
View File

@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: a36370dada5aac34aa74f79ffad8e647
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

52
Resources/UI/Lobby.uxml Normal file
View File

@@ -0,0 +1,52 @@
<ui:UXML xmlns:ui="UnityEngine.UIElements" editor-extension-mode="False">
<ui:VisualElement class="root">
<ui:VisualElement class="panel" name="Panel_Lobby">
<ui:Label class="title" text="LOBBY" />
<!-- Status and Code -->
<ui:VisualElement class="status-bar">
<ui:Label class="status" name="Text_Status" text="OFFLINE" />
<ui:Label class="lobby-code-label" text="CODE:" />
<ui:Label class="lobby-code" name="Text_LobbyCodeValue" text="------" />
<ui:Button class="small-button" name="Button_CopyCode" text="COPY" />
</ui:VisualElement>
<!-- Tabs -->
<ui:VisualElement class="tab-bar">
<ui:Button class="tab-button" name="Button_HostTab" text="HOST" />
<ui:Button class="tab-button" name="Button_JoinTab" text="JOIN" />
</ui:VisualElement>
<!-- Host Group -->
<ui:VisualElement class="tab-content" name="Group_Host">
<ui:DropdownField class="dropdown" name="Dropdown_MaxPlayers" label="Max Players" />
<ui:Toggle class="toggle" name="Toggle_PublicLobby" label="Public Lobby" />
<ui:Button class="button" name="Button_CreateLobby" text="CREATE LOBBY" />
</ui:VisualElement>
<!-- Join Group -->
<ui:VisualElement class="tab-content" name="Group_Join">
<ui:TextField class="text-field" name="Input_LobbyCode" label="Lobby Code" />
<ui:Button class="button" name="Button_Connect" text="CONNECT" />
</ui:VisualElement>
<!-- Players List -->
<ui:GroupBox class="players-group">
<ui:Label class="group-title" text="Players" />
<ui:Label class="player-count" name="Text_PlayerCount" text="0/4 Players" />
<ui:ScrollView class="players-scroll" name="Scroll_Players">
<ui:VisualElement name="Content_PlayersList" class="players-content" />
</ui:ScrollView>
</ui:GroupBox>
<!-- Action Buttons -->
<ui:VisualElement class="action-buttons">
<ui:Button class="button" name="Button_InviteFriends" text="INVITE FRIENDS" />
<ui:Button class="button" name="Button_ToggleReady" text="READY" />
<ui:Button class="button" name="Button_StartGame" text="START GAME" />
<ui:Button class="button" name="Button_LeaveLobby" text="LEAVE" />
<ui:Button class="button" name="Button_BackFromLobby" text="BACK" />
</ui:VisualElement>
</ui:VisualElement>
</ui:VisualElement>
</ui:UXML>

View File

@@ -0,0 +1,10 @@
fileFormatVersion: 2
guid: 538b6231934f83d4e81659acd0184683
ScriptedImporter:
internalIDToNameTable: []
externalObjects: {}
serializedVersion: 2
userData:
assetBundleName:
assetBundleVariant:
script: {fileID: 13804, guid: 0000000000000000e000000000000000, type: 0}

View File

@@ -0,0 +1,11 @@
<ui:UXML xmlns:ui="UnityEngine.UIElements" editor-extension-mode="False">
<ui:VisualElement class="root">
<ui:VisualElement class="panel" name="Panel_MainMenu">
<ui:Label class="title" text="MageKoop" />
<ui:Button class="button" name="Button_Play" text="PLAY" />
<ui:Button class="button" name="Button_Join" text="JOIN" />
<ui:Button class="button" name="Button_Settings" text="SETTINGS" />
<ui:Button class="button" name="Button_Quit" text="QUIT" />
</ui:VisualElement>
</ui:VisualElement>
</ui:UXML>

View File

@@ -0,0 +1,10 @@
fileFormatVersion: 2
guid: 23149369d8f3d49438ce80e1c8ead9b9
ScriptedImporter:
internalIDToNameTable: []
externalObjects: {}
serializedVersion: 2
userData:
assetBundleName:
assetBundleVariant:
script: {fileID: 13804, guid: 0000000000000000e000000000000000, type: 0}

View File

@@ -0,0 +1,50 @@
%YAML 1.1
%TAG !u! tag:unity3d.com,2011:
--- !u!114 &11400000
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 0}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 19101, guid: 0000000000000000e000000000000000, type: 0}
m_Name: New Panel Settings
m_EditorClassIdentifier: UnityEngine.dll::UnityEngine.UIElements.PanelSettings
themeUss: {fileID: -4733365628477956816, guid: ab886bbada599f947a3b93ee959a51c9, type: 3}
m_DisableNoThemeWarning: 0
m_TargetTexture: {fileID: 0}
m_RenderMode: 0
m_ColliderUpdateMode: 0
m_ColliderIsTrigger: 1
m_ScaleMode: 1
m_ReferenceSpritePixelsPerUnit: 100
m_PixelsPerUnit: 100
m_Scale: 1
m_ReferenceDpi: 278.1
m_FallbackDpi: 96
m_ReferenceResolution: {x: 1200, y: 800}
m_ScreenMatchMode: 0
m_Match: 0
m_SortingOrder: 0
m_TargetDisplay: 0
m_BindingLogLevel: 0
m_ClearDepthStencil: 1
m_ClearColor: 0
m_ColorClearValue: {r: 0, g: 0, b: 0, a: 0}
m_VertexBudget: 0
m_DynamicAtlasSettings:
m_MinAtlasSize: 64
m_MaxAtlasSize: 4096
m_MaxSubTextureSize: 64
m_ActiveFilters: -1
m_AtlasBlitShader: {fileID: 9101, guid: 0000000000000000f000000000000000, type: 0}
m_RuntimeShader: {fileID: 9100, guid: 0000000000000000f000000000000000, type: 0}
m_RuntimeWorldShader: {fileID: 9102, guid: 0000000000000000f000000000000000, type: 0}
m_SDFShader: {fileID: 19011, guid: 0000000000000000f000000000000000, type: 0}
m_BitmapShader: {fileID: 9001, guid: 0000000000000000f000000000000000, type: 0}
m_SpriteShader: {fileID: 19012, guid: 0000000000000000f000000000000000, type: 0}
m_ICUDataAsset: {fileID: 0}
forceGammaRendering: 0
textSettings: {fileID: 0}

View File

@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 89f4f7ac6e9bec34fac6f5115884e279
NativeFormatImporter:
externalObjects: {}
mainObjectFileID: 11400000
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,29 @@
<ui:UXML xmlns:ui="UnityEngine.UIElements" editor-extension-mode="False">
<ui:VisualElement class="root">
<ui:VisualElement class="panel" name="Panel_SelectChampion">
<ui:Label class="title" text="SELECT CHAMPION" />
<ui:ScrollView class="champion-scroll">
<ui:ListView class="champion-list" name="ListView_Champions" />
</ui:ScrollView>
<ui:VisualElement class="champion-info" name="Panel_ChampionInfo">
<ui:Label class="champion-name" name="Label_ChampionName" text="Select a Champion" />
<ui:Label class="champion-description" name="Label_ChampionDescription" text="Choose your champion to begin the game." />
<ui:VisualElement class="champion-stats">
<ui:Label class="stat-label" text="Health:" />
<ui:Label class="stat-value" name="Label_Health" text="--" />
<ui:Label class="stat-label" text="Attack:" />
<ui:Label class="stat-value" name="Label_Attack" text="--" />
<ui:Label class="stat-label" text="Speed:" />
<ui:Label class="stat-value" name="Label_Speed" text="--" />
</ui:VisualElement>
</ui:VisualElement>
<ui:VisualElement class="button-row">
<ui:Button class="button" name="Button_SelectChampion" text="SELECT" />
<ui:Button class="button" name="Button_BackFromChampionSelect" text="BACK" />
</ui:VisualElement>
</ui:VisualElement>
</ui:VisualElement>
</ui:UXML>

View File

@@ -0,0 +1,10 @@
fileFormatVersion: 2
guid: 30d3a72f007fb4846939f7f6a51cdbb0
ScriptedImporter:
internalIDToNameTable: []
externalObjects: {}
serializedVersion: 2
userData:
assetBundleName:
assetBundleVariant:
script: {fileID: 13804, guid: 0000000000000000e000000000000000, type: 0}

View File

@@ -0,0 +1,26 @@
<ui:UXML xmlns:ui="UnityEngine.UIElements" editor-extension-mode="False">
<ui:VisualElement class="root">
<ui:VisualElement class="panel" name="Panel_Settings">
<ui:Label class="title" text="SETTINGS" />
<ui:GroupBox class="settings-group">
<ui:Label class="group-title" text="Graphics" />
<ui:DropdownField class="dropdown" name="Dropdown_Quality" label="Quality" />
<ui:DropdownField class="dropdown" name="Dropdown_Resolution" label="Resolution" />
<ui:Toggle class="toggle" name="Toggle_Fullscreen" label="Fullscreen" />
</ui:GroupBox>
<ui:GroupBox class="settings-group">
<ui:Label class="group-title" text="Audio" />
<ui:Slider class="slider" name="Slider_MasterVolume" label="Master Volume" low-value="0" high-value="100" value="100" />
<ui:Slider class="slider" name="Slider_MusicVolume" label="Music Volume" low-value="0" high-value="100" value="80" />
<ui:Slider class="slider" name="Slider_SFXVolume" label="SFX Volume" low-value="0" high-value="100" value="100" />
</ui:GroupBox>
<ui:VisualElement class="button-row">
<ui:Button class="button" name="Button_ApplySettings" text="APPLY" />
<ui:Button class="button" name="Button_BackFromSettings" text="BACK" />
</ui:VisualElement>
</ui:VisualElement>
</ui:VisualElement>
</ui:UXML>

View File

@@ -0,0 +1,10 @@
fileFormatVersion: 2
guid: d519118cd99e75140b82a612e7bea482
ScriptedImporter:
internalIDToNameTable: []
externalObjects: {}
serializedVersion: 2
userData:
assetBundleName:
assetBundleVariant:
script: {fileID: 13804, guid: 0000000000000000e000000000000000, type: 0}

View File

@@ -0,0 +1,72 @@
%YAML 1.1
%TAG !u! tag:unity3d.com,2011:
--- !u!114 &11400000
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 0}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: ca76b4b0176a31c4c970c750d21e86ab, type: 3}
m_Name: UIPanelSettings
m_EditorClassIdentifier: Assembly-CSharp::MegaKoop.UI.UIPanelSettings
mainMenuPanel: {fileID: 9197481963319205126, guid: 23149369d8f3d49438ce80e1c8ead9b9, type: 3}
settingsPanel: {fileID: 9197481963319205126, guid: d519118cd99e75140b82a612e7bea482, type: 3}
lobbyPanel: {fileID: 9197481963319205126, guid: 538b6231934f83d4e81659acd0184683, type: 3}
championSelectPanel: {fileID: 9197481963319205126, guid: 30d3a72f007fb4846939f7f6a51cdbb0, type: 3}
themeStyleSheet: {fileID: 7433441132597879392, guid: 09cd88ad290da0c4b87b4b1d8e9eeca0, type: 3}
mainMenuStyleSheet: {fileID: 0}
settingsStyleSheet: {fileID: 0}
lobbyStyleSheet: {fileID: 0}
championSelectStyleSheet: {fileID: 0}
enableTransitions: 1
transitionDuration: 0.3
enableSoundEffects: 1
enableHapticFeedback: 0
enableResponsiveDesign: 1
minResolution: {x: 1280, y: 720}
maxResolution: {x: 3840, y: 2160}
showCurve:
m_Curve:
- time: 0
value: 0
inSlope: 0
outSlope: 0
tangentMode: 0
weightedMode: 0
inWeight: 0
outWeight: 0
- time: 1
value: 1
inSlope: 0
outSlope: 0
tangentMode: 0
weightedMode: 0
inWeight: 0
outWeight: 0
m_PreInfinity: 2
m_PostInfinity: 2
m_RotationOrder: 4
hideCurve:
m_Curve:
- time: 0
value: 1
inSlope: 0
outSlope: 0
tangentMode: 0
weightedMode: 0
inWeight: 0
outWeight: 0
- time: 1
value: 0
inSlope: 0
outSlope: 0
tangentMode: 0
weightedMode: 0
inWeight: 0
outWeight: 0
m_PreInfinity: 2
m_PostInfinity: 2
m_RotationOrder: 4

View File

@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 6c08e0f100d3df7409c38fff4a7a2e31
NativeFormatImporter:
externalObjects: {}
mainObjectFileID: 11400000
userData:
assetBundleName:
assetBundleVariant:

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: f51635c50073da5cabba6efdf303ee59
timeCreated: 1488638231
licenseType: Store
DefaultImporter:
userData:
assetBundleName:
assetBundleVariant:

8
Scripts.meta Normal file
View File

@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 389bd161b6106e84b8268a9d3fc2c6aa
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

8
Scripts/UI.meta Normal file
View File

@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 74e5ebdb8ee4aab43bbb71822815ffcc
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -2,20 +2,28 @@
%TAG !u! tag:unity3d.com,2011:
--- !u!21 &2100000
Material:
serializedVersion: 6
serializedVersion: 8
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_Name: LiberationSans SDF - Metalic Green
m_Shader: {fileID: 4800000, guid: 68e6db2ebdc24f95958faec2be5558d6, type: 3}
m_ShaderKeywords: BEVEL_ON GLOW_ON OUTLINE_ON UNDERLAY_ON
m_Parent: {fileID: 0}
m_ModifiedSerializedProperties: 0
m_ValidKeywords:
- BEVEL_ON
- GLOW_ON
- OUTLINE_ON
- UNDERLAY_ON
m_InvalidKeywords: []
m_LightmapFlags: 5
m_EnableInstancingVariants: 0
m_DoubleSidedGI: 0
m_CustomRenderQueue: -1
stringTagMap: {}
disabledShaderPasses: []
m_LockedProperties:
m_SavedProperties:
serializedVersion: 3
m_TexEnvs:
@@ -32,14 +40,14 @@ Material:
m_Scale: {x: 1, y: 1}
m_Offset: {x: 0, y: 0}
- _MainTex:
m_Texture: {fileID: 28684132378477856, guid: 8f586378b4e144a9851e7b34d9b748ee,
type: 2}
m_Texture: {fileID: 28684132378477856, guid: 8f586378b4e144a9851e7b34d9b748ee, type: 2}
m_Scale: {x: 1, y: 1}
m_Offset: {x: 0, y: 0}
- _OutlineTex:
m_Texture: {fileID: 2800000, guid: f88677df267a41d6be1e7a6133e7d227, type: 3}
m_Scale: {x: 1, y: 1}
m_Offset: {x: 0, y: 0}
m_Ints: []
m_Floats:
- _Ambient: 0
- _Bevel: 1
@@ -50,6 +58,7 @@ Material:
- _BumpFace: 0
- _BumpOutline: 0
- _ColorMask: 15
- _CullMode: 0
- _Diffuse: 0
- _FaceDilate: 0.15
- _FaceUVSpeedX: 0
@@ -69,8 +78,8 @@ Material:
- _PerspectiveFilter: 0
- _Reflectivity: 12.76
- _ScaleRatioA: 0.9
- _ScaleRatioB: 0.6525
- _ScaleRatioC: 0.6525
- _ScaleRatioB: 0.59624994
- _ScaleRatioC: 0.59624994
- _ScaleX: 1
- _ScaleY: 1
- _ShaderFlags: 0
@@ -102,3 +111,5 @@ Material:
- _ReflectOutlineColor: {r: 0, g: 0, b: 0, a: 1}
- _SpecularColor: {r: 0.7689687, g: 1, b: 0.75000346, a: 1}
- _UnderlayColor: {r: 0, g: 0, b: 0, a: 0.5}
m_BuildTextureStacks: []
m_AllowLocking: 1

File diff suppressed because one or more lines are too long

View File

@@ -6,6 +6,7 @@ using TMPro;
#else
using TMPro;
#endif
using System.Collections.Generic;
namespace MegaKoop.UI
{
@@ -24,19 +25,20 @@ namespace MegaKoop.UI
private GameObject panelSettings;
private GameObject panelLobby;
// Main buttons
private Button btnNewGame;
private Button btnContinue;
private Button btnMultiplayer;
private Button btnSettings;
private Button btnQuit;
[Header("Manual Wiring - Main Menu Buttons")]
[SerializeField] private Button btnMultiplayer;
[SerializeField] private Button btnSettings;
[SerializeField] private Button btnQuit;
// Settings controls
private TMP_Dropdown ddQuality;
private Toggle tgFullscreen;
private TMP_Dropdown ddResolution;
private Button btnApplySettings;
private Button btnBackFromSettings;
[Header("Manual Wiring - Settings Controls")]
[SerializeField] private TMP_Dropdown ddQuality;
[SerializeField] private Toggle tgFullscreen;
[SerializeField] private TMP_Dropdown ddResolution;
[SerializeField] private Button btnApplySettings;
[SerializeField] private Button btnBackFromSettings;
[SerializeField] private Slider slMaster;
[SerializeField] private Slider slMusic;
[SerializeField] private Slider slSFX;
private void Awake()
{
@@ -44,21 +46,27 @@ namespace MegaKoop.UI
RefreshPanelReferences();
// Buttons
btnNewGame = FindButton("Button_NewGame");
btnContinue = FindButton("Button_Continue");
btnMultiplayer = FindButton("Button_Multiplayer");
btnSettings = FindButton("Button_Settings");
btnQuit = FindButton("Button_Quit");
btnMultiplayer = EnsureButton(btnMultiplayer, "Button_Multiplayer", panelMain ? panelMain.transform : this.transform);
btnSettings = EnsureButton(btnSettings, "Button_Settings", panelMain ? panelMain.transform : this.transform);
btnQuit = EnsureButton(btnQuit, "Button_Quit", panelMain ? panelMain.transform : this.transform);
// Settings
ddQuality = FindDropdown("Dropdown_Quality");
tgFullscreen = FindToggle("Toggle_Fullscreen");
ddResolution = FindDropdown("Dropdown_Resolution");
btnApplySettings = FindButton("Button_ApplySettings");
btnBackFromSettings = FindButton("Button_BackFromSettings");
ddQuality = EnsureDropdown(ddQuality, "Dropdown_Quality", panelSettings ? panelSettings.transform : this.transform);
tgFullscreen = EnsureToggle(tgFullscreen, "Toggle_Fullscreen", panelSettings ? panelSettings.transform : this.transform);
ddResolution = EnsureDropdown(ddResolution, "Dropdown_Resolution", panelSettings ? panelSettings.transform : this.transform);
btnApplySettings = EnsureButton(btnApplySettings, "Button_ApplySettings", panelSettings ? panelSettings.transform : this.transform);
btnBackFromSettings = EnsureButton(btnBackFromSettings, "Button_BackFromSettings", panelSettings ? panelSettings.transform : this.transform);
slMaster = EnsureSlider(slMaster, "Slider_MasterVolume", panelSettings ? panelSettings.transform : this.transform);
slMusic = EnsureSlider(slMusic, "Slider_MusicVolume", panelSettings ? panelSettings.transform : this.transform);
slSFX = EnsureSlider(slSFX, "Slider_SFXVolume", panelSettings ? panelSettings.transform : this.transform);
if (!btnApplySettings && panelSettings) btnApplySettings = FindButtonByLabel("APPLY", panelSettings.transform);
if (!btnBackFromSettings && panelSettings) btnBackFromSettings = FindButtonByLabel("BACK", panelSettings.transform);
WireEvents();
InitializeSettingsUI();
// Ensure initial panels
ShowMainMenu();
}
@@ -72,6 +80,27 @@ namespace MegaKoop.UI
RefreshPanelReferences();
}
public void SetMainMenuButtons(Button multiplayerButton, Button settingsButton, Button quitButton)
{
btnMultiplayer = multiplayerButton;
btnSettings = settingsButton;
btnQuit = quitButton;
}
public void SetSettingsControls(TMP_Dropdown qualityDropdown, Toggle fullscreenToggle,
TMP_Dropdown resolutionDropdown, Slider masterVolume, Slider musicVolume, Slider sfxVolume,
Button applyButton, Button backButton)
{
ddQuality = qualityDropdown;
tgFullscreen = fullscreenToggle;
ddResolution = resolutionDropdown;
slMaster = masterVolume;
slMusic = musicVolume;
slSFX = sfxVolume;
btnApplySettings = applyButton;
btnBackFromSettings = backButton;
}
private void RefreshPanelReferences()
{
panelMain = panelMainRef ? panelMainRef : (FindAnyInScene("Panel_MainMenu") ?? panelMain);
@@ -79,6 +108,30 @@ namespace MegaKoop.UI
panelLobby = panelLobbyRef ? panelLobbyRef : (FindAnyInScene("Panel_Lobby") ?? panelLobby);
}
private Button EnsureButton(Button existing, string name, Transform scope)
{
if (existing) return existing;
return FindButton(name, scope);
}
private TMP_Dropdown EnsureDropdown(TMP_Dropdown existing, string name, Transform scope)
{
if (existing) return existing;
return FindDropdown(name, scope);
}
private Toggle EnsureToggle(Toggle existing, string name, Transform scope)
{
if (existing) return existing;
return FindToggle(name, scope);
}
private Slider EnsureSlider(Slider existing, string name, Transform scope)
{
if (existing) return existing;
return FindSlider(name, scope);
}
private GameObject FindAnyInScene(string name)
{
// Najde i neaktivní objekty
@@ -93,32 +146,106 @@ namespace MegaKoop.UI
return null;
}
private Button FindButton(string name)
private Button FindButton(string name, Transform scope = null)
{
var go = GameObject.Find(name);
return go ? go.GetComponent<Button>() : null;
if (scope != null)
{
var comps = scope.GetComponentsInChildren<Button>(true);
foreach (var c in comps)
{
if (c && c.name == name) return c;
}
return null;
}
var all = Resources.FindObjectsOfTypeAll<Button>();
foreach (var c in all)
{
if (c && c.name == name && c.gameObject.scene.IsValid() && c.gameObject.scene.isLoaded) return c;
}
return null;
}
private TMP_Dropdown FindDropdown(string name)
private TMP_Dropdown FindDropdown(string name, Transform scope = null)
{
var go = GameObject.Find(name);
return go ? go.GetComponent<TMP_Dropdown>() : null;
if (scope != null)
{
var comps = scope.GetComponentsInChildren<TMP_Dropdown>(true);
foreach (var c in comps)
{
if (c && c.name == name) return c;
}
return null;
}
var all = Resources.FindObjectsOfTypeAll<TMP_Dropdown>();
foreach (var c in all)
{
if (c && c.name == name && c.gameObject.scene.IsValid() && c.gameObject.scene.isLoaded) return c;
}
return null;
}
private Toggle FindToggle(string name)
private Toggle FindToggle(string name, Transform scope = null)
{
var go = GameObject.Find(name);
return go ? go.GetComponent<Toggle>() : null;
if (scope != null)
{
var comps = scope.GetComponentsInChildren<Toggle>(true);
foreach (var c in comps)
{
if (c && c.name == name) return c;
}
return null;
}
var all = Resources.FindObjectsOfTypeAll<Toggle>();
foreach (var c in all)
{
if (c && c.name == name && c.gameObject.scene.IsValid() && c.gameObject.scene.isLoaded) return c;
}
return null;
}
private Slider FindSlider(string name, Transform scope = null)
{
if (scope != null)
{
var comps = scope.GetComponentsInChildren<Slider>(true);
foreach (var c in comps)
{
if (c && c.name == name) return c;
}
return null;
}
var all = Resources.FindObjectsOfTypeAll<Slider>();
foreach (var c in all)
{
if (c && c.name == name && c.gameObject.scene.IsValid() && c.gameObject.scene.isLoaded) return c;
}
return null;
}
private Button FindButtonByLabel(string label, Transform scope)
{
if (!scope) return null;
var btns = scope.GetComponentsInChildren<Button>(true);
for (int i = 0; i < btns.Length; i++)
{
var t = btns[i].GetComponentInChildren<TextMeshProUGUI>(true);
if (t && string.Equals(t.text?.Trim(), label, System.StringComparison.OrdinalIgnoreCase))
return btns[i];
}
return null;
}
private void WireEvents()
{
if (btnNewGame) btnNewGame.onClick.AddListener(OnNewGame);
if (btnContinue) btnContinue.onClick.AddListener(OnContinue);
if (btnMultiplayer) btnMultiplayer.onClick.AddListener(OnOpenMultiplayer);
if (btnSettings) btnSettings.onClick.AddListener(OnOpenSettings);
if (btnQuit) btnQuit.onClick.AddListener(OnQuit);
if (btnMultiplayer) btnMultiplayer.onClick.AddListener(OnOpenMultiplayer); else Debug.LogWarning("[UGUIMainMenu] Button_Multiplayer not found");
if (btnSettings) btnSettings.onClick.AddListener(OnOpenSettings); else Debug.LogWarning("[UGUIMainMenu] Button_Settings not found");
if (btnQuit) btnQuit.onClick.AddListener(OnQuit); else Debug.LogWarning("[UGUIMainMenu] Button_Quit not found");
if (btnApplySettings) btnApplySettings.onClick.AddListener(ApplySettings);
if (btnBackFromSettings) btnBackFromSettings.onClick.AddListener(ShowMainMenu);
if (btnApplySettings) btnApplySettings.onClick.AddListener(OnApplySettings); else Debug.LogWarning("[UGUIMainMenu] Button_ApplySettings not found");
if (btnBackFromSettings) btnBackFromSettings.onClick.AddListener(ShowMainMenu); else Debug.LogWarning("[UGUIMainMenu] Button_BackFromSettings not found");
if (ddQuality) ddQuality.onValueChanged.AddListener(i => { Debug.Log("[UGUIMainMenu] Quality changed to index " + i); ddQuality.RefreshShownValue(); });
if (ddResolution) ddResolution.onValueChanged.AddListener(i => { Debug.Log("[UGUIMainMenu] Resolution changed to index " + i); ddResolution.RefreshShownValue(); });
if (tgFullscreen) tgFullscreen.onValueChanged.AddListener(v => { Debug.Log("[UGUIMainMenu] Fullscreen " + v); });
if (slMaster) slMaster.onValueChanged.AddListener(v => { AudioListener.volume = v / 100f; });
}
private void ShowMainMenu()
@@ -158,42 +285,7 @@ namespace MegaKoop.UI
if (panelMain) panelMain.SetActive(false);
if (panelSettings) panelSettings.SetActive(true);
if (panelLobby) panelLobby.SetActive(false);
}
private void OnNewGame()
{
// Co-op only: quick host path
UGUIMultiplayerLobbyController lobby;
#if UNITY_2023_1_OR_NEWER
lobby = Object.FindFirstObjectByType<UGUIMultiplayerLobbyController>();
#else
lobby = Object.FindObjectOfType<UGUIMultiplayerLobbyController>();
#endif
if (lobby)
{
if (panelMain) panelMain.SetActive(false);
if (panelSettings) panelSettings.SetActive(false);
if (panelLobby) panelLobby.SetActive(true);
lobby.QuickHost(4, true);
}
}
private void OnContinue()
{
// Co-op only: join path
UGUIMultiplayerLobbyController lobby;
#if UNITY_2023_1_OR_NEWER
lobby = Object.FindFirstObjectByType<UGUIMultiplayerLobbyController>();
#else
lobby = Object.FindObjectOfType<UGUIMultiplayerLobbyController>();
#endif
if (lobby)
{
if (panelMain) panelMain.SetActive(false);
if (panelSettings) panelSettings.SetActive(false);
if (panelLobby) panelLobby.SetActive(true);
lobby.ShowJoinTabPublic();
}
Debug.Log("[UGUIMainMenu] Open Settings");
}
private void OnQuit()
@@ -217,13 +309,190 @@ namespace MegaKoop.UI
}
if (ddResolution)
{
var opt = ddResolution.options[ddResolution.value].text; // e.g., "1920x1080"
var parts = opt.ToLower().Split('x');
if (parts.Length == 2 && int.TryParse(parts[0], out int w) && int.TryParse(parts[1], out int h))
if (ddResolution.options != null && ddResolution.options.Count > 0 && ddResolution.value >= 0 && ddResolution.value < ddResolution.options.Count)
{
Screen.SetResolution(w, h, Screen.fullScreenMode);
var opt = ddResolution.options[ddResolution.value].text; // e.g., "1920x1080"
var parts = opt.ToLower().Split('x');
if (parts.Length == 2 && int.TryParse(parts[0], out int w) && int.TryParse(parts[1], out int h))
{
Screen.SetResolution(w, h, Screen.fullScreenMode);
}
}
}
if (slMaster)
{
AudioListener.volume = slMaster.value / 100f;
}
}
private void OnApplySettings()
{
ApplySettings();
SaveSettings();
ShowMainMenu();
Debug.Log("[UGUIMainMenu] Apply & Back to Main");
}
private void InitializeSettingsUI()
{
if (ddQuality)
{
var names = QualitySettings.names;
if (names != null && names.Length > 0)
{
ddQuality.ClearOptions();
ddQuality.AddOptions(new List<string>(names));
ddQuality.value = Mathf.Clamp(QualitySettings.GetQualityLevel(), 0, ddQuality.options.Count - 1);
ddQuality.RefreshShownValue();
}
ddQuality.interactable = true;
FixDropdownRaycasts(ddQuality);
SetupDropdownTemplateCanvas(ddQuality);
}
if (ddResolution)
{
var seen = new HashSet<string>();
var opts = new List<string>();
string cur = Screen.currentResolution.width + "x" + Screen.currentResolution.height;
int curIdx = 0;
foreach (var r in Screen.resolutions)
{
string s = r.width + "x" + r.height;
if (seen.Add(s))
{
if (s == cur) curIdx = opts.Count;
opts.Add(s);
}
}
if (opts.Count == 0)
{
opts.Add(cur);
curIdx = 0;
}
ddResolution.ClearOptions();
ddResolution.AddOptions(opts);
ddResolution.value = Mathf.Clamp(curIdx, 0, ddResolution.options.Count - 1);
ddResolution.RefreshShownValue();
ddResolution.interactable = true;
FixDropdownRaycasts(ddResolution);
SetupDropdownTemplateCanvas(ddResolution);
}
if (tgFullscreen)
{
tgFullscreen.isOn = Screen.fullScreen;
tgFullscreen.interactable = true;
}
LoadSettings();
}
private void SaveSettings()
{
if (ddQuality) PlayerPrefs.SetInt("QualityLevel", ddQuality.value);
PlayerPrefs.SetInt("Fullscreen", Screen.fullScreen ? 1 : 0);
if (ddResolution && ddResolution.options.Count > 0)
{
var opt = ddResolution.options[ddResolution.value].text;
var parts = opt.Split('x');
if (parts.Length == 2 && int.TryParse(parts[0], out int w) && int.TryParse(parts[1], out int h))
{
PlayerPrefs.SetInt("ResolutionWidth", w);
PlayerPrefs.SetInt("ResolutionHeight", h);
}
}
if (slMaster) PlayerPrefs.SetFloat("MasterVolume", slMaster.value);
if (slMusic) PlayerPrefs.SetFloat("MusicVolume", slMusic.value);
if (slSFX) PlayerPrefs.SetFloat("SFXVolume", slSFX.value);
PlayerPrefs.Save();
}
private void LoadSettings()
{
if (ddQuality && ddQuality.options.Count > 0)
{
int q = PlayerPrefs.GetInt("QualityLevel", QualitySettings.GetQualityLevel());
ddQuality.value = Mathf.Clamp(q, 0, ddQuality.options.Count - 1);
ddQuality.RefreshShownValue();
}
if (tgFullscreen)
{
bool full = PlayerPrefs.GetInt("Fullscreen", Screen.fullScreen ? 1 : 0) == 1;
tgFullscreen.isOn = full;
}
if (ddResolution && ddResolution.options.Count > 0)
{
int w = PlayerPrefs.GetInt("ResolutionWidth", Screen.currentResolution.width);
int h = PlayerPrefs.GetInt("ResolutionHeight", Screen.currentResolution.height);
string key = w + "x" + h;
int idx = ddResolution.options.FindIndex(o => o.text == key);
if (idx >= 0)
{
ddResolution.value = idx;
ddResolution.RefreshShownValue();
}
}
if (slMaster)
{
var mv = PlayerPrefs.GetFloat("MasterVolume", 100f);
slMaster.value = mv;
AudioListener.volume = mv / 100f;
}
if (slMusic)
{
slMusic.value = PlayerPrefs.GetFloat("MusicVolume", 80f);
}
if (slSFX)
{
slSFX.value = PlayerPrefs.GetFloat("SFXVolume", 100f);
}
}
private void FixDropdownRaycasts(TMP_Dropdown dd)
{
if (!dd || dd.template == null) return;
var tpl = dd.template;
var vp = tpl.Find("Viewport");
if (vp)
{
var img = vp.GetComponent<Image>();
if (img) img.raycastTarget = false;
}
}
private void SetupDropdownTemplateCanvas(TMP_Dropdown dd)
{
if (!dd || dd.template == null) return;
var tpl = dd.template;
var cv = tpl.GetComponent<Canvas>();
if (cv == null) cv = tpl.gameObject.AddComponent<Canvas>();
cv.overrideSorting = true;
cv.sortingOrder = 5000;
if (!tpl.GetComponent<GraphicRaycaster>()) tpl.gameObject.AddComponent<GraphicRaycaster>();
}
private void FixDropdownRaycasts(Dropdown dd)
{
if (!dd || dd.template == null) return;
var tpl = dd.template;
var vp = tpl.Find("Viewport");
if (vp)
{
var img = vp.GetComponent<Image>();
if (img) img.raycastTarget = false;
}
}
private void SetupDropdownTemplateCanvas(Dropdown dd)
{
if (!dd || dd.template == null) return;
var tpl = dd.template;
var cv = tpl.GetComponent<Canvas>();
if (cv == null) cv = tpl.gameObject.AddComponent<Canvas>();
cv.overrideSorting = true;
cv.sortingOrder = 5000;
if (!tpl.GetComponent<GraphicRaycaster>()) tpl.gameObject.AddComponent<GraphicRaycaster>();
}
}
}

View File

@@ -23,47 +23,47 @@ namespace MegaKoop.UI
private GameObject groupJoin;
private GameObject groupHost;
// Header & Code
private TMP_Text textStatus;
private TMP_Text textLobbyCodeValue;
private Button btnCopyCode;
[Header("Manual Wiring - Header & Code")]
[SerializeField] private TMP_Text textStatus;
[SerializeField] private TMP_Text textLobbyCodeValue;
[SerializeField] private Button btnCopyCode;
// Tabs
private Button btnHostTab;
private Button btnJoinTab;
[Header("Manual Wiring - Tabs")]
[SerializeField] private Button btnHostTab;
[SerializeField] private Button btnJoinTab;
// Join
private TMP_InputField inputLobbyCode;
private Button btnConnect;
[Header("Manual Wiring - Join Section")]
[SerializeField] private TMP_InputField inputLobbyCode;
[SerializeField] private Button btnConnect;
// Host
private TMP_Dropdown ddMaxPlayers;
private Toggle tgPublicLobby;
private Button btnCreateLobby;
[Header("Manual Wiring - Host Section")]
[SerializeField] private TMP_Dropdown ddMaxPlayers;
[SerializeField] private Toggle tgPublicLobby;
[SerializeField] private Button btnCreateLobby;
// Players
private TMP_Text textPlayerCount;
private ScrollRect scrollPlayers;
private Transform contentPlayers;
private GameObject playerItemTemplate;
private GameObject emptyPlayers;
[Header("Manual Wiring - Players List")]
[SerializeField] private TMP_Text textPlayerCount;
[SerializeField] private ScrollRect scrollPlayers;
[SerializeField] private Transform contentPlayers;
[SerializeField] private GameObject playerItemTemplate;
[SerializeField] private GameObject emptyPlayers;
// Friends picker (fallback when overlay unavailable)
private GameObject panelFriends;
private Transform contentFriends;
private GameObject emptyFriends;
private Button btnBackFromFriends;
private Button btnCloseFriendsOverlay;
[Header("Manual Wiring - Friends Picker")]
[SerializeField] private GameObject panelFriends;
[SerializeField] private Transform contentFriends;
[SerializeField] private GameObject emptyFriends;
[SerializeField] private Button btnBackFromFriends;
[SerializeField] private Button btnCloseFriendsOverlay;
// Host controls
private Button btnInviteFriends;
private Button btnKickSelected;
[Header("Manual Wiring - Host Controls")]
[SerializeField] private Button btnInviteFriends;
[SerializeField] private Button btnKickSelected;
// Ready & footer
private Button btnToggleReady;
private Button btnStartGame;
private Button btnLeaveLobby;
private Button btnBackFromLobby;
[Header("Manual Wiring - Footer")]
[SerializeField] private Button btnToggleReady;
[SerializeField] private Button btnStartGame;
[SerializeField] private Button btnLeaveLobby;
[SerializeField] private Button btnBackFromLobby;
[Header("Player Ready Styling")]
[SerializeField] private Color readyBorderColor = new Color(0.2f, 0.82f, 0.35f, 1f);
@@ -87,48 +87,49 @@ namespace MegaKoop.UI
private string LobbyCode => steam != null ? steam.LobbyCode : string.Empty;
private bool clientStartedFromSignal = false;
private bool leftDueToKick = false;
private bool inviteOverlayRequested = false;
private void Awake()
{
// Find UI (including inactive)
panelLobby = FindAnyGO("Panel_Lobby");
groupJoin = FindAnyGO("Group_Join");
groupHost = FindAnyGO("Group_Host");
panelLobby = EnsureGO(panelLobby, "Panel_Lobby");
groupJoin = EnsureGO(groupJoin, "Group_Join");
groupHost = EnsureGO(groupHost, "Group_Host");
textStatus = FindText("Text_Status");
textLobbyCodeValue = FindText("Text_LobbyCodeValue");
btnCopyCode = FindButton("Button_CopyCode");
textStatus = EnsureText(textStatus, "Text_Status");
textLobbyCodeValue = EnsureText(textLobbyCodeValue, "Text_LobbyCodeValue");
btnCopyCode = EnsureButton(btnCopyCode, "Button_CopyCode");
btnHostTab = FindButton("Button_HostTab");
btnJoinTab = FindButton("Button_JoinTab");
btnHostTab = EnsureButton(btnHostTab, "Button_HostTab");
btnJoinTab = EnsureButton(btnJoinTab, "Button_JoinTab");
inputLobbyCode = FindInput("Input_LobbyCode");
btnConnect = FindButton("Button_Connect");
inputLobbyCode = EnsureInput(inputLobbyCode, "Input_LobbyCode");
btnConnect = EnsureButton(btnConnect, "Button_Connect");
ddMaxPlayers = FindDropdown("Dropdown_MaxPlayers");
tgPublicLobby = FindToggle("Toggle_PublicLobby");
btnCreateLobby = FindButton("Button_CreateLobby");
ddMaxPlayers = EnsureDropdown(ddMaxPlayers, "Dropdown_MaxPlayers");
tgPublicLobby = EnsureToggle(tgPublicLobby, "Toggle_PublicLobby");
btnCreateLobby = EnsureButton(btnCreateLobby, "Button_CreateLobby");
textPlayerCount = FindText("Text_PlayerCount");
scrollPlayers = FindScroll("Scroll_Players");
contentPlayers = FindAnyGO("Content_PlayersList")?.transform;
emptyPlayers = FindAnyGO("Empty_Players");
playerItemTemplate = FindAnyGO("PlayerItemTemplate");
textPlayerCount = EnsureText(textPlayerCount, "Text_PlayerCount");
scrollPlayers = EnsureScroll(scrollPlayers, "Scroll_Players");
contentPlayers = EnsureTransform(contentPlayers, "Content_PlayersList");
emptyPlayers = EnsureGO(emptyPlayers, "Empty_Players");
playerItemTemplate = EnsureGO(playerItemTemplate, "PlayerItemTemplate");
btnInviteFriends = FindButton("Button_InviteFriends");
btnKickSelected = FindButton("Button_KickSelected");
btnInviteFriends = EnsureButton(btnInviteFriends, "Button_InviteFriends");
btnKickSelected = EnsureButton(btnKickSelected, "Button_KickSelected");
btnToggleReady = FindButton("Button_ToggleReady");
btnStartGame = FindButton("Button_StartGame");
btnLeaveLobby = FindButton("Button_LeaveLobby");
btnBackFromLobby = FindButton("Button_BackFromLobby");
btnToggleReady = EnsureButton(btnToggleReady, "Button_ToggleReady");
btnStartGame = EnsureButton(btnStartGame, "Button_StartGame");
btnLeaveLobby = EnsureButton(btnLeaveLobby, "Button_LeaveLobby");
btnBackFromLobby = EnsureButton(btnBackFromLobby, "Button_BackFromLobby");
// Friends picker
panelFriends = FindAnyGO("Panel_Friends");
contentFriends = FindAnyGO("Content_FriendsList")?.transform;
emptyFriends = FindAnyGO("Empty_Friends");
btnBackFromFriends = FindButton("Button_BackFromFriends");
btnCloseFriendsOverlay = FindButton("Button_CloseFriendsOverlay");
panelFriends = EnsureGO(panelFriends, "Panel_Friends");
contentFriends = EnsureTransform(contentFriends, "Content_FriendsList");
emptyFriends = EnsureGO(emptyFriends, "Empty_Friends");
btnBackFromFriends = EnsureButton(btnBackFromFriends, "Button_BackFromFriends");
btnCloseFriendsOverlay = EnsureButton(btnCloseFriendsOverlay, "Button_CloseFriendsOverlay");
EnsureSteamServices();
RegisterSteamEvents();
@@ -321,12 +322,78 @@ namespace MegaKoop.UI
panelLobbyRef = root;
}
private TMP_Text FindText(string name) => FindAnyComponent<TMP_Text>(name);
private Button FindButton(string name) => FindAnyComponent<Button>(name);
private Toggle FindToggle(string name) => FindAnyComponent<Toggle>(name);
private TMP_Dropdown FindDropdown(string name) => FindAnyComponent<TMP_Dropdown>(name);
private TMP_InputField FindInput(string name) => FindAnyComponent<TMP_InputField>(name);
private ScrollRect FindScroll(string name) => FindAnyComponent<ScrollRect>(name);
public void SetHostJoinGroups(GameObject hostGroup, GameObject joinGroup)
{
groupHost = hostGroup;
groupJoin = joinGroup;
}
public void SetHeaderControls(TMP_Text status, TMP_Text lobbyCode, Button copyCode)
{
textStatus = status;
textLobbyCodeValue = lobbyCode;
btnCopyCode = copyCode;
}
public void SetTabButtons(Button hostTab, Button joinTab)
{
btnHostTab = hostTab;
btnJoinTab = joinTab;
}
public void SetJoinSection(TMP_InputField lobbyCodeInput, Button connectButton)
{
inputLobbyCode = lobbyCodeInput;
btnConnect = connectButton;
}
public void SetHostSection(TMP_Dropdown maxPlayersDropdown, Toggle publicToggle, Button createLobbyButton)
{
ddMaxPlayers = maxPlayersDropdown;
tgPublicLobby = publicToggle;
btnCreateLobby = createLobbyButton;
}
public void SetPlayersList(TMP_Text playerCountText, ScrollRect playersScroll, Transform playersContent, GameObject itemTemplate, GameObject emptyState)
{
textPlayerCount = playerCountText;
scrollPlayers = playersScroll;
contentPlayers = playersContent;
playerItemTemplate = itemTemplate;
emptyPlayers = emptyState;
}
public void SetFriendsPicker(GameObject friendsPanel, Transform friendsContent, GameObject friendsEmpty, Button backButton, Button closeOverlayButton)
{
panelFriends = friendsPanel;
contentFriends = friendsContent;
emptyFriends = friendsEmpty;
btnBackFromFriends = backButton;
btnCloseFriendsOverlay = closeOverlayButton;
}
public void SetHostControls(Button inviteFriendsButton, Button kickSelectedButton)
{
btnInviteFriends = inviteFriendsButton;
btnKickSelected = kickSelectedButton;
}
public void SetFooterControls(Button toggleReadyButton, Button startGameButton, Button leaveLobbyButton, Button backButton)
{
btnToggleReady = toggleReadyButton;
btnStartGame = startGameButton;
btnLeaveLobby = leaveLobbyButton;
btnBackFromLobby = backButton;
}
private TMP_Text EnsureText(TMP_Text existing, string name) => existing ? existing : FindAnyComponent<TMP_Text>(name);
private Button EnsureButton(Button existing, string name) => existing ? existing : FindAnyComponent<Button>(name);
private Toggle EnsureToggle(Toggle existing, string name) => existing ? existing : FindAnyComponent<Toggle>(name);
private TMP_Dropdown EnsureDropdown(TMP_Dropdown existing, string name) => existing ? existing : FindAnyComponent<TMP_Dropdown>(name);
private TMP_InputField EnsureInput(TMP_InputField existing, string name) => existing ? existing : FindAnyComponent<TMP_InputField>(name);
private ScrollRect EnsureScroll(ScrollRect existing, string name) => existing ? existing : FindAnyComponent<ScrollRect>(name);
private Transform EnsureTransform(Transform existing, string name) => existing ? existing : FindAnyGO(name)?.transform;
private GameObject EnsureGO(GameObject existing, string name) => existing ? existing : FindAnyGO(name);
#region Steam
private void EnsureSteamServices()
@@ -456,8 +523,9 @@ namespace MegaKoop.UI
memberReadyCache.Clear();
UpdateUIFromSteam();
// Auto-open invite overlay for the host
if (steam != null && steam.IsInLobby && steam.IsHost)
if (inviteOverlayRequested && steam != null && steam.IsInLobby && steam.IsHost)
{
inviteOverlayRequested = false;
steam.InviteFriends();
if (!steam.IsOverlayEnabled()) ShowFriendsPanel();
}
@@ -469,8 +537,9 @@ namespace MegaKoop.UI
memberReadyCache.Clear();
UpdateUIFromSteam();
// Auto-open invite overlay if we are the host entering our lobby
if (steam != null && steam.IsInLobby && steam.IsHost)
if (inviteOverlayRequested && steam != null && steam.IsInLobby && steam.IsHost)
{
inviteOverlayRequested = false;
steam.InviteFriends();
if (!steam.IsOverlayEnabled()) ShowFriendsPanel();
}
@@ -481,6 +550,7 @@ namespace MegaKoop.UI
selectedPlayerSteamId = string.Empty;
memberReadyCache.Clear();
UpdateUIFromSteam();
inviteOverlayRequested = false;
}
private void HandleLobbyCreatedSync()
@@ -579,6 +649,7 @@ namespace MegaKoop.UI
if (!IsInLobby)
{
// Create a lobby first; overlay will auto-open in callbacks
inviteOverlayRequested = true;
CreateLobby();
}
else

17703
UI/UI_Canvas 1.prefab Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,7 @@
fileFormatVersion: 2
guid: fd82d2a29e7d6074384455d591a597e8
PrefabImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

17162
UI/UI_Canvas.prefab Normal file

File diff suppressed because it is too large Load Diff

7
UI/UI_Canvas.prefab.meta Normal file
View File

@@ -0,0 +1,7 @@
fileFormatVersion: 2
guid: 8f73add37dd781d47af917c56220d19b
PrefabImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

17122
UI/UI_Canvas_low_Fix.prefab Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,7 @@
fileFormatVersion: 2
guid: 440f89caf615ada4aa0202b06d7586fe
PrefabImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant: