using System; using System.Collections.Generic; using UnityEngine; using Random = UnityEngine.Random; public class TargetController : MonoBehaviour { [SerializeField] private GameObject environmentObj; [SerializeField] private GameObject agentObj; [SerializeField] private GameObject HUDObj; [SerializeField] private GameObject sceneBlockContainerObj; [SerializeField] private GameObject enemyContainerObj; [SerializeField] private GameObject environmentUIObj; // area public GameObject edgeUp; public GameObject edgeDown; public GameObject edgeLeft; public GameObject edgeRight; public GameObject edgeAgent_Enemy; public float minEnemyAreaX; [System.NonSerialized] public float maxEnemyAreaX; [System.NonSerialized] public float minEnemyAreaZ; [System.NonSerialized] public float maxEnemyAreaZ; [System.NonSerialized] public float minAgentAreaX; [System.NonSerialized] public float maxAgentAreaX; [System.NonSerialized] public float minAgentAreaZ; [System.NonSerialized] public float maxAgentAreaZ; [System.NonSerialized] public float startTime = 0f; [System.NonSerialized] public float leftTime = 0f; [SerializeField, Range(0f, 1f)] public float attackProb = 0.2f; [SerializeField, Range(0f, 1f)] public float gotoProb = 0.2f; [SerializeField, Range(0f, 1f)] public float defenceProb = 0.2f; [System.NonSerialized] public Targets targetType; [System.NonSerialized] public int gotoLevelNum; [System.NonSerialized] public int attackLevelNum; [System.NonSerialized] public float[] targetState = new float[5]; public enum EndType { Win, Lose, Running, Num }; [System.NonSerialized] public int targetNum = 0; private Dictionary oneHotRarget = new Dictionary(); private float freeProb; private float sceneBlockSize; private int randBlockType = 0; private int randLevel = 0; public int inArea = 0; public Vector3 targetPosition; private bool firstRewardFlag = true; public bool targetEnemySpawnFinish = false; private SceneBlockContainer sceneBlockCon; private EnemyContainer enemyCon; private EnvironmentUIControl envUICon; private CommonParameterContainer commonParamCon; private CharacterController agentCharaCon; private HUDController hudCon; private MessageBoxController messageBoxCon; // start scene datas 0=train 1=play private int gamemode; // Start is called before the first frame update private void Start() { commonParamCon = CommonParameterContainer.Instance; sceneBlockCon = sceneBlockContainerObj.GetComponent(); envUICon = environmentUIObj.GetComponent(); enemyCon = enemyContainerObj.GetComponent(); agentCharaCon = agentObj.GetComponent(); hudCon = HUDObj.GetComponent(); messageBoxCon = HUDObj.GetComponent(); // get parameter from ParameterContainer gamemode = commonParamCon.gameMode; attackProb = commonParamCon.attackProb; gotoProb = commonParamCon.gotoProb; defenceProb = commonParamCon.defenceProb; // initialize spawn area minEnemyAreaX = edgeLeft.transform.localPosition.x + 1.0f; maxEnemyAreaX = edgeRight.transform.localPosition.x - 1.0f; minEnemyAreaZ = edgeAgent_Enemy.transform.localPosition.z + 1.0f; maxEnemyAreaZ = edgeUp.transform.localPosition.z - 1.0f; minAgentAreaX = edgeLeft.transform.localPosition.x + 1.0f; maxAgentAreaX = edgeRight.transform.localPosition.x - 1.0f; minAgentAreaZ = edgeDown.transform.localPosition.z + 1.0f; maxAgentAreaZ = edgeAgent_Enemy.transform.localPosition.z - 1.0f; freeProb = 1 - attackProb - gotoProb - defenceProb; targetNum = (int)Targets.Num; gotoLevelNum = commonParamCon.scenePrefabSet.GetLevelNumber(Targets.Go); attackLevelNum = commonParamCon.scenePrefabSet.GetLevelNumber(Targets.Attack); if (freeProb < 0) { Debug.LogError("TargetController.Start: target percentage wrong"); } // initialize a simple fake onehot encoder. for (int i = 0; i < targetNum; i++) { float[] onehotList = new float[targetNum]; for (int j = 0; j < targetNum; j++) { onehotList[j] = 0; } onehotList[i] = 1; oneHotRarget.Add(i, onehotList); } } private void Update() { // if gamemode is play, then time will keep paramCon.timeLimit if (gamemode == 1) { leftTime = commonParamCon.timeLimit; // print out time // Debug.Log("Playing Time: " + leftTime); } else { leftTime = commonParamCon.timeLimit - Time.time + startTime; } } /// /// Generates a new scene configuration by selecting a random target type and spawning related scene blocks. /// /// /// This method is responsible for creating a new scene configuration, which involves selecting a target type /// (Go, Attack, Defence, or Free) based on predefined probabilities. Depending on the chosen target type, /// the method spawns the associated scene blocks, updates various flags, and informs the user interface about /// the selected target type. /// public void RollNewScene() { startTime = Time.time;// Reset StartTime as now time leftTime = commonParamCon.timeLimit - Time.time + startTime; float randTargetType = UnityEngine.Random.Range(0f, 1f); if (randTargetType <= gotoProb) { // goto target spawn Debug.Log("GOTO THIS TARGET!"); targetType = Targets.Go; RandomSpawnSceneBlock(Targets.Go); // set startDistance firstRewardFlag = true; } else if (randTargetType > gotoProb && randTargetType <= gotoProb + attackProb) { // attack target spawn Debug.Log("ATTACK Mode Start"); targetType = Targets.Attack; RandomSpawnSceneBlock(Targets.Attack); // set startDistance firstRewardFlag = true; targetEnemySpawnFinish = false; } else if (randTargetType > gotoProb + attackProb && randTargetType <= gotoProb + attackProb + defenceProb) { // defence target spawn Debug.Log("DEFENCE Mode Start"); targetType = Targets.Defence; RandomSpawnSceneBlock(Targets.Defence); // set startDistance firstRewardFlag = true; } else { Debug.Log("Free Mode Start"); targetType = Targets.Free; enemyCon.DestroyAllEnemys(); enemyCon.RandomInitEnemys(hudCon.enemyNum); MoveAgentToSpwanArea(); sceneBlockCon.DestroyBlock(); } UpdateTargetStates(); envUICon.UpdateTargetType(targetType); } #region Agent Move Method /// /// Move the agent to the spawn area. /// 将Agent移动到生成区域。 /// private void MoveAgentToSpwanArea() { float randX = UnityEngine.Random.Range(minAgentAreaX, maxAgentAreaX); ; float randZ = 0f; if (commonParamCon.spawnAgentInAllMap) { // spawn agent in all around map randZ = UnityEngine.Random.Range(minAgentAreaZ, maxEnemyAreaZ); } else { // spawn agent in only agent spawn area randZ = UnityEngine.Random.Range(minAgentAreaZ, maxAgentAreaZ); } int Y = 1; Vector3 initAgentLoc = new Vector3(randX, Y, randZ); MoveAgentTo(initAgentLoc); } /// /// Move the agent to the specified position. /// 将代理移动到指定位置。 /// /// 要移动到的位置。 /// /// When moving the character using transform.localPosition, /// must disable the character controller, or it won't work properly. /// 使用 transform.localPosition 移动角色时, /// 必须禁用角色控制器,否则它将无法正常工作。 /// public void MoveAgentTo(Vector3 position) { // while using transform.localPosition to move character // u should turn off character Controller or it won't work agentCharaCon.enabled = false; agentObj.transform.localPosition = position; agentCharaCon.enabled = true; } #endregion Agent Move Method /// /// Randomly spawns a scene block based on the target type. /// 根据目标类型随机生成场景块。 /// /// 要生成的场景块的目标类型。The target type of the scene block to be generated. /// /// This method generates a random scene block based on the target type and spawns enemies at the specified location. /// 此方法根据目标类型生成一个随机场景块,并在指定位置生成敌人。 /// private void RandomSpawnSceneBlock(Targets targetType) { randLevel = RollRandomLevelIndex(targetType); randBlockType = Random.Range(0, commonParamCon.scenePrefabSet.GetBlockNumber(randLevel,targetType)); sceneBlockSize = commonParamCon.scenePrefabSet.GetBlockSize(randLevel, randBlockType, targetType); float randX = UnityEngine.Random.Range(minEnemyAreaX + sceneBlockSize / 2 + 1f, maxEnemyAreaX - sceneBlockSize / 2 - 1f); float randZ = UnityEngine.Random.Range(minEnemyAreaZ + sceneBlockSize / 2 + 1f, maxEnemyAreaZ - sceneBlockSize / 2 - 1f); targetPosition = new Vector3(randX, 0, randZ); // init scene block sceneBlockCon.DestroyBlock(); sceneBlockCon.CreateNewBlock(targetType, randLevel, randBlockType, targetPosition, commonParamCon.group1Tag, commonParamCon.group2Tag); enemyCon.DestroyAllEnemys(); enemyCon.RandomInitEnemysExcept(hudCon.enemyNum, targetPosition, sceneBlockSize); sceneBlockCon.nowBlock.InitBlock(environmentObj); } /// /// Spawns a scene block at a specified position. /// /// The type of the target, determining the type of block to generate. /// The level of the block, affecting its properties. /// The number of the block, used to distinguish different blocks of the same level. /// The position of the block in the scene. /// /// This method first destroys the current block in the scene, then creates a new block based on the provided parameters and initializes it. /// 'sceneBlockCon' is responsible for managing the creation, destruction, and initialization of scene blocks. /// 'commonParamCon' provides additional parameters required for block creation. /// 'environmentObj' is used during the initialization process of the block. /// private void SpawnSceneBlock(Targets targetType, int level,int blockNum, Vector3 blockPosition) { sceneBlockCon.DestroyBlock(); sceneBlockCon.CreateNewBlock(targetType, level, blockNum, blockPosition, commonParamCon.group1Tag, commonParamCon.group2Tag); sceneBlockCon.InitializeBlock(environmentObj); } #region Play Mode Method /// /// Initializes the game in play mode. /// 初始化游戏playMode。 /// /// /// This method is used to initialize the game in play mode, /// including setting the target type, updating target states, /// updating UI display, moving the agent to the spawn area, /// destroying all enemies, and scene blocks. /// 该方法用于初始化游戏播放模式,包括设置目标类型、更新目标状态、更新UI显示、 /// 将代理移动到生成区域、销毁所有敌人和场景块。 /// public void PlayModeInitialize() { targetType = Targets.Stay; UpdateTargetStates(); envUICon.UpdateTargetType(targetType); MoveAgentToSpwanArea(); enemyCon.DestroyAllEnemys(); sceneBlockCon.DestroyBlock(); } /// /// Changes the target type in play mode and updates the game environment accordingly. /// /// The new target type to be set. /// Spawn position for scene blocks. /// Level parameter for spawning scene blocks. /// Number of blocks to spawn. /// /// This method updates the target type to the specified new target. If a spawn position is provided, it spawns scene blocks at that position /// with the specified level and block number. The target states are then updated based on the new setup. The UI is also refreshed to reflect the new target type. /// public void PlayTargetChange(Targets newTargetType, Vector3? spawnPosition = null, int level = 0, int blockNum = 0) { targetType = newTargetType; if(spawnPosition.HasValue) { SpawnSceneBlock(targetType, level, blockNum, spawnPosition.Value); UpdateTargetStates(spawnPosition.Value); } else { UpdateTargetStates(); } envUICon.UpdateTargetType(targetType); } #endregion Play Mode Method /// /// Gets the target observation states. /// 获取目标观测状态。 /// /// The target position (optional). private void UpdateTargetStates(Vector3? targetPosition = null) { // targettype, x,y,z, firebasesAreaDiameter targetState[0] = (int)targetType; if (targetPosition != null) { this.targetPosition = (Vector3)targetPosition; } if (targetType == (int)Targets.Free || targetType == Targets.Stay) { for (int i = 1; i < targetState.Length; i++) // set target position state to 0 targetState[i] = 0f; } else { Vector3 fireBasePosition = sceneBlockCon.nowBlock.firebasesAreaPosition; Vector3 convertedPosition = fireBasePosition - this.transform.position; targetState[1] = convertedPosition.x; targetState[2] = convertedPosition.y; targetState[3] = convertedPosition.z; targetState[4] = sceneBlockCon.nowBlock.firebasesAreaDiameter; } } /// /// Gets the in-area state. /// 获取是否在区域内的State /// /// The in-area state. public int GetInAreaState() { if (targetType == Targets.Go) { return inArea; } else { return 0; } } /// /// Gets a random level index based on the target type. /// 根据目标类型获取随机关卡索引。 /// /// The target type. /// A random level index. public int RollRandomLevelIndex(Targets target) { Debug.Log(target); List targetProbs; targetProbs = commonParamCon.levelProbs[target]; // sample random level depends on the target probabilities float randomNum = UnityEngine.Random.Range(0f, 1f); float sumProb = 0f; for (int i = 0; i < targetProbs.Count; i++) { sumProb += targetProbs[i]; if (randomNum < sumProb) { return i; } } // If no level was returned, log an error and return -1 messageBoxCon.PushMessage( new List { "[ERROR]TargetController:RollRandomLevelIndex", "level index out of range" }, new List { "orange" }); return -1; } }