Compare commits
2 Commits
b4c22edcbd
...
958ae1cb75
| Author | SHA1 | Date | |
|---|---|---|---|
| 958ae1cb75 | |||
| 9dea9daaa9 |
@@ -49,10 +49,12 @@
|
||||
<Analyzer Include="C:\Program Files\Unity\Hub\Editor\6000.3.5f2\Editor\Data\Tools\BuildPipeline\Unity.SourceGenerators\Unity.UIToolkit.SourceGenerator.dll" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Compile Include="Assets\Scripts\Editor\NetworkUIBuilder.cs" />
|
||||
<Compile Include="Assets\Scripts\Editor\MonsterPrefabSetup.cs" />
|
||||
<Compile Include="Assets\FlatKit\[Render Pipeline] URP\Water\Editor\Tooltips.cs" />
|
||||
<Compile Include="Assets\Scripts\Editor\EnemyPortalEditor.cs" />
|
||||
<Compile Include="Assets\Scripts\Editor\ObstacleSpawnerEditor.cs" />
|
||||
<Compile Include="Assets\Scripts\Editor\QuickNetworkSetupEditor.cs" />
|
||||
<Compile Include="Assets\Scripts\Editor\IPrefabSetup.cs" />
|
||||
<Compile Include="Assets\FlatKit\[Render Pipeline] URP\Water\Editor\WaterEditor.cs" />
|
||||
<Compile Include="Assets\Scripts\Editor\TowerPopulator.cs" />
|
||||
@@ -65,7 +67,9 @@
|
||||
<Compile Include="Assets\Scripts\Editor\CreepPrefabSetup.cs" />
|
||||
<Compile Include="Assets\Scripts\Editor\TemplateCreator.cs" />
|
||||
<Compile Include="Assets\FlatKit\Shaders\Editor\Tooltips.cs" />
|
||||
<Compile Include="Assets\Scripts\Editor\NetworkConnectionWindow.cs" />
|
||||
<Compile Include="Assets\FlatKit\Shaders\Editor\StylizedSurfaceEditor.cs" />
|
||||
<Compile Include="Assets\Scripts\Editor\NetworkConnectionHelperEditor.cs" />
|
||||
<Compile Include="Assets\Scripts\Editor\FogOfWarVisibilitySetup.cs" />
|
||||
<Compile Include="Assets\FlatKit\Shaders\Editor\MaterialPropertyExtensions.cs" />
|
||||
<Compile Include="Assets\FlatKit\Shaders\Editor\TerrainEditor.cs" />
|
||||
|
||||
@@ -86,6 +86,7 @@
|
||||
<Compile Include="Assets\Scripts\AttackAction.cs" />
|
||||
<Compile Include="Assets\Scripts\IInteractable.cs" />
|
||||
<Compile Include="Assets\Scripts\ServerResourceManager.cs" />
|
||||
<Compile Include="Assets\Scripts\QuickNetworkSetup.cs" />
|
||||
<Compile Include="Assets\FlatKit\Demos\Common\Scripts\UvScroller.cs" />
|
||||
<Compile Include="Assets\Data\Scripts\DataClasses\TowerDataExtensions.cs" />
|
||||
<Compile Include="Assets\Scripts\GlobalTimer.cs" />
|
||||
@@ -94,6 +95,7 @@
|
||||
<Compile Include="Assets\Scripts\BuildingHealthBar.cs" />
|
||||
<Compile Include="Assets\Data\Scripts\DataClasses\CreepData.cs" />
|
||||
<Compile Include="Assets\Scripts\PlayerSpawnPoint.cs" />
|
||||
<Compile Include="Assets\Scripts\NetworkJoinUI.cs" />
|
||||
<Compile Include="Assets\FlatKit\Demos\Common\Scripts\AutoLoadPipelineAsset.cs" />
|
||||
<Compile Include="Assets\Scripts\AutoHost.cs" />
|
||||
<Compile Include="Assets\Scripts\MonsterAnimationController.cs" />
|
||||
@@ -120,6 +122,7 @@
|
||||
<Compile Include="Assets\Scripts\EnemyAIState.cs" />
|
||||
<Compile Include="Assets\Scripts\ShortcutNetworkStarter.cs" />
|
||||
<Compile Include="Assets\Scripts\EquipmentData.cs" />
|
||||
<Compile Include="Assets\Scripts\NetworkConnectionHelper.cs" />
|
||||
<Compile Include="Assets\Scripts\NetworkConnectionHandler.cs" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
|
||||
@@ -3,24 +3,38 @@ using Unity.Netcode;
|
||||
|
||||
public class AutoHost : MonoBehaviour
|
||||
{
|
||||
// 에디터에서만 작동하도록 설정
|
||||
void Start()
|
||||
[Header("Auto Host Settings")]
|
||||
[SerializeField] private bool enableAutoHost = true;
|
||||
[SerializeField] private bool onlyInEditor = true;
|
||||
|
||||
private void Start()
|
||||
{
|
||||
if (ShouldAutoStart())
|
||||
{
|
||||
#if UNITY_EDITOR
|
||||
// 1. NetworkManager가 씬에 존재하는지 확인
|
||||
if (NetworkManager.Singleton != null)
|
||||
{
|
||||
// 2. 이미 서버나 클라이언트가 실행 중이 아닐 때만 실행
|
||||
if (!NetworkManager.Singleton.IsServer && !NetworkManager.Singleton.IsClient)
|
||||
{
|
||||
NetworkManager.Singleton.StartHost();
|
||||
Debug.Log("<color=yellow><b>[AutoHost]</b> 에디터 전용 호스트 자동 시작됨</color>");
|
||||
Debug.Log("<color=yellow><b>[AutoHost]</b> Auto-host started</color>");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Debug.LogError("[AutoHost] NetworkManager를 찾을 수 없습니다!");
|
||||
Debug.LogError("[AutoHost] NetworkManager not found!");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private bool ShouldAutoStart()
|
||||
{
|
||||
if (!enableAutoHost)
|
||||
return false;
|
||||
|
||||
#if UNITY_EDITOR
|
||||
return true;
|
||||
#else
|
||||
return !onlyInEditor;
|
||||
#endif
|
||||
}
|
||||
}
|
||||
@@ -193,6 +193,22 @@ namespace Northbound
|
||||
return;
|
||||
}
|
||||
|
||||
// 자원 확인 및 소비
|
||||
var coreResourceManager = CoreResourceManager.Instance;
|
||||
if (coreResourceManager == null)
|
||||
{
|
||||
Debug.LogWarning("<color=red>[BuildingManager] CoreResourceManager 인스턴스를 찾을 수 없습니다.</color>");
|
||||
return;
|
||||
}
|
||||
|
||||
if (!coreResourceManager.CanAfford(data.mana))
|
||||
{
|
||||
Debug.LogWarning($"<color=yellow>[BuildingManager] 코어 자원이 부족합니다. 필요: {data.mana} (클라이언트: {requestingClientId})</color>");
|
||||
return;
|
||||
}
|
||||
|
||||
coreResourceManager.SpendResources(data.mana);
|
||||
|
||||
// 배치 가능 여부 확인
|
||||
if (!IsValidPlacement(data, position, rotation, out Vector3 snappedPosition))
|
||||
{
|
||||
@@ -345,6 +361,22 @@ namespace Northbound
|
||||
return;
|
||||
}
|
||||
|
||||
// 자원 확인 및 소비
|
||||
var coreResourceManager = CoreResourceManager.Instance;
|
||||
if (coreResourceManager == null)
|
||||
{
|
||||
Debug.LogWarning("<color=red>[BuildingManager] CoreResourceManager 인스턴스를 찾을 수 없습니다.</color>");
|
||||
return;
|
||||
}
|
||||
|
||||
if (!coreResourceManager.CanAfford(data.mana))
|
||||
{
|
||||
Debug.LogWarning($"<color=yellow>[BuildingManager] 코어 자원이 부족합니다. 필요: {data.mana}</color>");
|
||||
return;
|
||||
}
|
||||
|
||||
coreResourceManager.SpendResources(data.mana);
|
||||
|
||||
// 토대 프리팹 확인
|
||||
if (foundationPrefab == null)
|
||||
{
|
||||
|
||||
@@ -332,12 +332,16 @@ namespace Northbound
|
||||
// Check if placement is valid
|
||||
bool isValid = BuildingManager.Instance.IsValidPlacement(data, hit.point, currentRotation, out Vector3 snappedPosition);
|
||||
|
||||
// Check affordability
|
||||
var coreResourceManager = CoreResourceManager.Instance;
|
||||
bool canAfford = coreResourceManager != null && coreResourceManager.CanAfford(data.mana);
|
||||
|
||||
// Update preview position (placementOffset 적용)
|
||||
previewObject.transform.position = snappedPosition + data.placementOffset;
|
||||
previewObject.transform.rotation = Quaternion.Euler(0, currentRotation * 90f, 0);
|
||||
|
||||
// Update material based on validity
|
||||
Material targetMat = isValid ? validMaterial : invalidMaterial;
|
||||
// Update material based on validity and affordability
|
||||
Material targetMat = (isValid && canAfford) ? validMaterial : invalidMaterial;
|
||||
foreach (var renderer in previewRenderers)
|
||||
{
|
||||
Material[] mats = new Material[renderer.materials.Length];
|
||||
@@ -462,6 +466,10 @@ namespace Northbound
|
||||
Vector3 dragEndPosition = hit.point;
|
||||
List<Vector3> positions = CalculateDragBuildingPositions(dragStartPosition, dragEndPosition, data);
|
||||
|
||||
// Check affordability for all buildings
|
||||
var coreResourceManager = CoreResourceManager.Instance;
|
||||
bool canAffordAll = coreResourceManager != null && coreResourceManager.CanAfford(data.mana * positions.Count);
|
||||
|
||||
// 기존 프리뷰 정리
|
||||
ClearDragPreviews();
|
||||
|
||||
@@ -490,7 +498,7 @@ namespace Northbound
|
||||
preview.transform.rotation = Quaternion.Euler(0, currentRotation * 90f, 0);
|
||||
|
||||
// Apply materials
|
||||
Material targetMat = isValid ? validMaterial : invalidMaterial;
|
||||
Material targetMat = (isValid && canAffordAll) ? validMaterial : invalidMaterial;
|
||||
Renderer[] renderers = preview.GetComponentsInChildren<Renderer>();
|
||||
foreach (var renderer in renderers)
|
||||
{
|
||||
@@ -569,6 +577,14 @@ namespace Northbound
|
||||
|
||||
TowerData selectedData = BuildingManager.Instance.availableBuildings[selectedBuildingIndex];
|
||||
|
||||
// Check affordability
|
||||
var coreResourceManager = CoreResourceManager.Instance;
|
||||
if (coreResourceManager == null || !coreResourceManager.CanAfford(selectedData.mana * dragBuildingPositions.Count))
|
||||
{
|
||||
Debug.Log("<color=yellow>[BuildingPlacement] 자원이 부족합니다.</color>");
|
||||
return;
|
||||
}
|
||||
|
||||
int successCount = 0;
|
||||
foreach (var position in dragBuildingPositions)
|
||||
{
|
||||
@@ -609,6 +625,14 @@ namespace Northbound
|
||||
{
|
||||
TowerData selectedData = BuildingManager.Instance.availableBuildings[selectedBuildingIndex];
|
||||
|
||||
// Check affordability
|
||||
var coreResourceManager = CoreResourceManager.Instance;
|
||||
if (coreResourceManager != null && !coreResourceManager.CanAfford(selectedData.mana))
|
||||
{
|
||||
Debug.Log("<color=yellow>[BuildingPlacement] 자원이 부족합니다.</color>");
|
||||
return;
|
||||
}
|
||||
|
||||
if (BuildingManager.Instance.IsValidPlacement(selectedData, hit.point, currentRotation, out Vector3 groundPosition))
|
||||
{
|
||||
// 토대 배치 요청
|
||||
|
||||
@@ -68,6 +68,28 @@ namespace Northbound
|
||||
InitializeSlots();
|
||||
}
|
||||
|
||||
private void Update()
|
||||
{
|
||||
if (quickslotPanel != null && quickslotPanel.activeSelf)
|
||||
{
|
||||
UpdateCostDisplays();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 모든 슬롯의 비용 표시 업데이트
|
||||
/// </summary>
|
||||
public void UpdateCostDisplays()
|
||||
{
|
||||
foreach (var slot in slotButtons)
|
||||
{
|
||||
if (slot != null)
|
||||
{
|
||||
slot.UpdateCostDisplay();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 퀵슬롯 Input Actions 초기화 및 구독 (직접 참조)
|
||||
/// </summary>
|
||||
|
||||
@@ -52,6 +52,20 @@ namespace Northbound
|
||||
UpdateVisuals();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 비용 표시 업데이트 (자원 변화 시 호출)
|
||||
/// </summary>
|
||||
public void UpdateCostDisplay()
|
||||
{
|
||||
if (nameText != null && buildingData != null)
|
||||
{
|
||||
var coreResourceManager = CoreResourceManager.Instance;
|
||||
bool canAfford = coreResourceManager != null && coreResourceManager.CanAfford(buildingData.mana);
|
||||
string costText = canAfford ? $"{buildingData.mana}" : $"<color=red>{buildingData.mana}</color>";
|
||||
nameText.text = $"{buildingData.buildingName}\n비용: {costText}";
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// UI 업데이트
|
||||
/// </summary>
|
||||
@@ -75,7 +89,10 @@ namespace Northbound
|
||||
// 이름 설정
|
||||
if (nameText != null)
|
||||
{
|
||||
nameText.text = buildingData.buildingName;
|
||||
var coreResourceManager = CoreResourceManager.Instance;
|
||||
bool canAfford = coreResourceManager != null && coreResourceManager.CanAfford(buildingData.mana);
|
||||
string costText = canAfford ? $"{buildingData.mana}" : $"<color=red>{buildingData.mana}</color>";
|
||||
nameText.text = $"{buildingData.buildingName}\n비용: {costText}";
|
||||
}
|
||||
|
||||
// 배경 색상
|
||||
|
||||
176
Assets/Scripts/Editor/NetworkConnectionHelperEditor.cs
Normal file
176
Assets/Scripts/Editor/NetworkConnectionHelperEditor.cs
Normal file
@@ -0,0 +1,176 @@
|
||||
using UnityEngine;
|
||||
using UnityEditor;
|
||||
using Unity.Netcode;
|
||||
|
||||
namespace Northbound.Editor
|
||||
{
|
||||
[CustomEditor(typeof(NetworkConnectionHelper))]
|
||||
public class NetworkConnectionHelperEditor : UnityEditor.Editor
|
||||
{
|
||||
private NetworkConnectionHelper _helper;
|
||||
private GUIStyle _statusStyle;
|
||||
|
||||
private void OnEnable()
|
||||
{
|
||||
_helper = (NetworkConnectionHelper)target;
|
||||
}
|
||||
|
||||
public override void OnInspectorGUI()
|
||||
{
|
||||
_statusStyle = new GUIStyle(EditorStyles.boldLabel);
|
||||
_statusStyle.alignment = TextAnchor.MiddleCenter;
|
||||
_statusStyle.fontSize = 12;
|
||||
|
||||
DrawDefaultInspector();
|
||||
EditorGUILayout.Space(10);
|
||||
|
||||
DrawConnectionControls();
|
||||
EditorGUILayout.Space(10);
|
||||
|
||||
DrawStatus();
|
||||
EditorGUILayout.Space(10);
|
||||
|
||||
DrawQuickActions();
|
||||
}
|
||||
|
||||
private void DrawConnectionControls()
|
||||
{
|
||||
EditorGUILayout.LabelField("Network Controls", EditorStyles.boldLabel);
|
||||
|
||||
EditorGUI.BeginDisabledGroup(IsConnected());
|
||||
|
||||
if (GUILayout.Button("Start Host", GUILayout.Height(30)))
|
||||
{
|
||||
_helper.StartHost();
|
||||
}
|
||||
|
||||
if (GUILayout.Button("Start Server", GUILayout.Height(30)))
|
||||
{
|
||||
_helper.StartServer();
|
||||
}
|
||||
|
||||
if (GUILayout.Button("Start Client", GUILayout.Height(30)))
|
||||
{
|
||||
_helper.StartClient();
|
||||
}
|
||||
|
||||
EditorGUI.EndDisabledGroup();
|
||||
|
||||
EditorGUI.BeginDisabledGroup(!IsConnected());
|
||||
|
||||
if (GUILayout.Button("Disconnect", GUILayout.Height(30)))
|
||||
{
|
||||
_helper.Disconnect();
|
||||
}
|
||||
|
||||
EditorGUI.EndDisabledGroup();
|
||||
}
|
||||
|
||||
private void DrawStatus()
|
||||
{
|
||||
EditorGUILayout.LabelField("Status", EditorStyles.boldLabel);
|
||||
|
||||
string status = GetDetailedStatus();
|
||||
Color bgColor = GetStatusColor();
|
||||
|
||||
var oldBgColor = GUI.backgroundColor;
|
||||
GUI.backgroundColor = bgColor;
|
||||
|
||||
EditorGUILayout.LabelField(status, _statusStyle, GUILayout.Height(25));
|
||||
|
||||
GUI.backgroundColor = oldBgColor;
|
||||
|
||||
if (NetworkManager.Singleton != null && IsConnected())
|
||||
{
|
||||
DrawNetworkInfo();
|
||||
}
|
||||
}
|
||||
|
||||
private void DrawNetworkInfo()
|
||||
{
|
||||
EditorGUILayout.LabelField("Network Information", EditorStyles.boldLabel);
|
||||
|
||||
using (new EditorGUILayout.VerticalScope(EditorStyles.helpBox))
|
||||
{
|
||||
if (NetworkManager.Singleton.IsHost)
|
||||
{
|
||||
EditorGUILayout.LabelField($"Mode: Host");
|
||||
}
|
||||
else if (NetworkManager.Singleton.IsServer)
|
||||
{
|
||||
EditorGUILayout.LabelField($"Mode: Server");
|
||||
}
|
||||
else if (NetworkManager.Singleton.IsClient)
|
||||
{
|
||||
EditorGUILayout.LabelField($"Mode: Client");
|
||||
}
|
||||
|
||||
EditorGUILayout.LabelField($"Connected Clients: {NetworkManager.Singleton.ConnectedClients.Count}");
|
||||
EditorGUILayout.LabelField($"Local Client ID: {NetworkManager.Singleton.LocalClientId}");
|
||||
|
||||
var transport = NetworkManager.Singleton.GetComponent<Unity.Netcode.Transports.UTP.UnityTransport>();
|
||||
if (transport != null)
|
||||
{
|
||||
EditorGUILayout.LabelField($"Address: {transport.ConnectionData.Address}");
|
||||
EditorGUILayout.LabelField($"Port: {transport.ConnectionData.Port}");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void DrawQuickActions()
|
||||
{
|
||||
EditorGUILayout.LabelField("Quick Actions", EditorStyles.boldLabel);
|
||||
|
||||
EditorGUILayout.BeginHorizontal();
|
||||
|
||||
if (GUILayout.Button("Open Connection Window"))
|
||||
{
|
||||
NetworkConnectionWindow.ShowWindow();
|
||||
}
|
||||
|
||||
if (GUILayout.Button("Test Connection"))
|
||||
{
|
||||
TestConnection();
|
||||
}
|
||||
|
||||
EditorGUILayout.EndHorizontal();
|
||||
}
|
||||
|
||||
private bool IsConnected()
|
||||
{
|
||||
return NetworkManager.Singleton != null &&
|
||||
(NetworkManager.Singleton.IsClient || NetworkManager.Singleton.IsServer);
|
||||
}
|
||||
|
||||
private string GetDetailedStatus()
|
||||
{
|
||||
if (NetworkManager.Singleton == null)
|
||||
return "NetworkManager Not Found";
|
||||
|
||||
if (NetworkManager.Singleton.IsHost)
|
||||
return "HOSTING";
|
||||
if (NetworkManager.Singleton.IsServer)
|
||||
return "SERVER RUNNING";
|
||||
if (NetworkManager.Singleton.IsClient)
|
||||
return "CONNECTED";
|
||||
|
||||
return "NOT CONNECTED";
|
||||
}
|
||||
|
||||
private Color GetStatusColor()
|
||||
{
|
||||
if (NetworkManager.Singleton == null)
|
||||
return new Color(0.8f, 0.3f, 0.3f);
|
||||
|
||||
if (NetworkManager.Singleton.IsHost || NetworkManager.Singleton.IsClient || NetworkManager.Singleton.IsServer)
|
||||
return new Color(0.3f, 0.8f, 0.3f);
|
||||
|
||||
return new Color(0.8f, 0.6f, 0.3f);
|
||||
}
|
||||
|
||||
private void TestConnection()
|
||||
{
|
||||
Debug.Log($"[NetworkConnectionHelper] Connection test - Status: {_helper.GetStatus()}");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 36e75e477fa8d69459450bd36b5c6878
|
||||
295
Assets/Scripts/Editor/NetworkConnectionWindow.cs
Normal file
295
Assets/Scripts/Editor/NetworkConnectionWindow.cs
Normal file
@@ -0,0 +1,295 @@
|
||||
using UnityEngine;
|
||||
using UnityEditor;
|
||||
using Unity.Netcode;
|
||||
using System.IO;
|
||||
using UnityEngine.SceneManagement;
|
||||
|
||||
namespace Northbound.Editor
|
||||
{
|
||||
public class NetworkConnectionWindow : UnityEditor.EditorWindow
|
||||
{
|
||||
private string _serverIP = "127.0.0.1";
|
||||
private string _port = "7777";
|
||||
private bool _isHost = true;
|
||||
private NetworkConnectionMode _connectionMode = NetworkConnectionMode.Host;
|
||||
private string _savedSettingsPath;
|
||||
|
||||
private enum NetworkConnectionMode
|
||||
{
|
||||
Host,
|
||||
Client,
|
||||
Server
|
||||
}
|
||||
|
||||
[MenuItem("Window/Network/Connection Manager %#n")]
|
||||
public static void ShowWindow()
|
||||
{
|
||||
var window = GetWindow<NetworkConnectionWindow>("Network Connection");
|
||||
window.minSize = new Vector2(350, 250);
|
||||
}
|
||||
|
||||
private void OnEnable()
|
||||
{
|
||||
_savedSettingsPath = Path.Combine(Application.persistentDataPath, "NetworkConnectionSettings.json");
|
||||
LoadSettings();
|
||||
}
|
||||
|
||||
private void OnGUI()
|
||||
{
|
||||
EditorGUILayout.Space(10);
|
||||
|
||||
GUILayout.Label("Network Connection Manager", EditorStyles.boldLabel);
|
||||
EditorGUILayout.Space(5);
|
||||
|
||||
DrawConnectionModeUI();
|
||||
EditorGUILayout.Space(10);
|
||||
|
||||
DrawConnectionSettings();
|
||||
EditorGUILayout.Space(10);
|
||||
|
||||
DrawStatusDisplay();
|
||||
EditorGUILayout.Space(10);
|
||||
|
||||
DrawActionButtons();
|
||||
EditorGUILayout.Space(10);
|
||||
|
||||
DrawSettingsButtons();
|
||||
}
|
||||
|
||||
private void DrawConnectionModeUI()
|
||||
{
|
||||
EditorGUILayout.LabelField("Connection Mode", EditorStyles.boldLabel);
|
||||
_connectionMode = (NetworkConnectionMode)EditorGUILayout.EnumPopup("Mode", _connectionMode);
|
||||
|
||||
if (_connectionMode == NetworkConnectionMode.Host)
|
||||
{
|
||||
_isHost = true;
|
||||
}
|
||||
}
|
||||
|
||||
private void DrawConnectionSettings()
|
||||
{
|
||||
EditorGUILayout.LabelField("Connection Settings", EditorStyles.boldLabel);
|
||||
|
||||
if (_connectionMode == NetworkConnectionMode.Client)
|
||||
{
|
||||
_serverIP = EditorGUILayout.TextField("Server IP", _serverIP);
|
||||
_port = EditorGUILayout.TextField("Port", _port);
|
||||
}
|
||||
else if (_connectionMode == NetworkConnectionMode.Host || _connectionMode == NetworkConnectionMode.Server)
|
||||
{
|
||||
_port = EditorGUILayout.TextField("Port", _port);
|
||||
}
|
||||
|
||||
if (GUI.changed)
|
||||
{
|
||||
SaveSettings();
|
||||
}
|
||||
}
|
||||
|
||||
private void DrawStatusDisplay()
|
||||
{
|
||||
EditorGUILayout.LabelField("Status", EditorStyles.boldLabel);
|
||||
|
||||
string status = GetNetworkStatus();
|
||||
Color statusColor = GetStatusColor();
|
||||
|
||||
var oldColor = GUI.color;
|
||||
GUI.color = statusColor;
|
||||
EditorGUILayout.LabelField(status, EditorStyles.helpBox);
|
||||
GUI.color = oldColor;
|
||||
}
|
||||
|
||||
private void DrawActionButtons()
|
||||
{
|
||||
EditorGUI.BeginDisabledGroup(IsNetworkActive());
|
||||
|
||||
if (GUILayout.Button(_connectionMode == NetworkConnectionMode.Client ? "Connect" : "Start", GUILayout.Height(30)))
|
||||
{
|
||||
StartConnection();
|
||||
}
|
||||
|
||||
EditorGUI.EndDisabledGroup();
|
||||
|
||||
EditorGUI.BeginDisabledGroup(!IsNetworkActive());
|
||||
|
||||
if (GUILayout.Button("Disconnect", GUILayout.Height(30)))
|
||||
{
|
||||
Disconnect();
|
||||
}
|
||||
|
||||
EditorGUI.EndDisabledGroup();
|
||||
}
|
||||
|
||||
private void DrawSettingsButtons()
|
||||
{
|
||||
EditorGUILayout.BeginHorizontal();
|
||||
|
||||
if (GUILayout.Button("Save Settings"))
|
||||
{
|
||||
SaveSettings();
|
||||
Debug.Log("[NetworkConnectionWindow] Settings saved.");
|
||||
}
|
||||
|
||||
if (GUILayout.Button("Load Settings"))
|
||||
{
|
||||
LoadSettings();
|
||||
Debug.Log("[NetworkConnectionWindow] Settings loaded.");
|
||||
}
|
||||
|
||||
EditorGUILayout.EndHorizontal();
|
||||
}
|
||||
|
||||
private void StartConnection()
|
||||
{
|
||||
if (NetworkManager.Singleton == null)
|
||||
{
|
||||
EditorUtility.DisplayDialog("Error", "NetworkManager not found in the scene!", "OK");
|
||||
return;
|
||||
}
|
||||
|
||||
ushort port;
|
||||
if (!ushort.TryParse(_port, out port))
|
||||
{
|
||||
EditorUtility.DisplayDialog("Error", "Invalid port number!", "OK");
|
||||
return;
|
||||
}
|
||||
|
||||
NetworkManager.Singleton.GetComponent<Unity.Netcode.Transports.UTP.UnityTransport>()?.SetConnectionData(
|
||||
_serverIP,
|
||||
port
|
||||
);
|
||||
|
||||
switch (_connectionMode)
|
||||
{
|
||||
case NetworkConnectionMode.Host:
|
||||
NetworkManager.Singleton.StartHost();
|
||||
Debug.Log($"[NetworkConnectionWindow] Started Host on port {port}");
|
||||
break;
|
||||
|
||||
case NetworkConnectionMode.Client:
|
||||
NetworkManager.Singleton.StartClient();
|
||||
Debug.Log($"[NetworkConnectionWindow] Started Client connecting to {_serverIP}:{port}");
|
||||
break;
|
||||
|
||||
case NetworkConnectionMode.Server:
|
||||
NetworkManager.Singleton.StartServer();
|
||||
Debug.Log($"[NetworkConnectionWindow] Started Server on port {port}");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
private void Disconnect()
|
||||
{
|
||||
if (NetworkManager.Singleton != null)
|
||||
{
|
||||
NetworkManager.Singleton.Shutdown();
|
||||
Debug.Log("[NetworkConnectionWindow] Disconnected");
|
||||
}
|
||||
}
|
||||
|
||||
private bool IsNetworkActive()
|
||||
{
|
||||
return NetworkManager.Singleton != null &&
|
||||
(NetworkManager.Singleton.IsClient || NetworkManager.Singleton.IsServer);
|
||||
}
|
||||
|
||||
private string GetNetworkStatus()
|
||||
{
|
||||
if (NetworkManager.Singleton == null)
|
||||
return "NetworkManager not found";
|
||||
|
||||
if (NetworkManager.Singleton.IsHost)
|
||||
return $"Hosting (Port: {GetActivePort()})";
|
||||
if (NetworkManager.Singleton.IsServer)
|
||||
return $"Server (Port: {GetActivePort()})";
|
||||
if (NetworkManager.Singleton.IsClient)
|
||||
return $"Client connected to {GetActiveIP()}:{GetActivePort()}";
|
||||
|
||||
return "Not connected";
|
||||
}
|
||||
|
||||
private Color GetStatusColor()
|
||||
{
|
||||
if (NetworkManager.Singleton == null)
|
||||
return Color.red;
|
||||
|
||||
if (NetworkManager.Singleton.IsHost || NetworkManager.Singleton.IsClient || NetworkManager.Singleton.IsServer)
|
||||
return new Color(0f, 0.7f, 0f);
|
||||
|
||||
return new Color(1f, 0.7f, 0f);
|
||||
}
|
||||
|
||||
private string GetActiveIP()
|
||||
{
|
||||
if (NetworkManager.Singleton == null) return "N/A";
|
||||
|
||||
var transport = NetworkManager.Singleton.GetComponent<Unity.Netcode.Transports.UTP.UnityTransport>();
|
||||
if (transport != null)
|
||||
{
|
||||
return transport.ConnectionData.Address;
|
||||
}
|
||||
return "N/A";
|
||||
}
|
||||
|
||||
private string GetActivePort()
|
||||
{
|
||||
if (NetworkManager.Singleton == null) return "N/A";
|
||||
|
||||
var transport = NetworkManager.Singleton.GetComponent<Unity.Netcode.Transports.UTP.UnityTransport>();
|
||||
if (transport != null)
|
||||
{
|
||||
return transport.ConnectionData.Port.ToString();
|
||||
}
|
||||
return "N/A";
|
||||
}
|
||||
|
||||
private void SaveSettings()
|
||||
{
|
||||
try
|
||||
{
|
||||
string json = $"{{\"serverIP\":\"{_serverIP}\",\"port\":\"{_port}\",\"connectionMode\":{_connectionMode}}}";
|
||||
File.WriteAllText(_savedSettingsPath, json);
|
||||
}
|
||||
catch (System.Exception e)
|
||||
{
|
||||
Debug.LogError($"[NetworkConnectionWindow] Failed to save settings: {e.Message}");
|
||||
}
|
||||
}
|
||||
|
||||
private void LoadSettings()
|
||||
{
|
||||
try
|
||||
{
|
||||
if (File.Exists(_savedSettingsPath))
|
||||
{
|
||||
string json = File.ReadAllText(_savedSettingsPath);
|
||||
var settings = JsonUtility.FromJson<ConnectionSettings>(json);
|
||||
_serverIP = settings.serverIP;
|
||||
_port = settings.port;
|
||||
_connectionMode = (NetworkConnectionMode)settings.connectionMode;
|
||||
}
|
||||
}
|
||||
catch (System.Exception e)
|
||||
{
|
||||
Debug.LogError($"[NetworkConnectionWindow] Failed to load settings: {e.Message}");
|
||||
}
|
||||
}
|
||||
|
||||
private void Update()
|
||||
{
|
||||
if (IsNetworkActive())
|
||||
{
|
||||
Repaint();
|
||||
}
|
||||
}
|
||||
|
||||
[System.Serializable]
|
||||
private class ConnectionSettings
|
||||
{
|
||||
public string serverIP;
|
||||
public string port;
|
||||
public int connectionMode;
|
||||
}
|
||||
}
|
||||
}
|
||||
2
Assets/Scripts/Editor/NetworkConnectionWindow.cs.meta
Normal file
2
Assets/Scripts/Editor/NetworkConnectionWindow.cs.meta
Normal file
@@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: b7b2610829b9bba4f801fb9d527963c9
|
||||
176
Assets/Scripts/Editor/NetworkUIBuilder.cs
Normal file
176
Assets/Scripts/Editor/NetworkUIBuilder.cs
Normal file
@@ -0,0 +1,176 @@
|
||||
using UnityEngine;
|
||||
using UnityEngine.UI;
|
||||
using UnityEditor;
|
||||
|
||||
namespace Northbound.Editor
|
||||
{
|
||||
public class NetworkUIBuilder
|
||||
{
|
||||
[MenuItem("GameObject/Network/Create Network Join UI")]
|
||||
public static void CreateNetworkJoinUI()
|
||||
{
|
||||
Canvas canvas = FindOrCreateCanvas();
|
||||
GameObject panel = CreatePanel(canvas.transform);
|
||||
|
||||
InputField ipInput = CreateInputField(panel.transform, "IP Input", "127.0.0.1", 150);
|
||||
InputField portInput = CreateInputField(panel.transform, "Port Input", "7777", 100);
|
||||
|
||||
Button joinButton = CreateButton(panel.transform, "Join", new Vector2(0, -80));
|
||||
Button hostButton = CreateButton(panel.transform, "Host", new Vector2(-60, -80));
|
||||
Button disconnectButton = CreateButton(panel.transform, "Disconnect", new Vector2(60, -80));
|
||||
|
||||
Text statusText = CreateStatusText(panel.transform);
|
||||
|
||||
NetworkJoinUI networkJoinUI = panel.AddComponent<NetworkJoinUI>();
|
||||
networkJoinUI.SetUIReferences(panel, ipInput, portInput, joinButton, hostButton, disconnectButton, statusText);
|
||||
|
||||
Selection.activeGameObject = panel;
|
||||
Debug.Log("[NetworkUIBuilder] Network Join UI created!");
|
||||
}
|
||||
|
||||
private static Canvas FindOrCreateCanvas()
|
||||
{
|
||||
Canvas[] canvases = Object.FindObjectsByType<Canvas>(FindObjectsSortMode.None);
|
||||
|
||||
if (canvases.Length > 0)
|
||||
{
|
||||
return canvases[0];
|
||||
}
|
||||
|
||||
GameObject canvasObj = new GameObject("NetworkCanvas");
|
||||
Canvas canvas = canvasObj.AddComponent<Canvas>();
|
||||
canvas.renderMode = RenderMode.ScreenSpaceOverlay;
|
||||
canvasObj.AddComponent<CanvasScaler>();
|
||||
canvasObj.AddComponent<GraphicRaycaster>();
|
||||
|
||||
return canvas;
|
||||
}
|
||||
|
||||
private static GameObject CreatePanel(Transform parent)
|
||||
{
|
||||
GameObject panel = new GameObject("NetworkJoinPanel");
|
||||
panel.transform.SetParent(parent, false);
|
||||
|
||||
RectTransform rect = panel.AddComponent<RectTransform>();
|
||||
rect.anchorMin = new Vector2(0.5f, 0.5f);
|
||||
rect.anchorMax = new Vector2(0.5f, 0.5f);
|
||||
rect.pivot = new Vector2(0.5f, 0.5f);
|
||||
rect.sizeDelta = new Vector2(300, 250);
|
||||
rect.anchoredPosition = Vector2.zero;
|
||||
|
||||
Image image = panel.AddComponent<Image>();
|
||||
image.color = new Color(0.1f, 0.1f, 0.1f, 0.95f);
|
||||
|
||||
return panel;
|
||||
}
|
||||
|
||||
private static InputField CreateInputField(Transform parent, string name, string placeholder, float width)
|
||||
{
|
||||
GameObject fieldObj = new GameObject(name);
|
||||
fieldObj.transform.SetParent(parent, false);
|
||||
|
||||
RectTransform rect = fieldObj.AddComponent<RectTransform>();
|
||||
rect.anchorMin = new Vector2(0.5f, 0.5f);
|
||||
rect.anchorMax = new Vector2(0.5f, 0.5f);
|
||||
rect.pivot = new Vector2(0.5f, 0.5f);
|
||||
rect.sizeDelta = new Vector2(width, 30);
|
||||
|
||||
int yPos = name.Contains("IP") ? 80 : 40;
|
||||
rect.anchoredPosition = new Vector2(0, yPos);
|
||||
|
||||
Image bgImage = fieldObj.AddComponent<Image>();
|
||||
bgImage.color = new Color(0.2f, 0.2f, 0.2f, 1f);
|
||||
|
||||
InputField inputField = fieldObj.AddComponent<InputField>();
|
||||
inputField.textComponent = CreateTextComponent(fieldObj.transform, "");
|
||||
inputField.text = placeholder;
|
||||
inputField.contentType = InputField.ContentType.Standard;
|
||||
|
||||
Text placeholderText = CreateTextComponent(fieldObj.transform, placeholder);
|
||||
placeholderText.color = new Color(0.5f, 0.5f, 0.5f, 0.5f);
|
||||
inputField.placeholder = placeholderText;
|
||||
|
||||
Text label = CreateTextComponent(parent, name.ToUpper());
|
||||
RectTransform labelRect = label.GetComponent<RectTransform>();
|
||||
labelRect.anchoredPosition = new Vector2(-width / 2 - 30, yPos);
|
||||
|
||||
return inputField;
|
||||
}
|
||||
|
||||
private static Button CreateButton(Transform parent, string text, Vector2 position)
|
||||
{
|
||||
GameObject buttonObj = new GameObject(text + "Button");
|
||||
buttonObj.transform.SetParent(parent, false);
|
||||
|
||||
RectTransform rect = buttonObj.AddComponent<RectTransform>();
|
||||
rect.anchorMin = new Vector2(0.5f, 0.5f);
|
||||
rect.anchorMax = new Vector2(0.5f, 0.5f);
|
||||
rect.pivot = new Vector2(0.5f, 0.5f);
|
||||
rect.sizeDelta = new Vector2(80, 30);
|
||||
rect.anchoredPosition = position;
|
||||
|
||||
Image bgImage = buttonObj.AddComponent<Image>();
|
||||
bgImage.color = new Color(0.3f, 0.5f, 0.8f, 1f);
|
||||
|
||||
Button button = buttonObj.AddComponent<Button>();
|
||||
|
||||
Text buttonText = CreateTextComponent(buttonObj.transform, text);
|
||||
buttonText.alignment = TextAnchor.MiddleCenter;
|
||||
|
||||
return button;
|
||||
}
|
||||
|
||||
private static Text CreateTextComponent(Transform parent, string text)
|
||||
{
|
||||
GameObject textObj = new GameObject("Text");
|
||||
textObj.transform.SetParent(parent, false);
|
||||
|
||||
RectTransform rect = textObj.AddComponent<RectTransform>();
|
||||
rect.anchorMin = Vector2.zero;
|
||||
rect.anchorMax = Vector2.one;
|
||||
rect.offsetMin = Vector2.zero;
|
||||
rect.offsetMax = Vector2.zero;
|
||||
|
||||
Text textComponent = textObj.AddComponent<Text>();
|
||||
textComponent.text = text;
|
||||
textComponent.font = Resources.GetBuiltinResource<Font>("Arial.ttf");
|
||||
textComponent.fontSize = 14;
|
||||
textComponent.color = Color.white;
|
||||
textComponent.alignment = TextAnchor.MiddleCenter;
|
||||
|
||||
return textComponent;
|
||||
}
|
||||
|
||||
private static Text CreateStatusText(Transform parent)
|
||||
{
|
||||
GameObject textObj = new GameObject("StatusText");
|
||||
textObj.transform.SetParent(parent, false);
|
||||
|
||||
RectTransform rect = textObj.AddComponent<RectTransform>();
|
||||
rect.anchorMin = new Vector2(0.5f, 0.5f);
|
||||
rect.anchorMax = new Vector2(0.5f, 0.5f);
|
||||
rect.pivot = new Vector2(0.5f, 0.5f);
|
||||
rect.sizeDelta = new Vector2(280, 30);
|
||||
rect.anchoredPosition = new Vector2(0, -120);
|
||||
|
||||
Text textComponent = textObj.AddComponent<Text>();
|
||||
textComponent.text = "Not connected";
|
||||
textComponent.font = Resources.GetBuiltinResource<Font>("Arial.ttf");
|
||||
textComponent.fontSize = 12;
|
||||
textComponent.color = Color.yellow;
|
||||
textComponent.alignment = TextAnchor.MiddleCenter;
|
||||
|
||||
return textComponent;
|
||||
}
|
||||
|
||||
[MenuItem("GameObject/Network/Create Network Helper")]
|
||||
public static void CreateNetworkHelper()
|
||||
{
|
||||
GameObject helperObj = new GameObject("NetworkConnectionHelper");
|
||||
helperObj.AddComponent<NetworkConnectionHelper>();
|
||||
|
||||
Selection.activeGameObject = helperObj;
|
||||
Debug.Log("[NetworkUIBuilder] Network Connection Helper created!");
|
||||
}
|
||||
}
|
||||
}
|
||||
2
Assets/Scripts/Editor/NetworkUIBuilder.cs.meta
Normal file
2
Assets/Scripts/Editor/NetworkUIBuilder.cs.meta
Normal file
@@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 010611d682d1a064caa47bc3667f0675
|
||||
93
Assets/Scripts/Editor/QuickNetworkSetupEditor.cs
Normal file
93
Assets/Scripts/Editor/QuickNetworkSetupEditor.cs
Normal file
@@ -0,0 +1,93 @@
|
||||
using UnityEngine;
|
||||
using UnityEditor;
|
||||
|
||||
namespace Northbound.Editor
|
||||
{
|
||||
public class QuickNetworkSetupEditor
|
||||
{
|
||||
[MenuItem("Tools/Network/Quick Setup for IP Connection")]
|
||||
public static void QuickSetup()
|
||||
{
|
||||
bool disableAutoHost = EditorUtility.DisplayDialog(
|
||||
"Quick Network Setup",
|
||||
"This will set up your scene for manual IP-based network connections.\n\n" +
|
||||
"It will:\n" +
|
||||
"- Create a NetworkConnectionHelper component\n" +
|
||||
"- Disable AutoHost (to prevent auto-start)\n\n" +
|
||||
"Do you want to disable AutoHost?",
|
||||
"Yes, disable AutoHost",
|
||||
"No, keep AutoHost"
|
||||
);
|
||||
|
||||
CreateNetworkHelper();
|
||||
|
||||
if (disableAutoHost)
|
||||
{
|
||||
DisableAutoHost();
|
||||
}
|
||||
|
||||
EditorUtility.DisplayDialog(
|
||||
"Setup Complete",
|
||||
"Quick network setup complete!\n\n" +
|
||||
"Next steps:\n" +
|
||||
"1. Open Window > Network > Connection Manager\n" +
|
||||
"2. Or use the NetworkConnectionHelper in Inspector\n" +
|
||||
"3. Start Host on one instance\n" +
|
||||
"4. Start Client on other instances with the Host's IP",
|
||||
"OK"
|
||||
);
|
||||
}
|
||||
|
||||
[MenuItem("Tools/Network/Create Connection Helper")]
|
||||
public static void CreateNetworkHelper()
|
||||
{
|
||||
if (Object.FindObjectOfType<NetworkConnectionHelper>() == null)
|
||||
{
|
||||
GameObject helperObj = new GameObject("NetworkConnectionHelper");
|
||||
helperObj.AddComponent<NetworkConnectionHelper>();
|
||||
Selection.activeGameObject = helperObj;
|
||||
Debug.Log("[QuickNetworkSetup] NetworkConnectionHelper created");
|
||||
}
|
||||
else
|
||||
{
|
||||
Debug.Log("[QuickNetworkSetup] NetworkConnectionHelper already exists");
|
||||
}
|
||||
}
|
||||
|
||||
[MenuItem("Tools/Network/Disable AutoHost")]
|
||||
public static void DisableAutoHost()
|
||||
{
|
||||
AutoHost[] autoHosts = Object.FindObjectsByType<AutoHost>(FindObjectsSortMode.None);
|
||||
if (autoHosts.Length > 0)
|
||||
{
|
||||
foreach (var autoHost in autoHosts)
|
||||
{
|
||||
autoHost.enabled = false;
|
||||
}
|
||||
Debug.Log($"[QuickNetworkSetup] Disabled {autoHosts.Length} AutoHost component(s)");
|
||||
}
|
||||
else
|
||||
{
|
||||
Debug.Log("[QuickNetworkSetup] No AutoHost components found");
|
||||
}
|
||||
}
|
||||
|
||||
[MenuItem("Tools/Network/Enable AutoHost")]
|
||||
public static void EnableAutoHost()
|
||||
{
|
||||
AutoHost[] autoHosts = Object.FindObjectsByType<AutoHost>(FindObjectsSortMode.None);
|
||||
if (autoHosts.Length > 0)
|
||||
{
|
||||
foreach (var autoHost in autoHosts)
|
||||
{
|
||||
autoHost.enabled = true;
|
||||
}
|
||||
Debug.Log($"[QuickNetworkSetup] Enabled {autoHosts.Length} AutoHost component(s)");
|
||||
}
|
||||
else
|
||||
{
|
||||
Debug.Log("[QuickNetworkSetup] No AutoHost components found");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
2
Assets/Scripts/Editor/QuickNetworkSetupEditor.cs.meta
Normal file
2
Assets/Scripts/Editor/QuickNetworkSetupEditor.cs.meta
Normal file
@@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: d84f5c07a6e69614dad5178e0f994335
|
||||
176
Assets/Scripts/NETWORK_CONNECTION_GUIDE_KO.md
Normal file
176
Assets/Scripts/NETWORK_CONNECTION_GUIDE_KO.md
Normal file
@@ -0,0 +1,176 @@
|
||||
# 네트워크 연결 기능 사용 가이드
|
||||
|
||||
에디터와 빌드 모두에서 IP 주소를 통해 서버에 접속할 수 있는 기능이 추가되었습니다.
|
||||
|
||||
## 새로 추가된 기능
|
||||
|
||||
### 1. 네트워크 연결 윈도우 (NetworkConnectionWindow)
|
||||
GUI 기반의 전용 에디터 윈도우
|
||||
|
||||
**사용 방법:**
|
||||
- 메뉴: `Window > Network > Connection Manager` 또는 단축키 `Ctrl+Shift+N` / `Cmd+Shift+N`
|
||||
- 기능:
|
||||
- 연결 모드 선택 (호스트/클라이언트/서버)
|
||||
- 서버 IP와 포트 입력
|
||||
- 연결 시작/중지
|
||||
- 연결 상태 확인
|
||||
- 연결 설정 저장/로드
|
||||
|
||||
### 2. 네트워크 연결 헬퍼 (NetworkConnectionHelper)
|
||||
커스텀 인스펙터가 포함된 컴포넌트
|
||||
|
||||
**사용 방법:**
|
||||
1. 씬의 게임오브젝트에 `NetworkConnectionHelper` 컴포넌트 추가
|
||||
2. 인스펙터에서 설정:
|
||||
- 서버 IP (클라이언트 모드용)
|
||||
- 포트 번호
|
||||
- 자동 시작 옵션
|
||||
3. 인스펙터 버튼으로 제어:
|
||||
- Start Host
|
||||
- Start Server
|
||||
- Start Client
|
||||
- Disconnect
|
||||
4. 실시간 연결 상태와 네트워크 정보 확인
|
||||
|
||||
### 3. 네트워크 조인 UI (NetworkJoinUI)
|
||||
인게임 UI로 IP를 통한 접속 지원 (에디터/빌드 모두 작동)
|
||||
|
||||
**사용 방법:**
|
||||
1. 메뉴에서 `GameObject > Network > Create Network Join UI` 선택
|
||||
2. `J` 키로 UI 패널 토글
|
||||
3. UI 버튼으로 접속:
|
||||
- Join: 입력된 IP로 접속
|
||||
- Host: 호스트로 시작
|
||||
- Disconnect: 연결 종료
|
||||
|
||||
### 4. AutoHost 업데이트
|
||||
에디터 모드에서 자동 호스트 시작 (설정 가능)
|
||||
|
||||
**사용 방법:**
|
||||
- `AutoHost` 컴포넌트 확인
|
||||
- 설정:
|
||||
- `Enable Auto Host`: 자동 시작 활성화/비활성화
|
||||
- `Only In Editor`: 에디터에서만 자동 시작 (빌드에서는 비활성화)
|
||||
|
||||
## 빠른 설정
|
||||
|
||||
### 방법 A: 퀵 설정 메뉴 사용 (추천)
|
||||
```
|
||||
Tools > Network > Quick Setup for IP Connection
|
||||
```
|
||||
이 옵션은:
|
||||
- NetworkConnectionHelper 생성
|
||||
- AutoHost 비활성화 (선택 사항)
|
||||
|
||||
### 방법 B: 수동 설정
|
||||
1. `Tools > Network > Create Connection Helper`로 헬퍼 생성
|
||||
2. `Tools > Network > Disable AutoHost`로 자동 시작 비활성화
|
||||
|
||||
## 멀티플레이어 테스트 방법
|
||||
|
||||
### 1. 로컬 테스트 (같은 컴퓨터)
|
||||
1. 윈도우: `Window > Network > Connection Manager` 열기
|
||||
2. **인스턴스 1:**
|
||||
- 모드: Host 선택
|
||||
- 포트: 7777 (기본값)
|
||||
- "Start" 클릭
|
||||
3. **인스턴스 2:**
|
||||
- 모드: Client 선택
|
||||
- IP: 127.0.0.1 또는 localhost
|
||||
- 포트: 7777
|
||||
- "Connect" 클릭
|
||||
|
||||
### 2. 네트워크 테스트 (같은 네트워크)
|
||||
1. 호스트 컴퓨터에서 로컬 IP 확인 (cmd에서 `ipconfig` 실행)
|
||||
2. **호스트:**
|
||||
- Connection Manager에서 Host 모드로 시작
|
||||
3. **클라이언트:**
|
||||
- Connection Manager에서 Client 모드 선택
|
||||
- IP: 호스트 컴퓨터의 로컬 IP (예: 192.168.1.100)
|
||||
- 포트: 7777
|
||||
- "Connect" 클릭
|
||||
|
||||
### 3. 인스펙터로 테스트
|
||||
1. `NetworkConnectionHelper` 컴포넌트 추가
|
||||
2. 인스펙터 버튼으로 호스트/클라이언트 시작
|
||||
3. 상태와 연결 정보 확인
|
||||
|
||||
## 연결 모드 설명
|
||||
|
||||
### Host
|
||||
- 서버와 클라이언트 모두 역할 수행
|
||||
- 멀티플레이어 게임 호스팅
|
||||
- 플레이어들이 당신의 IP로 접속
|
||||
|
||||
### Client
|
||||
- 기존 서버에 접속
|
||||
- 서버 IP 주소 필요
|
||||
|
||||
### Server
|
||||
- 전용 서버 모드 (플레이어 없음)
|
||||
- 전용 서버용
|
||||
|
||||
## 네트워크 정보 표시
|
||||
|
||||
모든 연결 방법에서 다음을 표시:
|
||||
- 현재 모드 (호스트/클라이언트/서버)
|
||||
- 연결 상태
|
||||
- IP 주소와 포트
|
||||
- 접속된 클라이언트 수
|
||||
- 로컬 클라이언트 ID
|
||||
|
||||
## 팁
|
||||
|
||||
1. **로컬 테스트:**
|
||||
- `127.0.0.1` 또는 `localhost` 사용
|
||||
- 같은 네트워크에서 테스트할 때 로컬 IP 사용
|
||||
|
||||
2. **기본 포트:**
|
||||
- 기본 포트는 `7777`
|
||||
- 연결 컴포넌트 설정에서 변경 가능
|
||||
|
||||
3. **방화벽:**
|
||||
- 네트워크 연결을 위해 포트 개방 필요
|
||||
- Windows 방화벽이 연결을 차단할 수 있음
|
||||
|
||||
4. **여러 에디터 인스턴스:**
|
||||
- Unity의 멀티플레이 툴 사용
|
||||
- 또는 여러 빌드 실행
|
||||
|
||||
## 기존 시스템과의 호환성
|
||||
|
||||
새 기능들은 기존 시스템과 통합:
|
||||
- 플레이어 스폰은 `NetworkConnectionHandler`가 자동 처리
|
||||
- 연결 승인은 `NetworkConnectionHandler`가 담당
|
||||
- 기존 멀티플레이어 시스템과 호환
|
||||
- 기존 코드 수정 불필요
|
||||
|
||||
## 파일 목록
|
||||
|
||||
### 새로 생성된 파일
|
||||
- `Assets/Scripts/NetworkConnectionHelper.cs` - 네트워크 헬퍼 컴포넌트
|
||||
- `Assets/Scripts/NetworkJoinUI.cs` - 인게임 연결 UI
|
||||
- `Assets/Scripts/QuickNetworkSetup.cs` - 빠른 설정 스크립트
|
||||
- `Assets/Scripts/Editor/NetworkConnectionWindow.cs` - 연결 관리자 윈도우
|
||||
- `Assets/Scripts/Editor/NetworkConnectionHelperEditor.cs` - 헬퍼 커스텀 인스펙터
|
||||
- `Assets/Scripts/Editor/NetworkUIBuilder.cs` - UI 생성 도구
|
||||
- `Assets/Scripts/Editor/QuickNetworkSetupEditor.cs` - 빠른 설정 에디터 도구
|
||||
|
||||
### 수정된 파일
|
||||
- `Assets/Scripts/AutoHost.cs` - 설정 가능한 옵션 추가
|
||||
|
||||
## 문제 해결
|
||||
|
||||
### 연결 실패 시
|
||||
1. IP 주소와 포트 번호 확인
|
||||
2. 방화벽 설정 확인
|
||||
3. 같은 네트워크에 있는지 확인
|
||||
4. 호스트가 먼저 실행 중인지 확인
|
||||
|
||||
### 자동 시작 비활성화 방법
|
||||
1. `Tools > Network > Disable AutoHost` 선택
|
||||
2. 또는 `AutoHost` 컴포넌트의 `Enable Auto Host` 체크 해제
|
||||
|
||||
### UI가 보이지 않을 때
|
||||
1. `J` 키 눌러 토글
|
||||
2. 또는 인스펙터에서 `NetworkJoinUI` 활성화 확인
|
||||
7
Assets/Scripts/NETWORK_CONNECTION_GUIDE_KO.md.meta
Normal file
7
Assets/Scripts/NETWORK_CONNECTION_GUIDE_KO.md.meta
Normal file
@@ -0,0 +1,7 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 9e44a64498019a7468946f609fb954cd
|
||||
TextScriptImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
137
Assets/Scripts/NETWORK_CONNECTION_README.md
Normal file
137
Assets/Scripts/NETWORK_CONNECTION_README.md
Normal file
@@ -0,0 +1,137 @@
|
||||
# Network Connection Features
|
||||
|
||||
This project provides multiple ways to connect to multiplayer servers by IP address, both in the editor and in standalone builds.
|
||||
|
||||
## Features
|
||||
|
||||
### 1. Editor Window (NetworkConnectionWindow)
|
||||
A dedicated Editor window for managing network connections with a GUI.
|
||||
|
||||
**How to Use:**
|
||||
- Open via: `Window > Network > Connection Manager` or press `Ctrl+Shift+N` / `Cmd+Shift+N`
|
||||
- Features:
|
||||
- Select connection mode (Host/Client/Server)
|
||||
- Enter server IP and port
|
||||
- Start/Stop network connections
|
||||
- View connection status
|
||||
- Save/load connection settings
|
||||
|
||||
### 2. NetworkConnectionHelper Component
|
||||
A MonoBehaviour component with custom inspector for quick network control.
|
||||
|
||||
**How to Use:**
|
||||
1. Add `NetworkConnectionHelper` component to any GameObject in your scene
|
||||
2. Configure settings in Inspector:
|
||||
- Server IP (for client mode)
|
||||
- Port
|
||||
- Auto-start options
|
||||
3. Use inspector buttons to:
|
||||
- Start Host
|
||||
- Start Server
|
||||
- Start Client
|
||||
- Disconnect
|
||||
4. View real-time connection status and network info
|
||||
|
||||
### 3. NetworkJoinUI Component
|
||||
In-game UI for connecting to servers by IP (works in both editor and standalone builds).
|
||||
|
||||
**How to Use:**
|
||||
1. Create UI elements:
|
||||
- InputField for IP address
|
||||
- InputField for port
|
||||
- Buttons: Join, Host, Disconnect
|
||||
- Text for status display
|
||||
2. Add `NetworkJoinUI` component to a GameObject
|
||||
3. Assign UI references in Inspector
|
||||
4. Press `J` key (default) to toggle the UI panel
|
||||
5. Connect using the UI buttons
|
||||
|
||||
### 4. AutoHost Component (Updated)
|
||||
Auto-starts as host in editor mode (configurable).
|
||||
|
||||
**How to Use:**
|
||||
- Add `AutoHost` component to any GameObject
|
||||
- Settings:
|
||||
- `Enable Auto Host`: Enable/disable auto-start
|
||||
- `Only In Editor`: Only auto-start in editor (not in standalone builds)
|
||||
|
||||
## Setup Instructions
|
||||
|
||||
### For Editor Testing:
|
||||
|
||||
**Option A: Using Editor Window (Recommended)**
|
||||
1. Open `Window > Network > Connection Manager`
|
||||
2. Select mode: `Host` or `Client`
|
||||
3. Enter IP/port if needed
|
||||
4. Click `Start` or `Connect`
|
||||
|
||||
**Option B: Using NetworkConnectionHelper**
|
||||
1. Add `NetworkConnectionHelper` component to your scene
|
||||
2. Use Inspector buttons to control connections
|
||||
3. For auto-start on play, enable `Auto Start As Host`
|
||||
|
||||
**Option C: Using AutoHost (Original Behavior)**
|
||||
1. Keep or add `AutoHost` component
|
||||
2. It will auto-start as host when playing in editor
|
||||
|
||||
### For Standalone Builds:
|
||||
|
||||
**Option A: Using NetworkJoinUI (In-Game UI)**
|
||||
1. Add `NetworkJoinUI` component with UI references
|
||||
2. Build and run
|
||||
3. Press `J` to open connection panel
|
||||
4. Enter IP and connect
|
||||
|
||||
**Option B: Using NetworkConnectionHelper**
|
||||
1. Add `NetworkConnectionHelper` component
|
||||
2. Set `Auto Start As Host = true` and `Only In Editor = false`
|
||||
3. Build - it will auto-start as host
|
||||
|
||||
## Connection Modes
|
||||
|
||||
### Host
|
||||
- Acts as both server and client
|
||||
- Can host multiplayer games
|
||||
- Players connect to your IP
|
||||
|
||||
### Client
|
||||
- Connects to an existing server
|
||||
- Requires server IP address
|
||||
|
||||
### Server
|
||||
- Server-only mode (no player)
|
||||
- Used for dedicated servers
|
||||
|
||||
## Network Info Display
|
||||
|
||||
All connection methods display:
|
||||
- Current mode (Host/Client/Server)
|
||||
- Connection status
|
||||
- IP address and port
|
||||
- Number of connected clients
|
||||
- Local Client ID
|
||||
|
||||
## Tips
|
||||
|
||||
1. **Testing Multiplayer Locally:**
|
||||
- Use `127.0.0.1` or `localhost` for local testing
|
||||
- Or use your local IP for testing on same network
|
||||
|
||||
2. **Default Port:**
|
||||
- Default port is `7777`
|
||||
- Change in any connection component's settings
|
||||
|
||||
3. **Firewall:**
|
||||
- Ensure port is open for network connections
|
||||
- Windows Firewall may block connections
|
||||
|
||||
4. **Multiple Editor Instances:**
|
||||
- Use Unity's `ParrelSync` or run multiple standalone builds
|
||||
- Each instance needs different settings
|
||||
|
||||
## Integration
|
||||
|
||||
All features integrate with existing `NetworkManager` and `NetworkConnectionHandler`:
|
||||
- Player spawning is handled automatically
|
||||
- Connection approval is handled by `NetworkConnectionHandler`
|
||||
- Compatible with existing multiplayer systems
|
||||
7
Assets/Scripts/NETWORK_CONNECTION_README.md.meta
Normal file
7
Assets/Scripts/NETWORK_CONNECTION_README.md.meta
Normal file
@@ -0,0 +1,7 @@
|
||||
fileFormatVersion: 2
|
||||
guid: f75d808bc7fb71441b7c599cf75ab9a6
|
||||
TextScriptImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
192
Assets/Scripts/NETWORK_CONNECTION_SUMMARY.md
Normal file
192
Assets/Scripts/NETWORK_CONNECTION_SUMMARY.md
Normal file
@@ -0,0 +1,192 @@
|
||||
# Network Connection Feature Summary
|
||||
|
||||
## Overview
|
||||
Added comprehensive IP-based network connection features for both editor testing and standalone builds.
|
||||
|
||||
## Files Created
|
||||
|
||||
### Runtime Components
|
||||
1. **NetworkConnectionHelper.cs** (3.6KB)
|
||||
- MonoBehaviour component for network connection management
|
||||
- Start Host/Server/Client methods
|
||||
- Auto-start configuration
|
||||
- Status monitoring
|
||||
|
||||
2. **NetworkJoinUI.cs** (7.7KB)
|
||||
- In-game UI component for IP connection
|
||||
- Works in both editor and standalone builds
|
||||
- Toggle panel with J key
|
||||
- Supports Join/Host/Disconnect
|
||||
|
||||
3. **QuickNetworkSetup.cs** (1.6KB)
|
||||
- Quick scene setup script
|
||||
- Creates NetworkConnectionHelper
|
||||
- Optionally disables AutoHost
|
||||
|
||||
### Editor Tools
|
||||
4. **NetworkConnectionWindow.cs** (9.2KB)
|
||||
- Dedicated Editor window for network management
|
||||
- GUI-based IP/port configuration
|
||||
- Connection mode selection
|
||||
- Status display and settings persistence
|
||||
|
||||
5. **NetworkConnectionHelperEditor.cs** (5.8KB)
|
||||
- Custom Inspector for NetworkConnectionHelper
|
||||
- Quick action buttons
|
||||
- Real-time status display
|
||||
- Network information panel
|
||||
|
||||
6. **NetworkUIBuilder.cs** (7.4KB)
|
||||
- Automated UI creation for NetworkJoinUI
|
||||
- Creates complete UI structure
|
||||
- Menu integration
|
||||
|
||||
7. **QuickNetworkSetupEditor.cs** (2.0KB)
|
||||
- Menu items for quick setup
|
||||
- Tools menu integration
|
||||
- Enable/Disable AutoHost functionality
|
||||
|
||||
### Documentation
|
||||
8. **NETWORK_CONNECTION_README.md**
|
||||
- Comprehensive English documentation
|
||||
- Setup instructions
|
||||
- Usage examples
|
||||
|
||||
9. **NETWORK_CONNECTION_GUIDE_KO.md**
|
||||
- Korean translation of documentation
|
||||
- Step-by-step guide
|
||||
- Troubleshooting section
|
||||
|
||||
## Files Modified
|
||||
|
||||
1. **AutoHost.cs**
|
||||
- Made auto-start configurable
|
||||
- Added editor-only option
|
||||
- Added enable/disable toggle
|
||||
|
||||
## Features Added
|
||||
|
||||
### 1. Editor Window (NetworkConnectionWindow)
|
||||
- Open via: Window > Network > Connection Manager (Ctrl+Shift+N)
|
||||
- Connection modes: Host/Client/Server
|
||||
- IP and port configuration
|
||||
- Start/Stop connections
|
||||
- Real-time status
|
||||
- Settings persistence
|
||||
|
||||
### 2. NetworkConnectionHelper Component
|
||||
- Add to any GameObject in scene
|
||||
- Custom Inspector with quick buttons
|
||||
- Auto-start configuration
|
||||
- Status display
|
||||
- Network information panel
|
||||
|
||||
### 3. In-Game UI (NetworkJoinUI)
|
||||
- Works in editor and standalone builds
|
||||
- IP/port input fields
|
||||
- Join/Host/Disconnect buttons
|
||||
- Status display
|
||||
- Toggle with J key
|
||||
- Auto-generated via menu
|
||||
|
||||
### 4. Quick Setup Tools
|
||||
- Tools > Network > Quick Setup for IP Connection
|
||||
- Automatically disables AutoHost
|
||||
- Creates NetworkConnectionHelper
|
||||
|
||||
## Integration
|
||||
|
||||
All new features integrate seamlessly with existing systems:
|
||||
- **NetworkManager**: Works with Unity Netcode for GameObjects
|
||||
- **NetworkConnectionHandler**: Handles player spawning and approval
|
||||
- **AutoHost**: Configurable to avoid conflicts
|
||||
- **Transport**: Uses Unity Transport (UTP)
|
||||
|
||||
## Testing Workflow
|
||||
|
||||
### Method 1: Editor Window
|
||||
1. Window > Network > Connection Manager
|
||||
2. Select Host on instance 1
|
||||
3. Select Client on instance 2 with Host's IP
|
||||
4. Start/Connect
|
||||
|
||||
### Method 2: NetworkConnectionHelper
|
||||
1. Add component to scene
|
||||
2. Use Inspector buttons
|
||||
3. View status in real-time
|
||||
|
||||
### Method 3: Quick Setup
|
||||
1. Tools > Network > Quick Setup for IP Connection
|
||||
2. Use preferred connection method
|
||||
|
||||
### Method 4: In-Game UI
|
||||
1. GameObject > Network > Create Network Join UI
|
||||
2. Press J to open
|
||||
3. Enter IP and connect
|
||||
|
||||
## Configuration
|
||||
|
||||
### Default Settings
|
||||
- Default IP: 127.0.0.1
|
||||
- Default Port: 7777
|
||||
- Toggle Key: J (for UI)
|
||||
|
||||
### Options
|
||||
- Auto-start: Enable/disable in AutoHost
|
||||
- Editor-only: Configure per component
|
||||
- Persistence: Save/load in Editor Window
|
||||
|
||||
## Benefits
|
||||
|
||||
1. **Easy Testing**: No standalone build required
|
||||
2. **Team Collaboration**: Join teammates by IP
|
||||
3. **Production Ready**: Works in standalone builds
|
||||
4. **Flexible**: Multiple connection methods
|
||||
5. **Intuitive**: GUI-based interfaces
|
||||
6. **Integrated**: Works with existing systems
|
||||
|
||||
## Future Enhancements
|
||||
|
||||
Potential improvements:
|
||||
- Server list browsing
|
||||
- Favorites/Saved connections
|
||||
- LAN discovery
|
||||
- Connection history
|
||||
- Advanced transport settings
|
||||
- Password protection
|
||||
|
||||
## Usage Example
|
||||
|
||||
### Editor Testing
|
||||
```csharp
|
||||
// Instance 1 (Host)
|
||||
Tools > Network > Quick Setup for IP Connection
|
||||
Window > Network > Connection Manager > Start Host
|
||||
|
||||
// Instance 2 (Client)
|
||||
Tools > Network > Quick Setup for IP Connection
|
||||
Window > Network > Connection Manager > Connect to 127.0.0.1:7777
|
||||
```
|
||||
|
||||
### Standalone Build
|
||||
```csharp
|
||||
// Build with NetworkJoinUI component
|
||||
Press J to open connection panel
|
||||
Enter IP: 192.168.1.X
|
||||
Click Join
|
||||
```
|
||||
|
||||
## Compatibility
|
||||
|
||||
- Unity Netcode for GameObjects 2.8.1
|
||||
- Unity Transport (UTP)
|
||||
- Unity 2022.x+
|
||||
- Windows/Mac/Linux
|
||||
|
||||
## Notes
|
||||
|
||||
- All components are optional
|
||||
- AutoHost can be disabled
|
||||
- Existing AutoHost behavior preserved (when enabled)
|
||||
- No breaking changes to existing code
|
||||
- Works with custom scenes and setups
|
||||
7
Assets/Scripts/NETWORK_CONNECTION_SUMMARY.md.meta
Normal file
7
Assets/Scripts/NETWORK_CONNECTION_SUMMARY.md.meta
Normal file
@@ -0,0 +1,7 @@
|
||||
fileFormatVersion: 2
|
||||
guid: ef125ff435342b64f86eeea8dbef234f
|
||||
TextScriptImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
117
Assets/Scripts/NetworkConnectionHelper.cs
Normal file
117
Assets/Scripts/NetworkConnectionHelper.cs
Normal file
@@ -0,0 +1,117 @@
|
||||
using UnityEngine;
|
||||
using Unity.Netcode;
|
||||
|
||||
namespace Northbound
|
||||
{
|
||||
public class NetworkConnectionHelper : MonoBehaviour
|
||||
{
|
||||
[Header("Connection Settings")]
|
||||
[SerializeField] private string serverIP = "127.0.0.1";
|
||||
[SerializeField] private ushort port = 7777;
|
||||
|
||||
[Header("Auto Start")]
|
||||
[SerializeField] private bool autoStartAsHost = false;
|
||||
[SerializeField] private bool onlyInEditor = true;
|
||||
|
||||
private void Start()
|
||||
{
|
||||
if (autoStartAsHost && ShouldAutoStart())
|
||||
{
|
||||
StartHost();
|
||||
}
|
||||
}
|
||||
|
||||
private bool ShouldAutoStart()
|
||||
{
|
||||
#if UNITY_EDITOR
|
||||
return true;
|
||||
#else
|
||||
return !onlyInEditor;
|
||||
#endif
|
||||
}
|
||||
|
||||
public void StartHost()
|
||||
{
|
||||
if (NetworkManager.Singleton == null)
|
||||
{
|
||||
Debug.LogError("[NetworkConnectionHelper] NetworkManager not found!");
|
||||
return;
|
||||
}
|
||||
|
||||
ConfigureTransport("0.0.0.0", port);
|
||||
|
||||
if (!NetworkManager.Singleton.IsServer && !NetworkManager.Singleton.IsClient)
|
||||
{
|
||||
NetworkManager.Singleton.StartHost();
|
||||
Debug.Log($"[NetworkConnectionHelper] Started Host on port {port}");
|
||||
}
|
||||
}
|
||||
|
||||
public void StartClient()
|
||||
{
|
||||
if (NetworkManager.Singleton == null)
|
||||
{
|
||||
Debug.LogError("[NetworkConnectionHelper] NetworkManager not found!");
|
||||
return;
|
||||
}
|
||||
|
||||
ConfigureTransport(serverIP, port);
|
||||
|
||||
if (!NetworkManager.Singleton.IsClient && !NetworkManager.Singleton.IsServer)
|
||||
{
|
||||
NetworkManager.Singleton.StartClient();
|
||||
Debug.Log($"[NetworkConnectionHelper] Connecting to {serverIP}:{port}");
|
||||
}
|
||||
}
|
||||
|
||||
public void StartServer()
|
||||
{
|
||||
if (NetworkManager.Singleton == null)
|
||||
{
|
||||
Debug.LogError("[NetworkConnectionHelper] NetworkManager not found!");
|
||||
return;
|
||||
}
|
||||
|
||||
ConfigureTransport("0.0.0.0", port);
|
||||
|
||||
if (!NetworkManager.Singleton.IsServer && !NetworkManager.Singleton.IsClient)
|
||||
{
|
||||
NetworkManager.Singleton.StartServer();
|
||||
Debug.Log($"[NetworkConnectionHelper] Started Server on port {port}");
|
||||
}
|
||||
}
|
||||
|
||||
public void Disconnect()
|
||||
{
|
||||
if (NetworkManager.Singleton != null)
|
||||
{
|
||||
NetworkManager.Singleton.Shutdown();
|
||||
Debug.Log("[NetworkConnectionHelper] Disconnected");
|
||||
}
|
||||
}
|
||||
|
||||
public string GetStatus()
|
||||
{
|
||||
if (NetworkManager.Singleton == null)
|
||||
return "NetworkManager not found";
|
||||
|
||||
if (NetworkManager.Singleton.IsHost)
|
||||
return "Hosting";
|
||||
if (NetworkManager.Singleton.IsServer)
|
||||
return "Server";
|
||||
if (NetworkManager.Singleton.IsClient)
|
||||
return "Client";
|
||||
|
||||
return "Not connected";
|
||||
}
|
||||
|
||||
private void ConfigureTransport(string ip, ushort portNum)
|
||||
{
|
||||
var transport = NetworkManager.Singleton.GetComponent<Unity.Netcode.Transports.UTP.UnityTransport>();
|
||||
if (transport != null)
|
||||
{
|
||||
transport.SetConnectionData(ip, portNum);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
2
Assets/Scripts/NetworkConnectionHelper.cs.meta
Normal file
2
Assets/Scripts/NetworkConnectionHelper.cs.meta
Normal file
@@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 947211167dacac64092d1c6ea2b00af6
|
||||
269
Assets/Scripts/NetworkJoinUI.cs
Normal file
269
Assets/Scripts/NetworkJoinUI.cs
Normal file
@@ -0,0 +1,269 @@
|
||||
using UnityEngine;
|
||||
using UnityEngine.UI;
|
||||
using Unity.Netcode;
|
||||
|
||||
namespace Northbound
|
||||
{
|
||||
public class NetworkJoinUI : MonoBehaviour
|
||||
{
|
||||
[Header("UI References")]
|
||||
[SerializeField] private GameObject joinPanel;
|
||||
[SerializeField] private InputField ipInputField;
|
||||
[SerializeField] private InputField portInputField;
|
||||
[SerializeField] private Button joinButton;
|
||||
[SerializeField] private Button hostButton;
|
||||
[SerializeField] private Button disconnectButton;
|
||||
[SerializeField] private Text statusText;
|
||||
[SerializeField] private KeyCode toggleKey = KeyCode.J;
|
||||
|
||||
[Header("Settings")]
|
||||
[SerializeField] private string defaultIP = "127.0.0.1";
|
||||
[SerializeField] private string defaultPort = "7777";
|
||||
|
||||
private bool _isVisible = false;
|
||||
|
||||
private void Awake()
|
||||
{
|
||||
InitializeUI();
|
||||
}
|
||||
|
||||
private void Start()
|
||||
{
|
||||
SetupDefaultValues();
|
||||
SubscribeToEvents();
|
||||
UpdateStatus();
|
||||
}
|
||||
|
||||
private void Update()
|
||||
{
|
||||
if (Input.GetKeyDown(toggleKey))
|
||||
{
|
||||
TogglePanel();
|
||||
}
|
||||
|
||||
if (NetworkManager.Singleton != null &&
|
||||
(NetworkManager.Singleton.IsClient || NetworkManager.Singleton.IsServer))
|
||||
{
|
||||
UpdateStatus();
|
||||
}
|
||||
}
|
||||
|
||||
private void InitializeUI()
|
||||
{
|
||||
if (joinPanel != null)
|
||||
{
|
||||
joinPanel.SetActive(false);
|
||||
}
|
||||
}
|
||||
|
||||
private void SetupDefaultValues()
|
||||
{
|
||||
if (ipInputField != null)
|
||||
{
|
||||
ipInputField.text = defaultIP;
|
||||
}
|
||||
|
||||
if (portInputField != null)
|
||||
{
|
||||
portInputField.text = defaultPort;
|
||||
}
|
||||
}
|
||||
|
||||
private void SubscribeToEvents()
|
||||
{
|
||||
if (joinButton != null)
|
||||
{
|
||||
joinButton.onClick.AddListener(OnJoinClicked);
|
||||
}
|
||||
|
||||
if (hostButton != null)
|
||||
{
|
||||
hostButton.onClick.AddListener(OnHostClicked);
|
||||
}
|
||||
|
||||
if (disconnectButton != null)
|
||||
{
|
||||
disconnectButton.onClick.AddListener(OnDisconnectClicked);
|
||||
}
|
||||
}
|
||||
|
||||
private void UnsubscribeFromEvents()
|
||||
{
|
||||
if (joinButton != null)
|
||||
{
|
||||
joinButton.onClick.RemoveListener(OnJoinClicked);
|
||||
}
|
||||
|
||||
if (hostButton != null)
|
||||
{
|
||||
hostButton.onClick.RemoveListener(OnHostClicked);
|
||||
}
|
||||
|
||||
if (disconnectButton != null)
|
||||
{
|
||||
disconnectButton.onClick.RemoveListener(OnDisconnectClicked);
|
||||
}
|
||||
}
|
||||
|
||||
private void OnJoinClicked()
|
||||
{
|
||||
if (NetworkManager.Singleton == null)
|
||||
{
|
||||
ShowError("NetworkManager not found!");
|
||||
return;
|
||||
}
|
||||
|
||||
string ip = ipInputField != null ? ipInputField.text : defaultIP;
|
||||
string port = portInputField != null ? portInputField.text : defaultPort;
|
||||
|
||||
ushort portNum;
|
||||
if (!ushort.TryParse(port, out portNum))
|
||||
{
|
||||
ShowError("Invalid port number!");
|
||||
return;
|
||||
}
|
||||
|
||||
if (NetworkManager.Singleton.IsClient || NetworkManager.Singleton.IsServer)
|
||||
{
|
||||
ShowError("Already connected!");
|
||||
return;
|
||||
}
|
||||
|
||||
var transport = NetworkManager.Singleton.GetComponent<Unity.Netcode.Transports.UTP.UnityTransport>();
|
||||
if (transport != null)
|
||||
{
|
||||
transport.SetConnectionData(ip, portNum);
|
||||
}
|
||||
|
||||
NetworkManager.Singleton.StartClient();
|
||||
UpdateStatus();
|
||||
Debug.Log($"[NetworkJoinUI] Connecting to {ip}:{port}");
|
||||
}
|
||||
|
||||
private void OnHostClicked()
|
||||
{
|
||||
if (NetworkManager.Singleton == null)
|
||||
{
|
||||
ShowError("NetworkManager not found!");
|
||||
return;
|
||||
}
|
||||
|
||||
string port = portInputField != null ? portInputField.text : defaultPort;
|
||||
|
||||
ushort portNum;
|
||||
if (!ushort.TryParse(port, out portNum))
|
||||
{
|
||||
ShowError("Invalid port number!");
|
||||
return;
|
||||
}
|
||||
|
||||
if (NetworkManager.Singleton.IsClient || NetworkManager.Singleton.IsServer)
|
||||
{
|
||||
ShowError("Already connected!");
|
||||
return;
|
||||
}
|
||||
|
||||
var transport = NetworkManager.Singleton.GetComponent<Unity.Netcode.Transports.UTP.UnityTransport>();
|
||||
if (transport != null)
|
||||
{
|
||||
transport.SetConnectionData("0.0.0.0", portNum);
|
||||
}
|
||||
|
||||
NetworkManager.Singleton.StartHost();
|
||||
UpdateStatus();
|
||||
Debug.Log($"[NetworkJoinUI] Started Host on port {port}");
|
||||
}
|
||||
|
||||
private void OnDisconnectClicked()
|
||||
{
|
||||
if (NetworkManager.Singleton != null)
|
||||
{
|
||||
NetworkManager.Singleton.Shutdown();
|
||||
UpdateStatus();
|
||||
Debug.Log("[NetworkJoinUI] Disconnected");
|
||||
}
|
||||
}
|
||||
|
||||
private void UpdateStatus()
|
||||
{
|
||||
if (statusText == null) return;
|
||||
|
||||
string status = GetNetworkStatus();
|
||||
statusText.text = status;
|
||||
}
|
||||
|
||||
private string GetNetworkStatus()
|
||||
{
|
||||
if (NetworkManager.Singleton == null)
|
||||
return "NetworkManager not found";
|
||||
|
||||
if (NetworkManager.Singleton.IsHost)
|
||||
return $"Hosting (Port: {GetActivePort()})";
|
||||
if (NetworkManager.Singleton.IsServer)
|
||||
return $"Server (Port: {GetActivePort()})";
|
||||
if (NetworkManager.Singleton.IsClient)
|
||||
return $"Client connected to {GetActiveIP()}:{GetActivePort()}";
|
||||
|
||||
return "Not connected";
|
||||
}
|
||||
|
||||
private string GetActiveIP()
|
||||
{
|
||||
if (NetworkManager.Singleton == null) return "N/A";
|
||||
|
||||
var transport = NetworkManager.Singleton.GetComponent<Unity.Netcode.Transports.UTP.UnityTransport>();
|
||||
if (transport != null)
|
||||
{
|
||||
return transport.ConnectionData.Address;
|
||||
}
|
||||
return "N/A";
|
||||
}
|
||||
|
||||
private string GetActivePort()
|
||||
{
|
||||
if (NetworkManager.Singleton == null) return "N/A";
|
||||
|
||||
var transport = NetworkManager.Singleton.GetComponent<Unity.Netcode.Transports.UTP.UnityTransport>();
|
||||
if (transport != null)
|
||||
{
|
||||
return transport.ConnectionData.Port.ToString();
|
||||
}
|
||||
return "N/A";
|
||||
}
|
||||
|
||||
private void TogglePanel()
|
||||
{
|
||||
if (joinPanel != null)
|
||||
{
|
||||
_isVisible = !_isVisible;
|
||||
joinPanel.SetActive(_isVisible);
|
||||
UpdateStatus();
|
||||
}
|
||||
}
|
||||
|
||||
private void ShowError(string message)
|
||||
{
|
||||
Debug.LogError($"[NetworkJoinUI] {message}");
|
||||
if (statusText != null)
|
||||
{
|
||||
statusText.text = $"Error: {message}";
|
||||
}
|
||||
}
|
||||
|
||||
private void OnDestroy()
|
||||
{
|
||||
UnsubscribeFromEvents();
|
||||
}
|
||||
|
||||
public void SetUIReferences(GameObject panel, InputField ipField, InputField portField, Button joinBtn, Button hostBtn, Button disconnectBtn, Text status)
|
||||
{
|
||||
joinPanel = panel;
|
||||
ipInputField = ipField;
|
||||
portInputField = portField;
|
||||
joinButton = joinBtn;
|
||||
hostButton = hostBtn;
|
||||
disconnectButton = disconnectBtn;
|
||||
statusText = status;
|
||||
}
|
||||
}
|
||||
}
|
||||
2
Assets/Scripts/NetworkJoinUI.cs.meta
Normal file
2
Assets/Scripts/NetworkJoinUI.cs.meta
Normal file
@@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 824b1e5b92d2ce946b9f2c121a535c73
|
||||
54
Assets/Scripts/QuickNetworkSetup.cs
Normal file
54
Assets/Scripts/QuickNetworkSetup.cs
Normal file
@@ -0,0 +1,54 @@
|
||||
using UnityEngine;
|
||||
|
||||
namespace Northbound
|
||||
{
|
||||
public class QuickNetworkSetup : MonoBehaviour
|
||||
{
|
||||
[Header("Quick Setup Options")]
|
||||
[Tooltip("Create a NetworkConnectionHelper component")]
|
||||
[SerializeField] private bool createConnectionHelper = true;
|
||||
|
||||
[Tooltip("Disable AutoHost to prevent auto-start")]
|
||||
[SerializeField] private bool disableAutoHost = true;
|
||||
|
||||
private void Awake()
|
||||
{
|
||||
SetupScene();
|
||||
}
|
||||
|
||||
private void SetupScene()
|
||||
{
|
||||
if (createConnectionHelper)
|
||||
{
|
||||
CreateConnectionHelper();
|
||||
}
|
||||
|
||||
if (disableAutoHost)
|
||||
{
|
||||
DisableAutoHost();
|
||||
}
|
||||
|
||||
Destroy(this);
|
||||
}
|
||||
|
||||
private void CreateConnectionHelper()
|
||||
{
|
||||
if (FindObjectOfType<NetworkConnectionHelper>() == null)
|
||||
{
|
||||
GameObject helperObj = new GameObject("NetworkConnectionHelper");
|
||||
helperObj.AddComponent<NetworkConnectionHelper>();
|
||||
Debug.Log("[QuickNetworkSetup] NetworkConnectionHelper created");
|
||||
}
|
||||
}
|
||||
|
||||
private void DisableAutoHost()
|
||||
{
|
||||
AutoHost[] autoHosts = FindObjectsByType<AutoHost>(FindObjectsSortMode.None);
|
||||
foreach (var autoHost in autoHosts)
|
||||
{
|
||||
autoHost.enabled = false;
|
||||
Debug.Log("[QuickNetworkSetup] AutoHost disabled");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
2
Assets/Scripts/QuickNetworkSetup.cs.meta
Normal file
2
Assets/Scripts/QuickNetworkSetup.cs.meta
Normal file
@@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 572b945beced27f418a84bba613ff5b9
|
||||
@@ -20,6 +20,7 @@ namespace Northbound
|
||||
public float followDistance = 3f;
|
||||
public float movementSpeed = 3.5f;
|
||||
public int resourcesPerMining = 5;
|
||||
public int recruitmentCost = 10;
|
||||
|
||||
[Header("Interaction")]
|
||||
public string interactionAnimationTrigger = "Recruit";
|
||||
@@ -320,7 +321,17 @@ namespace Northbound
|
||||
|
||||
public bool CanInteract(ulong playerId)
|
||||
{
|
||||
return _ownerPlayerId.Value == ulong.MaxValue || _ownerPlayerId.Value == playerId;
|
||||
if (_ownerPlayerId.Value != ulong.MaxValue && _ownerPlayerId.Value != playerId)
|
||||
return false;
|
||||
|
||||
if (_ownerPlayerId.Value == ulong.MaxValue)
|
||||
{
|
||||
var coreResourceManager = CoreResourceManager.Instance;
|
||||
if (coreResourceManager != null && !coreResourceManager.CanAfford(recruitmentCost))
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public void Interact(ulong playerId)
|
||||
@@ -348,6 +359,20 @@ namespace Northbound
|
||||
[Rpc(SendTo.Server, InvokePermission = RpcInvokePermission.Everyone)]
|
||||
private void RecruitWorkerServerRpc(ulong playerId, ulong workerNetObjectId)
|
||||
{
|
||||
var coreResourceManager = CoreResourceManager.Instance;
|
||||
if (coreResourceManager == null)
|
||||
{
|
||||
Debug.LogWarning("[Worker] CoreResourceManager 인스턴스를 찾을 수 없습니다.");
|
||||
return;
|
||||
}
|
||||
|
||||
if (!coreResourceManager.CanAfford(recruitmentCost))
|
||||
{
|
||||
Debug.LogWarning($"[Worker] 코어 자원이 부족합니다. 필요: {recruitmentCost}");
|
||||
return;
|
||||
}
|
||||
|
||||
coreResourceManager.SpendResources(recruitmentCost);
|
||||
_ownerPlayerId.Value = playerId;
|
||||
SetState(WorkerState.Following);
|
||||
UpdatePlayerTransform();
|
||||
@@ -389,7 +414,11 @@ namespace Northbound
|
||||
{
|
||||
if (_ownerPlayerId.Value == ulong.MaxValue)
|
||||
{
|
||||
return "[E] 워커 채용";
|
||||
var coreResourceManager = CoreResourceManager.Instance;
|
||||
if (coreResourceManager != null && !coreResourceManager.CanAfford(recruitmentCost))
|
||||
return $"자원 부족 (필요: {recruitmentCost})";
|
||||
|
||||
return $"[E] 워커 채용 - 비용: {recruitmentCost}";
|
||||
}
|
||||
else if (NetworkManager.Singleton != null && _ownerPlayerId.Value == NetworkManager.Singleton.LocalClientId)
|
||||
{
|
||||
|
||||
@@ -11,6 +11,7 @@ namespace Northbound
|
||||
public float spawnRadius = 2f;
|
||||
public int maxWorkers = 5;
|
||||
public float spawnCooldown = 5f;
|
||||
public int recruitmentCost = 20;
|
||||
|
||||
[Header("Interaction")]
|
||||
public string interactionAnimationTrigger = "Build";
|
||||
@@ -59,6 +60,10 @@ namespace Northbound
|
||||
if (workerPrefab == null)
|
||||
return false;
|
||||
|
||||
var coreResourceManager = CoreResourceManager.Instance;
|
||||
if (coreResourceManager != null && !coreResourceManager.CanAfford(recruitmentCost))
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -82,6 +87,21 @@ namespace Northbound
|
||||
return;
|
||||
}
|
||||
|
||||
var coreResourceManager = CoreResourceManager.Instance;
|
||||
if (coreResourceManager == null)
|
||||
{
|
||||
Debug.LogWarning("[WorkerSpawner] CoreResourceManager 인스턴스를 찾을 수 없습니다.");
|
||||
return;
|
||||
}
|
||||
|
||||
if (!coreResourceManager.CanAfford(recruitmentCost))
|
||||
{
|
||||
Debug.LogWarning($"[WorkerSpawner] 코어 자원이 부족합니다. 필요: {recruitmentCost}");
|
||||
return;
|
||||
}
|
||||
|
||||
coreResourceManager.SpendResources(recruitmentCost);
|
||||
|
||||
Vector3 spawnPosition = spawnPoint != null ? spawnPoint.position : transform.position;
|
||||
|
||||
float randomAngle = Random.Range(0f, 360f);
|
||||
@@ -185,7 +205,11 @@ namespace Northbound
|
||||
if (cooldownRemaining > 0)
|
||||
return $"워커 생성 대기 중 ({cooldownRemaining:F1}s)";
|
||||
|
||||
return $"{interactionPrompt} ({_workerCount.Value}/{maxWorkers})";
|
||||
var coreResourceManager = CoreResourceManager.Instance;
|
||||
if (coreResourceManager != null && !coreResourceManager.CanAfford(recruitmentCost))
|
||||
return $"자원 부족 (필요: {recruitmentCost})";
|
||||
|
||||
return $"{interactionPrompt} ({_workerCount.Value}/{maxWorkers}) - 비용: {recruitmentCost}";
|
||||
}
|
||||
|
||||
public string GetInteractionAnimation()
|
||||
|
||||
Reference in New Issue
Block a user