Итак, я пытался сделать такую игру, как CandyCrush, но многопользовательскую, я использую услугу лобби Unitys для многопользовательской среды P2P, поэтому мне не нужно получать выделенные серверы.
Основная проблема - это то, что когда игрок 1 (так что первый игрок на поиск лобби) делает движение. В его собственном (поэтому у игрока есть та же самая стартовая доска на протяжении всей продолжительности).
Я пытался исправить это или определить проблему самому и с ИИ, но я не мог понять это, поэтому я ищу здесь помощь. ; (< /p>
using UnityEngine;
using System.Collections;
using System.Collections.Generic;
using Unity.Netcode;
public class GameManagerSc : MonoBehaviour
{
public static GameManagerSc Instance { get; private set; }
[Header("References")]
[SerializeField] public Transform gridParent;
[SerializeField] public GameObject[] candyPrefabs;
[SerializeField] private GameObject turnIndicator; // Optional visual indicator for turn
[Header("Settings")]
public float fallSpeed = 0.15f;
public float swapDuration = 0.3f;
public float destroyDelay = 0.1f;
public float respawnDelay = 0.1f;
public int gridWidth = 5;
public int gridHeight = 5;
public float cellSize = 0.75f;
public Vector2 gridOffset = new Vector2(-1.5f, 1.5f);
[Header("Match Counters")]
[SerializeField] private TMPro.TextMeshProUGUI[] matchCounterTexts; // Array of text elements for different candy types
[Header("Debug")]
[SerializeField] private bool debugLogging = true;
// Dictionary to store match counters for different candy types
private Dictionary matchCounters = new Dictionary();
// Array of tag names corresponding to candy types (should match the tags in CandyController)
private readonly string[] candyTags = { "blue", "black", "green", "red", "yellow" };
public GameObject[,] grid;
private bool isProcessing;
private bool isInitialized = false;
private float initializationTimeoutPeriod = 15f;
// Separate method to handle network-synced grid updates
public void UpdateGridFromNetworkData()
{
if (NetworkGridManager.Instance == null)
{
Debug.LogWarning("[GameManagerSc] UpdateGridFromNetworkData: NetworkGridManager.Instance is null!");
return;
}
NetworkGridData networkData = NetworkGridManager.Instance.GridData.Value;
if (networkData.cellIndices == null)
{
Debug.LogWarning("[GameManagerSc] UpdateGridFromNetworkData: networkData.cellIndices is null!");
return;
}
Debug.Log("[GameManagerSc] UpdateGridFromNetworkData: Updating grid...");
for (int x = 0; x < networkData.cellIndices.GetLength(0); x++)
{
for (int y = 0; y < networkData.cellIndices.GetLength(1); y++)
{
int cellIndex = networkData.cellIndices[x, y];
Debug.Log($"[GameManagerSc] UpdateGridFromNetworkData: Processing cell ({x},{y}) with index {cellIndex}");
if (cellIndex >= 0)
{
Vector2 targetPos = new Vector2(
gridOffset.x + (x * cellSize),
gridOffset.y - (y * cellSize)
);
Debug.Log($"[GameManagerSc] UpdateGridFromNetworkData: Target position for cell ({x},{y}) is {targetPos}");
// Destroy the existing candy if there is one
if (grid != null && grid[x, y] != null)
{
Debug.Log($"[GameManagerSc] UpdateGridFromNetworkData: Destroying existing candy at ({x},{y})");
Destroy(grid[x, y]);
}
// Create new candy at the correct position and type
GameObject newCandy = Instantiate(
candyPrefabs[cellIndex],
targetPos,
Quaternion.identity,
gridParent
);
newCandy.name = $"Candy_{x}_{y}";
if (grid != null)
{
Debug.Log($"[GameManagerSc] UpdateGridFromNetworkData: Created candy {newCandy.name} at ({x},{y})");
grid[x, y] = newCandy;
}
}
else
{
// Handle empty cells if needed
if (grid != null && grid[x, y] != null)
{
Debug.Log($"[GameManagerSc] UpdateGridFromNetworkData: Clearing cell ({x},{y})");
Destroy(grid[x, y]);
grid[x, y] = null;
}
}
}
}
Debug.Log("[GameManagerSc] UpdateGridFromNetworkData: Grid update complete.");
}
private string GetCandyTagFromIndex(int index)
{
// Map the index to your candy tags
string[] candyTags = { "blue", "black", "green", "red", "yellow" };
if (index >= 0 && index < candyTags.Length)
return candyTags[index];
return null;
}
private void UpdateCandyTypeAt(int x, int y, int candyTypeIndex)
{
// Implement this to update the candy type without changing its position
// This would likely involve changing the sprite or replacing the GameObject
}
// Process a network-determined safe index for a specific grid position
public void ProcessSafeIndex(int x, int y, int index)
{
if (grid == null || x < 0 || y < 0 || x >= grid.GetLength(0) || y >= grid.GetLength(1))
{
Debug.LogError($"[GameManagerSc] Invalid grid coordinates in ProcessSafeIndex: ({x},{y})");
return;
}
if (grid[x, y] == null)
{
if (debugLogging)
Debug.Log($"[GameManagerSc] Processing safe index {index} for position ({x},{y})");
// Create new candy at position above the grid
Vector2 spawnPos = new Vector2(
gridOffset.x + (x * cellSize),
gridOffset.y + (2 * cellSize) // Start above grid
);
// Validate index before using it
if (index < 0 || index >= candyPrefabs.Length)
{
Debug.LogError($"[GameManagerSc] Invalid candy index {index} for position ({x},{y})");
return;
}
GameObject newCandy = Instantiate(
candyPrefabs[index],
spawnPos,
Quaternion.identity,
gridParent
);
newCandy.name = $"Candy_{x}_{y}";
grid[x, y] = newCandy;
// Calculate final position
Vector2 targetPos = new Vector2(
gridOffset.x + (x * cellSize),
gridOffset.y - (y * cellSize)
);
// Animate falling into place
StartCoroutine(MoveToPosition(newCandy.transform, targetPos));
}
}
// Method to synchronize a swap received from the network
public void ProcessNetworkSwap(Vector2Int pos1, Vector2Int pos2)
{
GameObject candy1 = grid[pos1.x, pos1.y];
GameObject candy2 = grid[pos2.x, pos2.y];
if (candy1 == null || candy2 == null)
{
Debug.LogError($"[GameManagerSc] Null candy found in ProcessNetworkSwap at positions {pos1} or {pos2}");
return;
}
// Swap positions in the visual grid
Vector3 tempPos = candy1.transform.position;
candy1.transform.position = candy2.transform.position;
candy2.transform.position = tempPos;
// Update the grid references
grid[pos1.x, pos1.y] = candy2;
grid[pos2.x, pos2.y] = candy1;
Debug.Log($"[GameManagerSc] Processed network swap between {pos1} and {pos2}");
}
private IEnumerator AnimateNetworkSwap(GameObject candy1, GameObject candy2, Vector2Int pos1, Vector2Int pos2)
{
Vector3 startPos1 = candy1.transform.position;
Vector3 startPos2 = candy2.transform.position;
float elapsed = 0;
while (elapsed < swapDuration)
{
candy1.transform.position = Vector3.Lerp(startPos1, startPos2, elapsed/swapDuration);
candy2.transform.position = Vector3.Lerp(startPos2, startPos1, elapsed/swapDuration);
elapsed += Time.deltaTime;
yield return null;
}
// Ensure they reach final positions exactly
candy1.transform.position = startPos2;
candy2.transform.position = startPos1;
// Update local grid references
grid[pos1.x, pos1.y] = candy2;
grid[pos2.x, pos2.y] = candy1;
}
// Method to process match destruction from network
public void ProcessNetworkMatches(List matchPositions)
{
if (grid == null) return;
StartCoroutine(DestroyMatchedCandies(matchPositions));
}
private IEnumerator DestroyMatchedCandies(List positions)
{
foreach (Vector2Int pos in positions)
{
if (IsValidPosition(pos) && grid[pos.x, pos.y] != null)
{
Destroy(grid[pos.x, pos.y]);
grid[pos.x, pos.y] = null;
}
}
yield return new WaitForSeconds(destroyDelay);
}
private bool IsValidPosition(Vector2Int pos)
{
return grid != null && pos.x >= 0 && pos.x < grid.GetLength(0) &&
pos.y >= 0 && pos.y < grid.GetLength(1);
}
private void Awake()
{
if (Instance != null && Instance != this)
{
Destroy(gameObject);
return;
}
Instance = this;
// Initialize counter dictionary
foreach (string tag in candyTags)
{
matchCounters[tag] = 0;
}
if (debugLogging)
Debug.Log("[GameManagerSc] Initialized singleton instance");
}
private void Start()
{
if (debugLogging)
Debug.Log("[GameManagerSc] Starting initialization");
StartCoroutine(InitializeRoutine());
UpdateAllCounterUI();
}
private IEnumerator InitializeRoutine()
{
// Wait for network manager to be available
if (debugLogging)
Debug.Log("[GameManagerSc] Waiting for NetworkGridManager...");
float timer = 0f;
while (NetworkGridManager.Instance == null && timer < initializationTimeoutPeriod)
{
timer += Time.deltaTime;
yield return null;
}
if (NetworkGridManager.Instance == null)
{
Debug.LogError("[GameManagerSc] NetworkGridManager not found after timeout!");
yield break;
}
if (debugLogging)
Debug.Log("[GameManagerSc] NetworkGridManager found");
// Wait for grid data to be available
timer = 0f;
while (timer < initializationTimeoutPeriod)
{
// Check if grid data is initialized
if (NetworkGridManager.Instance.GridData.Value.cellIndices != null &&
NetworkGridManager.Instance.GridData.Value.cellIndices.Length > 0)
{
if (debugLogging)
Debug.Log("[GameManagerSc] Grid data available, initializing visual grid");
InitializeVisualGrid();
UpdateTurnIndicator();
yield break;
}
timer += 0.5f;
yield return new WaitForSeconds(0.5f);
}
Debug.LogError("[GameManagerSc] Grid data not available after timeout! Attempting to initialize anyway.");
// Last attempt - if we're the server, try to force grid initialization
if (NetworkManager.Singleton.IsServer)
{
if (debugLogging)
Debug.Log("[GameManagerSc] We are the server - forcing grid initialization");
NetworkGridManager.Instance.InitializeGrid();
yield return new WaitForSeconds(0.5f);
InitializeVisualGrid();
UpdateTurnIndicator();
}
}
public void InitializeVisualGrid()
{
if (debugLogging)
Debug.Log("[GameManagerSc] InitializeVisualGrid called");
// Add null checks
if (gridParent == null)
{
Debug.LogError("[GameManagerSc] Grid Parent reference is missing!");
return;
}
if (candyPrefabs == null || candyPrefabs.Length == 0)
{
Debug.LogError("[GameManagerSc] Candy Prefabs array is not set up!");
return;
}
if (NetworkGridManager.Instance == null)
{
Debug.LogError("[GameManagerSc] NetworkGridManager instance is null!");
return;
}
if (NetworkGridManager.Instance.GridData.Value.cellIndices == null)
{
Debug.LogError("[GameManagerSc] Grid data is null! Aborting visual grid initialization.");
return;
}
// Create the grid array
int width = NetworkGridManager.Instance.width;
int height = NetworkGridManager.Instance.height;
if (debugLogging)
Debug.Log($"[GameManagerSc] Creating grid with dimensions {width}x{height}");
grid = new GameObject[width, height];
// Clear existing grid before spawning new candies
ClearGrid();
// Verify grid size matches the network data
if (NetworkGridManager.Instance.GridData.Value.cellIndices.GetLength(0) != width ||
NetworkGridManager.Instance.GridData.Value.cellIndices.GetLength(1) != height)
{
Debug.LogError($"[GameManagerSc] Grid size mismatch! NetworkGridManager: {width}x{height}, " +
$"GridData: {NetworkGridManager.Instance.GridData.Value.cellIndices.GetLength(0)}x" +
$"{NetworkGridManager.Instance.GridData.Value.cellIndices.GetLength(1)}");
return;
}
// Spawn candies
for (int x = 0; x < width; x++)
{
for (int y = 0; y < height; y++)
{
int index = NetworkGridManager.Instance.GridData.Value.cellIndices[x, y];
// Handle empty cells (value -1)
if (index == -1)
{
grid[x, y] = null;
continue;
}
// Validate prefab index
if (index < 0 || index >= candyPrefabs.Length)
{
Debug.LogError($"[GameManagerSc] Invalid candy index {index} at position {x},{y}");
continue;
}
SpawnCandy(x, y, index);
}
}
isInitialized = true;
if (debugLogging)
Debug.Log("[GameManagerSc] Visual grid initialization complete");
}
public void SpawnCandy(int x, int y, int index)
{
// Calculate spawn position
Vector2 spawnPos = new Vector2(
gridOffset.x + (x * cellSize),
gridOffset.y - (y * cellSize)
);
if (debugLogging && index < candyPrefabs.Length)
Debug.Log($"[GameManagerSc] Spawning candy at ({x},{y}) - " +
$"World Position: {spawnPos} | Index: {index}");
// Add prefab validation
if (index < 0 || index >= candyPrefabs.Length)
{
Debug.LogError($"[GameManagerSc] Invalid index {index} at ({x},{y})");
return;
}
// Instantiate the candy
GameObject candy = Instantiate(
candyPrefabs[index],
spawnPos,
Quaternion.identity,
gridParent
);
candy.name = $"Candy_{x}_{y}";
grid[x, y] = candy;
}
public IEnumerator MoveToPosition(Transform sprite, Vector2 target)
{
float elapsed = 0;
Vector2 start = sprite.position;
while (elapsed < fallSpeed)
{
sprite.position = Vector2.Lerp(start, target, elapsed/fallSpeed);
elapsed += Time.deltaTime;
yield return null;
}
sprite.position = target;
}
public void ClearGrid()
{
if (debugLogging)
Debug.Log("[GameManagerSc] Clearing grid");
if (gridParent == null)
{
Debug.LogWarning("[GameManagerSc] Cannot clear grid: gridParent is null");
return;
}
foreach (Transform child in gridParent)
{
Destroy(child.gameObject);
}
}
// Debug utility
void Update()
{
// Force grid refresh with R key
if (Input.GetKeyDown(KeyCode.R))
{
Debug.Log("[GameManager] R key pressed - requesting visual sync only");
// Instead of a full grid reset, just synchronize the visual representation
SynchronizeVisualGridOnly();
}
// Listen for turn changes to update UI
if (NetworkGridManager.Instance != null && turnIndicator != null)
{
UpdateTurnIndicator();
}
}
private void SynchronizeVisualGridOnly()
{
// This should update ONLY the visual representation without modifying grid references
if (NetworkGridManager.Instance != null && NetworkGridManager.Instance.GridData.Value.cellIndices != null)
{
// Update only candy types, not positions
NetworkGridData networkData = NetworkGridManager.Instance.GridData.Value;
for (int x = 0; x < networkData.cellIndices.GetLength(0); x++)
{
for (int y = 0; y < networkData.cellIndices.GetLength(1); y++)
{
int cellIndex = networkData.cellIndices[x, y];
// Just ensure the candy type is correct, not its position
if (grid[x, y] != null && cellIndex >= 0)
{
string expectedTag = GetCandyTagFromIndex(cellIndex);
if (!grid[x, y].CompareTag(expectedTag))
{
UpdateCandyVisualType(grid[x, y], cellIndex);
}
}
}
}
}
}
private void UpdateCandyVisualType(GameObject candy, int typeIndex)
{
// Update only the visual appearance (sprite/color) not the position
// Implementation depends on how your candy visuals work
}
public void AddToCounter(string tag, int count)
{
if (string.IsNullOrEmpty(tag) || count = 0)
{
NetworkGridManager.Instance.UpdateScoreServerRpc(tagIndex, matchCounters[tag]);
}
}
}
// Update a specific counter's UI
private void UpdateCounterUI(string tag)
{
int tagIndex = System.Array.IndexOf(candyTags, tag);
if (tagIndex >= 0 && tagIndex < matchCounterTexts.Length && matchCounterTexts[tagIndex] != null)
{
matchCounterTexts[tagIndex].text = matchCounters[tag].ToString();
}
}
// Update all counter UIs
private void UpdateAllCounterUI()
{
for (int i = 0; i < candyTags.Length; i++)
{
if (i < matchCounterTexts.Length && matchCounterTexts != null)
{
string tag = candyTags;
if (matchCounters.ContainsKey(tag))
{
matchCounterTexts.text = matchCounters[tag].ToString();
}
else
{
matchCounterTexts.text = "0";
}
}
}
}
// Called by NetworkGridManager when scores are updated from the network
public void UpdateScoreFromNetwork(int tagIndex, int score)
{
if (tagIndex >= 0 && tagIndex < candyTags.Length)
{
string tag = candyTags[tagIndex];
matchCounters[tag] = score;
UpdateCounterUI(tag);
if (debugLogging)
Debug.Log($"[GameManagerSc] Updated {tag} score from network: {score}");
}
}
// Updates turn indicator UI based on whose turn it is
public void UpdateTurnIndicator()
{
if (turnIndicator == null) return;
bool isMyTurn = NetworkGridManager.Instance.IsMyTurn();
turnIndicator.SetActive(isMyTurn);
}
}
Подробнее здесь: https://stackoverflow.com/questions/796 ... ion-errors
Ошибки синхронизации многопользовательских районов Unity ⇐ C#
-
- Похожие темы
- Ответы
- Просмотры
- Последнее сообщение
-
-
Как обращаться с системой аутентификации пользователей в многопользовательских играх?
Anonymous » » в форуме JAVA - 0 Ответы
- 33 Просмотры
-
Последнее сообщение Anonymous
-
-
-
Почему видеопотоки WebRTC работают на ПК, но не на iPhone для многопользовательских вызовов?
Anonymous » » в форуме C# - 0 Ответы
- 13 Просмотры
-
Последнее сообщение Anonymous
-
-
-
Почему видеопотоки WebRTC работают на ПК, но не на iPhone для многопользовательских вызовов?
Anonymous » » в форуме C# - 0 Ответы
- 20 Просмотры
-
Последнее сообщение Anonymous
-
-
-
Почему видеопотоки WebRTC работают на ПК, но не на iPhone для многопользовательских вызовов?
Anonymous » » в форуме C# - 0 Ответы
- 10 Просмотры
-
Последнее сообщение Anonymous
-