我正在参加一个 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
}