add side channel to let python side know which target got win or lose. fix update time bug. may cause double gameover check.(got another lose after reset the game.)
419 lines
17 KiB
C#
419 lines
17 KiB
C#
using System;
|
|
using System.Collections;
|
|
using System.Collections.Generic;
|
|
using UnityEngine;
|
|
using Random = UnityEngine.Random;
|
|
|
|
public class TargetController : MonoBehaviour
|
|
{
|
|
public GameObject EnvironmentObj;
|
|
public GameObject AgentObj;
|
|
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 enum Targets { Free, Go, Attack, Defence, Num };// Num is use for get total target bumber
|
|
public enum EndType { Win, Lose, Running, Num };
|
|
[System.NonSerialized] public int targetNum = 0;
|
|
private Dictionary<int, float[]> oneHotRarget = new Dictionary<int, float[]>();
|
|
|
|
private float freeProb;
|
|
private float sceneSize;
|
|
private float lastDistance;
|
|
private Vector3 targetPosition;
|
|
private Vector3 targetLocalPosition;
|
|
private bool firstRewardFlag = true;
|
|
private SceneBlockContainer blockCont;
|
|
private EnemyContainer enemyCont;
|
|
private EnvironmentUIControl envUICon;
|
|
private ParameterContainer paramCon;
|
|
private CharacterController agentCharaCon;
|
|
private WorldUIController worldUICon;
|
|
|
|
// Start is called before the first frame update
|
|
void Start()
|
|
{
|
|
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;
|
|
|
|
blockCont = SceneBlockContainerObj.GetComponent<SceneBlockContainer>();
|
|
envUICon = EnvironmentUIObj.GetComponent<EnvironmentUIControl>();
|
|
enemyCont = EnemyContainerObj.GetComponent<EnemyContainer>();
|
|
agentCharaCon = AgentObj.GetComponent<CharacterController>();
|
|
paramCon = ParameterContainerObj.GetComponent<ParameterContainer>();
|
|
worldUICon = WorldUIObj.GetComponent<WorldUIController>();
|
|
freeProb = 1 - attackProb - gotoProb - defenceProb;
|
|
targetNum = (int)Targets.Num;
|
|
if (freeProb < 0)
|
|
{
|
|
Debug.LogError("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()
|
|
{
|
|
leftTime = paramCon.timeLimit - Time.time + startTime;
|
|
}
|
|
|
|
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)Targets.Go;
|
|
int randBlockType = Random.Range(0, blockCont.goBlockPrefabs.Length);
|
|
// get choosed scene size
|
|
sceneSize = blockCont.goBlockPrefabs[randBlockType].GetComponent<SceneBlock>().blockSize;
|
|
float randX = UnityEngine.Random.Range(minEnemyAreaX + sceneSize + 1f, maxEnemyAreaX - sceneSize - 1f);
|
|
float randZ = UnityEngine.Random.Range(minEnemyAreaZ + sceneSize + 1f, maxEnemyAreaZ - sceneSize - 1f);
|
|
targetPosition = new Vector3(randX, 0f, randZ);
|
|
// Init Agent position
|
|
moveAgentToSpwanArea();
|
|
// init scene block
|
|
blockCont.destroyBlock();
|
|
blockCont.createNewBlock(targetTypeInt, randBlockType, targetPosition, group1Tag, group2Tag);
|
|
enemyCont.destroyAllEnemys();
|
|
enemyCont.randomInitEnemysExcept(paramCon.enemyNum, targetPosition, sceneSize);
|
|
blockCont.thisBlock.initBlock(EnvironmentObj);
|
|
// set startDistance
|
|
firstRewardFlag = true;
|
|
}
|
|
else if (randTargetType > gotoProb && randTargetType <= gotoProb + attackProb)
|
|
{
|
|
// attack target spawn
|
|
Debug.Log("ATTACK!");
|
|
targetTypeInt = (int)Targets.Attack;
|
|
int randBlockType = Random.Range(0, blockCont.attackBlockPrefabs.Length);
|
|
// get choosed scene size
|
|
sceneSize = blockCont.attackBlockPrefabs[randBlockType].GetComponent<SceneBlock>().blockSize;
|
|
float randX = UnityEngine.Random.Range(minEnemyAreaX + sceneSize + 1f, maxEnemyAreaX - sceneSize - 1f);
|
|
float randZ = UnityEngine.Random.Range(minEnemyAreaZ + sceneSize + 1f, maxEnemyAreaZ - sceneSize - 1f);
|
|
targetPosition = new Vector3(randX, 0f, randZ);
|
|
// Init Agent position
|
|
moveAgentToSpwanArea();
|
|
// init scene block
|
|
blockCont.destroyBlock();
|
|
blockCont.createNewBlock(targetTypeInt, randBlockType, targetPosition, group1Tag, group2Tag);
|
|
enemyCont.destroyAllEnemys();
|
|
enemyCont.randomInitEnemysExcept(paramCon.enemyNum, targetPosition, sceneSize);
|
|
blockCont.thisBlock.initBlock(EnvironmentObj);
|
|
// set startDistance
|
|
firstRewardFlag = true;
|
|
}
|
|
else if (randTargetType > gotoProb + attackProb && randTargetType <= gotoProb + attackProb + defenceProb)
|
|
{
|
|
// defence target spawn
|
|
Debug.Log("DEFENCE!");
|
|
targetTypeInt = (int)Targets.Defence;
|
|
int randBlockType = Random.Range(0, blockCont.attackBlockPrefabs.Length);
|
|
// get choosed scene size
|
|
sceneSize = blockCont.defencePrefabs[randBlockType].GetComponent<SceneBlock>().blockSize;
|
|
float randX = UnityEngine.Random.Range(minEnemyAreaX + sceneSize + 1f, maxEnemyAreaX - sceneSize - 1f);
|
|
float randZ = UnityEngine.Random.Range(minEnemyAreaZ + sceneSize + 1f, maxEnemyAreaZ - sceneSize - 1f);
|
|
targetPosition = new Vector3(randX, 0f, randZ);
|
|
// Init Agent position
|
|
moveAgentTo(targetPosition);
|
|
// init scene block
|
|
blockCont.destroyBlock();
|
|
blockCont.createNewBlock(targetTypeInt, randBlockType, targetPosition, group1Tag, group2Tag);
|
|
enemyCont.destroyAllEnemys();
|
|
enemyCont.randomInitEnemysExcept(paramCon.enemyNum, targetPosition, sceneSize);
|
|
blockCont.thisBlock.initBlock(EnvironmentObj);
|
|
// set startDistance
|
|
firstRewardFlag = true;
|
|
}
|
|
else
|
|
{
|
|
Debug.Log("Free");
|
|
targetTypeInt = (int)Targets.Free;
|
|
enemyCont.destroyAllEnemys();
|
|
enemyCont.randomInitEnemys(paramCon.enemyNum);
|
|
moveAgentToSpwanArea();
|
|
blockCont.destroyBlock();
|
|
}
|
|
envUICon.updateTargetType(targetTypeInt);
|
|
}
|
|
|
|
// get target observation states
|
|
public float[] getTargetStates()
|
|
{
|
|
// targettype, x,y,z, firebasesAreaDiameter
|
|
List<float> targetState = new List<float>();
|
|
if (targetTypeInt == (int)Targets.Free)
|
|
{
|
|
targetState.AddRange(oneHotRarget[targetTypeInt]);
|
|
targetState.AddRange(new float[5] { 0f, 0f, 0f, 0f, 0f });
|
|
}
|
|
else
|
|
{
|
|
targetState.AddRange(oneHotRarget[targetTypeInt]);
|
|
targetState.AddRange(new float[5] { targetPosition.x, targetPosition.y, targetPosition.z, blockCont.thisBlock.firebasesAreaDiameter, blockCont.thisBlock.belongRatio });
|
|
}
|
|
return targetState.ToArray();
|
|
}
|
|
|
|
// move Agent into Agent Spawn Area
|
|
public void moveAgentToSpwanArea()
|
|
{
|
|
float randX = UnityEngine.Random.Range(minAgentAreaX, maxAgentAreaX);
|
|
float 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 thisPosition)
|
|
{
|
|
// while using transform.localPosition to move character
|
|
// u should turn off character Controller or it won't work
|
|
agentCharaCon.enabled = false;
|
|
AgentObj.transform.localPosition = thisPosition;
|
|
agentCharaCon.enabled = true;
|
|
}
|
|
|
|
// caulculate sceneReward if close to target then get great reward
|
|
public float getSceneReward(float nowDistance)
|
|
{
|
|
if (firstRewardFlag)
|
|
{
|
|
(lastDistance, _) = blockCont.getAgentTargetDistanceAndInside(AgentObj.transform.position);
|
|
firstRewardFlag = false;
|
|
}
|
|
float thisSceneReward = 0f;
|
|
thisSceneReward = paramCon.distanceReward * (lastDistance - nowDistance);
|
|
lastDistance = nowDistance;
|
|
return thisSceneReward;
|
|
}
|
|
|
|
// check over and get rewards
|
|
// 1 = success,2 = overtime,0 = notover
|
|
public (int, float) checkOverAndRewards()
|
|
{
|
|
int endTypeInt = 0;
|
|
float thisReward = 0;
|
|
int inArea = 0;
|
|
float nowDistance = 0f;
|
|
switch (targetTypeInt)
|
|
{
|
|
case (int)Targets.Go:
|
|
// goto
|
|
(nowDistance, inArea) = blockCont.getAgentTargetDistanceAndInside(AgentObj.transform.position);
|
|
envUICon.updateTargetGauge(blockCont.thisBlock.firebasesBelong, blockCont.thisBlock.belongMaxPoint);
|
|
if (blockCont.thisBlock.firebasesBelong >= blockCont.thisBlock.belongMaxPoint)
|
|
{
|
|
// win
|
|
// let the area belongs to me
|
|
thisReward = paramCon.goWinReward;
|
|
//thisReward = (paramCon.inAreaReward * inArea) + getSceneReward(nowDistance);
|
|
endTypeInt = (int)EndType.Win;
|
|
}
|
|
else if (leftTime <= 0)
|
|
{
|
|
// time out lose
|
|
thisReward = paramCon.loseReward;
|
|
//thisReward = (paramCon.inAreaReward * inArea) + getSceneReward(nowDistance);
|
|
endTypeInt = (int)EndType.Lose;
|
|
}
|
|
else
|
|
{
|
|
// keep on keeping on!
|
|
|
|
thisReward = (paramCon.inAreaReward * inArea) + getSceneReward(nowDistance);
|
|
endTypeInt = (int)EndType.Running;
|
|
}
|
|
break;
|
|
case (int)Targets.Attack:
|
|
// attack
|
|
(nowDistance, inArea) = blockCont.getAgentTargetDistanceAndInside(AgentObj.transform.position);
|
|
envUICon.updateTargetGauge(blockCont.thisBlock.firebasesBelong, blockCont.thisBlock.belongMaxPoint);
|
|
if (blockCont.thisBlock.firebasesBelong >= blockCont.thisBlock.belongMaxPoint && blockCont.thisBlock.getInAreaNumber(group2Tag) <= 0)
|
|
{
|
|
// win
|
|
// let the area belongs to me and kill every enmy in this area.
|
|
thisReward = paramCon.attackWinReward;
|
|
//thisReward = (paramCon.inAreaReward * inArea) + getSceneReward(nowDistance);
|
|
endTypeInt = (int)EndType.Win;
|
|
}
|
|
else if (leftTime <= 0)
|
|
{
|
|
// time out lose
|
|
thisReward = paramCon.loseReward;
|
|
//thisReward = (paramCon.inAreaReward * inArea) + getSceneReward(nowDistance);
|
|
endTypeInt = (int)EndType.Lose;
|
|
}
|
|
else
|
|
{
|
|
// keep on keeping on!
|
|
thisReward = (paramCon.inAreaReward * inArea) + getSceneReward(nowDistance);
|
|
endTypeInt = (int)EndType.Running;
|
|
}
|
|
break;
|
|
case (int)Targets.Defence:
|
|
//defence
|
|
(nowDistance, inArea) = blockCont.getAgentTargetDistanceAndInside(AgentObj.transform.position);
|
|
envUICon.updateTargetGauge(blockCont.thisBlock.firebasesBelong, blockCont.thisBlock.belongMaxPoint);
|
|
if (leftTime <= 0 && blockCont.thisBlock.firebasesBelong >= 0f)
|
|
{
|
|
// win
|
|
// time over and the area still mine
|
|
thisReward = paramCon.defenceWinReward;
|
|
//thisReward = (paramCon.inAreaReward * inArea) + getSceneReward(nowDistance);
|
|
endTypeInt = (int)EndType.Win;
|
|
}
|
|
else if (blockCont.thisBlock.firebasesBelong <= blockCont.thisBlock.belongMaxPoint)
|
|
{
|
|
// lost area lose
|
|
thisReward = paramCon.loseReward;
|
|
//thisReward = (paramCon.inAreaReward * inArea) + getSceneReward(nowDistance);
|
|
endTypeInt = (int)EndType.Lose;
|
|
}
|
|
else
|
|
{
|
|
// keep on keeping on!
|
|
thisReward = (paramCon.inAreaReward * inArea) + getSceneReward(nowDistance);
|
|
endTypeInt = (int)EndType.Running;
|
|
}
|
|
break;
|
|
default:
|
|
//free kill
|
|
if (EnemyContainerObj.transform.childCount <= 0)
|
|
{
|
|
// win
|
|
//thisReward = paramCon.winReward + (paramCon.timeBonusPerSecReward * leftTime);
|
|
thisReward = paramCon.freeWinReward;
|
|
endTypeInt = (int)EndType.Win;
|
|
}
|
|
else if (Time.time - startTime >= paramCon.timeLimit)
|
|
{
|
|
// lose
|
|
//thisReward = paramCon.loseReward;
|
|
thisReward = paramCon.loseReward;
|
|
endTypeInt = (int)EndType.Lose;
|
|
}
|
|
else
|
|
{
|
|
// keep on keeping on!
|
|
endTypeInt = (int)EndType.Running;
|
|
}
|
|
break;
|
|
}
|
|
envUICon.showResult(endTypeInt);
|
|
worldUICon.updateChart(targetTypeInt, endTypeInt);
|
|
return (endTypeInt, thisReward);
|
|
}
|
|
|
|
// calculate kill reward base on killed enemy's position
|
|
public float killReward(Vector3 enemyPosition)
|
|
{
|
|
float thisKillReward = 0f;
|
|
if (targetTypeInt == (int)Targets.Attack)
|
|
{
|
|
// attack mode
|
|
(_, int isInArea) = blockCont.thisBlock.getDist_inArea(enemyPosition);
|
|
if (isInArea == 1)
|
|
{
|
|
// kill in area enemy
|
|
thisKillReward = paramCon.killTargetEnemyReward;
|
|
}
|
|
else
|
|
{
|
|
thisKillReward = paramCon.killReward;
|
|
}
|
|
}
|
|
else if(targetTypeInt == (int)Targets.Free)
|
|
{
|
|
// free mode kill
|
|
thisKillReward = paramCon.killTargetEnemyReward;
|
|
}else
|
|
{
|
|
// goto & defence
|
|
thisKillReward = paramCon.killReward;
|
|
}
|
|
return thisKillReward;
|
|
}
|
|
|
|
// calculate hit reward base on killed enemy's position and now mode
|
|
public float hitReward(Vector3 enemyPosition)
|
|
{
|
|
float thisHitReward = 0f;
|
|
if (targetTypeInt == (int)Targets.Attack)
|
|
{
|
|
// attack mode
|
|
(_, int isInArea) = blockCont.thisBlock.getDist_inArea(enemyPosition);
|
|
if (isInArea == 1)
|
|
{
|
|
// hit in area enemy
|
|
thisHitReward = paramCon.hitTargetReward;
|
|
}
|
|
else
|
|
{
|
|
// hit not in area enemy
|
|
thisHitReward = paramCon.hitReward;
|
|
}
|
|
}
|
|
else if (targetTypeInt == (int)Targets.Free)
|
|
{
|
|
// free mode hit
|
|
thisHitReward = paramCon.hitTargetReward;
|
|
}
|
|
else
|
|
{
|
|
// goto & defence
|
|
thisHitReward = paramCon.hitReward;
|
|
}
|
|
return thisHitReward;
|
|
}
|
|
}
|