1

我正在参加一个 coursera 课程,其中我正在做的一项任务涉及完成“拿最后一块石头”式拼图的工作。对于这项任务,拼图需要在不同级别的 AI 难度下与两名 AI 玩家一起运行 600 次。问题是,在实施我的更改(涉及递归 Minimax 算法)后,我遇到了一个问题,即统一编辑器在几场比赛后似乎冻结了。它似乎总是在 newgamedelaytimer 运行时发生(教练提供的计时器代码)。我看到它在调试模式下运行,几场比赛后计时器停止增加,我必须重新启动 Unity。这是基本游戏的代码(调用 newgamedelaytimer 的地方)和课程提供的计时器。

我在这里缺少什么吗?

using UnityEngine;
using UnityEngine.Events;
using UnityEngine.SceneManagement;

/// <summary>
/// Game manager
/// </summary>
public class DontTakeTheLastTeddy : MonoBehaviour, ITakeTurnInvoker, IGameOverInvoker,
    IGameStartingInvoker
{
    Board board;
    Player player1;
    Player player2;

    // multiple games support
    Timer newGameDelayTimer;
    PlayerName firstMovePlayer = PlayerName.Player1;
    const int TotalGames = 600;
    int gamesPlayed = 0;
    
    // events invoked by class
    TakeTurn takeTurnEvent = new TakeTurn();
    GameOver gameOverEvent = new GameOver();
    GameStarting gameStartingEvent = new GameStarting();
    
    #region Constructor

    // Uncomment the code below after copying this class into the console
    // app for the automated grader. DON'T uncomment it now; it won't
    // compile in a Unity project

    /// <summary>
    /// Constructor
    /// 
    /// Note: The class in the Unity solution doesn't use a constructor;
    /// this constructor is to support automated grading
    /// </summary>
    /// <param name="gameObject">the game object the script is attached to</param>
    //public DontTakeTheLastTeddy(GameObject gameObject) :
    //    base(gameObject)
    //{
    //}

    #endregion
    
    /// <summary>
    /// Awake is called before Start
    /// 
    /// Leave this method public to support automated grading
    /// </summary>
    public void Awake()
    {
        // retrieve board and player references
        board = GameObject.FindGameObjectWithTag("Board").GetComponent<Board>();
        player1 = GameObject.FindGameObjectWithTag("Player1").GetComponent<Player>();
        player2 = GameObject.FindGameObjectWithTag("Player2").GetComponent<Player>();

        // register as invoker and listener
        EventManager.AddTakeTurnInvoker(this);
        EventManager.AddGameOverInvoker(this);
        EventManager.AddGameStartingInvoker(this);
        EventManager.AddTurnOverListener(HandleTurnOverEvent);
        EventManager.AddGameOverListener(HandleGameOverEvent);
        
        // set up timer for delay between games
        newGameDelayTimer = gameObject.AddComponent<Timer>();
        newGameDelayTimer.Duration = 1.5f;
        newGameDelayTimer.AddTimerFinishedListener(HandleTimerFinishedEvent);

        // initialize statistics class
        Statistics.Initialize();
    }

    private void HandleGameOverEvent(PlayerName arg0, Difficulty arg1, Difficulty arg2)
    {
        Debug.Log("Starting New Game Delay timer");
        newGameDelayTimer.Run();
        //HandleTimerFinishedEvent();
    }

    /// <summary>
    /// Use this for initialization
    /// </summary>
    void Start()
    {
        StartGame(PlayerName.Player1, Difficulty.Easy, Difficulty.Easy);
    }

    /// <summary>
    /// Adds the given listener for the TakeTurn event
    /// </summary>
    /// <param name="listener">listener</param>
    public void AddTakeTurnListener(UnityAction<PlayerName, Configuration> listener)
    {
        takeTurnEvent.AddListener(listener);
    }

    /// <summary>
    /// Adds the given listener for the GameOver event
    /// </summary>
    /// <param name="listener">listener</param>
    public void AddGameOverListener(
        UnityAction<PlayerName, Difficulty, Difficulty> listener)
    {
        gameOverEvent.AddListener(listener);
    }

    /// <summary>
    /// Adds the given listener for the GameStarting event
    /// </summary>
    /// <param name="listener">listener</param>
    public void AddGameStartingListener(UnityAction listener)
    {
        gameStartingEvent.AddListener(listener);
    }
    
    /// <summary>
    /// Starts a game with the given player taking the
    /// first turn
    /// 
    /// Leave this method public to support automated grading
    /// </summary>
    /// <param name="firstPlayer">player taking first turn</param>
    /// <param name="player1Difficulty">difficulty for player 1</param>
    /// <param name="player2Difficulty">difficulty for player 2</param>
    public void StartGame(PlayerName firstPlayer, Difficulty player1Difficulty,
        Difficulty player2Difficulty)
    {
        // set player difficulties
        player1.Difficulty = player1Difficulty;
        player2.Difficulty = player2Difficulty;

        // create new board
        board.CreateNewBoard();
        takeTurnEvent.Invoke(firstPlayer,
            board.Configuration);
    }

    /// <summary>
    /// Handles the TurnOver event by having the 
    /// other player take their turn
    /// </summary>
    /// <param name="player">who finished their turn</param>
    /// <param name="newConfiguration">the new board configuration</param>
    void HandleTurnOverEvent(PlayerName player, 
        Configuration newConfiguration)
    {
        board.Configuration = newConfiguration;

        // check for game over
        if (newConfiguration.Empty)
        {
            // fire event with winner
            if (player == PlayerName.Player1)
            {
                gameOverEvent.Invoke(PlayerName.Player2, player1.Difficulty, player2.Difficulty);
            }
            else
            {
                gameOverEvent.Invoke(PlayerName.Player1, player1.Difficulty, player2.Difficulty);
            }
        }
        else
        {
            // game not over, so give other player a turn
            if (player == PlayerName.Player1)
            {
                takeTurnEvent.Invoke(PlayerName.Player2,
                    newConfiguration);
            }
            else
            {
                takeTurnEvent.Invoke(PlayerName.Player1,
                    newConfiguration);
            }
        }
    }
    
    /// <summary>
    /// Starts a new game when the new game delay timer finishes
    /// </summary>
    void HandleTimerFinishedEvent()
    {
        Debug.Log("Finished new Game Timer");
        // constant provided for autograder support
        if (!GameConstants.PlaySingleGame)
        {
            if (gamesPlayed < TotalGames)
            {
                if (gamesPlayed % 100 == 0)
                {
                    // uncomment the line below and implement a
                    // SetPlayerDifficulties method
                    SetPlayerDifficulties(gamesPlayed);
                }

                gamesPlayed++;
                //Debug.Log("Games Played: " + gamesPlayed);
                // alternate player making first move in game
                if (firstMovePlayer == PlayerName.Player1)
                {
                    firstMovePlayer = PlayerName.Player2;
                }
                else
                {
                    firstMovePlayer = PlayerName.Player1;
                }
                
                StartGame(firstMovePlayer, player1.Difficulty, player2.Difficulty);
            }
            else
            {
                // move to statistics scene when all games have been played
                SceneManager.LoadScene("statistics");
            }
        }
    }

    private void SetPlayerDifficulties(int gamesPlayed)
    {
        if (gamesPlayed == 100)
        {
            player1.Difficulty = Difficulty.Medium;
            player2.Difficulty = Difficulty.Medium;
        }
        else if (gamesPlayed == 200)
        {
            player1.Difficulty = Difficulty.Hard;
            player2.Difficulty = Difficulty.Hard;
        }
        else if (gamesPlayed == 300)
        {
            player1.Difficulty = Difficulty.Easy;
            player2.Difficulty = Difficulty.Medium;
        }
        else if (gamesPlayed == 400)
        {
            player2.Difficulty = Difficulty.Hard;
        }
        else if (gamesPlayed == 500)
        {
            player1.Difficulty = Difficulty.Medium;
            player2.Difficulty = Difficulty.Hard;
        }
    }
}

定时器.cs

using UnityEngine;
using UnityEngine.Events;

/// <summary>
/// A timer
/// </summary>
public class Timer : MonoBehaviour
{
    #region Fields

    // timer duration
    float totalSeconds = 0;

    // timer execution
    float elapsedSeconds = 0;
    bool running = false;

    // support for TimerFinished event
    bool started = false;
    TimerFinished timerFinished = new TimerFinished();

    #endregion

    #region Properties

    /// <summary>
    /// Sets the duration of the timer
    /// The duration can only be set if the timer isn't currently running
    /// </summary>
    /// <value>duration</value>
    public float Duration
    {
        set
        {
            if (!running)
            {
                totalSeconds = value;
            }
        }
    }

    /// <summary>
    /// Gets whether or not the timer has finished running
    /// This property returns false if the timer has never been started
    /// </summary>
    /// <value>true if finished; otherwise, false.</value>
    public bool Finished
    {
        get { return started && !running; } 
    }

    /// <summary>
    /// Gets whether or not the timer is currently running
    /// </summary>
    /// <value>true if running; otherwise, false.</value>
    public bool Running
    {
        get { return running; }
    }

    /// <summary>
    /// Gets ho wmany seconds are left on the timer
    /// </summary>
    /// <value>seconds left</value>
    public float SecondsLeft
    {
        get 
        {
            if (running)
            {
                return totalSeconds - elapsedSeconds; 
            }
            else
            {
                return 0;
            }
        }
    }

    #endregion

    #region Methods

    /// <summary>
    /// Update is called once per frame
    /// </summary>
    void Update()
    {   
        // update timer and check for finished
        if (running)
        {
            elapsedSeconds += Time.fixedDeltaTime;
            if (elapsedSeconds >= totalSeconds)
            {
                running = false;
                timerFinished.Invoke();
            }
        }
    }

    /// <summary>
    /// Runs the timer
    /// Because a timer of 0 duration doesn't really make sense,
    /// the timer only runs if the total seconds is larger than 0
    /// This also makes sure the consumer of the class has actually 
    /// set the duration to something higher than 0
    /// </summary>
    public void Run()
    {   
        // only run with valid duration
        if (totalSeconds > 0)
        {
            started = true;
            running = true;
            elapsedSeconds = 0;
        }
    }

    /// <summary>
    /// Stops the timer
    /// </summary>
    public void Stop()
    {
        started = false;
        running = false;
    }

    /// <summary>
    /// Adds the given number of seconds to the timer
    /// </summary>
    /// <param name="seconds">time to add</param>
    public void AddTime(float seconds)
    {
        totalSeconds += seconds;
    }

    /// <summary>
    /// Adds the given listener for the TimerFinished event
    /// </summary>
    /// <param name="listener">listener</param>
    public void AddTimerFinishedListener(UnityAction listener)
    {
        timerFinished.AddListener(listener);
    }

    #endregion
}
4

0 回答 0