Aimbot-ParallelEnv/Assets/Script/InGame/TargetController.cs

609 lines
22 KiB
C#
Raw Normal View History

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;
public float[] targetState = new float[6];
public enum EndType
{ Win, Lose, Running, Num };
[System.NonSerialized] public int targetNum = 0;
private Dictionary<int, float[]> oneHotRarget = new Dictionary<int, float[]>();
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<SceneBlockContainer>();
envUICon = environmentUIObj.GetComponent<EnvironmentUIControl>();
enemyCon = enemyContainerObj.GetComponent<EnemyContainer>();
agentCharaCon = agentObj.GetComponent<CharacterController>();
paramCon = parameterContainerObj.GetComponent<ParameterContainer>();
worldUICon = worldUIObj.GetComponent<WorldUIController>();
hudCon = HUDObj.GetComponent<HUDController>();
messageBoxCon = HUDObj.GetComponent<MessageBoxController>();
// 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;
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<float> 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<string> { "[ERROR]TargetController:RandomLevel", "target type error" },
new List<string> { "#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<string> { "[ERROR]TargetController:RandomLevel", "level index out of range" },
new List<string> { "orange" });
return -1;
}
}