feat: IA por dificultad, eventos captura/jaque y consolidar SceneTransitionManager

This commit is contained in:
2026-08-16 16:21:07 -03:00
parent 17e6e5a9b3
commit 928f58675f
8 changed files with 187 additions and 243 deletions
+3 -3
View File
@@ -484,9 +484,9 @@ public class ProjectSetup
}
// SceneTransitionManager
if (Object.FindFirstObjectByType<AjedrezPurgatorio.UI.SceneTransitionManager>() == null)
if (Object.FindFirstObjectByType<SceneTransitionManager>() == null)
{
new GameObject("SceneTransitionManager").AddComponent<AjedrezPurgatorio.UI.SceneTransitionManager>();
new GameObject("SceneTransitionManager").AddComponent<SceneTransitionManager>();
Debug.Log(" ✅ SceneTransitionManager.");
}
@@ -602,7 +602,7 @@ public class ProjectSetup
if (cs != null) Ref(so, "_campaignState", cs);
so.ApplyModifiedPropertiesWithoutUndo();
new GameObject("SceneTransitionManager").AddComponent<AjedrezPurgatorio.UI.SceneTransitionManager>();
new GameObject("SceneTransitionManager").AddComponent<SceneTransitionManager>();
new GameObject("SaveSystem").AddComponent<AjedrezPurgatorio.Meta.SaveSystem>();
new GameObject("AudioManager").AddComponent<AjedrezPurgatorio.Audio.AudioManager>();
Debug.Log(" ✅ Managers MainMenu creados.");
+28 -11
View File
@@ -20,33 +20,50 @@ public class AIController : MonoBehaviour
private IAIStrategy _currentStrategy;
private bool _waitingForMove = false;
private bool _subscribedToTurnEvents = false;
void Start()
{
InitializeStrategy();
// Solo inicializar con la dificultad por defecto si nadie la configuró antes
// (CampaignManager puede aplicar la dificultad del capítulo antes del Start).
if (_currentStrategy == null)
{
InitializeStrategy();
}
// Reintentar suscripción por si OnEnable corrió antes que GameManager.Awake
SubscribeToTurnEvents();
}
void OnEnable()
{
// Suscribirse al evento de cambio de turno
if (GameManager.Instance != null)
{
GameManager.Instance.OnTurnChanged += HandleTurnChanged;
}
SubscribeToTurnEvents();
}
void OnDisable()
{
// Desuscribirse del evento para evitar memory leaks
if (GameManager.Instance != null)
{
GameManager.Instance.OnTurnChanged -= HandleTurnChanged;
}
UnsubscribeFromTurnEvents();
// Cancelar movimiento pendiente si existe
CancelInvoke(nameof(ExecuteAIMove));
}
private void SubscribeToTurnEvents()
{
if (_subscribedToTurnEvents || GameManager.Instance == null) return;
GameManager.Instance.OnTurnChanged += HandleTurnChanged;
_subscribedToTurnEvents = true;
}
private void UnsubscribeFromTurnEvents()
{
if (!_subscribedToTurnEvents || GameManager.Instance == null) return;
GameManager.Instance.OnTurnChanged -= HandleTurnChanged;
_subscribedToTurnEvents = false;
}
/// <summary>
/// Inicializa la estrategia según la dificultad configurada.
/// </summary>
@@ -648,10 +648,29 @@ public class CampaignManager : MonoBehaviour
Debug.Log($"[CampaignManager] Configurando IA: {aiConfig.difficulty} - {aiConfig.strategy}");
// TODO (S4-005): Configurar AIController con la dificultad del capítulo
// AIController necesita API pública para SetDifficulty() y SetStrategy()
// FindObjectOfType<AIController>()?.SetDifficulty(aiConfig.difficulty);
Debug.Log("[CampaignManager] TODO: Aplicar configuración a AIController");
AIController aiController = FindObjectOfType<AIController>();
if (aiController != null)
{
aiController.SetDifficulty(ParseAIDifficulty(aiConfig.difficulty));
aiController.SetEnabled(true);
}
else
{
Debug.LogWarning("[CampaignManager] AIController no encontrado en la escena. No se pudo aplicar la dificultad del capítulo.");
}
}
/// <summary>
/// Convierte la dificultad en string del AIConfig al enum AIDifficulty.
/// </summary>
private static AIDifficulty ParseAIDifficulty(string difficulty)
{
return difficulty?.ToLowerInvariant() switch
{
"medium" => AIDifficulty.Medium,
"hard" => AIDifficulty.Hard,
_ => AIDifficulty.Easy
};
}
/// <summary>
+24
View File
@@ -68,6 +68,16 @@ public class GameManager : MonoBehaviour
/// Se dispara después de que una pieza se mueve. Parámetros: pieza, posición anterior, posición nueva.
/// </summary>
public event Action<Piece, Vector2Int, Vector2Int> OnPieceMoved;
/// <summary>
/// Se dispara cuando una pieza es capturada. Parámetro: la pieza capturada.
/// </summary>
public event Action<Piece> OnPieceCaptured;
/// <summary>
/// Se dispara cuando un rey que estaba en jaque sale del jaque tras un movimiento.
/// </summary>
public event Action OnCheckResolved;
void Awake()
{
@@ -163,6 +173,7 @@ public class GameManager : MonoBehaviour
}
Vector2Int oldPos = piece.currentPos;
bool wasInCheckBeforeMove = _checkDetector.IsInCheck(_board, _whiteTurn);
// Tracking para en passant
_lastMovedPiece = piece;
@@ -202,6 +213,8 @@ public class GameManager : MonoBehaviour
}
}
OnPieceCaptured?.Invoke(captured);
Destroy(captured.gameObject);
}
@@ -214,6 +227,11 @@ public class GameManager : MonoBehaviour
}
}
if (isEnPassant && enPassantCaptured != null)
{
OnPieceCaptured?.Invoke(enPassantCaptured);
}
// Mover pieza
_board[oldPos.x, oldPos.y] = null;
_board[newPos.x, newPos.y] = piece;
@@ -266,6 +284,12 @@ public class GameManager : MonoBehaviour
OnCheck?.Invoke(isNextPlayerWhite);
}
// Resolución de jaque: si la pieza que estaba en jaque ya no lo está tras moverse
if (wasInCheckBeforeMove && !_checkDetector.IsInCheck(_board, _whiteTurn))
{
OnCheckResolved?.Invoke();
}
// Disparar evento de movimiento completado
OnPieceMoved?.Invoke(piece, oldPos, newPos);
+6 -10
View File
@@ -58,11 +58,9 @@ namespace AjedrezPurgatorio.UI
if (GameManager.Instance != null)
{
GameManager.Instance.OnTurnChanged += OnTurnChanged;
// TODO: Add OnPieceCaptured event to GameManager
// GameManager.Instance.OnPieceCaptured += OnPieceCaptured;
// TODO: Add OnCheck and OnCheckResolved events to GameManager
// GameManager.Instance.OnCheck += OnCheck;
// GameManager.Instance.OnCheckResolved += OnCheckResolved;
GameManager.Instance.OnPieceCaptured += OnPieceCaptured;
GameManager.Instance.OnCheck += OnCheck;
GameManager.Instance.OnCheckResolved += OnCheckResolved;
}
}
@@ -72,11 +70,9 @@ namespace AjedrezPurgatorio.UI
if (GameManager.Instance != null)
{
GameManager.Instance.OnTurnChanged -= OnTurnChanged;
// TODO: Unsubscribe from OnPieceCaptured
// GameManager.Instance.OnPieceCaptured -= OnPieceCaptured;
// TODO: Unsubscribe from OnCheck and OnCheckResolved
// GameManager.Instance.OnCheck -= OnCheck;
// GameManager.Instance.OnCheckResolved -= OnCheckResolved;
GameManager.Instance.OnPieceCaptured -= OnPieceCaptured;
GameManager.Instance.OnCheck -= OnCheck;
GameManager.Instance.OnCheckResolved -= OnCheckResolved;
}
}
@@ -1,207 +0,0 @@
using UnityEngine;
using UnityEngine.UI;
using UnityEngine.SceneManagement;
using System.Collections;
namespace AjedrezPurgatorio.UI
{
/// <summary>
/// Manages scene transitions with fade effects.
/// Singleton pattern for easy access from anywhere.
/// </summary>
public class SceneTransitionManager : MonoBehaviour
{
#region Singleton
private static SceneTransitionManager _instance;
public static SceneTransitionManager Instance
{
get
{
if (_instance == null)
{
GameObject go = new GameObject("SceneTransitionManager");
_instance = go.AddComponent<SceneTransitionManager>();
DontDestroyOnLoad(go);
_instance.Initialize();
}
return _instance;
}
}
#endregion
#region Configuration
[Header("Fade Settings")]
[SerializeField] private float _fadeOutDuration = 0.5f;
[SerializeField] private float _fadeInDuration = 0.5f;
[SerializeField] private Color _fadeColor = Color.black;
[Header("Debug")]
[SerializeField] private bool _enableDebugLogging = false;
#endregion
#region Private Fields
private Canvas _canvas;
private Image _fadeImage;
private bool _isTransitioning = false;
#endregion
#region Unity Lifecycle
private void Awake()
{
if (_instance != null && _instance != this)
{
Destroy(gameObject);
return;
}
_instance = this;
DontDestroyOnLoad(gameObject);
Initialize();
}
#endregion
#region Initialization
/// <summary>
/// Initializes the fade canvas and image.
/// </summary>
private void Initialize()
{
if (_canvas != null)
return; // Already initialized
// Create canvas
GameObject canvasGO = new GameObject("FadeCanvas");
canvasGO.transform.SetParent(transform);
_canvas = canvasGO.AddComponent<Canvas>();
_canvas.renderMode = RenderMode.ScreenSpaceOverlay;
_canvas.sortingOrder = 9999; // Top-most layer
CanvasScaler scaler = canvasGO.AddComponent<CanvasScaler>();
scaler.uiScaleMode = CanvasScaler.ScaleMode.ScaleWithScreenSize;
scaler.referenceResolution = new Vector2(1920, 1080);
canvasGO.AddComponent<GraphicRaycaster>();
// Create fade image
GameObject imageGO = new GameObject("FadeImage");
imageGO.transform.SetParent(canvasGO.transform, false);
_fadeImage = imageGO.AddComponent<Image>();
_fadeImage.color = new Color(_fadeColor.r, _fadeColor.g, _fadeColor.b, 0f);
RectTransform rt = imageGO.GetComponent<RectTransform>();
rt.anchorMin = Vector2.zero;
rt.anchorMax = Vector2.one;
rt.sizeDelta = Vector2.zero;
if (_enableDebugLogging)
Debug.Log("[SceneTransitionManager] Initialized.");
}
#endregion
#region Public API
/// <summary>
/// Transitions to a new scene with fade effect.
/// </summary>
/// <param name="sceneName">Name of the scene to load</param>
public void TransitionToScene(string sceneName)
{
if (_isTransitioning)
{
Debug.LogWarning("[SceneTransitionManager] Transition already in progress.");
return;
}
StartCoroutine(TransitionCoroutine(sceneName));
}
/// <summary>
/// Fades out the screen.
/// </summary>
public void FadeOut(System.Action onComplete = null)
{
StartCoroutine(FadeCoroutine(0f, 1f, _fadeOutDuration, onComplete));
}
/// <summary>
/// Fades in the screen.
/// </summary>
public void FadeIn(System.Action onComplete = null)
{
StartCoroutine(FadeCoroutine(1f, 0f, _fadeInDuration, onComplete));
}
#endregion
#region Coroutines
/// <summary>
/// Coroutine for scene transition with fade.
/// </summary>
private IEnumerator TransitionCoroutine(string sceneName)
{
_isTransitioning = true;
if (_enableDebugLogging)
Debug.Log($"[SceneTransitionManager] Starting transition to scene: {sceneName}");
// Fade out
yield return FadeCoroutine(0f, 1f, _fadeOutDuration, null);
// Load scene
AsyncOperation asyncLoad = SceneManager.LoadSceneAsync(sceneName);
while (!asyncLoad.isDone)
{
yield return null;
}
// Fade in
yield return FadeCoroutine(1f, 0f, _fadeInDuration, null);
_isTransitioning = false;
if (_enableDebugLogging)
Debug.Log($"[SceneTransitionManager] Transition to {sceneName} complete.");
}
/// <summary>
/// Coroutine for fade effect.
/// </summary>
private IEnumerator FadeCoroutine(float startAlpha, float endAlpha, float duration, System.Action onComplete)
{
float elapsed = 0f;
while (elapsed < duration)
{
elapsed += Time.deltaTime;
float t = Mathf.Clamp01(elapsed / duration);
float alpha = Mathf.Lerp(startAlpha, endAlpha, t);
_fadeImage.color = new Color(_fadeColor.r, _fadeColor.g, _fadeColor.b, alpha);
yield return null;
}
// Ensure final alpha
_fadeImage.color = new Color(_fadeColor.r, _fadeColor.g, _fadeColor.b, endAlpha);
onComplete?.Invoke();
}
#endregion
}
}
@@ -1,2 +0,0 @@
fileFormatVersion: 2
guid: 42a7f6937327de9469fc73fc1f0cae64
+103 -6
View File
@@ -1726,13 +1726,60 @@ MonoBehaviour:
m_GameObject: {fileID: 365129009}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: 42a7f6937327de9469fc73fc1f0cae64, type: 3}
m_Script: {fileID: 11500000, guid: e1e89932918bf4f45b1b8b65506d6dd9, type: 3}
m_Name:
m_EditorClassIdentifier: Assembly-CSharp::AjedrezPurgatorio.UI.SceneTransitionManager
_fadeOutDuration: 0.5
_fadeInDuration: 0.5
_fadeColor: {r: 0, g: 0, b: 0, a: 1}
_enableDebugLogging: 0
m_EditorClassIdentifier: Assembly-CSharp::SceneTransitionManager
_fadeCanvasGroup: {fileID: 0}
_fadeImage: {fileID: 0}
_fadeDuration: 0.8
_fadeOutCurve:
serializedVersion: 2
m_Curve:
- serializedVersion: 3
time: 0
value: 0
inSlope: 0
outSlope: 0
tangentMode: 0
weightedMode: 0
inWeight: 0
outWeight: 0
- serializedVersion: 3
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
_fadeInCurve:
serializedVersion: 2
m_Curve:
- serializedVersion: 3
time: 0
value: 0
inSlope: 0
outSlope: 0
tangentMode: 0
weightedMode: 0
inWeight: 0
outWeight: 0
- serializedVersion: 3
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
--- !u!4 &365129011
Transform:
m_ObjectHideFlags: 0
@@ -8995,6 +9042,55 @@ Canvas:
m_SortingLayerID: 0
m_SortingOrder: 120
m_TargetDisplay: 0
--- !u!1 &1910000001
GameObject:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
serializedVersion: 6
m_Component:
- component: {fileID: 1910000002}
- component: {fileID: 1910000003}
m_Layer: 0
m_Name: AIController
m_TagString: Untagged
m_Icon: {fileID: 0}
m_NavMeshLayer: 0
m_StaticEditorFlags: 0
m_IsActive: 1
--- !u!114 &1910000003
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 1910000001}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: 55be8b8bddc882c4c8c14fe8572460c9, type: 3}
m_Name:
m_EditorClassIdentifier: Assembly-CSharp::AIController
_difficulty: 0
_thinkDelayMin: 0.3
_thinkDelayMax: 0.8
_enableAI: 1
_positionTables: {fileID: 0}
--- !u!4 &1910000002
Transform:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 1910000001}
serializedVersion: 2
m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
m_LocalPosition: {x: 0, y: 0, z: 0}
m_LocalScale: {x: 1, y: 1, z: 1}
m_ConstrainProportionsScale: 0
m_Children: []
m_Father: {fileID: 0}
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
--- !u!1660057539 &9223372036854775807
SceneRoots:
m_ObjectHideFlags: 0
@@ -9019,3 +9115,4 @@ SceneRoots:
- {fileID: 1105990741}
- {fileID: 180650685}
- {fileID: 1129911953}
- {fileID: 1910000002}