using System; using System.Collections.Generic; using UnityEngine; using Random = UnityEngine.Random; public class TargetController : MonoBehaviour { public GameObject environmentObj; public GameObject agentObj; public GameObject HUDObj; public GameObject sceneBlockContainerObj; public GameObject enemyContainerObj; public GameObject parameterContainerObj; public GameObject environmentUIObj; public GameObject worldUIObj; // area public GameObject edgeUp; public GameObject edgeDown; public GameObject edgeLeft; public GameObject edgeRight; public GameObject edgeAgent_Enemy; //group public string group1Tag = "Player"; public string group2Tag = "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 int targetTypeInt; [System.NonSerialized] public int gotoLevelNum; [System.NonSerialized] public int attackLevelNum; public float[] targetState = new float[6]; public enum EndType { Win, Lose, Running, Num }; [System.NonSerialized] public int targetNum = 0; private Dictionary oneHotRarget = new Dictionary(); private int inArea = 0; private float freeProb; private float sceneBlockSize; private float lastDistance; private int randBlockType = 0; private int randLevel = 0; public Vector3 targetPosition; private bool firstRewardFlag = true; private bool targetEnemySpawnFinish = false; private SceneBlockContainer sceneBlockCon; private EnemyContainer enemyCon; private EnvironmentUIControl envUICon; private ParameterContainer paramCon; private CharacterController agentCharaCon; private WorldUIController worldUICon; 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() { sceneBlockCon = sceneBlockContainerObj.GetComponent(); envUICon = environmentUIObj.GetComponent(); enemyCon = enemyContainerObj.GetComponent(); agentCharaCon = agentObj.GetComponent(); paramCon = parameterContainerObj.GetComponent(); worldUICon = worldUIObj.GetComponent(); hudCon = HUDObj.GetComponent(); messageBoxCon = HUDObj.GetComponent(); // get parameter from ParameterContainer gamemode = paramCon.gameMode; attackProb = paramCon.attackProb; gotoProb = paramCon.gotoProb; defenceProb = paramCon.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)SceneBlockContainer.Targets.Num; gotoLevelNum = sceneBlockCon.scenePrefabSet.GetLevelNumber(SceneBlockContainer.Targets.Go); attackLevelNum = sceneBlockCon.scenePrefabSet.GetLevelNumber(SceneBlockContainer.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 = paramCon.timeLimit; // print out time // Debug.Log("Playing Time: " + leftTime); } else { leftTime = paramCon.timeLimit - Time.time + startTime; } } #region Train Mode Initialization Functions public void RollNewScene() { startTime = Time.time;// Reset StartTime as now time leftTime = paramCon.timeLimit - Time.time + startTime; float randTargetType = UnityEngine.Random.Range(0f, 1f); if (randTargetType <= gotoProb) { // goto target spawn Debug.Log("GOTO THIS TARGET!"); targetTypeInt = (int)SceneBlockContainer.Targets.Go; RandomSpawnSceneBlock(SceneBlockContainer.Targets.Go); // set startDistance firstRewardFlag = true; } else if (randTargetType > gotoProb && randTargetType <= gotoProb + attackProb) { // attack target spawn Debug.Log("ATTACK!"); targetTypeInt = (int)SceneBlockContainer.Targets.Attack; RandomSpawnSceneBlock(SceneBlockContainer.Targets.Attack); // set startDistance firstRewardFlag = true; targetEnemySpawnFinish = false; } else if (randTargetType > gotoProb + attackProb && randTargetType <= gotoProb + attackProb + defenceProb) { // defence target spawn Debug.Log("DEFENCE!"); targetTypeInt = (int)SceneBlockContainer.Targets.Defence; RandomSpawnSceneBlock(SceneBlockContainer.Targets.Defence); // set startDistance firstRewardFlag = true; } else { Debug.Log("Free"); targetTypeInt = (int)SceneBlockContainer.Targets.Free; enemyCon.DestroyAllEnemys(); enemyCon.RandomInitEnemys(hudCon.enemyNum); MoveAgentToSpwanArea(); sceneBlockCon.DestroyBlock(); } UpdateTargetStates(); envUICon.UpdateTargetType(targetTypeInt); } #endregion Train Mode Initialization Functions #region Agent Move Method // move Agent into Agent Spawn Area private void MoveAgentToSpwanArea() { float randX = UnityEngine.Random.Range(minAgentAreaX, maxAgentAreaX); ; float randZ = 0f; if (paramCon.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 Agent to this position 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 #region Random SceneBlock Spawn Method // initialize scene block by target type private void RandomSpawnSceneBlock(SceneBlockContainer.Targets targetType) { randLevel = GetRandomLevelIndex(targetType); randBlockType = Random.Range(0, sceneBlockCon.scenePrefabSet.GetBlockNumber(targetType,randLevel)); sceneBlockSize = sceneBlockCon.scenePrefabSet.GetBlockSize(targetType, randLevel, randBlockType); 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, group1Tag, group2Tag); enemyCon.DestroyAllEnemys(); enemyCon.RandomInitEnemysExcept(hudCon.enemyNum, targetPosition, sceneBlockSize); sceneBlockCon.nowBlock.InitBlock(environmentObj); } #endregion Random SceneBlock Spawn Method #region Reward function // check over and get rewards // 1 = success,2 = overtime,0 = notover public (int, float, float) CheckOverAndRewards() { int endTypeInt = 0; float nowReward = 0; float endReward = 0; float nowDistance = 0f; switch (targetTypeInt) { case (int)SceneBlockContainer.Targets.Go: // goto (nowDistance, inArea) = sceneBlockCon.GetAgentTargetDistanceAndInside(agentObj.transform.position); envUICon.UpdateTargetGauge(sceneBlockCon.nowBlock.firebasesBelong, sceneBlockCon.nowBlock.belongMaxPoint); float areaTargetReward = GetDistanceReward(nowDistance, inArea); //if(inArea != 0) if (sceneBlockCon.nowBlock.firebasesBelong >= sceneBlockCon.nowBlock.belongMaxPoint) { // win // let the area belongs to me nowReward = areaTargetReward; endReward = paramCon.goWinReward; //nowReward = (paramCon.inAreaReward * inArea) + getDistanceReward(nowDistance); endTypeInt = (int)EndType.Win; } else if (leftTime <= 0) { // time out lose nowReward = areaTargetReward; endReward = paramCon.loseReward; endTypeInt = (int)EndType.Lose; } else { // keep on keeping on! nowReward = areaTargetReward; endReward = 0; endTypeInt = (int)EndType.Running; } break; case (int)SceneBlockContainer.Targets.Attack: // attack (nowDistance, inArea) = sceneBlockCon.GetAgentTargetDistanceAndInside(agentObj.transform.position); envUICon.UpdateTargetGauge(sceneBlockCon.nowBlock.firebasesBelong, sceneBlockCon.nowBlock.belongMaxPoint); if (sceneBlockCon.nowBlock.GetInAreaNumber(group2Tag) <= 0 && targetEnemySpawnFinish) { // win // let the area belongs to me and kill every enmy in this area. nowReward = 0; endReward = paramCon.attackWinReward; //nowReward = (paramCon.inAreaReward * inArea) + getSceneReward(nowDistance); endTypeInt = (int)EndType.Win; targetEnemySpawnFinish = false; } else if (leftTime <= 0 && targetEnemySpawnFinish) { // time out lose nowReward = 0; endReward = paramCon.loseReward; //nowReward = (paramCon.inAreaReward * inArea) + getSceneReward(nowDistance); endTypeInt = (int)EndType.Lose; targetEnemySpawnFinish = false; } else { // keep on keeping on! // nowReward = (paramCon.inAreaReward * inArea) + getDistanceReward(nowDistance); nowReward = 0; endReward = 0; targetEnemySpawnFinish = true; endTypeInt = (int)EndType.Running; } break; case (int)SceneBlockContainer.Targets.Defence: //defence // !!! DIDN't FINISH!!! (nowDistance, inArea) = sceneBlockCon.GetAgentTargetDistanceAndInside(agentObj.transform.position); envUICon.UpdateTargetGauge(sceneBlockCon.nowBlock.firebasesBelong, sceneBlockCon.nowBlock.belongMaxPoint); if (leftTime <= 0 && sceneBlockCon.nowBlock.firebasesBelong >= 0f) { // win // time over and the area still mine nowReward = paramCon.defenceWinReward; //nowReward = (paramCon.inAreaReward * inArea) + getSceneReward(nowDistance); endTypeInt = (int)EndType.Win; } else if (sceneBlockCon.nowBlock.firebasesBelong <= sceneBlockCon.nowBlock.belongMaxPoint) { // lost area lose nowReward = paramCon.loseReward; //nowReward = (paramCon.inAreaReward * inArea) + getSceneReward(nowDistance); endTypeInt = (int)EndType.Lose; } else { // keep on keeping on! // nowReward = (paramCon.inAreaReward * inArea) + getDistanceReward(nowDistance); endTypeInt = (int)EndType.Running; } break; case (int)SceneBlockContainer.Targets.Stay: // Stay // endless nowReward = 0; endReward = 0; endTypeInt = (int)EndType.Running; break; default: //free kill if (enemyContainerObj.transform.childCount <= 0) { // win // nowReward = paramCon.winReward + (paramCon.timeBonusPerSecReward * leftTime); nowReward = 0; endReward = paramCon.freeWinReward; endTypeInt = (int)EndType.Win; } else if (leftTime <= 0) { // lose //nowReward = paramCon.loseReward; nowReward = 0; endReward = paramCon.loseReward; endTypeInt = (int)EndType.Lose; } else { // keep on keeping on! nowReward = 0; endReward = 0; endTypeInt = (int)EndType.Running; } break; } envUICon.ShowResult(endTypeInt); worldUICon.UpdateChart(targetTypeInt, endTypeInt); return (endTypeInt, nowReward, endReward); } // caulculate sceneReward if close to target then get great reward private float GetDistanceReward(float nowDistance, int inarea) { if (firstRewardFlag) { // first distance record (lastDistance, _) = sceneBlockCon.GetAgentTargetDistanceAndInside(agentObj.transform.position); firstRewardFlag = false; } float nowSeneReward = 0f; if (inarea != 0) { // in area nowSeneReward = paramCon.inAreaReward; } else { // out of area // nowSeneReward = paramCon.distanceReward * Math.Clamp(lastDistance - nowDistance, 0, 100); nowSeneReward = paramCon.distanceReward * (lastDistance - nowDistance); } lastDistance = nowDistance; return nowSeneReward; } // calculate kill reward base on killed enemy's position public float KillReward(Vector3 enemyPosition) { float nowKillReward = 0f; if (targetTypeInt == (int)SceneBlockContainer.Targets.Attack) { // attack mode (_, int isInArea) = sceneBlockCon.nowBlock.GetDistInArea(enemyPosition); if (isInArea == 1) { // kill in area enemy nowKillReward = paramCon.killTargetEnemyReward; } else { nowKillReward = paramCon.killNonTargetReward; } } else if (targetTypeInt == (int)SceneBlockContainer.Targets.Free) { // free mode hit nowKillReward = paramCon.killTargetEnemyReward; } else { // goto & defence nowKillReward = paramCon.killNonTargetReward; } return nowKillReward; } // calculate hit reward base on killed enemy's position and now mode public float HitEnemyReward(Vector3 enemyPosition) { float nowHitReward = 0f; if (targetTypeInt == (int)SceneBlockContainer.Targets.Attack) { // attack mode (_, int isInArea) = sceneBlockCon.nowBlock.GetDistInArea(enemyPosition); if (isInArea == 1) { // hit in area enemy nowHitReward = paramCon.hitTargetReward; } else { // hit not in area enemy nowHitReward = paramCon.hitNonTargetReward; } } else if (targetTypeInt == (int)SceneBlockContainer.Targets.Free) { // free mode hit nowHitReward = paramCon.hitTargetReward; } else { // goto & defence nowHitReward = paramCon.hitNonTargetReward; } return nowHitReward; } #endregion Reward function #region Play Mode Method // Initialize Play mode public void PlayInitialize() { targetTypeInt = (int)SceneBlockContainer.Targets.Stay; UpdateTargetStates(); envUICon.UpdateTargetType(targetTypeInt); MoveAgentToSpwanArea(); enemyCon.DestroyAllEnemys(); sceneBlockCon.DestroyBlock(); } // change to attack mode public void AttackModeChange(Vector3 targetPosition) { targetTypeInt = (int)SceneBlockContainer.Targets.Attack; UpdateTargetStates(targetPosition); envUICon.UpdateTargetType(targetTypeInt); } // change to free mode public void FreeModeChange() { targetTypeInt = (int)SceneBlockContainer.Targets.Free; UpdateTargetStates(); envUICon.UpdateTargetType(targetTypeInt); } // change to goto mode public void GotoModeChange(Vector3 targetPosition) { targetTypeInt = (int)SceneBlockContainer.Targets.Go; UpdateTargetStates(targetPosition); envUICon.UpdateTargetType(targetTypeInt); } // change to stay mode public void StayModeChange() { targetTypeInt = (int)SceneBlockContainer.Targets.Stay; UpdateTargetStates(); envUICon.UpdateTargetType(targetTypeInt); } #endregion Play Mode Method // get target observation states private void UpdateTargetStates(Vector3? targetPosition = null) { // targettype, x,y,z, firebasesAreaDiameter targetState[0] = targetTypeInt; if (targetPosition != null) { this.targetPosition = (Vector3)targetPosition; } if (targetTypeInt == (int)SceneBlockContainer.Targets.Free || targetTypeInt == (int)SceneBlockContainer.Targets.Stay) { for (int i = 1; i < targetState.Length; i++) // set target position state to 0 targetState[i] = 0f; } else { targetState[1] = this.targetPosition.x; targetState[2] = this.targetPosition.y; targetState[3] = this.targetPosition.z; targetState[4] = sceneBlockCon.nowBlock.firebasesAreaDiameter; targetState[5] = sceneBlockCon.nowBlock.belongRatio; } } // get in area state public int GetInAreaState() { if (targetTypeInt == (int)SceneBlockContainer.Targets.Go) { return inArea; } else { return 0; } } // get random Level by target type public int GetRandomLevelIndex(SceneBlockContainer.Targets target) { List targetProbs; switch (target) { case SceneBlockContainer.Targets.Attack: targetProbs = paramCon.attackLevelProbs; break; case SceneBlockContainer.Targets.Go: targetProbs = paramCon.gotoLevelProbs; break; case SceneBlockContainer.Targets.Defence: targetProbs = paramCon.defenceLevelProbs; break; default: messageBoxCon.PushMessage( new List { "[ERROR]TargetController:RandomLevel", "target type error" }, new List { "#800000ff" }); Debug.LogWarning("[ERROR]TargetController:RandomLevel:target type error"); return -1; // Exit early on default case } // 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:RandomLevel", "level index out of range" }, new List { "orange" }); return -1; } }