Compare commits

..

1 Commits

62 changed files with 36579 additions and 24273 deletions

View File

@ -144,10 +144,8 @@ private void CalculateCurrentIncrease(bool moving)
private void Update()
{
if (Input.GetKeyDown(KeyCode.M))
{
if (Input.GetKey(KeyCode.M))
_active = !_active;
}
if (!_active)
{

View File

@ -1 +1 @@
{"count":1,"self":13.296357599999999,"total":13.6899074,"children":{"InitializeActuators":{"count":2,"self":0.0015034999999999998,"total":0.0015034999999999998,"children":null},"InitializeSensors":{"count":2,"self":0.0010003,"total":0.0010003,"children":null},"AgentSendState":{"count":433,"self":0.0045045,"total":0.2859497,"children":{"CollectObservations":{"count":433,"self":0.2078226,"total":0.2078226,"children":null},"WriteActionMask":{"count":433,"self":0.0009994,"total":0.0009994,"children":null},"RequestDecision":{"count":433,"self":0.0726232,"total":0.0726232,"children":null}}},"DecideAction":{"count":433,"self":0.0019984,"total":0.0019984,"children":null},"AgentAct":{"count":433,"self":0.1020973,"total":0.1020973,"children":null}},"gauges":{},"metadata":{"timer_format_version":"0.1.0","start_time_seconds":"1704690165","unity_version":"2021.3.14f1","command_line_arguments":"C:\\Program Files\\Unity\\Hub\\Editor\\2021.3.14f1\\Editor\\Unity.exe -projectpath C:\\Users\\UCUNI\\OneDrive\\Unity\\ML-Agents\\Aimbot-ParallelEnv -useHub -hubIPC -cloudEnvironment production -licensingIpc LicenseClient-bbQe3pofOXaeruEXc-2O8 -hubSessionId fa8ba95e-3ad3-4e58-bbf6-617d74b7cdd4 -accessToken dLDx_lE1nZ6RAtxwNKiPge42G5i8CvH-Sv_7EIEhm2000ef","communication_protocol_version":"1.5.0","com.unity.ml-agents_version":"2.0.1","scene_name":"Play","end_time_seconds":"1704690179"}}
{"count":1,"self":6.6978924,"total":6.7403838,"children":{"InitializeActuators":{"count":2,"self":0.002,"total":0.002,"children":null},"InitializeSensors":{"count":2,"self":0.001,"total":0.001,"children":null},"AgentSendState":{"count":96,"self":0.0014992999999999999,"total":0.010006899999999999,"children":{"CollectObservations":{"count":96,"self":0.0050103,"total":0.0050103,"children":null},"WriteActionMask":{"count":96,"self":0.0014992,"total":0.0014992,"children":null},"RequestDecision":{"count":96,"self":0.0019981,"total":0.0019981,"children":null}}},"DecideAction":{"count":96,"self":0.0029998,"total":0.0029998,"children":null},"AgentAct":{"count":96,"self":0.0259858,"total":0.0259858,"children":null}},"gauges":{},"metadata":{"timer_format_version":"0.1.0","start_time_seconds":"1696776760","unity_version":"2021.3.14f1","command_line_arguments":"C:\\Program Files\\Unity\\Hub\\Editor\\2021.3.14f1\\Editor\\Unity.exe -projectpath C:\\Users\\UCUNI\\OneDrive\\Unity\\ML-Agents\\Aimbot-ParallelEnv -useHub -hubIPC -cloudEnvironment production -licensingIpc LicenseClient-XEerV7kYNt9lW-ScfLTdo -hubSessionId d21180ac-2b2e-4464-bc29-90cd6d320fc3 -accessToken FUlk05mLlb2dcwVANd09HxpJmsCNwwFXO6GIY0FY5_c00ef","communication_protocol_version":"1.5.0","com.unity.ml-agents_version":"2.0.1","scene_name":"Play","end_time_seconds":"1696776767"}}

View File

@ -1 +1 @@
{"count":1,"self":103.5483392,"total":107.75784449999999,"children":{"InitializeActuators":{"count":12,"self":0.001004,"total":0.001004,"children":null},"InitializeSensors":{"count":12,"self":0,"total":0,"children":null},"AgentSendState":{"count":1424,"self":0.0280132,"total":2.6909213,"children":{"CollectObservations":{"count":8544,"self":2.640354,"total":2.640354,"children":null},"WriteActionMask":{"count":8544,"self":0.0060129,"total":0.0060129,"children":null},"RequestDecision":{"count":8544,"self":0.0165412,"total":0.0165412,"children":null}}},"DecideAction":{"count":1424,"self":0.0235409,"total":0.0235409,"children":null},"AgentAct":{"count":1424,"self":1.4940384,"total":1.4940384,"children":null}},"gauges":{},"metadata":{"timer_format_version":"0.1.0","start_time_seconds":"1704689262","unity_version":"2021.3.14f1","command_line_arguments":"C:\\Program Files\\Unity\\Hub\\Editor\\2021.3.14f1\\Editor\\Unity.exe -projectpath C:\\Users\\UCUNI\\OneDrive\\Unity\\ML-Agents\\Aimbot-ParallelEnv -useHub -hubIPC -cloudEnvironment production -licensingIpc LicenseClient-bbQe3pofOXaeruEXc-2O8 -hubSessionId fa8ba95e-3ad3-4e58-bbf6-617d74b7cdd4 -accessToken dLDx_lE1nZ6RAtxwNKiPge42G5i8CvH-Sv_7EIEhm2000ef","communication_protocol_version":"1.5.0","com.unity.ml-agents_version":"2.0.1","scene_name":"StartScene","end_time_seconds":"1704689369"}}
{"count":1,"self":96.1897728,"total":96.582275299999992,"children":{"InitializeActuators":{"count":16,"self":0.00099899999999999989,"total":0.00099899999999999989,"children":null},"InitializeSensors":{"count":16,"self":0.0009993,"total":0.0009993,"children":null},"AgentSendState":{"count":867,"self":0.019012499999999998,"total":0.1588117,"children":{"CollectObservations":{"count":6936,"self":0.1277917,"total":0.1277917,"children":null},"WriteActionMask":{"count":6936,"self":0.0040009,"total":0.0040009,"children":null},"RequestDecision":{"count":6936,"self":0.0080066,"total":0.0080066,"children":null}}},"DecideAction":{"count":867,"self":0.0100142,"total":0.0100142,"children":null},"AgentAct":{"count":867,"self":0.2216761,"total":0.2216761,"children":null}},"gauges":{},"metadata":{"timer_format_version":"0.1.0","start_time_seconds":"1697442592","unity_version":"2021.3.14f1","command_line_arguments":"C:\\Program Files\\Unity\\Hub\\Editor\\2021.3.14f1\\Editor\\Unity.exe -projectpath C:\\Users\\UCUNI\\OneDrive\\Unity\\ML-Agents\\Aimbot-ParallelEnv -useHub -hubIPC -cloudEnvironment production -licensingIpc LicenseClient-UCUNI -hubSessionId e4e811a4-b1b9-40c9-bb87-1f58481c4f0b -accessToken CFEM6A4XwpAEfTmhoY1tJ77LSedBMNIqUlXBOmvKzJY00ef","communication_protocol_version":"1.5.0","com.unity.ml-agents_version":"2.0.1","scene_name":"StartScene","end_time_seconds":"1697442688"}}

View File

@ -1 +1 @@
{"count":1,"self":8.4565711999999991,"total":9.2154213,"children":{"InitializeActuators":{"count":12,"self":0.0009975,"total":0.0009975,"children":null},"InitializeSensors":{"count":12,"self":0.0010008999999999999,"total":0.0010008999999999999,"children":null},"AgentSendState":{"count":241,"self":0.0034963999999999998,"total":0.46725839999999996,"children":{"CollectObservations":{"count":1446,"self":0.460256,"total":0.460256,"children":null},"WriteActionMask":{"count":1446,"self":0.0010002,"total":0.0010002,"children":null},"RequestDecision":{"count":1446,"self":0.0025058,"total":0.0025058,"children":null}}},"DecideAction":{"count":241,"self":0.0050033,"total":0.0050033,"children":null},"AgentAct":{"count":241,"self":0.28359049999999997,"total":0.28359049999999997,"children":null}},"gauges":{},"metadata":{"timer_format_version":"0.1.0","start_time_seconds":"1704689378","unity_version":"2021.3.14f1","command_line_arguments":"C:\\Program Files\\Unity\\Hub\\Editor\\2021.3.14f1\\Editor\\Unity.exe -projectpath C:\\Users\\UCUNI\\OneDrive\\Unity\\ML-Agents\\Aimbot-ParallelEnv -useHub -hubIPC -cloudEnvironment production -licensingIpc LicenseClient-bbQe3pofOXaeruEXc-2O8 -hubSessionId fa8ba95e-3ad3-4e58-bbf6-617d74b7cdd4 -accessToken dLDx_lE1nZ6RAtxwNKiPge42G5i8CvH-Sv_7EIEhm2000ef","communication_protocol_version":"1.5.0","com.unity.ml-agents_version":"2.0.1","scene_name":"Train","end_time_seconds":"1704689387"}}
{"count":1,"self":28.5910528,"total":28.601550099999997,"children":{"InitializeActuators":{"count":16,"self":0.0010008,"total":0.0010008,"children":null},"InitializeSensors":{"count":16,"self":0.001,"total":0.001,"children":null},"AgentSendState":{"count":1,"self":0.0009989,"total":0.0034985,"children":{"CollectObservations":{"count":8,"self":0.0014998,"total":0.0014998,"children":null},"WriteActionMask":{"count":8,"self":0,"total":0,"children":null},"RequestDecision":{"count":8,"self":0.0009998,"total":0.0009998,"children":null}}},"DecideAction":{"count":1,"self":0.0005,"total":0.0005,"children":null},"AgentAct":{"count":1,"self":0.0044989,"total":0.0044989,"children":null}},"gauges":{},"metadata":{"timer_format_version":"0.1.0","start_time_seconds":"1697452285","unity_version":"2021.3.14f1","command_line_arguments":"C:\\Program Files\\Unity\\Hub\\Editor\\2021.3.14f1\\Editor\\Unity.exe -projectpath C:\\Users\\UCUNI\\OneDrive\\Unity\\ML-Agents\\Aimbot-ParallelEnv -useHub -hubIPC -cloudEnvironment production -licensingIpc LicenseClient-UCUNI -hubSessionId e4e811a4-b1b9-40c9-bb87-1f58481c4f0b -accessToken CFEM6A4XwpAEfTmhoY1tJ77LSedBMNIqUlXBOmvKzJY00ef","communication_protocol_version":"1.5.0","com.unity.ml-agents_version":"2.0.1","scene_name":"Train","end_time_seconds":"1697452314"}}

View File

@ -1005,8 +1005,8 @@ MonoBehaviour:
m_Script: {fileID: 11500000, guid: fe87c0e1cc204ed48ad3b37840f39efc, type: 3}
m_Name:
m_EditorClassIdentifier:
m_Material: {fileID: 0}
m_Color: {r: 1, g: 1, b: 1, a: 0}
m_Material: {fileID: 2100000, guid: f6d46f1e916b3486c90a448a441fac6a, type: 2}
m_Color: {r: 1, g: 1, b: 1, a: 0.392}
m_RaycastTarget: 1
m_RaycastPadding: {x: 0, y: 0, z: 0, w: 0}
m_Maskable: 1

View File

@ -14,7 +14,7 @@ GameObject:
- component: {fileID: 5589415219770305708}
- component: {fileID: 5589415219770305697}
m_Layer: 5
m_Name: SingleTargetProbabilityPanel
m_Name: SingleTargetLevelProbabilityPanel
m_TagString: Untagged
m_Icon: {fileID: 0}
m_NavMeshLayer: 0
@ -60,8 +60,8 @@ MonoBehaviour:
m_Script: {fileID: 11500000, guid: fe87c0e1cc204ed48ad3b37840f39efc, type: 3}
m_Name:
m_EditorClassIdentifier:
m_Material: {fileID: 2100000, guid: f6d46f1e916b3486c90a448a441fac6a, type: 2}
m_Color: {r: 0.8862745, g: 0.8862745, b: 0.8862745, a: 1}
m_Material: {fileID: 0}
m_Color: {r: 1, g: 1, b: 1, a: 0.392}
m_RaycastTarget: 1
m_RaycastPadding: {x: 0, y: 0, z: 0, w: 0}
m_Maskable: 1
@ -92,8 +92,6 @@ MonoBehaviour:
m_EditorClassIdentifier:
singleLevelProbabilityPanel: {fileID: 6536015492050286218, guid: 81d4d787c82b5174caf00af00e9a3cd1, type: 3}
targetTitleText: {fileID: 8396012208200061614, guid: 8a180acb9a0ba8b4aacd888419f082ed, type: 3}
singleLevelPanelsObjs: []
singleLevelPanels: []
--- !u!114 &5589415219770305697
MonoBehaviour:
m_ObjectHideFlags: 0

File diff suppressed because it is too large Load Diff

View File

@ -488,11 +488,11 @@ MonoBehaviour:
m_GameObject: {fileID: 237721381}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: 44d064c42ee56374e94671f4f9f9d650, type: 3}
m_Script: {fileID: 11500000, guid: 0195eb0e734c7e747bb7f8b669c80fe5, type: 3}
m_Name:
m_EditorClassIdentifier:
singleTargetLevelProbabilityPanel: {fileID: 5589415219770305710, guid: 324d8e84c24cdd04ba087763705db1d0, type: 3}
startSceneData: {fileID: 0}
startSceneData: {fileID: 1072325866}
targetLevelProbabilityPanel: []
--- !u!1 &294404779
GameObject:
@ -1763,88 +1763,6 @@ CanvasRenderer:
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 852513428}
m_CullTransparentMesh: 1
--- !u!1 &968918527
GameObject:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
serializedVersion: 6
m_Component:
- component: {fileID: 968918529}
- component: {fileID: 968918528}
m_Layer: 0
m_Name: CommonParameterContainer
m_TagString: Untagged
m_Icon: {fileID: 0}
m_NavMeshLayer: 0
m_StaticEditorFlags: 0
m_IsActive: 1
--- !u!114 &968918528
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 968918527}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: 34839c2831b759d4a8347ab655b00f36, type: 3}
m_Name:
m_EditorClassIdentifier:
lockMouse: 0
damage: 50
fireRate: 0.5
timeLimit: 30
lockCameraX: 0
lockCameraY: 1
oneHotRayTag: 1
spawnAgentInAllMap: 1
spinRecordMax: 40
spinPenaltyThreshold: 10
facingInviewEnemyDisCOEF: 0.5
group1Tag: Player
group2Tag: Enemy
hitTargetRewardDefault: 25
killTargetEnemyRewardDefault: 25
inAreaRewardDefault: 25
freeTimeBonusPerSec: 1
targetTimeBonusPerSec: 0.5
areaTimeBonusPerSec: 0.2
distanceReward: 50
facingTargetReward: 10
goWinRewardDefault: 999
attackWinRewardDefault: 999
defenceWinRewardDefault: 999
freeWinRewardDefault: 999
nonReward: -1
loseReward: -999
shootReward: -0.5
hitNonTargetReward: -5
killNonTargetReward: -5
shootWithoutReadyReward: -1.15
killBonusReward: 0
facingReward: 5
shootTargetAreaReward: 10
movePenalty: 0.1
spinPenalty: 0.08
mousePenalty: 0.06
scenePrefabSet: {fileID: 11400000, guid: 6ebbd27eb466c4a41bd2584c1b9c2e1f, type: 2}
--- !u!4 &968918529
Transform:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 968918527}
m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
m_LocalPosition: {x: 0, y: 0, z: 0}
m_LocalScale: {x: 1, y: 1, z: 1}
m_ConstrainProportionsScale: 0
m_Children: []
m_Father: {fileID: 0}
m_RootOrder: 5
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
--- !u!1 &1064099385
GameObject:
m_ObjectHideFlags: 0
@ -2126,6 +2044,58 @@ RectTransform:
m_AnchoredPosition: {x: -100, y: -7}
m_SizeDelta: {x: 160, y: 30}
m_Pivot: {x: 0.5, y: 0.5}
--- !u!1 &1072325866
GameObject:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
serializedVersion: 6
m_Component:
- component: {fileID: 1072325868}
- component: {fileID: 1072325867}
m_Layer: 0
m_Name: StartSceneDataTransfer
m_TagString: Untagged
m_Icon: {fileID: 0}
m_NavMeshLayer: 0
m_StaticEditorFlags: 0
m_IsActive: 1
--- !u!114 &1072325867
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 1072325866}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: 52ba7faaa6129cf418f26c5933d4ea0e, type: 3}
m_Name:
m_EditorClassIdentifier:
gameMode: 0
scenePrefabSet: {fileID: 11400000, guid: 6ebbd27eb466c4a41bd2584c1b9c2e1f, type: 2}
attackProb: 0
attackLevelProbs: []
gotoProb: 0
gotoLevelProbs: []
defenceProb: 0
defenceLevelProbs: []
--- !u!4 &1072325868
Transform:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 1072325866}
m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
m_LocalPosition: {x: 0, y: 0, z: 0}
m_LocalScale: {x: 1, y: 1, z: 1}
m_ConstrainProportionsScale: 0
m_Children: []
m_Father: {fileID: 0}
m_RootOrder: 4
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
--- !u!1 &1144208395
GameObject:
m_ObjectHideFlags: 0
@ -2278,7 +2248,7 @@ Transform:
m_ConstrainProportionsScale: 0
m_Children: []
m_Father: {fileID: 0}
m_RootOrder: 4
m_RootOrder: 5
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
--- !u!1 &1273196676
GameObject:
@ -2603,6 +2573,7 @@ MonoBehaviour:
m_EditorClassIdentifier:
waitTimeLimit: 45
sceneLoaderObj: {fileID: 1206568295}
startSceneDataObj: {fileID: 1072325866}
targetLevelProbabilityPanelOBJ: {fileID: 237721381}
messageTextObj: {fileID: 294404781}
waitTimeTextObj: {fileID: 431112176}

File diff suppressed because it is too large Load Diff

View File

@ -1,15 +0,0 @@
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.EventSystems;
public static class EventTriggerExtensions
{
public static void AddEventTrigger(this EventTrigger trigger, EventTriggerType type, UnityEngine.Events.UnityAction<BaseEventData> action)
{
EventTrigger.Entry entry = new EventTrigger.Entry();
entry.eventID = type;
entry.callback.AddListener(action);
trigger.triggers.Add(entry);
}
}

View File

@ -1,11 +0,0 @@
fileFormatVersion: 2
guid: 2fd51a74b20da1040b8f0d4db7d9ef70
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -1,11 +0,0 @@
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public static class Vector3PositionExtensions
{
public static Vector3 FixCanvas(this Vector3 position,Vector3 originalVanvas,Vector3 nowVanvas)
{
return position + nowVanvas - originalVanvas;
}
}

View File

@ -1,11 +0,0 @@
fileFormatVersion: 2
guid: 60aebf9c97041a74eaa7c10ce97e7ba0
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -2,7 +2,7 @@
using System.Collections.Generic;
using UnityEngine;
public static class TargetTypeExtensions
public static class targetTypeExtensions
{
public static int ToIndex(this Targets targetType)
{

View File

@ -5,7 +5,14 @@
public class AgentController : MonoBehaviour
{
[SerializeField] public Camera fpsCam;
public GameObject parameterContainerObj;
public GameObject environmentObj;
public GameObject enemyContainerObj;
public GameObject sceneBlockContainerObj;
public GameObject environmentUIControlObj;
public GameObject targetControllerObj;
public GameObject HUDObj;
public Camera fpsCam;
[Header("GetAxis() Simulate")]
public float moveSpeed = 9.0f;
@ -18,36 +25,48 @@ public class AgentController : MonoBehaviour
public float mouseYSensitivity = 200;
public float yRotation = 0.1f;//定义一个浮点类型的量记录围绕X轴旋转的角度
private List<float> spinRecord = new List<float>();
private bool lockMouse;
private float damage;
private float fireRate;
private bool lockCameraX;
private bool lockCameraY;
// environment
public float lastShootTime = 0.0f;
private float lastShootTime = 0.0f;
public int enemyKillCount = 0;
public Vector3 killEnemyPosition;
private int enemyKillCount = 0;
private Vector3 killEnemyPosition;
public bool defaultTPCamera = true;
[System.NonSerialized] public bool gunReadyToggle = true;
public string myTag = "";
private string myTag = "";
private float lastEnemyFacingDistance = 0f; // record last enemy facing minimum distance
private float lastTargetFacingDistance = 0f; // record last target facing minimum distance
// scripts
private RaySensors raySensors;
private CharacterController playerController;
private CommonParameterContainer commonPramCon;
private ParameterContainer paramContainer;
private SceneBlockContainer blockContainer;
private TargetController targetCon;
private void Start()
{
// initialize scripts
commonPramCon = CommonParameterContainer.Instance;
playerController = transform.GetComponent<CharacterController>();
paramContainer = parameterContainerObj.GetComponent<ParameterContainer>();
blockContainer = sceneBlockContainerObj.GetComponent<SceneBlockContainer>();
targetCon = targetControllerObj.GetComponent<TargetController>();
raySensors = GetComponent<RaySensors>();
playerController = this.transform.GetComponent<CharacterController>();
// initialize Environment parameters
lockMouse = commonPramCon.lockMouse;
fireRate = commonPramCon.fireRate;
lockCameraX = commonPramCon.lockCameraX;
lockCameraY = commonPramCon.lockCameraY;
lockMouse = paramContainer.lockMouse;
damage = paramContainer.damage;
fireRate = paramContainer.fireRate;
lockCameraX = paramContainer.lockCameraX;
lockCameraY = paramContainer.lockCameraY;
// initialize remainTime
// this agent's tag
@ -172,7 +191,234 @@ public void CameraControl(float Mouse_X, float Mouse_Y)
#endregion Camera Control
// Got Kill point
#region Reward Functions
// ballistic 射击弹道处理并返回获得reward
private float Ballistic(int shootState)
{
Vector3 point = new Vector3(fpsCam.pixelWidth / 2, fpsCam.pixelHeight / 2, 0);//发射位置
Ray ray = fpsCam.ScreenPointToRay(point);
RaycastHit hit;
// Debug.DrawRay(centerRay.origin, centerRay.direction * 100, Color.blue);
//按下鼠标左键
if (shootState != 0 && gunReadyToggle == true)
{
lastShootTime = Time.time;
if (Physics.Raycast(ray, out hit, 100))
{
if (hit.collider.tag != myTag && hit.collider.tag != "Wall" && hit.collider.tag != "Untagged")
{
// kill enemy
GameObject gotHitObj = hit.transform.gameObject;//获取受到Ray撞击的对象
gotHitObj.GetComponent<States>().ReactToHit(damage, gameObject);
shootState = 0;
return targetCon.HitEnemyReward(gotHitObj.transform.position);
}
}
if (targetCon.targetTypeInt == (int)Targets.Attack)
{
// while if attack mode
float targetDis = Vector3.Distance(blockContainer.nowBlock.transform.position, transform.position);
if (targetDis <= raySensors.viewDistance)
{
// Debug.DrawRay(new Vector3(0,0,0), viewPoint, Color.red);
if (Vector3.Distance(ray.origin + (ray.direction * targetDis), blockContainer.nowBlock.transform.position) <= blockContainer.nowBlock.firebasesAreaDiameter / 2)
{
// im shooting at target but didn't hit enemy
// Debug.DrawRay(centerRay.origin, viewPoint-centerRay.origin, Color.blue);
return paramContainer.shootTargetAreaReward;
}
}
}
shootState = 0;
return paramContainer.shootReward;
}
else if (shootState != 0 && gunReadyToggle == false)
{
// shoot without ready
shootState = 0;
return paramContainer.shootWithoutReadyReward;
}
else
{
// do not shoot
shootState = 0;
return paramContainer.nonReward;
}
}
private float FacingReward()
{
float nowReward = 0;
bool isFacingtoEnemy = false;
float enemyFacingDistance = 0f;
Vector3 screenCenter = new Vector3(fpsCam.pixelWidth / 2, fpsCam.pixelHeight / 2, 0);
Vector3 screenLeft = new Vector3(0, fpsCam.pixelHeight / 2, 0);
Ray centerRay = fpsCam.ScreenPointToRay(screenCenter);
Ray leftRay = fpsCam.ScreenPointToRay(screenLeft);
// target fireBaseArea Position, turen y to camera's y
Vector3 fireBaseArea = blockContainer.nowBlock.fireBasesAreaObj.transform.position;
fireBaseArea.y = fpsCam.transform.position.y;
// my position, turn y to camera's y
// Debug.DrawRay(fpsCam.transform.position, centerRay.direction * 100, Color.blue);
Vector3 myposition = transform.position;
myposition.y = fpsCam.transform.position.y;
// Target to Agent distance
//Debug.DrawLine(fireBaseArea, myposition, Color.red);
float targetDis = Vector3.Distance(fireBaseArea, myposition);
// point in centerRay and leftRay which distance is targetDis from camera center
Vector3 pointInCenterRay = fpsCam.transform.position + (centerRay.direction * targetDis);
Vector3 pointInLeftRay = fpsCam.transform.position + (leftRay.direction * targetDis);
// center of screen to target's distance
// Debug.DrawLine(pointInCenterRay, fireBaseArea,Color.green);
float camCenterToTarget = Vector3.Distance(pointInCenterRay, fireBaseArea);
// left of screen to target's distance
// Debug.DrawLine(pointInLeftRay, pointInCenterRay, Color.yellow);
float camCenterToViewEdge = Vector3.Distance(pointInLeftRay, pointInCenterRay);
switch (targetCon.targetTypeInt)
{
case (int)Targets.Free:
//free mode
RaycastHit hit;
if (Physics.Raycast(centerRay, out hit, 100))
{
// facing to an enemy
if (hit.collider.tag != myTag && hit.collider.tag != "Wall")
{
nowReward = paramContainer.facingReward;
isFacingtoEnemy = true;
}
}
if (raySensors.inViewEnemies.Count > 0 && !isFacingtoEnemy)
{
// have enemy in view
List<float> projectionDis = new List<float>();
foreach (GameObject theEnemy in raySensors.inViewEnemies)
{
// for each enemy in view
Vector3 projection = Vector3.Project(theEnemy.transform.position - transform.position, (centerRay.direction * 10));
Vector3 verticalToRay = transform.position + projection - theEnemy.transform.position;
projectionDis.Add(verticalToRay.magnitude);
// Debug.Log("enemy!" + verticalToRay.magnitude);
// Debug.DrawRay(transform.position, (centerRay.direction * 100), Color.cyan);
// Debug.DrawRay(transform.position, theEnemy.transform.position - transform.position, Color.yellow);
// Debug.DrawRay(transform.position, projection, Color.blue);
// Debug.DrawRay(theEnemy.transform.position, verticalToRay, Color.magenta);
}
enemyFacingDistance = projectionDis.Min();
if (enemyFacingDistance <= lastEnemyFacingDistance)
{
// closing to enemy
nowReward = 1 / MathF.Sqrt(paramContainer.facingInviewEnemyDisCOEF * enemyFacingDistance + 0.00001f);
}
else
{
nowReward = 0;
}
// enemy in view Reward
lastEnemyFacingDistance = enemyFacingDistance;
if (nowReward >= paramContainer.facingReward) nowReward = paramContainer.facingReward; // limit
if (nowReward <= -paramContainer.facingReward) nowReward = -paramContainer.facingReward; // limit
// Debug.Log("ninimum = " + nowReward);
}
break;
case (int)Targets.Attack:
// attack mode
if (targetDis <= raySensors.viewDistance)
{
// Debug.DrawRay(new Vector3(0,0,0), viewPoint, Color.red);
// while center of screen between target's distance is lower than firebasesAreaDiameter
// while facing to target
if (camCenterToTarget <= blockContainer.nowBlock.firebasesAreaDiameter / 2)
{
// Debug.DrawRay(centerRay.origin, viewPoint-centerRay.origin, Color.blue);
nowReward = paramContainer.facingReward;
}
else
{
// while not facing to target
nowReward = (lastTargetFacingDistance - camCenterToTarget) * paramContainer.facingTargetReward;
}
}
// update lastTargetFacingDistance
lastTargetFacingDistance = camCenterToTarget;
break;
case (int)Targets.Go:
// goto mode
if (camCenterToTarget <= camCenterToViewEdge)
{
// fireArea is in view
nowReward = paramContainer.facingReward;
}
else
{
nowReward = 0;
}
break;
default:
Debug.LogError("Wrong target type");
break;
}
return nowReward;
}
public float RewardCalculate(float sceneReward, float mouseX, float movement, int shootState)
{
float epreward = 0f;
// 击杀reward判断
if (enemyKillCount > 0)
{
for (int i = 0; i < enemyKillCount; i++)
{
// get
epreward += targetCon.KillReward(killEnemyPosition);
}
enemyKillCount = 0;
}
else
{
enemyKillCount = 0;
}
// 射击动作reward判断
epreward += Ballistic(shootState) + sceneReward;
// facing reward
epreward += FacingReward();
// Penalty
// spin penalty
spinRecord.Add(mouseX);
if (spinRecord.Count >= paramContainer.spinRecordMax)
{
spinRecord.RemoveAt(0);
}
float spinPenaltyReward = Math.Abs(spinRecord.ToArray().Sum() * paramContainer.spinPenalty);
if (spinPenaltyReward >= paramContainer.spinPenaltyThreshold)
{
epreward -= spinPenaltyReward;
}
else
{
epreward -= Math.Abs(mouseX) * paramContainer.mousePenalty;
}
// move penalty
if (movement != 0)
{
epreward -= paramContainer.movePenalty;
}
return epreward;
}
#endregion Reward Functions
// GotKill 获得击杀时用于被呼出
public void KillRecord(Vector3 killEnemyPosition)
{
enemyKillCount += 1;

View File

@ -2,9 +2,9 @@
public class EnemyContainer : MonoBehaviour
{
[SerializeField] public GameObject enemyPrefab;
[SerializeField] private GameObject environmentObj;
[SerializeField] private GameObject targetControllerObj;
public GameObject enemyPrefab;
public GameObject environmentObj;
public GameObject targetControllerObj;
private TargetController targetCon;

View File

@ -1,121 +0,0 @@
using System;
using System.Collections.Generic;
using TMPro;
using UnityEngine;
public class CommonParameterContainer : Singleton<CommonParameterContainer>
{
[Header("Env")]
public bool lockMouse = false;
public float damage = 50; // damage to enemy
public float fireRate = 0.5f;
public int timeLimit = 30;
public bool lockCameraX = false;
public bool lockCameraY = true;
public bool oneHotRayTag = true;
public bool spawnAgentInAllMap = true;
public int spinRecordMax = 40;
public float spinPenaltyThreshold = 10;
public float facingInviewEnemyDisCOEF = 0.5f;
public string group1Tag = "Player";
public string group2Tag = "Enemy";
[Header("Dynamic Defaut Rewards")]
//[Tooltip("Hit Enemy reward")]
//public float hitRewardDefault = 60.0f;
[Tooltip("Free mode Hit Enemy reward")]
public float hitTargetRewardDefault = 25f;
//[Tooltip("Enemy down reward")]
//public float killRewardDefault = 60.0f;
[Tooltip("Enemy down in area Reward")]
public float killTargetEnemyRewardDefault = 25f;
[Tooltip("stay in firebasesArea reward")]
public float inAreaRewardDefault = 25.0f;
[Tooltip("free left time bonus reward. ALLR + leftTime * r")]
public float freeTimeBonusPerSec = 1.0f;
[Tooltip("target left time bonus reward. ALLR + leftTime * r")]
public float targetTimeBonusPerSec = 0.5f;
[Tooltip("in area left time bonus reward. ALLR + leftTime * r")]
public float areaTimeBonusPerSec = 0.2f;
[Tooltip("distance reward reward = r*(1-(nowDis/startDis))")]
public float distanceReward = 50.0f;
[Tooltip("facing to Target distance reward reward = r*(1-(nowDis/startDis))")]
public float facingTargetReward = 10.0f;
[Space(10)]
[Tooltip("Goto Win reward")]
public float goWinRewardDefault = 999f;
[Tooltip("Attack Win reward")]
public float attackWinRewardDefault = 999f;
[Tooltip("Defence Win reward")]
public float defenceWinRewardDefault = 999f;
[Tooltip("free Win reward")]
public float freeWinRewardDefault = 999f;
[Header("Static Rewards")]
[Tooltip("Nothing happened reward")]
public float nonReward = -1f;
[Tooltip("Episode Lose reward")]
public float loseReward = -999f;
[Tooltip("Agent Do shoot action reward")]
public float shootReward = -0.5f;
[Tooltip("Hit Not target Enemy reward")]
public float hitNonTargetReward = -5f;
[Tooltip("Not Target Enemy down reward")]
public float killNonTargetReward = -5f;
[Tooltip("Agent Do shoot action but gun is not read")]
public float shootWithoutReadyReward = -1.15f;
[Tooltip("Kill bonus reward stack to nothing happend reward")]
public float killBonusReward = 0.0f;
[Tooltip("Facing to enemy's reward")]
public float facingReward = 5f;
[Tooltip("Shoot at target area but didn't hit enemy")]
public float shootTargetAreaReward = 10f;
[Header("Penalty Rewards")]
[Tooltip("move Penalty Reward")]
public float movePenalty = 0.1f;
[Tooltip("spiiiiiiin Panalty Reward")]
public float spinPenalty = 0.08f;
[Tooltip("while move mouse a little bit's penalty")]
public float mousePenalty = 0.06f;
public SceneBlocksSet scenePrefabSet;
[NonSerialized] public int gameMode; // 0 = trainning mode, 1 = play mode
[NonSerialized] public float attackProb = 0f;
[NonSerialized] public float gotoProb = 0f;
[NonSerialized] public float defenceProb = 0f;
public Dictionary<Targets, List<float>> levelProbs = new Dictionary<Targets, List<float>>();
protected override void Awake()
{
base.Awake();
scenePrefabSet.InitializeSceneBlocksSet();
InitializeLevelProbs();
}
private void Start()
{
Instance.KeepThroughSceneChange();
}
// Initialize Common Parameters
private void InitializeLevelProbs()
{
for(int i = 0; i< scenePrefabSet.targetLevels.Length; i++)
{
Targets nowTarget = scenePrefabSet.targets[i];
levelProbs[nowTarget] = new List<float>();
float levelNum = scenePrefabSet.GetLevelNumber(nowTarget);
float averageProbability = 1f / levelNum;
float lastLevelProbability = 1f - averageProbability * (levelNum - 1);
for (int j = 0; j < levelNum-1; j++)
{
levelProbs[nowTarget].Add(averageProbability);
}
levelProbs[nowTarget].Add(lastLevelProbability);
}
}
}

View File

@ -1,11 +0,0 @@
fileFormatVersion: 2
guid: 34839c2831b759d4a8347ab655b00f36
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -7,18 +7,19 @@
public class MLAgentsCustomController : Agent
{
[SerializeField] private GameObject paramContainerObj;
[SerializeField] private GameObject targetControllerObj;
[SerializeField] private GameObject environmentUIObj;
[SerializeField] private GameObject sideChannelObj;
[SerializeField] private GameObject worldUIControllerObj;
[SerializeField] private GameObject hudObj;
public GameObject paramContainerObj;
public GameObject targetControllerObj;
public GameObject environmentUIObj;
public GameObject sideChannelObj;
public GameObject hudUIObj;
[Header("Env")]
public bool oneHotRayTag = true;
// script
private AgentController agentController;
private ParameterContainer paramContainer;
private CommonParameterContainer commonParamCon;
private TargetController targetController;
private EnvironmentUIControl envUIController;
private HUDController hudController;
@ -26,41 +27,42 @@ public class MLAgentsCustomController : Agent
private RaySensors raySensors;
private MessageBoxController messageBoxController;
private AimBotSideChannelController sideChannelController;
private WorldUIController worldUICon;
private RewardFunction rewardFunction;
// observation
private float[] myObserve = new float[5];
private float[] myObserve = new float[4];
private float[] rayTagResult;
private float[] rayTagResultOnehot;
private float[] rayDisResult;
private float[] targetStates;
private float remainTime;
private float inFireBaseState;
private float inAreaState;
private int endTypeInt;
private int finishedState;
private int step = 0;
private int EP = 0;
private void Start()
{
agentController = transform.GetComponent<AgentController>();
raySensors = transform.GetComponent<RaySensors>();
paramContainer = paramContainerObj.GetComponent<ParameterContainer>();
commonParamCon = CommonParameterContainer.Instance;
targetController = targetControllerObj.GetComponent<TargetController>();
envUIController = environmentUIObj.GetComponent<EnvironmentUIControl>();
hudController = hudObj.GetComponent<HUDController>();
targetUIController = hudObj.GetComponent<TargetUIController>();
messageBoxController = hudObj.GetComponent<MessageBoxController>();
hudController = hudUIObj.GetComponent<HUDController>();
targetUIController = hudUIObj.GetComponent<TargetUIController>();
messageBoxController = hudUIObj.GetComponent<MessageBoxController>();
sideChannelController = sideChannelObj.GetComponent<AimBotSideChannelController>();
rewardFunction = gameObject.GetComponent<RewardFunction>();
worldUICon = worldUIControllerObj.GetComponent<WorldUIController>();
}
#region On episode begin function
public override void OnEpisodeBegin()
{
step = 0;
agentController.UpdateLockMouse();
paramContainer.ResetTimeBonusReward();
if (commonParamCon.gameMode == 0)
if (paramContainer.gameMode == 0)
{
// train mode
Debug.Log("MLAgentCustomController.OnEpisodeBegin: train mode start");
@ -70,7 +72,7 @@ public override void OnEpisodeBegin()
{
Debug.Log("MLAgentCustomController.OnEpisodeBegin: play mode start");
// play mode
targetController.PlayModeInitialize();
targetController.PlayInitialize();
// reset target UI
targetUIController.ClearGamePressed();
}
@ -83,6 +85,10 @@ public override void OnEpisodeBegin()
raySensors.UpdateRayInfo(); // update raycast
}
#endregion On episode begin function
#region Observation sensor function
public override void CollectObservations(VectorSensor sensor)
{
//List<float> enemyLDisList = RaySensors.enemyLDisList;// All Enemy Lside Distances
@ -91,45 +97,35 @@ public override void CollectObservations(VectorSensor sensor)
myObserve[1] = transform.localPosition.y / raySensors.viewDistance;
myObserve[2] = transform.localPosition.z / raySensors.viewDistance;
myObserve[3] = transform.eulerAngles.y / 360f;**/
float obsNum = 0f;
float angleInRadians = transform.eulerAngles.y * Mathf.Deg2Rad;
myObserve[0] = transform.localPosition.x;
myObserve[1] = transform.localPosition.y;
myObserve[2] = transform.localPosition.z;
myObserve[3] = MathF.Sin(angleInRadians);
myObserve[4] = MathF.Cos(angleInRadians);
rayTagResult = raySensors.rayTagResult;// 探测用RayTag类型结果 float[](raySensorNum,1)
rayTagResultOnehot = raySensors.rayTagResultOneHot; // 探测用RayTagonehot结果 List<int>[](raySensorNum*Tags,1)
rayDisResult = raySensors.rayDisResult; // 探测用RayDis距离结果 float[](raySensorNum,1)
myObserve[3] = transform.eulerAngles.y / 36f;
rayTagResult = raySensors.rayTagResult;// 探测用RayTag结果 float[](raySensorNum,1)
rayTagResultOnehot = raySensors.rayTagResultOneHot.ToArray(); // 探测用RayTagonehot结果 List<int>[](raySensorNum*Tags,1)
rayDisResult = raySensors.rayDisResult; // 探测用RayDis结果 float[](raySensorNum,1)
targetStates = targetController.targetState; // (6) targettype, target x,y,z, firebasesAreaDiameter
remainTime = targetController.leftTime;
inFireBaseState = targetController.GetInAreaState();
inAreaState = targetController.GetInAreaState();
agentController.UpdateGunState();
//float[] focusEnemyObserve = RaySensors.focusEnemyInfo;// 最近的Enemy情报 float[](3,1) MinEnemyIndex,x,z
//sensor.AddObservation(allEnemyNum); // 敌人数量 int
sensor.AddObservation(targetController.targetState);// (5) targettype, target x,y,z, firebasesAreaDiameter
sensor.AddObservation(inFireBaseState); // (1)
sensor.AddObservation(targetStates);// (6) targettype, target x,y,z, firebasesAreaDiameter
sensor.AddObservation(inAreaState); // (1)
sensor.AddObservation(remainTime); // (1)
sensor.AddObservation(agentController.gunReadyToggle); // (1) save gun is ready?
sensor.AddObservation(myObserve); // (5)自机位置xyz+朝向 float[](5,1)
// count observation number
obsNum = targetController.targetState.Length+1+1+1+myObserve.Length;
Debug.Log(obsNum);
if (commonParamCon.oneHotRayTag)
sensor.AddObservation(myObserve); // (4)自机位置xyz+朝向 float[](4,1)
if (oneHotRayTag)
{
sensor.AddObservation(rayTagResultOnehot); // 探测用RayTag结果 float[](raySensorNum,1)
obsNum += rayTagResultOnehot.Length;
}
else
{
sensor.AddObservation(rayTagResult);
obsNum += rayTagResult.Length;
}
Debug.Log(obsNum);
sensor.AddObservation(rayDisResult); // 探测用RayDis距离结果 float[](raySensorNum,1)
obsNum += rayDisResult.Length;
envUIController.UpdateStateText(targetController.targetState, inFireBaseState, remainTime, agentController.gunReadyToggle, myObserve, rayTagResultOnehot, rayDisResult);
Debug.Log(obsNum);
sensor.AddObservation(rayDisResult); // 探测用RayDis结果 float[](raySensorNum,1)
envUIController.UpdateStateText(targetStates, inAreaState, remainTime, agentController.gunReadyToggle, myObserve, rayTagResultOnehot, rayDisResult);
/*foreach(float aaa in rayDisResult)
{
Debug.Log(aaa);
@ -140,6 +136,10 @@ public override void CollectObservations(VectorSensor sensor)
//sensor.AddObservation(remainTime); // RemainTime int
}
#endregion Observation sensor function
#region Action received function
public override void OnActionReceived(ActionBuffers actionBuffers)
{
//获取输入
@ -158,27 +158,27 @@ public override void OnActionReceived(ActionBuffers actionBuffers)
//判断结束
float sceneReward = 0f;
float endReward = 0f;
(endTypeInt, sceneReward, endReward) = rewardFunction.CheckOverAndRewards();
float nowReward = rewardFunction.RewardCalculate(sceneReward + endReward, Mouse_X, Math.Abs(vertical) + Math.Abs(horizontal), mouseShoot);
(finishedState, sceneReward, endReward) = targetController.CheckOverAndRewards();
float nowRoundReward = agentController.RewardCalculate(sceneReward + endReward, Mouse_X, Math.Abs(vertical) + Math.Abs(horizontal), mouseShoot);
if (hudController.chartOn)
{
envUIController.UpdateChart(nowReward);
envUIController.UpdateChart(nowRoundReward);
}
else
{
envUIController.RemoveChart();
}
worldUICon.UpdateChart(targetController.targetType, endTypeInt);
//Debug.Log("reward = " + nowReward);
if (endTypeInt != (int)TargetController.EndType.Running)
//Debug.Log("reward = " + nowRoundReward);
if (finishedState != (int)TargetController.EndType.Running)
{
// Win or lose Finished
Debug.Log("Finish reward = " + nowReward);
string targetString = Enum.GetName(typeof(Targets), targetController.targetType);
switch (endTypeInt)
Debug.Log("Finish reward = " + nowRoundReward);
EP += 1;
string targetString = Enum.GetName(typeof(Targets), targetController.targetTypeInt);
switch (finishedState)
{
case (int)TargetController.EndType.Win:
sideChannelController.SendSideChannelMessage("Result", targetString + "|Win");
sideChannelController.SendSideChannelMessage("Result",targetString+ "|Win");
messageBoxController.PushMessage(
new List<string> { "Game Win" },
new List<string> { "green" });
@ -195,16 +195,21 @@ public override void OnActionReceived(ActionBuffers actionBuffers)
Debug.LogWarning("TypeError");
break;
}
SetReward(nowReward);
SetReward(nowRoundReward);
EndEpisode();
}
else
{
// game not over yet
step += 1;
}
SetReward(nowReward);
SetReward(nowRoundReward);
}
#endregion Action received function
#region Heuristic function
public override void Heuristic(in ActionBuffers actionsOut)
{
//-------------------BUILD
@ -262,4 +267,6 @@ public override void Heuristic(in ActionBuffers actionsOut)
//continuousActions[2] = timeLimit;
//^^^^^^^^^^^^^^^^^^^^^^^^^^^^^continuous-Control^^^^^^^^^^^^^^^^^^^^^^
}
#endregion Heuristic function
}

View File

@ -3,16 +3,110 @@
public class ParameterContainer : MonoBehaviour
{
[SerializeField] private GameObject targetConObj;
[SerializeField] private GameObject blockConObj;
[SerializeField] private GameObject agentObj;
[SerializeField] private GameObject hudObj;
public GameObject targetConObj;
public GameObject blockConObj;
public GameObject agentObj;
public GameObject hudObj;
private TargetController targetCon;
private SceneBlockContainer blockCont;
private CommonParameterContainer commonParamCont;
private StartSeneData startSceneData;
private MessageBoxController messageCon;
private float agentDistance;
private int agentInArea;
[Header("Env")]
public bool lockMouse = false;
public float damage = 50; // damage to enemy
public float fireRate = 0.5f;
public int timeLimit = 30;
public bool lockCameraX = false;
public bool lockCameraY = true;
public bool spawnAgentInAllMap = true;
public int spinRecordMax = 40;
public float spinPenaltyThreshold = 10;
public float facingInviewEnemyDisCOEF = 0.5f;
[Header("Dynamic Defaut Rewards")]
//[Tooltip("Hit Enemy reward")]
//public float hitRewardDefault = 60.0f;
[Tooltip("Free mode Hit Enemy reward")]
public float hitTargetRewardDefault = 25f;
//[Tooltip("Enemy down reward")]
//public float killRewardDefault = 60.0f;
[Tooltip("Enemy down in area Reward")]
public float killTargetEnemyRewardDefault = 25f;
[Tooltip("stay in firebasesArea reward")]
public float inAreaRewardDefault = 12f;
[Tooltip("free left time bonus reward. ALLR + leftTime * r")]
public float freeTimeBonusPerSec = 1.0f;
[Tooltip("target left time bonus reward. ALLR + leftTime * r")]
public float targetTimeBonusPerSec = 0.5f;
[Tooltip("in area left time bonus reward. ALLR + leftTime * r")]
public float areaTimeBonusPerSec = 0.2f;
[Tooltip("distance reward reward = r*(1-(nowDis/startDis))")]
public float distanceReward = 50.0f;
[Tooltip("facing to Target distance reward reward = r*(1-(nowDis/startDis))")]
public float facingTargetReward = 10.0f;
[Space(10)]
[Tooltip("Goto Win reward")]
public float goWinRewardDefault = 999f;
[Tooltip("Attack Win reward")]
public float attackWinRewardDefault = 999f;
[Tooltip("Defence Win reward")]
public float defenceWinRewardDefault = 999f;
[Tooltip("free Win reward")]
public float freeWinRewardDefault = 999f;
[Header("Static Rewards")]
[Tooltip("Nothing happened reward")]
public float nonReward = -1f;
[Tooltip("Episode Lose reward")]
public float loseReward = -999f;
[Tooltip("Agent Do shoot action reward")]
public float shootReward = -0.5f;
[Tooltip("Hit Not target Enemy reward")]
public float hitNonTargetReward = -5f;
[Tooltip("Not Target Enemy down reward")]
public float killNonTargetReward = -5f;
[Tooltip("Agent Do shoot action but gun is not read")]
public float shootWithoutReadyReward = -1.15f;
[Tooltip("Kill bonus reward stack to nothing happend reward")]
public float killBonusReward = 0.0f;
[Tooltip("Facing to enemy's reward")]
public float facingReward = 5f;
[Tooltip("Shoot at target area but didn't hit enemy")]
public float shootTargetAreaReward = 10f;
[Header("Penalty Rewards")]
[Tooltip("move Penalty Reward")]
public float movePenalty = 0.1f;
[Tooltip("spiiiiiiin Panalty Reward")]
public float spinPenalty = 0.08f;
[Tooltip("while move mouse a little bit's penalty")]
public float mousePenalty = 0.06f;
[Header("Dynamic Rewards")]
[Tooltip("Free mode Hit Enemy reward")]
public float hitTargetReward = 60.0f;
@ -36,45 +130,63 @@ public class ParameterContainer : MonoBehaviour
[Tooltip("free Win reward")]
public float freeWinReward = 50.0f;
[Tooltip("Scene Prefab Set")]
public SceneBlocksSet scenePrefabSet;
private float targetTimeBonus = 0f;
private float areaTimeBonus = 0f;
private float freeTimeBonus = 0f;
private float targetInAreaTime = 0f;
private float lastFrameTime = 0f;
private float areaTimeBonusPerSec;
private float freeTimeBonusPerSec;
private float targetTimeBonusPerSec;
private int timeLimit = 30;
[System.NonSerialized] public int gameMode; // 0 = trainning mode, 1 = play mode
[System.NonSerialized] public float attackProb = 0f;
[System.NonSerialized] public List<float> attackLevelProbs = new List<float>();
[System.NonSerialized] public float gotoProb = 0f;
[System.NonSerialized] public List<float> gotoLevelProbs = new List<float>();
[System.NonSerialized] public float defenceProb = 0f;
[System.NonSerialized] public List<float> defenceLevelProbs = new List<float>();
private void Start()
{
targetCon = targetConObj.GetComponent<TargetController>();
blockCont = blockConObj.GetComponent<SceneBlockContainer>();
commonParamCont = CommonParameterContainer.Instance;
areaTimeBonusPerSec = commonParamCont.areaTimeBonusPerSec;
freeTimeBonusPerSec = commonParamCont.freeTimeBonusPerSec;
targetTimeBonusPerSec = commonParamCont.targetTimeBonusPerSec;
timeLimit = commonParamCont.timeLimit;
messageCon = hudObj.GetComponent<MessageBoxController>();
areaTimeBonus = areaTimeBonusPerSec * timeLimit;
freeTimeBonus = freeTimeBonusPerSec * timeLimit;
targetTimeBonus = targetTimeBonusPerSec * timeLimit;
// Win Rewards
goWinReward = commonParamCont.goWinRewardDefault;
attackWinReward = commonParamCont.attackWinRewardDefault;
defenceWinReward = commonParamCont.defenceWinRewardDefault;
freeWinReward = commonParamCont.freeWinRewardDefault;
try
{
// try get start scene data
startSceneData = GameObject.Find("StartSceneDataTransfer").GetComponent<StartSeneData>();
messageCon.PushMessage(
new List<string> { "ParameterContainer:", "StartSceneDataTransfer found!" },
new List<string> { "green", "white" });
}
catch
{
// if not found, find dummy StartSeneData
startSceneData = GameObject.Find("StartSceneDataTransferDummy").GetComponent<StartSeneData>();
messageCon.PushMessage(
new List<string> { "ParameterContainer:", "StartSceneDataTransfer not found!Use Dummy." },
new List<string> { "orange" });
}
gameMode = startSceneData.gameMode;
attackProb = startSceneData.attackProb;
attackLevelProbs = startSceneData.attackLevelProbs;
gotoProb = startSceneData.gotoProb;
gotoLevelProbs = startSceneData.gotoLevelProbs;
defenceProb = startSceneData.defenceProb;
defenceLevelProbs = startSceneData.defenceLevelProbs;
scenePrefabSet = startSceneData.scenePrefabSet;
}
private void Update()
{
// get target distance and in area
if (targetCon.targetType is Targets.Go or Targets.Attack)
if (targetCon.targetTypeInt is (int)Targets.Go or (int)Targets.Attack)
{
(_, agentInArea) = blockCont.GetAgentTargetDistanceAndInside(agentObj.transform.position);
(agentDistance, agentInArea) = blockCont.GetAgentTargetDistanceAndInside(agentObj.transform.position);
// attack goto or defence target
if (agentInArea == 1)
{
@ -94,9 +206,15 @@ private void Update()
targetTimeBonus = targetTimeBonusPerSec * targetCon.leftTime;
}
hitTargetReward = commonParamCont.hitTargetRewardDefault + targetTimeBonus;
killTargetEnemyReward = commonParamCont.killTargetEnemyRewardDefault + targetTimeBonus;
inAreaReward = commonParamCont.inAreaRewardDefault + areaTimeBonus;
hitTargetReward = hitTargetRewardDefault + targetTimeBonus;
killTargetEnemyReward = killTargetEnemyRewardDefault + targetTimeBonus;
inAreaReward = inAreaRewardDefault + areaTimeBonus;
// Win Rewards
goWinReward = goWinRewardDefault;
attackWinReward = attackWinRewardDefault;
defenceWinReward = defenceWinRewardDefault;
freeWinReward = freeWinRewardDefault;
}
public void ResetTimeBonusReward()

View File

@ -6,11 +6,11 @@
public class RaySensors : MonoBehaviour
{
[SerializeField] private Camera agentCam;
[SerializeField] private Camera TPSCam;
[SerializeField] private Material lineMeterial;
[SerializeField] private GameObject rayInfoPrefab;
[SerializeField] private GameObject agentCanvas;
public Camera agentCam;
public Camera TPSCam;
public Material lineMeterial;
public GameObject rayInfoPrefab;
public GameObject agentCanvas;
[SerializeField, Range(0, 500)] public float viewDistance = 100; // how long the ray can detect
@ -30,7 +30,7 @@ public class RaySensors : MonoBehaviour
[Header("RayCastResult")]
public float[] rayTagResult;
public float[] rayTagResultOneHot;
public List<float> rayTagResultOneHot;
public float[] rayDisResult;
[System.NonSerialized] public int totalRayNum;
@ -49,7 +49,6 @@ private void Start()
totalRayNum = halfOuterRayNum * 2 + focusRayNum;
rayTagResult = new float[totalRayNum];
rayDisResult = new float[totalRayNum];
rayTagResultOneHot = new float[totalRayNum*ObjectTags.Tags.Count];
linesOBJ = new GameObject[totalRayNum];
lineRenderers = new LineRenderer[totalRayNum];
rayInfoOBJ = new GameObject[totalRayNum];
@ -71,60 +70,85 @@ private void Start()
}
}
private void SingleRaycastUpdate(Ray ray,int rayIndex)
public int TagToInt(string tag)
{
switch (tag)
{
case "Wall":
return 1;
default:
if (tag != myTag)
{
return 2;
}
return 0;
}
}
private void SingleRaycastUpdate(Ray ray, LineRenderer lineRenderer, RayInfoUI rayInfoUI, out float rayTagResult, out float rayDisResult)
{
// get Raycast hit infomation and return Tag and distance
RaycastHit nowHit;
Color rayColor = Color.gray;
Color rayColor = Color.cyan;
float lineLength = viewDistance;
string rayInfoText = "";
Vector3 rayInfoPosition;
if (Physics.Raycast(ray, out nowHit, viewDistance)) // 若在viewDistance范围内有碰撞
{
// Tag
string thisTag = nowHit.collider.tag;
// result update
rayTagResult[rayIndex] = ObjectTags.TagToInt(thisTag);
rayDisResult[rayIndex] = nowHit.distance;
int oneHotIndex = rayIndex * ObjectTags.Tags.Count + ObjectTags.TagToInt(thisTag);
rayTagResultOneHot[oneHotIndex] = 1f;
if (rayTagResult[rayIndex] == ObjectTags.TagToInt("Enemy"))
rayInfoText = nowHit.collider.tag;
rayTagResult = TagToInt(rayInfoText);
rayTagResultOneHot.AddRange(oneHotTags.Encoder(rayInfoText));
rayDisResult = nowHit.distance;
lineLength = rayDisResult;
rayInfoText += "\n" + Convert.ToString(rayDisResult);
// rayDisResult = rayDisResult / viewDistance; // Normalization!
// 输出log
switch (rayTagResult)
{
inViewEnemies.Add(nowHit.transform.gameObject);
case 1:// Wall
rayColor = Color.white;
break;
case 2: // Enemy
rayColor = Color.red;
inViewEnemies.Add(nowHit.transform.gameObject);
break;
case -1: // Hit Nothing
rayColor = Color.gray;
break;
default: // default,got wrong
rayColor = Color.cyan;
break;
}
// ingame info update
lineLength = nowHit.distance;
rayInfoText = thisTag + "\n" + Convert.ToString(nowHit.distance);
rayColor = ObjectTags.TagToCololr(thisTag);
}
else // 若在viewDistance范围无碰撞
{
// Result update
// rayTagResultOneHot keep zero
rayTagResult[rayIndex] = -1;
rayDisResult[rayIndex] = -1;
// Info
rayInfoText = "Empty";
rayTagResultOneHot.AddRange(oneHotTags.Encoder());
rayTagResult = -1f;
rayDisResult = -1f;
//输出log
//Debug.Log(0);
//Debug.Log(0);
}
// draw Info In Game
rayInfoPosition = ray.origin + (ray.direction * lineLength);
if (showInGameRay)
{
DrawLine(ray, lineLength, lineRenderers[rayIndex], rayColor);
DrawLine(ray, lineLength, lineRenderer, rayColor);
}
else
{
TurnOffLine(lineRenderers[rayIndex], rayColor);
TurnOffLine(lineRenderer, rayColor);
}
// drawRay in game
if (showInGameRayInfo) rayInfoUIs[rayIndex].UpdateInfo(rayInfoText, rayInfoPosition, rayColor, TPSCam);
if (showInGameRayInfo) rayInfoUI.UpdateInfo(rayInfoText, rayInfoPosition, rayColor, TPSCam);
// Show log
if (showDebugRay) Debug.DrawRay(ray.origin, ray.direction * viewDistance, rayColor); // drawRay in debug
// Debug.Log(ray.origin + ray.direction);
// Debug.Log(thisRayTagResult);
// Debug.Log(tagToInt(nowHit.collider.thisTag));
// Debug.Log(rayTagResult);
// Debug.Log(tagToInt(nowHit.collider.tag));
}
private void DrawLine(Ray ray, float lineLength, LineRenderer lineRenderer, Color lineColor)
@ -153,31 +177,24 @@ public void UpdateRayInfo()
float focusREdge = agentCam.pixelWidth * (1 + focusRange) / 2;
float camPixelHeight = agentCam.pixelHeight;
inViewEnemies.Clear();
ClearResult();
rayTagResultOneHot.Clear();
for (int i = 0; i < halfOuterRayNum; i++) // create left outside rays; 0 ~ focusLeftEdge
{
Vector3 point = new Vector3(i * focusLEdge / (halfOuterRayNum - 1), camPixelHeight / 2, 0);
Ray nowRay = agentCam.ScreenPointToRay(point);
SingleRaycastUpdate(nowRay,i);
SingleRaycastUpdate(nowRay, lineRenderers[i], rayInfoUIs[i], out rayTagResult[i], out rayDisResult[i]);
}
for (int i = 0; i < halfOuterRayNum; i++) // create right outside rays; focusRightEdge ~ MaxPixelHeight
{
Vector3 point = new Vector3(focusREdge + (i * focusLEdge / (halfOuterRayNum - 1)), camPixelHeight / 2, 0);
Ray nowRay = agentCam.ScreenPointToRay(point);
SingleRaycastUpdate(nowRay, halfOuterRayNum + i);
SingleRaycastUpdate(nowRay, lineRenderers[halfOuterRayNum + i], rayInfoUIs[halfOuterRayNum + i], out rayTagResult[halfOuterRayNum + i], out rayDisResult[halfOuterRayNum + i]);
}
for (int i = 0; i < focusRayNum; i++) // create center focus rays; focusLeftEdge ~ focusLeftEdge
{
Vector3 point = new Vector3(focusLEdge + ((i + 1) * (focusREdge - focusLEdge) / (focusRayNum + 1)), camPixelHeight / 2, 0);
Ray nowRay = agentCam.ScreenPointToRay(point);
SingleRaycastUpdate(nowRay, halfOuterRayNum * 2 + i);
SingleRaycastUpdate(nowRay, lineRenderers[halfOuterRayNum * 2 + i], rayInfoUIs[halfOuterRayNum * 2 + i], out rayTagResult[halfOuterRayNum * 2 + i], out rayDisResult[halfOuterRayNum * 2 + i]);
}
}
private void ClearResult()
{
rayTagResult = new float[totalRayNum];
rayDisResult = new float[totalRayNum];
rayTagResultOneHot = new float[totalRayNum * ObjectTags.Tags.Count];
}
}

View File

@ -1,615 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
using UnityEngine;
public class RewardFunction : MonoBehaviour
{
public enum EndType
{ Win, Lose, Running, Num };
[SerializeField] private GameObject parameterContainerObj;
[SerializeField] private GameObject sceneBlockContainerObj;
[SerializeField] private GameObject targetControllerObj;
[SerializeField] private GameObject environmentUIObj;
[SerializeField] private GameObject enemyContainerObj;
private GameObject agentObj;
private Camera fpsCam;
private CommonParameterContainer commonParamCon;
private SceneBlockContainer sceneBlockCon;
private ParameterContainer paramCon;
private TargetController targetCon;
private EnvironmentUIControl envUICon;
private AgentController agentCon;
private RaySensors raySensors;
private bool firstRewardFlag = false;
private float lastDistance;
private float lastEnemyFacingDistance = 0f; // record last enemy facing minimum distance
private float lastTargetFacingDistance = 0f; // record last target facing minimum distance
private List<float> spinRecord = new List<float>();
private void Start()
{
agentObj = gameObject;
agentCon = agentObj.GetComponent<AgentController>();
fpsCam = agentCon.fpsCam;
commonParamCon = CommonParameterContainer.Instance;
paramCon = parameterContainerObj.GetComponent<ParameterContainer>();
sceneBlockCon = sceneBlockContainerObj.GetComponent<SceneBlockContainer>();
targetCon = targetControllerObj.GetComponent<TargetController>();
envUICon = environmentUIObj.GetComponent<EnvironmentUIControl>();
raySensors = GetComponent<RaySensors>();
}
/// <summary>
/// Calculates the reward value.
/// </summary>
/// <param name="sceneReward">Reward value from the scene.</param>
/// <param name="mouseX">Movement amount of the mouse along the X-axis.</param>
/// <param name="movement">Movement of discrete.</param>
/// <param name="shootState">State of the shooting action.</param>
/// <returns>Returns the calculated total reward value.</returns>
/// <remarks>
/// This method calculates the total reward based on the provided parameters,
/// taking into account rewards for enemy kills, shooting actions, facing reward,
/// and penalties such as spin and movement.
/// </remarks>
public float RewardCalculate(float sceneReward, float mouseX, float movement, int shootState)
{
float epreward = 0f;
// Got kill point reward
if (agentCon.enemyKillCount > 0)
{
for (int i = 0; i < agentCon.enemyKillCount; i++)
{
// get
epreward += KillReward(agentCon.killEnemyPosition);
}
agentCon.enemyKillCount = 0;
}
else
{
agentCon.enemyKillCount = 0;
}
// Shoot action reward
epreward += Ballistic(shootState) + sceneReward;
// facing reward
epreward += FacingReward();
// Penalty
// spin penalty
spinRecord.Add(mouseX);
if (spinRecord.Count >= commonParamCon.spinRecordMax)
{
spinRecord.RemoveAt(0);
}
float spinPenaltyReward = Math.Abs(spinRecord.ToArray().Sum() * commonParamCon.spinPenalty);
if (spinPenaltyReward >= commonParamCon.spinPenaltyThreshold)
{
epreward -= spinPenaltyReward;
}
else
{
epreward -= Math.Abs(mouseX) * commonParamCon.mousePenalty;
}
// move penalty
if (movement != 0)
{
epreward -= commonParamCon.movePenalty;
}
return epreward;
}
/// <summary>
/// Calculates the reward value for shooting actions.
/// </summary>
/// <param name="shootState">State value of the shooting action.</param>
/// <returns>Returns the reward value associated with shooting.</returns>
/// <remarks>
/// This method calculates the reward value based on the shooting state and other related conditions,
/// such as whether the enemy was hit, whether the shot was towards the target area, and whether the gun was ready to shoot.
/// </remarks>
private float Ballistic(int shootState)
{
Vector3 point = new Vector3(fpsCam.pixelWidth / 2, fpsCam.pixelHeight / 2, 0);// start position
Ray ray = fpsCam.ScreenPointToRay(point);
RaycastHit hit;
// Debug.DrawRay(centerRay.origin, centerRay.direction * 100, Color.blue);
// Mouse Pressed
if (shootState != 0 && agentCon.gunReadyToggle == true)
{
agentCon.lastShootTime = Time.time;
if (Physics.Raycast(ray, out hit, 100))
{
if (hit.collider.tag != agentCon.myTag && hit.collider.tag != "Wall" && hit.collider.tag != "Untagged")
{
// kill enemy
GameObject gotHitObj = hit.transform.gameObject;
gotHitObj.GetComponent<States>().ReactToHit(commonParamCon.damage, gameObject);
shootState = 0;
return HitEnemyReward(gotHitObj.transform.position);
}
}
if (targetCon.targetType == Targets.Attack)
{
// while if attack mode
float targetDis = Vector3.Distance(sceneBlockCon.nowBlock.transform.position, transform.position);
if (targetDis <= raySensors.viewDistance)
{
// Debug.DrawRay(new Vector3(0,0,0), viewPoint, Color.red);
if (Vector3.Distance(ray.origin + (ray.direction * targetDis), sceneBlockCon.nowBlock.transform.position) <= sceneBlockCon.nowBlock.firebasesAreaDiameter / 2)
{
// im shooting at target but didn't hit enemy
// Debug.DrawRay(centerRay.origin, viewPoint-centerRay.origin, Color.blue);
return commonParamCon.shootTargetAreaReward;
}
}
}
shootState = 0;
return commonParamCon.shootReward;
}
else if (shootState != 0 && agentCon.gunReadyToggle == false)
{
// shoot without ready
shootState = 0;
return commonParamCon.shootWithoutReadyReward;
}
else
{
// do not shoot
shootState = 0;
return commonParamCon.nonReward;
}
}
/// <summary>
/// Retrieves the reward value based on the character's facing direction.
/// </summary>
/// <returns>Returns the reward value for the facing direction.</returns>
/// <remarks>
/// This method calculates a reward value based on the relationship between the character's facing direction and the target.
/// in free mode, if the character is facing an enemy, the reward is a fixed value
/// in attack mode, the reward depends on the distance between the character and the target, among other factors.
/// </remarks>
private float FacingReward()
{
Vector3 screenCenter = new Vector3(fpsCam.pixelWidth / 2, fpsCam.pixelHeight / 2, 0);
Vector3 screenLeft = new Vector3(0, fpsCam.pixelHeight / 2, 0);
Ray centerRay = fpsCam.ScreenPointToRay(screenCenter);
Ray leftRay = fpsCam.ScreenPointToRay(screenLeft);
switch (targetCon.targetType)
{
case Targets.Free:
return FacingRewardFree(centerRay);
case Targets.Attack:
return FacingRewardAttack(centerRay, leftRay);
case Targets.Go:
return FacingRewardGo(centerRay, leftRay);
case Targets.Stay:
// stay mode has no facing reward
return 0f;
default:
Debug.LogError("Wrong target type");
return 0f;
}
}
private float FacingRewardFree(Ray centerRay)
{
float nowReward = 0;
float enemyFacingDistance = 0f;
bool isFacingtoEnemy = false;
RaycastHit hit;
if (Physics.Raycast(centerRay, out hit, 100))
{
// facing to an enemy
if (hit.collider.tag != agentCon.myTag && hit.collider.tag != "Wall")
{
nowReward = commonParamCon.facingReward;
isFacingtoEnemy = true;
}
}
if (raySensors.inViewEnemies.Count > 0 && !isFacingtoEnemy)
{
// have enemy in view
List<float> projectionDis = new List<float>();
foreach (GameObject theEnemy in raySensors.inViewEnemies)
{
// for each enemy in view
Vector3 projection = Vector3.Project(theEnemy.transform.position - transform.position, (centerRay.direction * 10));
Vector3 verticalToRay = transform.position + projection - theEnemy.transform.position;
projectionDis.Add(verticalToRay.magnitude);
// Debug.Log("enemy!" + verticalToRay.magnitude);
// Debug.DrawRay(transform.position, (centerRay.direction * 100), Color.cyan);
// Debug.DrawRay(transform.position, theEnemy.transform.position - transform.position, Color.yellow);
// Debug.DrawRay(transform.position, projection, Color.blue);
// Debug.DrawRay(theEnemy.transform.position, verticalToRay, Color.magenta);
}
enemyFacingDistance = projectionDis.Min();
if (enemyFacingDistance <= lastEnemyFacingDistance)
{
// closing to enemy
nowReward = 1 / MathF.Sqrt(commonParamCon.facingInviewEnemyDisCOEF * enemyFacingDistance + 0.00001f);
}
else
{
nowReward = 0;
}
// enemy in view Reward
lastEnemyFacingDistance = enemyFacingDistance;
if (nowReward >= commonParamCon.facingReward) nowReward = commonParamCon.facingReward; // limit
if (nowReward <= -commonParamCon.facingReward) nowReward = -commonParamCon.facingReward; // limit
// Debug.Log("ninimum = " + nowReward);
}
return nowReward;
}
private float FacingRewardGo(Ray centerRay, Ray leftRay)
{
float nowReward = 0;
float camCenterToFireBase;
float camCenterToViewEdge;
(camCenterToFireBase, camCenterToViewEdge, _) = CameraCenterToFireBaseAndViewEdge(centerRay, leftRay);
// goto mode
if (camCenterToFireBase <= camCenterToViewEdge)
{
// fireArea is in view
nowReward = commonParamCon.facingReward;
}
else
{
nowReward = 0;
}
return nowReward;
}
private float FacingRewardAttack(Ray centerRay, Ray leftRay)
{
float nowReward = 0;
float camCenterToFireBase;
float targetDis;
(camCenterToFireBase, _, targetDis) = CameraCenterToFireBaseAndViewEdge(centerRay, leftRay);
// attack mode
if (targetDis <= raySensors.viewDistance)
{
// Debug.DrawRay(new Vector3(0,0,0), viewPoint, Color.red);
// while center of screen between target's distance is lower than firebasesAreaDiameter
// while facing to target
if (camCenterToFireBase <= sceneBlockCon.nowBlock.firebasesAreaDiameter / 2)
{
// Debug.DrawRay(centerRay.origin, viewPoint-centerRay.origin, Color.blue);
nowReward = commonParamCon.facingReward;
}
else
{
// while not facing to target
nowReward = (lastTargetFacingDistance - camCenterToFireBase) * commonParamCon.facingTargetReward;
}
}
// update lastTargetFacingDistance
lastTargetFacingDistance = camCenterToFireBase;
return nowReward;
}
private (float, float, float) CameraCenterToFireBaseAndViewEdge(Ray centerRay, Ray leftRay)
{
// target fireBaseArea Position, turen y to camera's y
Vector3 fireBaseArea = sceneBlockCon.nowBlock.fireBasesAreaObj.transform.position;
fireBaseArea.y = fpsCam.transform.position.y;
// my position, turn y to camera's y
// Debug.DrawRay(fpsCam.transform.position, centerRay.direction * 100, Color.blue);
Vector3 myposition = transform.position;
myposition.y = fpsCam.transform.position.y;
// Target to Agent distance
//Debug.DrawLine(fireBaseArea, myposition, Color.red);
float targetDis = Vector3.Distance(fireBaseArea, myposition);
// point in centerRay and leftRay which distance is targetDis from camera center
Vector3 pointInCenterRay = fpsCam.transform.position + (centerRay.direction * targetDis);
Vector3 pointInLeftRay = fpsCam.transform.position + (leftRay.direction * targetDis);
// center of screen to target's distance
// Debug.DrawLine(pointInCenterRay, fireBaseArea,Color.green);
float camCenterToFireBase = Vector3.Distance(pointInCenterRay, fireBaseArea);
// left of screen to target's distance
// Debug.DrawLine(pointInLeftRay, pointInCenterRay, Color.yellow);
float camCenterToViewEdge = Vector3.Distance(pointInLeftRay, pointInCenterRay);
return (camCenterToFireBase, camCenterToViewEdge, targetDis);
}
/// <summary>
/// Checks the game's end state and retrieves rewards.
/// </summary>
/// <returns>A tuple containing the game's end type, current reward, and final reward.
/// 1 = success,2 = overtime,0 = notover</returns>
public (int, float, float) CheckOverAndRewards()
{
int endTypeInt = 0;
float nowReward = 0;
float endReward = 0;
switch (targetCon.targetType)
{
case Targets.Go:
// goto
(endTypeInt, nowReward, endReward) = CheckOverAndRewardsGo();
break;
case Targets.Attack:
// attack
(endTypeInt, nowReward, endReward) = CheckOverAndRewardsAttack();
break;
case Targets.Defence:
//defence
(endTypeInt, nowReward, endReward) = CheckOverAndRewardsDefence();
break;
case Targets.Stay:
// Stay
// endless
nowReward = 0;
endReward = 0;
endTypeInt = (int)EndType.Running;
break;
default:
//free kill
(endTypeInt, nowReward, endReward) = CheckOverAndRewardsFreeKill();
break;
}
envUICon.ShowResult(endTypeInt);
return (endTypeInt, nowReward, endReward);
}
private (int, float, float) CheckOverAndRewardsGo()
{
int endTypeInt = 0;
float nowReward = 0;
float endReward = 0;
float nowDistance = 0;
(nowDistance, targetCon.inArea) = sceneBlockCon.GetAgentTargetDistanceAndInside(agentObj.transform.position);
envUICon.UpdateTargetGauge(sceneBlockCon.nowBlock.firebasesBelong, sceneBlockCon.nowBlock.belongMaxPoint);
float areaTargetReward = GetDistanceReward(nowDistance, targetCon.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 (targetCon.leftTime <= 0)
{
// time out lose
nowReward = areaTargetReward;
endReward = commonParamCon.loseReward;
endTypeInt = (int)EndType.Lose;
}
else
{
// keep on keeping on!
nowReward = areaTargetReward;
endReward = 0;
endTypeInt = (int)EndType.Running;
}
return (endTypeInt, nowReward, endReward);
}
private (int, float, float) CheckOverAndRewardsAttack()
{
int endTypeInt = 0;
float nowReward = 0;
float endReward = 0;
float nowDistance = 0;
(nowDistance, targetCon.inArea) = sceneBlockCon.GetAgentTargetDistanceAndInside(agentObj.transform.position);
envUICon.UpdateTargetGauge(sceneBlockCon.nowBlock.firebasesBelong, sceneBlockCon.nowBlock.belongMaxPoint);
if (sceneBlockCon.nowBlock.GetInAreaNumber(commonParamCon.group2Tag) <= 0 && targetCon.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;
targetCon.targetEnemySpawnFinish = false;
}
else if (targetCon.leftTime <= 0 && targetCon.targetEnemySpawnFinish)
{
// time out lose
nowReward = 0;
endReward = commonParamCon.loseReward;
//nowReward = (paramCon.inAreaReward * inArea) + getSceneReward(nowDistance);
endTypeInt = (int)EndType.Lose;
targetCon.targetEnemySpawnFinish = false;
}
else
{
// keep on keeping on!
// nowReward = (paramCon.inAreaReward * inArea) + getDistanceReward(nowDistance);
nowReward = 0;
endReward = 0;
targetCon.targetEnemySpawnFinish = true;
endTypeInt = (int)EndType.Running;
}
return (endTypeInt, nowReward, endReward);
}
private (int, float, float) CheckOverAndRewardsDefence()
{
// !!! NOT FINISHED YET!!!
int endTypeInt = 0;
float nowReward = 0;
float endReward = 0;
float nowDistance = 0;
(nowDistance, targetCon.inArea) = sceneBlockCon.GetAgentTargetDistanceAndInside(agentObj.transform.position);
envUICon.UpdateTargetGauge(sceneBlockCon.nowBlock.firebasesBelong, sceneBlockCon.nowBlock.belongMaxPoint);
if (targetCon.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 = commonParamCon.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;
}
return (endTypeInt, nowReward, endReward);
}
private (int, float, float) CheckOverAndRewardsFreeKill()
{
int endTypeInt = 0;
float nowReward = 0;
float endReward = 0;
if (enemyContainerObj.transform.childCount <= 0)
{
// win
// nowReward = paramCon.winReward + (paramCon.timeBonusPerSecReward * leftTime);
nowReward = 0;
endReward = paramCon.freeWinReward;
endTypeInt = (int)EndType.Win;
}
else if (targetCon.leftTime <= 0)
{
// lose
//nowReward = paramCon.loseReward;
nowReward = 0;
endReward = commonParamCon.loseReward;
endTypeInt = (int)EndType.Lose;
}
else
{
// keep on keeping on!
nowReward = 0;
endReward = 0;
endTypeInt = (int)EndType.Running;
}
return (endTypeInt, nowReward, endReward);
}
/// <summary>
/// Calculates scene reward based on distance, granting higher rewards for being closer to the target.
/// </summary>
/// <param name="nowDistance">The current distance.</param>
/// <param name="inarea">Whether inside an area.</param>
/// <returns>The reward value calculated based on distance.</returns>
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 = commonParamCon.distanceReward * (lastDistance - nowDistance);
}
lastDistance = nowDistance;
return nowSeneReward;
}
/// <summary>
/// Calculates kill reward based on the position of the killed enemy.
/// </summary>
/// <param name="enemyPosition">The position of the killed enemy.</param>
/// <returns>The reward value calculated based on the kill position.</returns>
public float KillReward(Vector3 enemyPosition)
{
float nowKillReward = 0f;
if (targetCon.targetType == Targets.Attack)
{
// attack mode
(_, int isInArea) = sceneBlockCon.nowBlock.GetDistInArea(enemyPosition);
if (isInArea == 1)
{
// kill in area enemy
nowKillReward = paramCon.killTargetEnemyReward;
}
else
{
nowKillReward = commonParamCon.killNonTargetReward;
}
}
else if (targetCon.targetType == Targets.Free)
{
// free mode hit
nowKillReward = paramCon.killTargetEnemyReward;
}
else
{
// goto & defence
nowKillReward = commonParamCon.killNonTargetReward;
}
return nowKillReward;
}
/// <summary>
/// Calculates hit reward based on the position of the hit enemy and the current mode.
/// </summary>
/// <param name="enemyPosition">The position of the hit enemy.</param>
/// <returns>The reward value calculated based on the hit position and mode.</returns>
public float HitEnemyReward(Vector3 enemyPosition)
{
float nowHitReward = 0f;
if (targetCon.targetType == 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 = commonParamCon.hitNonTargetReward;
}
}
else if (targetCon.targetType == Targets.Free)
{
// free mode hit
nowHitReward = paramCon.hitTargetReward;
}
else
{
// goto & defence
nowHitReward = commonParamCon.hitNonTargetReward;
}
return nowHitReward;
}
}

View File

@ -1,11 +0,0 @@
fileFormatVersion: 2
guid: 04b45691af6143d4d84b5d024d062bf7
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -109,7 +109,7 @@ public void InitBlock(GameObject envObj)
// Debug.Log("SceneBlock.Start: enemyContainerObj not found, get it by name");
enemyContainerObj = transform.Find("EnemyContainer").gameObject;
}
firebasesAreaPosition = fireBasesAreaObj.transform.position;
firebasesAreaPosition = transform.position + fireBasesAreaObj.transform.position;
firebasesAreaScale = fireBasesAreaObj.transform.localScale.x;
firebasesAreaDiameter = firebasesAreaScale * blockSize;
firebasesBelong = -belongMaxPoint;

View File

@ -2,12 +2,10 @@
public class SceneBlockContainer : MonoBehaviour
{
[SerializeField]
private float sceneSize = 10f;
[SerializeField]
private GameObject environmentObj;
[SerializeField]
private GameObject hudObj;
public float sceneSize = 10f;
public GameObject environmentObj;
public GameObject parameterContainerObj;
public GameObject hudObj;
// public GameObject[] attackBlockPrefabs = new GameObject[1];
// public GameObject[] goBlockPrefabs = new GameObject[1];
@ -15,11 +13,11 @@ public class SceneBlockContainer : MonoBehaviour
public SceneBlock nowBlock;
private GameObject nowBlockObj;
private CommonParameterContainer commonParamCon;
private ParameterContainer paramCon;
private void Start()
{
commonParamCon = CommonParameterContainer.Instance;
paramCon = parameterContainerObj.GetComponent<ParameterContainer>();
}
/// <summary>
@ -42,7 +40,7 @@ public void CreateNewBlock(Targets targetType, int level, int blockType, Vector3
DestroyBlock();
}
// choose target type
nowBlockObj = Instantiate(commonParamCon.scenePrefabSet.GetPrefab(level, blockType, targetType), blockPosition + environmentObj.transform.position, Quaternion.identity, transform);
nowBlockObj = Instantiate(paramCon.scenePrefabSet.GetPrefab(level, blockType, targetType), blockPosition + environmentObj.transform.position, Quaternion.identity, transform);
nowBlock = nowBlockObj.GetComponent<SceneBlock>();
nowBlock.group1Tag = tag1;
nowBlock.group2Tag = tag2;

View File

@ -5,12 +5,14 @@
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;
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;
@ -20,6 +22,11 @@ public class TargetController : MonoBehaviour
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;
@ -35,10 +42,10 @@ public class TargetController : MonoBehaviour
[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 targetTypeInt;
[System.NonSerialized] public int gotoLevelNum;
[System.NonSerialized] public int attackLevelNum;
[System.NonSerialized] public float[] targetState = new float[5];
public float[] targetState = new float[6];
public enum EndType
{ Win, Lose, Running, Num };
@ -46,20 +53,21 @@ public enum EndType
[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 int inArea = 0;
public Vector3 targetPosition;
private bool firstRewardFlag = true;
public bool targetEnemySpawnFinish = false;
private bool targetEnemySpawnFinish = false;
private SceneBlockContainer sceneBlockCon;
private EnemyContainer enemyCon;
private EnvironmentUIControl envUICon;
private CommonParameterContainer commonParamCon;
private ParameterContainer paramCon;
private CharacterController agentCharaCon;
private WorldUIController worldUICon;
private HUDController hudCon;
private MessageBoxController messageBoxCon;
@ -69,19 +77,20 @@ public enum EndType
// Start is called before the first frame update
private void Start()
{
commonParamCon = CommonParameterContainer.Instance;
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 = commonParamCon.gameMode;
attackProb = commonParamCon.attackProb;
gotoProb = commonParamCon.gotoProb;
defenceProb = commonParamCon.defenceProb;
gamemode = paramCon.gameMode;
attackProb = paramCon.attackProb;
gotoProb = paramCon.gotoProb;
defenceProb = paramCon.defenceProb;
// initialize spawn area
minEnemyAreaX = edgeLeft.transform.localPosition.x + 1.0f;
@ -96,8 +105,8 @@ private void Start()
freeProb = 1 - attackProb - gotoProb - defenceProb;
targetNum = (int)Targets.Num;
gotoLevelNum = commonParamCon.scenePrefabSet.GetLevelNumber(Targets.Go);
attackLevelNum = commonParamCon.scenePrefabSet.GetLevelNumber(Targets.Attack);
gotoLevelNum = paramCon.scenePrefabSet.GetLevelNumber(Targets.Go);
attackLevelNum = paramCon.scenePrefabSet.GetLevelNumber(Targets.Attack);
if (freeProb < 0)
{
Debug.LogError("TargetController.Start: target percentage wrong");
@ -121,13 +130,13 @@ private void Update()
// if gamemode is play, then time will keep paramCon.timeLimit
if (gamemode == 1)
{
leftTime = commonParamCon.timeLimit;
leftTime = paramCon.timeLimit;
// print out time
// Debug.Log("Playing Time: " + leftTime);
}
else
{
leftTime = commonParamCon.timeLimit - Time.time + startTime;
leftTime = paramCon.timeLimit - Time.time + startTime;
}
}
@ -143,13 +152,13 @@ private void Update()
public void RollNewScene()
{
startTime = Time.time;// Reset StartTime as now time
leftTime = commonParamCon.timeLimit - Time.time + startTime;
leftTime = paramCon.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;
targetTypeInt = (int)Targets.Go;
RandomSpawnSceneBlock(Targets.Go);
// set startDistance
firstRewardFlag = true;
@ -158,7 +167,7 @@ public void RollNewScene()
{
// attack target spawn
Debug.Log("ATTACK Mode Start");
targetType = Targets.Attack;
targetTypeInt = (int)Targets.Attack;
RandomSpawnSceneBlock(Targets.Attack);
// set startDistance
firstRewardFlag = true;
@ -168,7 +177,7 @@ public void RollNewScene()
{
// defence target spawn
Debug.Log("DEFENCE Mode Start");
targetType = Targets.Defence;
targetTypeInt = (int)Targets.Defence;
RandomSpawnSceneBlock(Targets.Defence);
// set startDistance
firstRewardFlag = true;
@ -176,14 +185,14 @@ public void RollNewScene()
else
{
Debug.Log("Free Mode Start");
targetType = Targets.Free;
targetTypeInt = (int)Targets.Free;
enemyCon.DestroyAllEnemys();
enemyCon.RandomInitEnemys(hudCon.enemyNum);
MoveAgentToSpwanArea();
sceneBlockCon.DestroyBlock();
}
UpdateTargetStates();
envUICon.UpdateTargetType(targetType);
envUICon.UpdateTargetType(targetTypeInt);
}
#region Agent Move Method
@ -196,7 +205,7 @@ private void MoveAgentToSpwanArea()
{
float randX = UnityEngine.Random.Range(minAgentAreaX, maxAgentAreaX); ;
float randZ = 0f;
if (commonParamCon.spawnAgentInAllMap)
if (paramCon.spawnAgentInAllMap)
{
// spawn agent in all around map
randZ = UnityEngine.Random.Range(minAgentAreaZ, maxEnemyAreaZ);
@ -234,6 +243,8 @@ public void MoveAgentTo(Vector3 position)
#endregion Agent Move Method
#region Random SceneBlock Spawn Method
/// <summary>
/// Randomly spawns a scene block based on the target type.
/// 根据目标类型随机生成场景块。
@ -246,8 +257,8 @@ public void MoveAgentTo(Vector3 position)
private void RandomSpawnSceneBlock(Targets targetType)
{
randLevel = RollRandomLevelIndex(targetType);
randBlockType = Random.Range(0, commonParamCon.scenePrefabSet.GetBlockNumber(randLevel,targetType));
sceneBlockSize = commonParamCon.scenePrefabSet.GetBlockSize(randLevel, randBlockType, targetType);
randBlockType = Random.Range(0, paramCon.scenePrefabSet.GetBlockNumber(randLevel,targetType));
sceneBlockSize = paramCon.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);
@ -255,32 +266,268 @@ private void RandomSpawnSceneBlock(Targets targetType)
// init scene block
sceneBlockCon.DestroyBlock();
sceneBlockCon.CreateNewBlock(targetType, randLevel, randBlockType, targetPosition, commonParamCon.group1Tag, commonParamCon.group2Tag);
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
/// <summary>
/// Spawns a scene block at a specified position.
/// Checks the game's end state and retrieves rewards.
/// </summary>
/// <param name="targetType">The type of the target, determining the type of block to generate.</param>
/// <param name="level">The level of the block, affecting its properties.</param>
/// <param name="blockNum">The number of the block, used to distinguish different blocks of the same level.</param>
/// <param name="blockPosition">The position of the block in the scene.</param>
/// <remarks>
/// 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.
/// </remarks>
private void SpawnSceneBlock(Targets targetType, int level,int blockNum, Vector3 blockPosition)
/// <returns>A tuple containing the game's end type, current reward, and final reward.
/// 1 = success,2 = overtime,0 = notover</returns>
public (int, float, float) CheckOverAndRewards()
{
sceneBlockCon.DestroyBlock();
sceneBlockCon.CreateNewBlock(targetType, level, blockNum, blockPosition, commonParamCon.group1Tag, commonParamCon.group2Tag);
sceneBlockCon.InitializeBlock(environmentObj);
int endTypeInt = 0;
float nowReward = 0;
float endReward = 0;
float nowDistance = 0f;
switch (targetTypeInt)
{
case (int)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)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)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)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);
}
/// <summary>
/// Calculates scene reward based on distance, granting higher rewards for being closer to the target.
/// 根据距离计算场景奖励,靠近目标则获得更高奖励。
/// </summary>
/// <param name="nowDistance">The current distance.</param>
/// <param name="inarea">Whether inside an area.</param>
/// <returns>The reward value calculated based on distance.</returns>
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;
}
/// <summary>
/// Calculates kill reward based on the position of the killed enemy.
/// 根据击杀的敌人位置计算击杀奖励。
/// </summary>
/// <param name="enemyPosition">The position of the killed enemy.</param>
/// <returns>The reward value calculated based on the kill position.</returns>
public float KillReward(Vector3 enemyPosition)
{
float nowKillReward = 0f;
if (targetTypeInt == (int)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)Targets.Free)
{
// free mode hit
nowKillReward = paramCon.killTargetEnemyReward;
}
else
{
// goto & defence
nowKillReward = paramCon.killNonTargetReward;
}
return nowKillReward;
}
/// <summary>
/// Calculates hit reward based on the position of the hit enemy and the current mode.
/// 根据击中的敌人位置和当前模式计算击中Reward。
/// </summary>
/// <param name="enemyPosition">The position of the hit enemy.</param>
/// <returns>The reward value calculated based on the hit position and mode.</returns>
public float HitEnemyReward(Vector3 enemyPosition)
{
float nowHitReward = 0f;
if (targetTypeInt == (int)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)Targets.Free)
{
// free mode hit
nowHitReward = paramCon.hitTargetReward;
}
else
{
// goto & defence
nowHitReward = paramCon.hitNonTargetReward;
}
return nowHitReward;
}
#endregion Reward function
#region Play Mode Method
/// <summary>
@ -295,40 +542,46 @@ private void SpawnSceneBlock(Targets targetType, int level,int blockNum, Vector3
/// 该方法用于初始化游戏播放模式包括设置目标类型、更新目标状态、更新UI显示、
/// 将代理移动到生成区域、销毁所有敌人和场景块。
/// </remarks>
public void PlayModeInitialize()
public void PlayInitialize()
{
targetType = Targets.Stay;
targetTypeInt = (int)Targets.Stay;
UpdateTargetStates();
envUICon.UpdateTargetType(targetType);
envUICon.UpdateTargetType(targetTypeInt);
MoveAgentToSpwanArea();
enemyCon.DestroyAllEnemys();
sceneBlockCon.DestroyBlock();
}
/// <summary>
/// Changes the target type in play mode and updates the game environment accordingly.
/// </summary>
/// <param name="newTargetType">The new target type to be set.</param>
/// <param name="spawnPosition">Spawn position for scene blocks.</param>
/// <param name="level">Level parameter for spawning scene blocks.</param>
/// <param name="blockNum">Number of blocks to spawn.</param>
/// <remarks>
/// 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.
/// </remarks>
public void PlayTargetChange(Targets newTargetType, Vector3? spawnPosition = null, int level = 0, int blockNum = 0)
// change to attack mode
public void AttackModeChange(Vector3 targetPosition)
{
targetType = newTargetType;
if(spawnPosition.HasValue)
{
SpawnSceneBlock(targetType, level, blockNum, spawnPosition.Value);
UpdateTargetStates(spawnPosition.Value);
}
else
{
UpdateTargetStates();
}
envUICon.UpdateTargetType(targetType);
targetTypeInt = (int)Targets.Attack;
UpdateTargetStates(targetPosition);
envUICon.UpdateTargetType(targetTypeInt);
}
// change to free mode
public void FreeModeChange()
{
targetTypeInt = (int)Targets.Free;
UpdateTargetStates();
envUICon.UpdateTargetType(targetTypeInt);
}
// change to goto mode
public void GotoModeChange(Vector3 targetPosition)
{
targetTypeInt = (int)Targets.Go;
UpdateTargetStates(targetPosition);
envUICon.UpdateTargetType(targetTypeInt);
}
// change to stay mode
public void StayModeChange()
{
targetTypeInt = (int)Targets.Stay;
UpdateTargetStates();
envUICon.UpdateTargetType(targetTypeInt);
}
#endregion Play Mode Method
@ -341,12 +594,12 @@ public void PlayTargetChange(Targets newTargetType, Vector3? spawnPosition = nul
private void UpdateTargetStates(Vector3? targetPosition = null)
{
// targettype, x,y,z, firebasesAreaDiameter
targetState[0] = (int)targetType;
targetState[0] = targetTypeInt;
if (targetPosition != null)
{
this.targetPosition = (Vector3)targetPosition;
}
if (targetType == (int)Targets.Free || targetType == Targets.Stay)
if (targetTypeInt == (int)Targets.Free || targetTypeInt == (int)Targets.Stay)
{
for (int i = 1; i < targetState.Length; i++)
// set target position state to 0
@ -354,12 +607,11 @@ private void UpdateTargetStates(Vector3? targetPosition = null)
}
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[1] = this.targetPosition.x;
targetState[2] = this.targetPosition.y;
targetState[3] = this.targetPosition.z;
targetState[4] = sceneBlockCon.nowBlock.firebasesAreaDiameter;
targetState[5] = sceneBlockCon.nowBlock.belongRatio;
}
}
@ -370,7 +622,7 @@ private void UpdateTargetStates(Vector3? targetPosition = null)
/// <returns>The in-area state.</returns>
public int GetInAreaState()
{
if (targetType == Targets.Go)
if (targetTypeInt == (int)Targets.Go)
{
return inArea;
}
@ -388,9 +640,26 @@ public int GetInAreaState()
/// <returns>A random level index.</returns>
public int RollRandomLevelIndex(Targets target)
{
Debug.Log(target);
List<float> targetProbs;
targetProbs = commonParamCon.levelProbs[target];
switch (target)
{
case Targets.Attack:
targetProbs = paramCon.attackLevelProbs;
break;
case Targets.Go:
targetProbs = paramCon.gotoLevelProbs;
break;
case 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);
@ -406,7 +675,7 @@ public int RollRandomLevelIndex(Targets target)
// If no level was returned, log an error and return -1
messageBoxCon.PushMessage(
new List<string> { "[ERROR]TargetController:RollRandomLevelIndex", "level index out of range" },
new List<string> { "[ERROR]TargetController:RandomLevel", "level index out of range" },
new List<string> { "orange" });
return -1;
}

View File

@ -1,27 +0,0 @@
using System.Collections.Generic;
using UnityEngine;
public static class ObjectTags
{
public static List<string> Tags = new List<string>() { "Wall", "Enemy", "Player" };
public static int TagToInt(string tag)
{
return Tags.IndexOf(tag);
}
public static Color TagToCololr(string tag)
{
switch (tag)
{
case "Wall":
return Color.white;
case "Enemy":
return Color.red;
case "Player":
return Color.green;
default:
return Color.cyan;
}
}
}

View File

@ -1,11 +0,0 @@
fileFormatVersion: 2
guid: 38fdb6190ddd36f448389fc04284710d
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -6,28 +6,21 @@ public class MouseInMap : MonoBehaviour
public float targetDistanceThreshold = 6f;
public float enemyDistanceThreshold = 1f;
[SerializeField]
private Camera playCamera;
[SerializeField]
private GameObject AgentObj;
[SerializeField]
private GameObject environmentObj;
[SerializeField]
private GameObject mousePreviewObj;
[SerializeField]
private GameObject enemyContainerObj;
[SerializeField]
private GameObject sceneBlockContainerObj;
[SerializeField]
private GameObject targetControllerObj;
[SerializeField]
private GameObject HUDObj;
public Camera playCamera;
public GameObject AgentObj;
public GameObject environmentObj;
public GameObject mousePreviewObj;
public GameObject enemyContainerObj;
public GameObject sceneBlockContainerObj;
public GameObject targetControllerObj;
public GameObject parameterContainerObj;
public GameObject HUDObj;
private Vector3 nowHitPosition = Vector3.zero;
private Vector3 nowHitPositionRelative = Vector3.zero;
private LayerMask groundMask;
private int blockNum;
private CommonParameterContainer commonParamCon;
private ParameterContainer paramCon;
private GameObject previewModel;
private TargetController targetCon;
private MousePreview mousePreviewCon;
@ -54,7 +47,7 @@ public enum MouseMode
private void Start()
{
commonParamCon = CommonParameterContainer.Instance;
paramCon = parameterContainerObj.GetComponent<ParameterContainer>();
groundMask = LayerMask.GetMask("Ground");
targetCon = targetControllerObj.GetComponent<TargetController>();
mousePreviewCon = mousePreviewObj.GetComponent<MousePreview>();
@ -87,7 +80,9 @@ private void Update()
else
{
// if agent or enemy is not nearby, create new block
targetCon.PlayTargetChange(Targets.Attack, nowHitPositionRelative, blockLevel, blockNum);
sceneBlockCon.CreateNewBlock(Targets.Attack, blockLevel, blockNum, nowHitPositionRelative);
sceneBlockCon.InitializeBlock(environmentObj);
targetCon.AttackModeChange(nowHitPositionRelative);
ChangeMouseMode(MouseMode.Default);
}
break;
@ -102,7 +97,9 @@ private void Update()
else
{
// if agent or enemy is not nearby, create new block
targetCon.PlayTargetChange(Targets.Go, nowHitPositionRelative, blockLevel, blockNum);
sceneBlockCon.CreateNewBlock(Targets.Go, blockLevel, blockNum, nowHitPositionRelative);
sceneBlockCon.InitializeBlock(environmentObj);
targetCon.GotoModeChange(nowHitPositionRelative);
ChangeMouseMode(MouseMode.Default);
}
break;
@ -143,7 +140,7 @@ public void ChangeMouseMode(MouseMode mouseMode, int level = -1, int blockNum =
// while blockLevel is not set, send error message
messageCon.PushMessage(new List<string> { "[ERROR]MouseInMap:ChangeMouseMode:", "Level not set!", "mouseMode=", mouseMode.ToString() },
new List<string> { messageCon.errorColor });
blockLevel = commonParamCon.scenePrefabSet.GetLevelNumber(nowTargetType);
blockLevel = paramCon.scenePrefabSet.GetLevelNumber(nowTargetType);
}
else
{
@ -155,7 +152,7 @@ public void ChangeMouseMode(MouseMode mouseMode, int level = -1, int blockNum =
if (blockNum < 0)
{
// while blockNum is not set, random choose block type
this.blockNum = Random.Range(0, commonParamCon.scenePrefabSet.GetBlockNumber(blockLevel, nowTargetType));
this.blockNum = Random.Range(0, paramCon.scenePrefabSet.GetBlockNumber(blockLevel, nowTargetType));
}
else
{
@ -163,7 +160,7 @@ public void ChangeMouseMode(MouseMode mouseMode, int level = -1, int blockNum =
this.blockNum = blockNum;
}
// set previewModel
previewModel = commonParamCon.scenePrefabSet.GetPrefab(blockLevel, this.blockNum, nowTargetType);
previewModel = paramCon.scenePrefabSet.GetPrefab(blockLevel, this.blockNum, nowTargetType);
mousePreviewCon.ChangePreviewTo(previewModel);
break;

View File

@ -4,23 +4,22 @@
public class PlayerCamera : MonoBehaviour
{
private float normalSpeed = 0.0035f;
private float shiftSpeed = 0.06f;
private float zoomSpeed = -10.0f;
private float rotateSpeed = 0.1f;
public float normalSpeed = 0.0035f;
public float shiftSpeed = 0.06f;
public float zoomSpeed = -10.0f;
public float rotateSpeed = 0.1f;
private float maxHeight = 40f;
private float minHeight = 6f;
public float maxHeight = 40f;
public float minHeight = 6f;
private Vector2 startMouseP;
private Vector2 dragMouseP;
private Vector3 defaultCamPosition;
public Vector2 startMouseP;
public Vector2 dragMouseP;
private float speed;
// Start is called before the first frame update
void Start()
{
defaultCamPosition = transform.position;
}
// Update is called once per frame
@ -38,12 +37,6 @@ void Update()
zoomSpeed = 10.0f;
}
// reset camera position
if (Input.GetKeyDown(KeyCode.R))
{
transform.position = defaultCamPosition;
}
float hsp = transform.position.y * speed * Input.GetAxis("Horizontal"); // horizontal movement speed
float vsp = transform.position.y * speed * Input.GetAxis("Vertical"); // vertical movement speed
float scrollSp = Mathf.Log(transform.position.y) * -zoomSpeed * Input.GetAxis("Mouse ScrollWheel"); // scroll speed

View File

@ -1,40 +0,0 @@
using UnityEngine;
public class Singleton<T> : MonoBehaviour where T : MonoBehaviour
{
private static T _instance;
public static T Instance
{
get
{
if (_instance == null)
{
_instance = (T)FindObjectOfType(typeof(T));
if (_instance == null)
{
Debug.LogError("An instance of " + typeof(T) + " is needed in the scene, but there is none.");
}
}
return _instance;
}
}
protected virtual void Awake()
{
if (_instance == null)
{
_instance = this as T;
}
else
{
Destroy(gameObject);
}
}
// keep this instance through scene change
public void KeepThroughSceneChange()
{
DontDestroyOnLoad(this.gameObject);
}
}

View File

@ -1,11 +0,0 @@
fileFormatVersion: 2
guid: a056057ed828e274b954ff7b8b2cb2f2
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -1,7 +1,7 @@
using System.Collections.Generic;
using UnityEngine;
public class StartSeneDataremove : MonoBehaviour
public class StartSeneData : MonoBehaviour
{
[Header("Game mode")]
public int gameMode = 0;// default trainning mode

View File

@ -12,8 +12,8 @@
[CreateAssetMenu(menuName = "All Scene Prefab Set")]
public class SceneBlocksSet : ScriptableObject
{
public TargetLevelsSet[] targetLevels = new TargetLevelsSet[2];
public Targets[] targets = new Targets[2];
public TargetLevelsSet[] targetLevels = new TargetLevelsSet[3];
public Targets[] targets = new Targets[3];
private GameObject hudObj;
private MessageBoxController messageBoxController;

View File

@ -7,10 +7,10 @@
public class ButtonActivateColorChanger : MonoBehaviour
{
[SerializeField] private List<Button> clickableButton = new List<Button>();
[SerializeField] private List<Button> unclickableButton = new List<Button>();
public List<Button> clickableButton = new List<Button>();
public List<Button> unclickableButton = new List<Button>();
[SerializeField] private float colorChangeSpeed = 0.1f;
public float colorChangeSpeed = 0.1f;
public bool clickable = true;

View File

@ -7,17 +7,17 @@
public class EnvironmentUIControl : MonoBehaviour
{
[SerializeField] private GameObject targetControllerObj;
[SerializeField] private GameObject parameterContainerObj;
[SerializeField] private GameObject groundCanvasObj;
[SerializeField] private GameObject chartObj;
[SerializeField] private GameObject HUDObj;
[SerializeField] private TextMeshProUGUI remainTimeText;
[SerializeField] private TextMeshProUGUI targetTypeText;
[SerializeField] private TextMeshProUGUI winLoseText;
[SerializeField] private TextMeshProUGUI stateText;
[SerializeField] private float resultTimeout = 1f;
[SerializeField] private GameObject gaugeImgObj;
public GameObject targetControllerObj;
public GameObject parameterContainerObj;
public GameObject groundCanvasObj;
public GameObject chartObj;
public GameObject HUDObj;
public TextMeshProUGUI remainTimeText;
public TextMeshProUGUI targetTypeText;
public TextMeshProUGUI winLoseText;
public TextMeshProUGUI stateText;
public float resultTimeout = 1f;
public GameObject gaugeImgObj;
private StringBuilder stateBuilder = new StringBuilder();
private LineChart realTimeRewardChart = null;
@ -131,31 +131,31 @@ public void UpdateTargetGauge(float firebasesBelong, float belongMaxPoint)
}
// update targetType text
public void UpdateTargetType(Targets targetInt)
public void UpdateTargetType(int targetInt)
{
switch (targetInt)
{
case Targets.Go:
case (int)Targets.Go:
targetTypeText.text = "GOTO";
targetTypeText.color = Color.blue;
break;
case Targets.Attack:
case (int)Targets.Attack:
targetTypeText.text = "Attack!";
targetTypeText.color = Color.red;
break;
case Targets.Defence:
case (int)Targets.Defence:
targetTypeText.text = "Defence";
targetTypeText.color = Color.green;
break;
case Targets.Free:
case (int)Targets.Free:
targetTypeText.text = "Free";
targetTypeText.color = Color.yellow;
break;
case Targets.Stay:
case (int)Targets.Stay:
targetTypeText.text = "Stay";
targetTypeText.color = Color.white;
break;
@ -169,10 +169,10 @@ public void UpdateTargetType(Targets targetInt)
// update state text
// public TextMeshProUGUI stateText;
// targetState[0] = targetType;
// targetState[1] = targetEndPosition.x / raySensors.viewDistance; // normalization
// targetState[2] = targetEndPosition.y / raySensors.viewDistance;
// targetState[3] = targetEndPosition.z / raySensors.viewDistance;
// targetState[0] = targetTypeInt;
// targetState[1] = targetPosition.x / raySensors.viewDistance; // normalization
// targetState[2] = targetPosition.y / raySensors.viewDistance;
// targetState[3] = targetPosition.z / raySensors.viewDistance;
// targetState[4] = blockCont.thisBlock.firebasesAreaDiameter / raySensors.viewDistance;
// targetState[5] = blockCont.thisBlock.belongRatio;
// float[] myObserve = { transform.localPosition.x/raySensors.viewDistance, transform.localPosition.y / raySensors.viewDistance, transform.localPosition.z / raySensors.viewDistance, transform.eulerAngles.y/360f }
@ -190,6 +190,8 @@ public void UpdateStateText(float[] targetStates, float inAreaState, float remai
stateBuilder.Append(targetStates[3]);
stateBuilder.Append("\r\nTargetDiameter:");
stateBuilder.Append(targetStates[4]);
stateBuilder.Append("\r\nTargetBelongRatio:");
stateBuilder.Append(targetStates[5]);
stateBuilder.Append("\r\nInArea:");
stateBuilder.Append(inAreaState);
stateBuilder.Append("\r\nRemainTime:");
@ -197,10 +199,9 @@ public void UpdateStateText(float[] targetStates, float inAreaState, float remai
stateBuilder.Append("\r\nGunReady:");
stateBuilder.Append(gunReadyToggle);
stateBuilder.Append("\r\nMyPosition:");
stateBuilder.Append(myObserve[0]+"_").Append(myObserve[1]+"_").Append(myObserve[2]);
stateBuilder.Append(myObserve[0]).Append(myObserve[1]).Append(myObserve[2]);
stateBuilder.Append("\r\nMyRotation:");
stateBuilder.Append(myObserve[3]+"_").Append(myObserve[4]);
stateBuilder.Append(myObserve[3]);
stateText.text = stateBuilder.ToString();
}

View File

@ -7,11 +7,11 @@
public class HUDController : MonoBehaviour
{
public bool chartOn = false;
[SerializeField] private GameObject sideChannelObj;
[SerializeField] private Toggle chartOnToggleObj;
[SerializeField] private Button saveModelButton;
[SerializeField] private TMP_InputField chartOnTimeOutInputObj;
[SerializeField] private TMP_InputField enemyNumInputObj;
public GameObject sideChannelObj;
public Toggle chartOnToggleObj;
public Button saveModelButton;
public TMP_InputField chartOnTimeOutInputObj;
public TMP_InputField enemyNumInputObj;
public float chartOnTimeOut = 1;
public int enemyNum = 3;
public float chartOnTimeOutDefault = 120f;
@ -44,6 +44,18 @@ public void OnChartOnToggleChange()
chartOn = chartOnToggleObj.isOn;
}
public void OnEnemyNumTextChange()
{
try
{
enemyNum = Math.Abs(int.Parse(enemyNumInputObj.GetComponent<TMP_InputField>().text));
}
catch (NullReferenceException)
{
enemyNum = 3;
}
}
public void OnChartTimeOutTextChange()
{
try

View File

@ -6,7 +6,7 @@
public class LevelButton : MonoBehaviour
{
public int level;
[SerializeField] private TextMeshProUGUI levelText;
public TextMeshProUGUI levelText;
public void Initialization(int level)
{
this.level = level;

View File

@ -8,9 +8,9 @@ public class LevelPanel : MonoBehaviour
{
private int levelNum = 0;
private float buttonHeight = 30;
[SerializeField] private TargetUIController.PrimaryButtonType primaryButtonType;
[SerializeField] private GameObject levelButtonPrefab;
[SerializeField] private GameObject hudObj;
public TargetUIController.PrimaryButtonType primaryButtonType;
public GameObject levelButtonPrefab;
public GameObject hudObj;
private TargetUIController targetUIController;
public Vector2 defaultPosition = Vector2.zero;
public Vector2 targetPosition = Vector2.zero;

View File

@ -1,23 +0,0 @@
using System.Collections.Generic;
using UnityEngine;
public class LevelProbabilityPanel : MonoBehaviour
{
[SerializeField] private GameObject singleTargetLevelProbabilityPanel;
[SerializeField] private GameObject startSceneData;
private SceneBlocksSet scenePrefabSet;
public List<TargetLevelProbabilityPanel> targetLevelProbabilityPanel = new List<TargetLevelProbabilityPanel>();
private void Start()
{
scenePrefabSet = CommonParameterContainer.Instance.scenePrefabSet;
for (int i = 0; i < scenePrefabSet.targetLevels.Length; i++)
{
Targets nowTarget = scenePrefabSet.targets[i];
targetLevelProbabilityPanel.Add(Instantiate(singleTargetLevelProbabilityPanel, transform).GetComponent<TargetLevelProbabilityPanel>());
targetLevelProbabilityPanel[i].IntializePanels(nowTarget, nowTarget.ToString());
}
}
}

View File

@ -1,11 +0,0 @@
fileFormatVersion: 2
guid: 44d064c42ee56374e94671f4f9f9d650
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -9,10 +9,11 @@ public class MessageBoxController : MonoBehaviour
public string warningColor = "#ffa500ff";
public string errorColor = "#800000ff";
public string goodColor = "#00ff00ff";
[SerializeField] private GameObject messagePanelObj;
[SerializeField] private GameObject messageTextPrefab;
public GameObject messagePanelObj;
public GameObject messageTextPrefab;
[SerializeField] private List<Message> messages = new List<Message>();
[SerializeField]
private List<Message> messages = new List<Message>();
/// <summary>
/// Pushes a simple message to the message list.
@ -44,7 +45,7 @@ public void PushMessage(string text)
/// This method pushes multi-color text messages to the message list and handles message overflow to ensure that the message list does not grow indefinitely.
/// If the lengths of the message text list and the color list do not match, it either removes excess colors or adds white color to the extra messages.
/// </remarks>
public void PushMessage(List<string> messageList, List<string> colorList)
public void PushMessage(List<string> messageList,List<string> colorList)
{
// check messages and colors list length match
if (messageList.Count != colorList.Count)
@ -52,7 +53,7 @@ public void PushMessage(List<string> messageList, List<string> colorList)
// delete extra colors or add white color to extra messages
if (messageList.Count > colorList.Count)
{
while (messageList.Count > colorList.Count)
while(messageList.Count > colorList.Count)
{
colorList.Add(defaultColor);
}
@ -61,6 +62,7 @@ public void PushMessage(List<string> messageList, List<string> colorList)
{
colorList.RemoveRange(messageList.Count, colorList.Count - messageList.Count);
}
}
MessageOverflowHandler();
Message newMessage = new Message();

View File

@ -5,11 +5,11 @@
public class SingleLevelProbabilityPanel : MonoBehaviour
{
public TextMeshProUGUI levelNameText;
public TMP_InputField inputField;
[SerializeField] private TextMeshProUGUI levelNameText;
[SerializeField] private Button lockButton;
[SerializeField] private Image lockImg;
[SerializeField] private Image unlockImg;
public Button lockButton;
public Image lockImg;
public Image unlockImg;
public Slider probabilitySlider;

View File

@ -3,23 +3,22 @@
public class StartMenuAnimations : MonoBehaviour
{
[SerializeField] private GameObject maskObj;
[SerializeField] private GameObject mixButton;
[SerializeField] private GameObject attackButton;
[SerializeField] private GameObject gotoButton;
[SerializeField] private GameObject freeButton;
public GameObject maskObj;
public GameObject mixButton;
public GameObject attackButton;
public GameObject gotoButton;
public GameObject freeButton;
[Header("Animation Parameter")]
[SerializeField] private float animeDuration = 0.2f;
public float animeDuration = 0.2f;
[SerializeField] private float animeMoveXDistance = 20f;
[SerializeField] private float animeMoveYDistance = 20f;
public float animeMoveXDistance = 20f;
public float animeMoveYDistance = 20f;
[SerializeField] private float animeScaleX = 1.2f;
[SerializeField] private float animeScaleY = 1.2f;
public float animeScaleX = 1.2f;
public float animeScaleY = 1.2f;
[SerializeField] private float maskScaleX = 1;
[SerializeField] private float maskScaleY = 0.4f;
public float maskScaleX = 1;
public float maskScaleY = 0.4f;
private Vector3 mixOriginDestination;
private Vector3 attackOriginDestination;
@ -110,6 +109,8 @@ private void MaximizeMaskObj()
private Vector3 fixCanvas(Vector3 vector)
{
// fix position of button while canvas is changed
return vector.FixCanvas(originalCanvas, transform.parent.position);
Vector3 fixedV = vector;
fixedV += (transform.parent.position - originalCanvas);
return fixedV;
}
}

View File

@ -3,20 +3,20 @@
public class StartMenuProbabilityPanel : MonoBehaviour
{
[SerializeField] private GameObject singleTargetLevelProbabilityPanel;
[SerializeField] private GameObject startSceneData;
public GameObject singleTargetLevelProbabilityPanel;
public GameObject startSceneData;
private SceneBlocksSet scenePrefabSet;
public List<TargetLevelProbabilityPanel> targetLevelProbabilityPanel = new List<TargetLevelProbabilityPanel>();
private void Start()
{
scenePrefabSet = CommonParameterContainer.Instance.scenePrefabSet;
scenePrefabSet = startSceneData.GetComponent<StartSeneData>().scenePrefabSet;
for (int i = 0; i < scenePrefabSet.targetLevels.Length; i++)
{
Targets nowTarget = scenePrefabSet.targets[i];
targetLevelProbabilityPanel.Add(Instantiate(singleTargetLevelProbabilityPanel, transform).GetComponent<TargetLevelProbabilityPanel>());
targetLevelProbabilityPanel[i].IntializePanels(nowTarget, nowTarget.ToString());
targetLevelProbabilityPanel[i].IntializePanels(scenePrefabSet.GetLevelNumber(nowTarget), nowTarget.ToString());
}
}
}

View File

@ -5,25 +5,26 @@
public class StartUIManager : MonoBehaviour
{
[SerializeField] private int waitTimeLimit = 45;
[SerializeField] private GameObject sceneLoaderObj;
[SerializeField] private GameObject targetLevelProbabilityPanelOBJ;
[SerializeField] private TextMeshProUGUI messageTextObj;
[SerializeField] private TextMeshProUGUI waitTimeTextObj;
public int waitTimeLimit = 45;
public GameObject sceneLoaderObj;
public GameObject startSceneDataObj;
public GameObject targetLevelProbabilityPanelOBJ;
public TextMeshProUGUI messageTextObj;
public TextMeshProUGUI waitTimeTextObj;
private SceneLoader sceneLoader;
private StartSeneData startSceneData;
private ButtonActivateColorChanger buttonActivateColorChanger;
private LevelProbabilityPanel startMenuProbabilityPanel;
private CommonParameterContainer commonParameterContainer;
private StartMenuProbabilityPanel startMenuProbabilityPanel;
private float startTime;
private float nowTime;
// Start is called before the first frame update
void Start()
{
sceneLoader = sceneLoaderObj.GetComponent<SceneLoader>();
startSceneData = startSceneDataObj.GetComponent<StartSeneData>();
buttonActivateColorChanger = GetComponent<ButtonActivateColorChanger>();
startMenuProbabilityPanel = targetLevelProbabilityPanelOBJ.GetComponent<LevelProbabilityPanel>();
commonParameterContainer = CommonParameterContainer.Instance;
startMenuProbabilityPanel = targetLevelProbabilityPanelOBJ.GetComponent<StartMenuProbabilityPanel>();
messageTextObj.text = "";
buttonActivateColorChanger.InitializeAllButtonColor();
}
@ -35,19 +36,15 @@ private void Update()
waitTimeTextObj.text = ((int)Math.Round(leftTime)).ToString();
if (leftTime <= 1)
{
// if time limit is over, end game
# if UNITY_EDITOR
UnityEditor.EditorApplication.isPlaying = false;
#else
StopGame();
#endif
// if time limit is over, load Train Scene
sceneLoader.LoadGameScene(SceneLoader.SceneType.Train);
}
}
// while StartButton-Play Pressed
public void OnPlayButtonPressed()
{
commonParameterContainer.gameMode = 1;
startSceneData.gameMode = 1;
sceneLoader.LoadGameScene(SceneLoader.SceneType.Play);
messageTextObj.text = "Loading Play Scene...";
}
@ -55,30 +52,30 @@ public void OnPlayButtonPressed()
public void OnAttackTrainButtonPressed()
{
// while Train-Attack-Button Pressed
commonParameterContainer.attackProb = 1f;
startSceneData.attackProb = 1f;
ApplicateProbabilityToData();
LoadTrainScene();
}
public void OnGotoTrainButtonPressed()
{
// while Train-Goto-Button Pressed
commonParameterContainer.gotoProb = 1f;
startSceneData.gotoProb = 1f;
ApplicateProbabilityToData();
LoadTrainScene();
}
public void OnFreeTrainButtonPressed()
{
// while Train-Free-Button Pressed
commonParameterContainer.attackProb = 0f;
commonParameterContainer.gotoProb = 0f;
commonParameterContainer.defenceProb = 0f;
startSceneData.attackProb = 0f;
startSceneData.gotoProb = 0f;
startSceneData.defenceProb = 0f;
LoadTrainScene();
}
public void OnMixTrainButtonPressed()
{
// while Train-Mix-Button Pressed
commonParameterContainer.attackProb = 0.333f;
commonParameterContainer.gotoProb = 0.333f;
startSceneData.attackProb = 0.333f;
startSceneData.gotoProb = 0.333f;
LoadTrainScene();
}
private void LoadTrainScene()
@ -90,20 +87,13 @@ private void LoadTrainScene()
private void ApplicateProbabilityToData()
{
for(int targetIndex = 0;targetIndex < commonParameterContainer.scenePrefabSet.targetLevels.Length; targetIndex++)
for (int i = 0; i < startSceneData.scenePrefabSet.targetLevels[0].levelSize; i++)
{
Targets nowTarget = commonParameterContainer.scenePrefabSet.targets[targetIndex];
for(int levelIndex = 0; levelIndex < commonParameterContainer.scenePrefabSet.targetLevels[targetIndex].levelSize; levelIndex++)
{
commonParameterContainer.levelProbs[nowTarget] = startMenuProbabilityPanel.targetLevelProbabilityPanel[targetIndex].GetTargetProb();
}
startSceneData.gotoLevelProbs.Add(startMenuProbabilityPanel.targetLevelProbabilityPanel[0].singleLevelPanels[i].ProbabilityValue);
}
for(int i = 0; i < startSceneData.scenePrefabSet.targetLevels[1].levelSize; i++)
{
startSceneData.attackLevelProbs.Add(startMenuProbabilityPanel.targetLevelProbabilityPanel[1].singleLevelPanels[i].ProbabilityValue);
}
}
private void StopGame()
{
// stop game,end program
Debug.Log("Game End");
Application.Quit();
}
}

View File

@ -3,36 +3,27 @@
using System.Linq;
using TMPro;
using UnityEngine;
using UnityEngine.EventSystems;
public class TargetLevelProbabilityPanel : MonoBehaviour
{
[SerializeField] private GameObject singleLevelProbabilityPanel;
[SerializeField] private GameObject targetTitleText;
public GameObject singleLevelProbabilityPanel;
public GameObject targetTitleText;
private GameObject titleText;
public List<GameObject> singleLevelPanelsObjs = new List<GameObject>();
public List<SingleLevelProbabilityPanel> singleLevelPanels = new List<SingleLevelProbabilityPanel>();
private int panelNum = 0;
private Targets thisTarget;
/// <summary>
/// Initializes panels for a specific target, including the title and level probabilities.
/// </summary>
/// <param name="thisTarget">The specified target, used to obtain associated level numbers and probability data.</param>
/// <param name="titleName">The title name for the panel.</param>
/// <remarks>
/// This method first calculates the panel dimensions, then creates and sets the title text. Subsequently, it creates individual probability panels for each level and adds event listeners for sliders and input fields.
/// It relies on the CommonParameterContainer instance to access scene prefab sets and level probabilities.
/// </remarks>
public void IntializePanels(Targets thisTarget, string titleName)
public void IntializePanels(int levelNum, string titleName)
{
this.thisTarget = thisTarget;
int levelNum = CommonParameterContainer.Instance.scenePrefabSet.GetLevelNumber(thisTarget);
// initialize target level probability panel size
float defaultWidth = singleLevelProbabilityPanel.GetComponent<RectTransform>().sizeDelta.x;
float defaultLevelHeight = singleLevelProbabilityPanel.GetComponent<RectTransform>().sizeDelta.y;
float titleHeight = targetTitleText.GetComponent<RectTransform>().sizeDelta.y;
float averageProbability = 1f / levelNum;
float lastLevelProbability = 1f - averageProbability * (levelNum - 1);
// Debug.Log("averageProbability: " + averageProbability);
transform.GetComponent<RectTransform>().sizeDelta = new Vector2(defaultWidth, (defaultLevelHeight * levelNum) + titleHeight);
// create title text
@ -44,8 +35,7 @@ public void IntializePanels(Targets thisTarget, string titleName)
int tempIndex = i;
singleLevelPanelsObjs.Add(Instantiate(singleLevelProbabilityPanel, transform));
singleLevelPanels.Add(singleLevelPanelsObjs[i].GetComponent<SingleLevelProbabilityPanel>());
Debug.Log(CommonParameterContainer.Instance.levelProbs.Count);
singleLevelPanels[i].InitializeLevelProbabilityPanel(i, CommonParameterContainer.Instance.levelProbs[thisTarget][i]);
singleLevelPanels[i].InitializeLevelProbabilityPanel(i, i == levelNum - 1 ? lastLevelProbability : averageProbability);
//add onValueChanged event to slider and input field
singleLevelPanels[i].probabilitySlider.onValueChanged.AddListener((value) => OnProbabilityValueChange(value, tempIndex));
singleLevelPanels[i].inputField.onEndEdit.AddListener((value) => OnProbabilityValueChange(value, tempIndex));
@ -54,21 +44,22 @@ public void IntializePanels(Targets thisTarget, string titleName)
}
/// <summary>
/// Retrieves the probability values for all levels of the current target.
/// Adds an event trigger entry to an event trigger.
/// </summary>
/// <returns>A list of floating-point numbers containing the probability values for each level.</returns>
/// <remarks>
/// This method iterates through the panels of all levels, collecting and returning the probability value for each level.
/// It relies on the singleLevelPanels list, which should have been properly initialized and populated before calling this method.
/// </remarks>
public List<float> GetTargetProb()
/// <param name="trigger">The event trigger object.</param>
/// <param name="type">The event trigger type.</param>
/// <param name="action">The event handler method to execute.</param>
private void AddEventTrigger(GameObject gameObject, EventTriggerType triggerType, System.Action<BaseEventData> action)
{
List<float> targetProb = new List<float>();
for (int i = 0; i < panelNum; i++)
EventTrigger eventTrigger = gameObject.GetComponent<EventTrigger>();
if (eventTrigger == null)
{
targetProb.Add(singleLevelPanels[i].ProbabilityValue);
eventTrigger = gameObject.AddComponent<EventTrigger>();
}
return targetProb;
EventTrigger.Entry entry = new EventTrigger.Entry();
entry.eventID = triggerType;
entry.callback.AddListener(new UnityEngine.Events.UnityAction<BaseEventData>(action));
eventTrigger.triggers.Add(entry);
}
/// <summary>
@ -84,20 +75,17 @@ private void OnProbabilityValueChange<T>(T value, int exceptedIndex)
case float floatValue:
changedValue = floatValue;
break;
case string stringValue:
changedValue = float.Parse(stringValue);
// limit the value between 0 and 1
if (changedValue > 1 && changedValue <= 100)
if(changedValue>1 && changedValue <=100)
{
changedValue /= 100;
}
else if (changedValue > 100)
}else if(changedValue>100)
{
changedValue = 1;
}
break;
default:
Debug.LogError("Invalid value type!");
throw new ArgumentException("Unsupported value type");
@ -176,19 +164,8 @@ private void enableSliderListener()
}
/// <summary>
/// Recalculates correction values.
/// calculate the correction value to each panel,while the total value is not equal to 1
/// </summary>
/// <param name="correctionValues">The current array of correction values.</param>
/// <param name="exceptedIndex">The index of the panel that is expected not to change.</param>
/// <param name="value">The expected probability value.</param>
/// <param name="extraValue">Additional correction value.</param>
/// <param name="maxLimitValue">The maximum limit for the probability value.</param>
/// <returns>Returns a tuple containing a float array and an integer.
/// The float array is the new correction values, and the integer is the number of panels that need to be corrected in the next iteration.</returns>
/// <remarks>
/// This method calculates new correction values based on the provided parameters.
/// During the iteration, some panels might exceed set limits, and their values will need to be corrected in the next iteration.
/// </remarks>
private (float[], int) reCalculateCorrectionValues(float[] correctionValues, int exceptedIndex, float value, float extraValue, float maxLimitValue)
{
// the number of panels which need to be corrected in next iteration
@ -272,20 +249,20 @@ private void enableSliderListener()
}
/// <summary>
/// Applies correction values to adjust the probability values for each level of the current target.
/// applicate correction value to each panel
/// </summary>
/// <param name="correctionValues">An array of correction values to be applied to the probability value of each level.</param>
/// <remarks>
/// This method iterates through all the level panels, updating the probability by subtracting the corresponding correction value from the probability value of each level.
/// It also synchronously updates the level probabilities for this target stored in the CommonParameterContainer instance.
/// This method assumes that the length of the correctionValues array matches the number of level panels.
/// </remarks>
private void applyCorrectionValue(float[] correctionValues)
{
for (int i = 0; i < panelNum; i++)
{
/* if (singleLevelPanels[i].ProbabilityValue - correctionValues[i] < 0)
{
Debug.LogWarning("Probability value is less than 0");
Debug.Log(i);
Debug.Log(singleLevelPanels[i].ProbabilityValue);
Debug.Log(correctionValues[i]);
}*/
singleLevelPanels[i].SetProbability(singleLevelPanels[i].ProbabilityValue - correctionValues[i]);
CommonParameterContainer.Instance.levelProbs[thisTarget][i] = singleLevelPanels[i].ProbabilityValue;
}
}
}

View File

@ -1,4 +1,5 @@
using System.Collections.Generic;
using System.Collections.ObjectModel;
using UnityEngine;
using UnityEngine.EventSystems;
using UnityEngine.UI;
@ -7,27 +8,27 @@ public class TargetUIController : MonoBehaviour
{
// Controller to control the UI of the target,
// select target type, select prefeb to set or sth.
public GameObject targetControllerObj;
[SerializeField] private GameObject targetControllerObj;
[SerializeField] private GameObject mouseSelectorObj;
[SerializeField] private GameObject environmentUIObj;
public GameObject mouseSelectorObj;
public GameObject environmentUIObj;
[SerializeField] private float levelButtonHeight = 30;
public float levelButtonHeight = 30;
[Header("PrimaryButton")]
[SerializeField] private Button clearGameButton;
[SerializeField] private Button setEnemyButton;
[SerializeField] private Button setAttackButton;
[SerializeField] private Button setGotoButton;
[SerializeField] private Button setFreeButton;
[SerializeField] private Button setStayButton;
public Button clearGameButton;
public Button setEnemyButton;
public Button setAttackButton;
public Button setGotoButton;
public Button setFreeButton;
public Button setStayButton;
public int primaryButtonNumber = 6;
[Header("LevelPanel")]
[SerializeField] private GameObject gotoLevelPanel;
public GameObject gotoLevelPanel;
[SerializeField] private GameObject attackLevelPanel;
[SerializeField] private float levelPanelAnimeTime = 0.2f;
public GameObject attackLevelPanel;
public float levelPanelAnimeTime = 0.2f;
private MouseInMap mouseInMapCon;
private EnvironmentUIControl envUICon;
@ -91,7 +92,7 @@ public void LevelButtonPressed(PrimaryButtonType primaryButtonType, int level =
public void ClearGamePressed()
{
// Clear all enemies and targets. set gamemode to Stay mode
targetCon.PlayTargetChange(Targets.Stay);
targetCon.StayModeChange();
mouseInMapCon.ChangeMouseMode(MouseInMap.MouseMode.Default);
// disable setStayButton and enable other buttons
buttonColorChanger.ChangeInteractableColor(clearGameButton, true);
@ -100,14 +101,14 @@ public void ClearGamePressed()
buttonColorChanger.ChangeInteractableColor(setGotoButton, true);
buttonColorChanger.ChangeInteractableColor(setFreeButton, true);
buttonColorChanger.ChangeInteractableColor(setStayButton, false);
targetCon.PlayModeInitialize();
targetCon.PlayInitialize();
}
// Set Free Button Pressed, change Target mode to free mode
public void SetFreePressed()
{
mouseInMapCon.ChangeMouseMode(MouseInMap.MouseMode.Default);
targetCon.PlayTargetChange(Targets.Free);
targetCon.FreeModeChange();
buttonColorChanger.ChangeInteractableColor(setStayButton, true);
buttonColorChanger.ChangeInteractableColor(setAttackButton, true);
buttonColorChanger.ChangeInteractableColor(setGotoButton, true);
@ -118,7 +119,7 @@ public void SetFreePressed()
public void SetStayPressed()
{
mouseInMapCon.ChangeMouseMode(MouseInMap.MouseMode.Default);
targetCon.PlayTargetChange(Targets.Stay);
targetCon.StayModeChange();
buttonColorChanger.ChangeInteractableColor(setStayButton, false);
buttonColorChanger.ChangeInteractableColor(setAttackButton, true);
buttonColorChanger.ChangeInteractableColor(setGotoButton, true);
@ -161,9 +162,9 @@ private void SetAttackPressed(int level)
/// <param name="levelPanel">The level panel object containing the level buttons.</param>
private void AddLevelButtonToColorChanger(GameObject levelPanel)
{
foreach (Button btn in levelPanel.GetComponent<LevelPanel>().LevelButtonList)
foreach(Button btn in levelPanel.GetComponent<LevelPanel>().LevelButtonList)
{
buttonColorChanger.AddButtonToColorChangerButtonList(btn, true);
buttonColorChanger.AddButtonToColorChangerButtonList(btn,true);
}
}

View File

@ -4,7 +4,7 @@
public class WorldUIController : MonoBehaviour
{
[SerializeField] private LineChart winChart;
public LineChart winChart;
public int[] totalGames;
public int[] winGames;
@ -26,33 +26,32 @@ private void Start()
}
}
public void UpdateChart(Targets targetType, int endType)
public void UpdateChart(int targetType, int endType)
{
float winRatio = 0f;
int targetTypeInt = (int)targetType;
switch (endType)
{
case (int)TargetController.EndType.Win:
//Win
totalGames[targetTypeInt] += 1;
winGames[targetTypeInt] += 1;
winRatio = (float)winGames[targetTypeInt] / totalGames[targetTypeInt];
winChart.AddData(targetTypeInt, winRatio);
if (totalGames[targetTypeInt] > maxXAxis)
totalGames[targetType] += 1;
winGames[targetType] += 1;
winRatio = (float)winGames[targetType] / totalGames[targetType];
winChart.AddData(targetType, winRatio);
if (totalGames[targetType] > maxXAxis)
{
maxXAxis = totalGames[targetTypeInt];
maxXAxis = totalGames[targetType];
winChart.AddXAxisData(Convert.ToString(maxXAxis));
}
break;
case (int)TargetController.EndType.Lose:
//lose
totalGames[targetTypeInt] += 1;
winRatio = (float)winGames[targetTypeInt] / totalGames[targetTypeInt];
winChart.AddData(targetTypeInt, winRatio);
if (totalGames[targetTypeInt] > maxXAxis)
totalGames[targetType] += 1;
winRatio = (float)winGames[targetType] / totalGames[targetType];
winChart.AddData(targetType, winRatio);
if (totalGames[targetType] > maxXAxis)
{
maxXAxis = totalGames[targetTypeInt];
maxXAxis = totalGames[targetType];
winChart.AddXAxisData(Convert.ToString(maxXAxis));
}
break;

View File

@ -1,151 +0,0 @@
using DG.Tweening; // Importing the DOTween library for smooth animations.
using UnityEngine;
using UnityEngine.EventSystems; // Using the EventSystem to handle UI interactions.
public class UIVisibilityController : MonoBehaviour
{
[SerializeField]
private GameObject targetUIObj;
[SerializeField]
private GameObject triggerUIObj;
[SerializeField]
private GameObject canvasObj;
[SerializeField]
private Vector3 slideDirection = Vector3.zero;
[SerializeField]
private Vector3 slideDistance = Vector3.zero;
[SerializeField]
private float slideDuration = 0.2f;
// Cached positions and states for logic and animation.
private Vector3 targetOriginalPosition;
private Vector3 triggerOriginalPosition;
private Vector3 targetEndPosition;
private Vector3 triggerEndPosition;
private Vector3 originalCanvasPosition;
private bool isInActive = true; // Indicates if the UI is currently inactive.
private bool isOutActive = false; // Indicates if the UI is currently active.
private bool isMouseInTarget = false; // Is the mouse currently over the target UI?
private bool isMouseInTrigger = false; // Is the mouse currently over the trigger UI?
private void Start()
{
// Initial setup of component's states and values.
InitializeCanvasPosition();
InitializeSlideValues();
InitializeEventTrigger();
}
private void InitializeCanvasPosition()
{
if (canvasObj == null)
{
// Set canvasObj to the parent if not assigned.
canvasObj = transform.parent.gameObject;
}
originalCanvasPosition = canvasObj.transform.position;
}
private void InitializeSlideValues()
{
if (slideDirection == Vector3.zero)
{
// default slide direction is up (set to right).
slideDirection = Vector3.right;
Debug.LogError("SliderDirection not set, set default right");
}
if (slideDistance == Vector3.zero)
{
// default slide distance is based on targetUIObj dimensions.
RectTransform targetRT = targetUIObj.GetComponent<RectTransform>();
slideDistance = new Vector3(targetRT.rect.width, targetRT.rect.height, 0);
}
// Determine the start and end positions for sliding.
targetOriginalPosition = targetUIObj.transform.position;
triggerOriginalPosition = triggerUIObj.transform.position;
targetEndPosition = targetOriginalPosition + Vector3.Scale(slideDirection, slideDistance);
triggerEndPosition = triggerOriginalPosition + Vector3.Scale(slideDirection, slideDistance);
}
private void InitializeEventTrigger()
{
// Setting up event triggers for mouse interactions.
EventTrigger triggerObjET = triggerUIObj.GetComponent<EventTrigger>();
if (triggerObjET == null)
{
triggerObjET = triggerUIObj.AddComponent<EventTrigger>();
}
EventTrigger targetObjET = targetUIObj.GetComponent<EventTrigger>();
if (targetObjET == null)
{
targetObjET = targetUIObj.AddComponent<EventTrigger>();
}
triggerObjET.AddEventTrigger(EventTriggerType.PointerEnter, (eventData) => OnMouseIn(triggerUIObj));
triggerObjET.AddEventTrigger(EventTriggerType.PointerExit, (eventData) => OnMouseOut(triggerUIObj));
targetObjET.AddEventTrigger(EventTriggerType.PointerEnter, (eventData) => OnMouseIn(targetUIObj));
targetObjET.AddEventTrigger(EventTriggerType.PointerExit, (eventData) => OnMouseOut(targetUIObj));
}
private void Update()
{
// Check mouse interactions to determine whether to slide in or out the UI.
if ((isMouseInTrigger || isMouseInTarget) && isInActive)
{
SlideInScreen();
isInActive = false;
isOutActive = true;
}
else if (!isMouseInTrigger && !isMouseInTarget && isOutActive)
{
SlideOutScreen();
isInActive = true;
isOutActive = false;
}
}
private void OnMouseIn(GameObject type)
{
// Update flags based on which UI object the mouse enters.
if (type == triggerUIObj)
{
isMouseInTrigger = true;
}
else if (type == targetUIObj)
{
isMouseInTarget = true;
}
}
private void OnMouseOut(GameObject type)
{
// Update flags based on which UI object the mouse exits.
if (type == triggerUIObj)
{
isMouseInTrigger = false;
}
else if (type == targetUIObj)
{
isMouseInTarget = false;
}
}
private void SlideInScreen()
{
// Animate the UI objects to slide in.
triggerUIObj.transform.DOMove(triggerEndPosition, slideDuration).SetEase(Ease.OutCirc).Play();
targetUIObj.transform.DOMove(targetEndPosition, slideDuration).SetEase(Ease.OutCirc).Play();
}
private void SlideOutScreen()
{
// Animate the UI objects to slide out.
triggerUIObj.transform.DOMove(triggerOriginalPosition, slideDuration).SetEase(Ease.OutCirc).Play();
targetUIObj.transform.DOMove(targetOriginalPosition, slideDuration).SetEase(Ease.OutCirc).Play();
}
}

View File

@ -1,11 +0,0 @@
fileFormatVersion: 2
guid: b459fcafb205c9540a282b421abdf6d1
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -1,155 +0,0 @@
# Aimbot-ParallelEnv
これはUnity ML-Agentsに基づいたFPSゲームのマルチエージェントトレーニング環境で、特定の目標と対応する難易度を生成し、エージェントのアクションに基づいて報酬をフィードバックして強化学習エージェントをトレーニングするための環境です。
[![Chinese badge](https://img.shields.io/badge/简体中文-Simplified_Chinese-blue)](./README.md)
[![Japanese badge](https://img.shields.io/badge/日本語-Japanese-blue)](./README-JP.md)
## Description
プロジェクトは[ML-Agents 2.0.1](https://github.com/Unity-Technologies/ml-agents/tree/develop)を基にして、Unity 2021.3.14f1を使用して開発されています。
Python側では、[mlagents-envs 0.30.0](https://pypi.org/project/mlagents-envs/)を使用してゲーム環境と通信を行います。
## Quick start
プロジェクト[Aimbot-PPO](https://koha9.asuscomm.com/yurugit/Koha9/Aimbot-PPO)では、この環境を使用してPPOアルゴリズムのトレーニングが行われています。具体的な使用例は[AimbotEnv.py](https://koha9.asuscomm.com/yurugit/Koha9/Aimbot-PPO/src/branch/OffP-FullMNN-V2/Aimbot-PPO-Python/Pytorch/AimbotEnv.py)で記載されています。
## ゲーム環境の概要
本環境は初代Doomの基本モードを模したFPSゲームで、エージェントには異なる目標が与えられます。エージェントは目標の種類と位置、自身の状態に基づいて、撃つかどうか、撃つ方向、移動方向を決定します。この環境には2つのモードと3種類の目標があります訓練モードとテストモード、そして`FreeTarget`、`GotoTarget`、`AttackTarget`です。`FreeTarget`では、エージェントは武器を使ってエリア内のすべての敵を倒す必要があります。`GotoTarget`と`AttackTarget`では、エリア内に目標ブロックが生成され、エージェントはそれに応じた異なる行動を取る必要があります。さらに、`GotoTarget`と`AttackTarget`では、生成される目標ブロックの構造に応じて5つの異なる難易度に分けられています。
## ゲームの概要
この環境は初代Doomの基本モードを模したFPSゲームで、エージェントには異なる目標が与えられます。エージェントは目標の種類と位置、自身の状態に基づいて、撃つかどうか、撃つ方向、移動方向を決定します。この環境には2つのモードと3種類の目標があります訓練モードとテストモード、そして`FreeTarget`、`GotoTarget`、`AttackTarget`です。`FreeTarget`では、エージェントは武器を使ってエリア内のすべての敵を倒す必要があります。`GotoTarget`と`AttackTarget`では、エリア内に目標ブロックが生成され、エージェントはそれに応じた異なる行動を取る必要があります。さらに、`GotoTarget`と`AttackTarget`では、生成される目標ブロックの構造によって、5つの異なる難易度に分けられています。
## モード
訓練モードとテストモードは、Python側と通信が必要で、この環境から観測されたObservationに基づいてActionを生成し、環境にフィードバックします。環境を起動する際、ゲーム内でユーザーが操作してモードを選択する必要があります。ML-Agentsにはタイムリミットが存在するため、45秒以内にモードを選択する必要があります。45秒を超えると、自動的にゲーム環境から退出します。
### 訓練モード
訓練モードに入る前に、手動でスタート画面から`Train-Free`、`Train-Goto`、`Train-Attack`(単一モードの訓練)または`Train-Mix`(全モードの訓練)の中から選択する必要があります。選択後、訓練モードに入りますが、この選択は後で変更できません。複数の難易度がある目標ブロック`Train-Goto`と`Train-Attack`のモードでは、スタート画面に異なる難易度の目標ブロックを生成する確率を調整するパネルが画面の左上にあります。デフォルトでは、各難易度の生成確率は均等となっています。このパネルでは、各モードの難易度の合計確率が1になるように、一つの難易度の確率を調整すると他の難易度の確率が自動的に調整されます。ユーザーは右側のロックボタンをクリックして、特定の難易度の確率をロックまたは解除することができます。このパネルは訓練モードの実行中にも表示され、ユーザーが訓練中に確率パネルを調整すると、次の目標ブロックの生成時に適用されます。
<br>![TargetLevelProbabilityPanel](./ReadmeImages/LevelProbabilityPanel.jpg)
### テストモード
テストモードでは、ユーザーが手動でエージェントに命令を指定する必要があります。画面右上のメニューをクリックすることで、目標ブロックや敵を生成したり、目標モードを切り替えたりすることができます。このモードでは、訓練モードにはない新しいターゲット`StayTarget`が追加されています。ユーザーが特定の目標を指定しない場合やゲームをクリアした場合、`StayTarget`がエージェントに割り当てられます。右上隅のメニューをクリックすると、マウスモードを切り替えて敵Enemyを生成するモードや、特定の難易度のターゲットブロックを生成するモードに変更できます。マウスがブロック生成モードで、マウスをゲームエリアに移動すると、生成するオブジェクトのプレビューが表示され、マウスの右クリックで指定した位置に対応する敵やブロックを生成できます。ターゲットブロックを生成すると、対応するターゲットが自動的にエージェントに割り当てられます。`FreeTarget`を割り当てる必要がある場合は、`FreeMode`ボタンをクリックします。`ClearGame`をクリックすると、すべての敵とブロックがクリアされ、エージェントのターゲットが`StayTarget`に設定されます。`StayMode`をクリックすると、エージェントのターゲットを強制的に`StayTarget`に設定できます。
## Target
### FreeTarget
エージェントが`FreeTarget`に割り当てられた場合、エージェントはエリア内のすべての敵を武器で倒す必要があります。このモードでは、ラウンド開始時にエリア内にランダムな数の敵が生成されますデフォルトは6体。エージェントはエリアのランダムな位置に生成され、向きは前回のラウンドを引き継ぎます最初のラウンドの場合はデフォルトで角度0。エージェントがすべての敵を倒せば勝利と判断され、30秒以内にすべての敵を倒せない場合は失敗と判断されます。
### GotoTarget
エージェントが`GotoTarget`に割り当てられた場合、指定された目標エリアの特定の位置に移動することが目標です。このモードでは、ラウンドが始まるとエージェントの位置は前のラウンドを引き継ぎます最初のラウンドではマップの左下隅にデフォルトで生成されます。Gotoの目標ブロックは、難易度の確率に応じてエリア内にランダムに生成され、異なる難易度には複数のプリセットブロックが用意されています。これらのプリセットブロックは[`SceneBlocksSet`](https://koha9.asuscomm.com/yurugit/Koha9/Aimbot-ParallelEnv/src/branch/main/Assets/Script/TargetContainer/SceneBlocksSet.cs)によって[Prefabフォルダ](https://koha9.asuscomm.com/yurugit/Koha9/Aimbot-ParallelEnv/src/branch/main/Assets/Prefeb)に保存されています。すべての目標ブロックの合計サイズは10x10で、ブロックが生成される際にはエージェントの位置を避けます。指定された目標エリアは`FireBase`と呼ばれる円形のエリアで、その範囲は目標ブロックを超えることはありませんが、位置とサイズはプリセットブロックによって異なります。エージェントが目標ブロックの中心位置に正常に移動すると勝利と判断され、30秒以内に中心位置に移動できない場合は失敗と判断されます。
### AttackTarget
エージェントが`AttackTarget`に割り当てられた場合、目標エリアに火力を集中し、主な目標はブロック内のすべての敵を倒すことです。このモードでのエージェントと目標ブロックの生成戦略、および`FireBase`の構成は`GotoTarget`と同様です。しかし、`AttackTarget`の`FireBase`エリア内には必ず敵が生成されます。エージェントがブロック内のすべての敵を倒せば勝利と判断され、30秒以内にすべての敵を倒せない場合は失敗と判断されます。
---
# Environment
## Observation Space
本強化学習訓練設定では、状態(State)は環境を理解し、分析する上での鍵となります。観測環境は3つの主要な部分で構成されています`TargetState`(ターゲット状態)、`AgentState`(自機状態)、そして`RaycastState`レイキャスト状態。これらの観測データはML-Agentsの`VectorSensor`クラスを通じて収集され、Python側に送信され、意思決定に必要な情報を提供します。Observationのソースコードは[`MLAgentsCustomController.cs`](https://koha9.asuscomm.com/yurugit/Koha9/Aimbot-ParallelEnv/src/branch/main/Assets/Script/GameScript/MLAgentsCustomController.cs)にあるoverrideされた`CollectObservations`関数内に格納されています。
### TargetStateターゲット状態
`TargetState`は、エージェントが受け取ったTargetとその関連情報を含んで、合計六つあります。ターゲットの種類、x,y,z座標、TargetAreaの直径、そしてエージェントがTargetArea内にいるか、およびそのラウンドの残り時間などの情報を含みます。
| 番号 | 観測項目 | サイズ | 状態空間 | 説明 |
|------|-----------------|-------|------------|---------------------------------------------------------------------------------------------------------------|
| 0 | Targetの種類 | 1 | 0,1,2,3,4 | 割り当てられたTargetの種類を記述0=FreeTarget、1=GotoTarget、2=AttackTarget、3=DefenceTarget未使用、4=StayTarget |
| 1~3 | Targetの座標 | 3 | 0~47 | Target中のFirebaseの連続空間座標を記述、範囲は0から47の連続値 |
| 4 | FireBaseの直径 | 1 | 1~10 | FireBaseの直径を記述、範囲は1から10の連続値 |
| 5 | InFireBase状態 | 1 | 0,1 | エージェントがFireBase内にいるかどうかを記述、0=False、1=True |
| 6 | 残り時間 | 1 | 0~30 | ラウンドの残り時間を記述、範囲は0から30の連続値 |
### AgentState自機の状態
`AgentState`は、エージェントの武器が攻撃可能な状態かどうか、エージェントのx, y, z座標、およびエージェントの向きの角度を含んでいます。エージェントのGameObjectの向きの角度を直接使用すると、0から360度の変化時に値が大幅に変化してしまいます。より良い周期的な表現を実現するために、エージェントのGameObjectの回転角度のコサインとサインの値を使用してエージェントの向きを表しています。
| 番号 | 観測項目 | サイズ | 状態空間 | 説明 |
|------|------------|-------|---------|---------------------------------------------------------------------------------------|
| 7 | GunState | 1 | 0,1 | エージェントの武器が使用可能な状態かどうかを記述。0=False、1=True |
| 8~10 | エージェントの座標 | 3 | 0~47 | エージェントの連続空間座標を記述、範囲は0から47の連続値 |
| 11~12| エージェントの向き | 2 | -1~1 | エージェントのGameObjectの回転角度のcosとsin値を計算してエージェントの正面の向きを記述。 |
### RaycastState視野状態
`RaycastState`はエージェント視界内で発射されたレイキャストが検出したオブジェクトのタグとその距離を記録します。タグはラベルエンコーディングとワンホットエンコーディングの2つの方法で記録することができ、デフォルトではラベルエンコーディングが使用されます。これは`CommonParameterContainer`オブジェクトで調整することが可能です。`RaycastState`は[`RaySensors`](https://koha9.asuscomm.com/yurugit/Koha9/Aimbot-ParallelEnv/src/branch/main/Assets/Script/GameScript/RaySensors.cs)クラスによって管理されており、非均一なレイキャストの分布が実装されています。デフォルトでは、視界の中央15のエリアに射線がより密集して分布し、両側はより希薄です。中央の密集エリアからは5本の射線が、両側の希薄エリアからはそれぞれ7本の射線が発射されます。各レイキャストが検出可能な最大距離は100ユニットで、その距離を超えると0が返されます。視界の中央エリアのカバー範囲、エリア内のレイキャスト数、検出可能な距離は、各`Agent`のGameObjectのInspectorで調整できます。
<br><img src="./ReadmeImages/RayCastLayout.png" alt = "射線の分布方式" width="500" height = "auto">
| 番号 | 観測項目 | サイズ | 状態空間 | 説明 |
|-------------------------------------------------------------------|-------------------|---------------------|--------------|----------------------------------------------------------------------------------------|
| 11~Raynum+11 | TagType(Label) | Raycast数 | 0, 1, 2 | レイキャストが検出した物体のタグ。0=Wall, 1=Enemy, 2=Player, -1=Nothing |
| 11~Raynum * 3+11 | TagType(Onehot) | Raycast数 * 3 | 0, 1 | レイキャストが検出した物体のタグ。ワンホットエンコーディングで記録 |
| Raynum+12~2* Raynum+12ラベル,<br>3* Raynum+12~4* Raynum+12ワンホット | 距離 | Raycast数 | 0~MaxDistance| レイキャストが検出した物体までの距離。範囲は0〜MaxDistance。MaxDistanceのデフォルトは100 |
*注nは各エージェントのRaycast数、MaxDistanceは各エージェントのレイキャストの最大探知距離を指します。*
## Action Space
FPSゲームにおけるプレイヤーのキーボードとマウスの同時操作をシミュレートするため、本環境のActionSpaceは`Discrete Action`(離散動作)と`Continuous Action`連続動作の2つの部分に分かれています。`Discrete Action`はプレイヤーのキーボードによる離散的な操作を模擬し、`Continuous Action`はマウスによる連続的な操作を模擬します。両方のActionSpaceに対応する操作は[`MLAgentsCustomController.cs`](https://koha9.asuscomm.com/yurugit/Koha9/Aimbot-ParallelEnv/src/branch/main/Assets/Script/GameScript/MLAgentsCustomController.cs)内のoverrideされた`Heuristic`関数で参照できます。具体的な実装は[`AgentController.cs`](https://koha9.asuscomm.com/yurugit/Koha9/Aimbot-ParallelEnv/src/branch/main/Assets/Script/GameScript/Character/AgentController.cs)内の`MoveAgent`関数と`CameraControl`関数にあります。
### Discrete Action離散動作
`Discrete Action`では、エージェントは各方向の移動と攻撃を離散的に操作できます。移動操作には垂直移動と水平移動が含まれ、攻撃操作では武器の操作が可能です。
| 番号 | 動作 | ActionSpace | 説明 |
|------|-------------------|----------------|---------------------------------------------------------------------------|
| 0 | 垂直移動 | 0, 1, -1 | エージェントをその正面方向の垂直軸で制御。0=停止、1=前進、-1=後退 |
| 1 | 水平移動 | 0, 1, -1 | エージェントをその正面方向の水平軸で制御。0=停止、1=右移動、-1=左移動 |
| 2 | 攻撃 | 0, 1 | エージェントの攻撃操作を制御。0=攻撃しない、1=攻撃する |
### Continuous Action連続動作
`Continuous Action`では、エージェントの視点を制御できます。本環境は初期のDoomのFPS環境を模擬しているため、エージェントの視点は水平方向のみで回転可能と設定されています。
| 番号 | 動作 | ActionSpace | 説明 |
|------|-------------------|----------------|---------------------------------------------------------------------------------|
| 0 | 水平回転 | -Inf ~ Inf | エージェントの水平方向(左右)の視角回転を制御。正の値で右に、負の値で左に回転する。 |
## Reward
与えられた各ターゲットにおいて、エージェントが異なるタスクを達成することに注力するため、リワードの設計もそれぞれ異なります。訓練モードでは、リワードは共通リワード「Common Reward」と各ターゲット専用のリワードで構成されています。
### Common Reward共通リワード
エージェントが訓練中に目標達成に役立つ行動を取るように導き、無意味な動作を避けるために、`Common Reward`では以下のリワードとペナルティが設計されています。ML-AgentsはPythonに直接ラウンド終了の結果を伝えることができないため、他のリワードの値が小さすぎたり大きすぎたりして勝利または失敗を判断できない場合に備えて、ラウンド終了時のリワードには極大値と極小値が追加されています。
| リワード/ペナルティ | 値/シンボル | 説明 |
|-------------------|----------------|---------------------------------------------------------------|
| 非ターゲット命中 | 3 | エージェントがターゲットではない敵を撃つか倒すと得られるリワード |
| ターゲット命中 | 25 | エージェントがターゲットとなる敵を撃つか倒すと得られるリワード |
| 移動ペナルティ | -0.5 | エージェントがフィールド上を移動するときに与えられるペナルティ |
| 回転ペナルティ | $P_s$ | エージェントが視点を回転するときに与えられるペナルティ |
| 勝利リワード | 999 | ラウンドが終了し、そのラウンドでエージェントが目標を達成したときに与えられるリワード |
| 失敗リワード | 999 | ラウンドが終了し、そのラウンドでエージェントが失敗したときに与えられるリワード |
エージェントが訓練中に無意味な回転を行わないように、また回転速度が速すぎないように、以下の関数を使用して`SpinPenalty`を与えます。40個のマウス操作の回転記録が閾値に達していない場合、小さなペナルティが与えられます。閾値を超えると、エージェントが同じ方向に持続的に回転していることがわかるので、大きなペナルティが与えられます。ここでの<i>P<sub>s<sub>t</sub></sub></i>は時刻`t`の回転ペナルティ、<i>mouseX<sub>t</sub></i>は時刻`t`のエージェントの`Contunuouse Action`内の回転アクションの値を指します。
$$
P_{st} =
\begin{cases}
-|mouseX_t| \cdot 0.06 &, & \left| \sum_{t=0}^{-40} mouseX_t \cdot 0.08 \right| < 10 \\
-\left| \sum_{t=0}^{-40} mouseX_t \cdot 0.08 \right| & , & \left| \sum_{t=0}^{-40} mouseX_t \cdot 0.08 \right| \geq 10
\end{cases}
$$
### FreeTarget
`FreeTarget`では、主な目標は全ての敵を消滅させることです。そのため、敵を追跡する能力が非常に重要です。このモードでは、2種類の報酬メカニズムが設定されています視界の中心で敵を検出した場合、エージェントには2の報酬が与えられます。敵を検出しなかった場合、視界の中心から検出された敵までの距離 \(D\) に基づいて2未満の報酬が与えられます。距離の計算は、Raycastで検出された敵にのみ考慮され、距離が0.5に近づくほど、つまり敵の半径がRewardに近づくほど、報酬は2に近づきます。たとえば、下図では`Enemy1`と`Enemy3`との距離のみが計算され、最短距離 \(D_1\) が報酬の計算に使用されます。検出されなかった`Enemy2`と`Enemy4`は除外されます。このモードでは、すべての敵がターゲットと見なされ、敵が倒されたり、ヒットした場合、エージェントは`Common Reward`の`TargetHit Reward`を獲得します。
<br><img src="./ReadmeImages/FreeTargetFacingreward.png" alt = "FacingReward" width="500" height = "auto">
| 報酬/ペナルティ | 値/記号 | 説明 |
|----------------|----------------|-----------------------------------------|
| FacingReward | \(R_f\) | 視界の中心と検出された最も近い敵との距離に基づく報酬 |
$$
R_{f} =
\begin{cases}
2 &, &min(D) \leq 0.5 \\
\frac{1}{\sqrt{\frac{min(D)}{2}}} &,& min(D) \gt 0.5
\end{cases}
$$
### GotoTarget
`GotoTarget`では、主な目標は指定された`FireBase`への移動です。時刻 \(t\) と \(t-1\) でのプレイヤーとターゲット間の距離 \(D_t\) と \(D_{t-1}\) の差に基づいて、各ステップで獲得できる距離報酬 \(R_d\) が設定されています。エージェントが目標に近づくと正の報酬が与えられ、目標から離れると0未満のペナルティが与えられます。プレイヤーが`FireBase`内にいる場合、より高い固定報酬を得ることができますが、この場合`DistanceReward`は効果を発揮しません。このモードでは、すべての敵は攻撃対象とは見なされませんが、敵をヒットまたは倒した場合、`Common Reward`の`NonTargetHit`を獲得できます。
| 報酬/ペナルティ | 値/記号 | 説明 |
|----------------|---------|----------------------------------------|
| DistanceReward | \(R_d\) | エージェントが目標外にいる際の目標との距離差に基づく報酬 |
| InAreaReward | 5 | エージェントが目標内にいる際の持続報酬 |
$$
R_{d} = (D_{t-1} - D_{t}) \cdot 20
$$
### AttackTarget
`Attack Mode`では、主な目的は目標地点の敵を排除し、エージェントが目標地点に対して行動をとることを奨励することです。したがって、`FreeTarget`と同様に、目標地点への視線の移動も重要です。エージェントが目標地点に面して攻撃する場合、それは抑圧行動と見なされ、より低い`SuppressiveReward`が与えられます。目標地点に面しているが攻撃していない場合、さらに低い`FacingAreaReward`が継続して与えられます。`FireBase`内の敵は目標と見なされ、敵が倒されたりヒットした場合、エージェントは`Common Reward`の`TargetHit Reward`を獲得します。一方、`FireBase`外の敵は目標とは見なされず、敵が倒されたりヒットした場合、エージェントは`Common Reward`の`NonTargetHit Reward`のみを獲得します。
| 報酬/ペナルティ | 値/記号 | 説明 |
|-----------------------|---------|------------------------------------------------|
| SuppressiveReward | 5 | エージェントが`FireBase`に対して抑圧攻撃を行った際の報酬 |
| FacingAreaReward | 2 | エージェントが目標に向かっている間の継続報酬 |
## Side Channel
環境のデバッグやUnityとPython間の非リアルタイム通信を実現するため、この環境ではML-Agentsが提供するSide Channelを使用しています。ここではhuggingfaceの[Custom Side Channels](https://github.com/huggingface/ml-agents-patch/blob/develop/docs/Custom-SideChannels.md)をベースで開発を進みました。Unity側では、Side Channelの実装は[`AimbotSideChannel.cs`](https://koha9.asuscomm.com/yurugit/Koha9/Aimbot-ParallelEnv/src/branch/main/Assets/Script/GameScript/SideChannel/AimbotSideChannel.cs)と[`AimBotSideChannelController.cs`](https://koha9.asuscomm.com/yurugit/Koha9/Aimbot-ParallelEnv/src/branch/main/Assets/Script/GameScript/SideChannel/AimBotSideChannelController.cs)に位置しています。各ログ情報は`|`でフィールドを区切り、最初のフィールドはログのタイプで、次のフィールドはカスタムログ情報です。この環境では、UnityのSide Channelはすべての`LogType.Warning`と`LogType.Error`タイプのログをPython側に送信します。`LogType.Warning`はラウンド終了後の勝敗情報やUnityからPythonへの指示を伝えます。勝敗情報は`Warning|Result|Win`や`Warning|Result|Lose`のようになります。一方、指令は次のトレーニング終了後にモデルを保存するコマンドで、その内容は`Warning|Command`となります。

143
README.md
View File

@ -1,144 +1 @@
# Aimbot-ParallelEnv
这是一个基于Unity ML-Agents的基于FPS游戏的多智能体训练环境用于生成指定目标和对应难度并且根据Agent的Action反馈Reward来训练强化学习Agent的环境。
[![Chinese badge](https://img.shields.io/badge/简体中文-Simplified_Chinese-blue)](./README.md)
[![Japanese badge](https://img.shields.io/badge/日本語-Japanese-blue)](./README-JP.md)
## Description
项目基于[ML-Agents 2.0.1](https://github.com/Unity-Technologies/ml-agents/tree/develop)使用Unity 2021.3.14f1开发。
Python侧则使用[mlagents-envs 0.30.0](https://pypi.org/project/mlagents-envs/)与环境进行通信。
## 快速开始
在项目[Aimbot-PPO](https://koha9.asuscomm.com/yurugit/Koha9/Aimbot-PPO)中使用了本环境进行了PPO算法的训练其中在[AimbotEnv.py](https://koha9.asuscomm.com/yurugit/Koha9/Aimbot-PPO/src/branch/OffP-FullMNN-V2/Aimbot-PPO-Python/Pytorch/AimbotEnv.py)中给出了本环境的使用方法。
## 游戏简介
这是一个模仿初代Doom的基本模式的FPS游戏环境Agent将会被给到不同的目标Agent需要根据目标的类型和位置以及自身的状态来决定是否开枪射击射击的方向以及移动方向。环境内包含两种模式和3种目标分别是训练模式和测试模式以及`FreeTarget``GotoTarget`和`AttackTarget`。`FreeTarget`需要Agent使用武器击倒区域内所有的敌人`GotoTarget`和`AttackTarget`会在区域内生成一个目标区块Agent需要对目标区块进行不同的对应来完成任务。同时`GotoTarget`和`AttackTarget`中根据生成目标区块的构造不同分为了5种不同的难度。
## 模式介绍
训练模式和测试模式都需要与Python侧进行通信根据从本环境中观测到的Observation生成Action并传递回本环境。在环境启动时需要使用者在游戏内进行操作来选择模式由于ML-Agents存在Time limit所以需要在45s内进行模式选择超过45s后将自动退出游戏环境。
### 训练模式
在进入训练模式前使用者需要从Start界面中只训练单个模式的`Train-Free``Train-Goto``Train-Attack`和训练所有模式的`Train-Mix`中进行选择。在选择后,将会进入训练模式,并且注意该选择不能在之后进行更改。对于拥有多种难度的目标区块的模式`Train-Goto`和`Train-Attack`在Start界面将会有一个用来调整生成不同难易度目标区块的概率的面板默认的生成概率为平均。面板为了保证该模式各难易度生成概率总和为1在调整某一难度的生成概率时其他难度的生成概率将会被自动调整用户可以通过点击右侧锁定按钮以锁定或解锁某一难度的生成概率。该面板将在训练模式执行中也可见用户在训练模式执行中对概率面板进行调整后将会在应用在下次目标区块生成时。
<br>![TargetLevelProbabilityPanel](./ReadmeImages/LevelProbabilityPanel.jpg)
### 测试模式
测试模式中用户需要手动来对Agent下达命令用户可以通过点击右上角的菜单来执行生成目标区块或敌人切换目标模式等。在该模式中比训练模式多出一个新的Target为StayTarget当用户未指定目标或者清空游戏时将会以将该Target指派给Agent。通过点击右上角的菜单可以将鼠标的模式切换为生成Enemy或者生成对应难度Target区块的模式此时鼠标移动到游戏区域内后将会出现生成物件的预览内点击鼠标右键则可以在对应的位置生成对应的Enemy或者区块。当生成Target区块时会自动指派对应target给agent需要指派`FreeTarget`时则需要点击`FreeMode`按钮。通过点击`ClearGame`可以清空所有enemy和区块并将Agent的目标指派为`StayTarget`,通过点击`StayMode`则可以强制将Agent目标指派为`StayTarget`。
## 目标介绍
### FreeTarget
在Agent被指派到`FreeTarget`时Agent需要使用武器击倒区域内所有的敌人。在该模式中一个回合开始时将会在区域内随机生成一定数量的敌人默认为6Agent将会生成在区域的随机位置Agent朝向继承上一回合如果是第一回合则是默认角度为0。Agent在成功击倒所有敌人后被判断为胜利或者超过30s未能成功击倒所有敌人则被判断为失败。
### GotoTarget
在Agent被指派到`GotoTarget`时Agent的目标是移动到指定目标区块的指定位置。在该模式中一个回合开始时Agent的位置总是继承上一个回合如果是第一回合将会默认生成在地图的左下角。Goto的目标区块将会根据难度概率在区域内随机生成对应难度的预设区块不同难易度拥有多个已保存好的预设区块这些预设区块通过[`SceneBlocksSet`](https://koha9.asuscomm.com/yurugit/Koha9/Aimbot-ParallelEnv/src/branch/main/Assets/Script/TargetContainer/SceneBlocksSet.cs)保存在[Prefab文件夹](https://koha9.asuscomm.com/yurugit/Koha9/Aimbot-ParallelEnv/src/branch/main/Assets/Prefeb)中。所有目标区块的总大小为10*10生成目标区块时区块将会避开Agent的所在位置。指定目标区域是名为`FireBase`的圆形区域该区域所覆盖的范围不会超过目标区块但是它的位置和大小会根据预设区块的不同而不同。Agent在成功移动到目标区块中心位置后被判断为胜利或者超过30s未能成功移动到目标区块中心位置则被判断为失败。
### AttackTarget
在Agent被指派到`AttackTarget`时Agent的目标是对目标区域进行火力压制同时主要目标是击倒区块内所有敌人。在该模式中Agent与目标区块的生成策略还有`FireBase`的构成与`GotoTarget`相同。但是在`AttackTarget`的`FireBase`区域内必定会有Enemy生成。Agent在成功击倒区块内所有敌人后被判断为胜利或者超过30s未能成功击倒区块内所有敌人则被判断为失败。
---
# Environment
## Observation Space
在本强化学习训练设置中,观测数据是理解和交互环境的关键。观测环境由三个主要部分组成:`TargetState`(目标状态)、`AgentState`(自机状态)和`RaycastState`射线探测状态。这些观测数据通过ML-Agents的`VectorSensor`类进行收集并发送给Python侧为决策提供必要的信息。Observation的Source Code位于[`MLAgentsCustomController.cs`](https://koha9.asuscomm.com/yurugit/Koha9/Aimbot-ParallelEnv/src/branch/main/Assets/Script/GameScript/MLAgentsCustomController.cs)中被override的`CollectObservations`函数中。
### TargetState目标状态
`TargetState`包含了Agent接收的目标指令和相关信息,总大小为6。其中包含Target的类型Target的x,y,z坐标TargetArea的直径以及Agent是否处于TargetArea和本回合剩余时间内这些信息。
| Num | Observation | Size | State Space | Description |
|-----|------------------|------|-------------|-------------------------------------------------------------------------------------------------------------|
| 0 | Target类型 | 1 | 0,1,2,3,4 | 描述被指派的Target类型0=FreeTarget1=GotoTarget2=AttackTarget3=DefenceTarget未使用4=StayTarget |
| 1~3 | Target坐标 | 3 | 0~47 | 描述Target中Firebase的连续空间坐标取值范围为0到47之间的连续值 |
| 4 | FireBaseDiameter | 1 | 1~10 | 描述FireBase的直径取值范围为在1到10之间的连续值 |
| 5 | InFireBaseState | 1 | 0,1 | 描述Agent是否处于FireBase中0=False1=True |
| 6 | RemainTime | 1 | 0~30 | 描述本回合剩余时间取值范围为在0到30之间的连续值 |
### AgentState自机状态
`AgentState`包含了Agent的武器可攻击状态Agent的x,y,z坐标和Agent的朝向角度。为了避免直接使用Agent GameObject朝向角度时0到360度变化时值的大幅度跳变同时为了实现更好的周期性表示这里使用了Agent GameObject旋转角度的余弦和正弦值来表示Agent的朝向。
| Num | Observation | Size | State Space | Description |
|------|-------------|------|-------------|---------------------------------------------------------------------|
| 7 | GunState | 1 | 0,1 | 描述Agent武器是否处于可使用状态。0=False1=True |
| 8~10 | Agent坐标 | 3 | 0~47 | 描述Agent的连续空间坐标取值范围为0到47之间的连续值 |
| 11~12 | Agent朝向 | 2 | -1~1 | 通过计算Agent GameObject旋转角度的余弦和正弦以描述Agent的正面朝向。 |
### RaycastState射线探测状态
`RaycastState`记录了视野内发射的射线探测到的Object的Tag及其距离、其中Tag可以通过Label Encoding和OneHot Encoding两种方式来记录默认使用Label Encoding可以在Object`CommonParameterContainer`中进行调整。`RaycastState`由[`RaySensors`](https://koha9.asuscomm.com/yurugit/Koha9/Aimbot-ParallelEnv/src/branch/main/Assets/Script/GameScript/RaySensors.cs)类管理。它实现了射线的非均匀分布默认视野中间15%的区域射线分布更密集两侧则较为稀疏默认中间密集部分共射出5条两侧稀疏部分各7条。 每个射线可探测的对象最远距离为100个单位超过探测距离则返回0。其中视野中间区域的覆盖范围区域内RayCast数量以及可探测距离均可以在每个`Agent`GameObject的Inspector中进行调整。
<br><img src="./ReadmeImages/RayCastLayout.png" alt = "射线分布方式" width="500" height = "auto">
| Num | Observation | Size | State Space | Description |
|------------------------------------------------------------------|-----------------|--------------------|---------------|----------------------------------------------------------------------------------|
| 11~Raynum+11 | TagType(Label) | Number of Raycasts | 0, 1, 2 | 描述Raycast所探测到物体的Tag, 0=Wall, 1=Enemy, 2=Player, -1=Nothing |
| 11~Raynum * 3+11 | TagType(Onehot) | Raynum * 3 | 0, 1 | 描述Raycast所探测到物体的Tag, 使用Onehot编码记录 |
| Raynum+12~2* Raynum+12(Label),<br>3* Raynum+12~4* Raynum+12(OneHot) | Distance | Number of Raycasts | 0~MaxDistance | 描述Raycast所探测到物体的距离取值范围在0~MaxDistance其中MaxDistance默认为100 |
*注n为每个Agent的RayCast数量MaxDistance为每个Agent的RayCast最大探测距离*
## Action Space
为了模拟FPS游戏中玩家对于键盘和鼠标的同时操作本环境中的Action Space分为两个部分分别是`Discrete Action`和`Continuous Action`。其中`Discrete Action`用于模拟玩家对于键盘的离散操作,`Continuous Action`用于模拟玩家对于鼠标的连续操作。两个Action Space的对应操作可以参考于[`MLAgentsCustomController.cs`](https://koha9.asuscomm.com/yurugit/Koha9/Aimbot-ParallelEnv/src/branch/main/Assets/Script/GameScript/MLAgentsCustomController.cs)中被override的`Heuristic`函数。具体实现则于[`AgentController.cs`](https://koha9.asuscomm.com/yurugit/Koha9/Aimbot-ParallelEnv/src/branch/main/Assets/Script/GameScript/Character/AgentController.cs)中的`MoveAgent`和`CameraControl`函数中。
### Discrete Action
在`Discrete Action`中Agent可以对各方向的移动和攻击进行离散操作其中移动操作包括垂直移动操作和水平移动操作攻击操作可以对武器进行操。
| Num | Action | Action Space | Description |
|-----|-----------------|--------------|---------------------------------------------------------------------------|
| 0 | Vertical Move | 0,1,-1 | 用于控制Agent在其正面方向的垂直轴上移动。0=停止1=向前移动,-1=向后移动 |
| 1 | Horizontal Move | 0,1,-1 | 用于控制Agent在其正面方向的水平轴上移动。0=停止1=向右移动,-1=向左移动 |
| 2 | Attack | 0,1 | 用于控制Agent的攻击操作。0=不攻击1=执行攻击 |
### Continuous Action
在`Continuous Action`中可以对Agent的视角进行操作。由于本环境是模拟早期Doom的FPS环境所以Agent的视角被设定为仅能在水平方向上进行旋转。
| Num | Action | Action Space | Description |
|-----|-------------------|--------------|---------------------------------------------------------------------------------|
| 0 | Vertical Rotation | -Inf~Inf | 控制Agent在水平方向左右上的视角旋转。正值使视角向右负值则使视角向左旋转。 |
## Reward
在各给定的目标中为了让Agent注重于完成不同的任务Reward的设计也有所不同。在训练模式中Reward由共同Reward`Common Reward`和各个目标的专用Reward组成。
### Common Reward
为了引导Agent在训练过程中执行对完成目标有帮助的操作和避免无意义的动作在`Common Reward`中环境为Agent设计了以下的Reward和Penalty。其中由于ML-Agents不能向Python直接传递回合结束的结果同时为了避免其他Reward数值过小或者过大导致无法判断胜利或者失败因此在回合结束的Reward中添加了极大数和极小数来表示胜利和失败。
| Reward/Penalty | Value/Symbol | Description |
|----------------|---------------|-----------------------------------------------------|
| NonTargetHit | 3 | 当Agent击中或者击倒不作为Target的敌人时所获得Reward |
| TargetHit | 25 | 当Agent击中或者击倒作为Target的敌人时所获得Reward |
| MovePenalty | -0.5 | 当Agent在场地中移动时给到的Penalty |
| SpinPenalty | $P_s$ | 当Agent进行视角旋转时给到的Penalty |
| WinReward | 999 | 当回合结束并且本回合Agent达成目标时给到的Reward |
| LoseReward | 999 | 当回合结束并且本回合Agent失败时给到的Reward |
为了防止Agent在训练中出现无意义的旋转和旋转过快这里用下列函数来给到Agent一个`SpinPenalty`。当40个Action的旋转记录未达到阈值时会给到一个较小的Penalty而当超过阈值时我们可以知道Agent正在向同一方向持续旋转则给到一个较大的Penalty。其中<i>P<sub>s<sub>t</sub></sub></i>为在`t`时刻时的旋转的Penalty<i>mouseX<sub>t</sub></i>为`t`时刻Agent的`Contunuouse Action`中的旋转Action数值。
$$
P_{st} =
\begin{cases}
-|mouseX_t| \cdot 0.06 &, & \left| \sum_{t=0}^{-40} mouseX_t \cdot 0.08 \right| < 10 \\
-\left| \sum_{t=0}^{-40} mouseX_t \cdot 0.08 \right| & , & \left| \sum_{t=0}^{-40} mouseX_t \cdot 0.08 \right| \geq 10
\end{cases}
$$
### FreeTarget
在`FreeTarget`中主要目标是消灭所有敌人因此追踪敌人的能力非常关键。本模式设置了两种奖励机制当视野中心探测到敌人时给到Agent一个为2的Reward当未探测到敌人时则根据探测到的敌人距离视野中心的距离 $D$ 给到一个小于`2`的Reward。距离的计算仅考虑通过 Raycast 探测到的敌人且当距离越接近0.5即Enemy半径则Reward越接近`2`。例如,在图中,只计算了与`Enemy1`和`Enemy3`的距离,并且取用最短距离 $D_1$ 来计算Reward。未探测到的`Enemy2`和`Enemy4`被排除在外。在这个模式下所有敌人都被视为目标当敌人被击败或击中时Agent 将获得`Common Reward`中的`TargetHit Reward`。
<br><img src="./ReadmeImages/FreeTargetFacingreward.png" alt = "FacingReward" width="500" height = "auto">
| Reward/Penalty | Value/Symbol | Description |
|----------------|----------------------|----------------------------------------|
| FacingReward | $R_f$ | 视野中心与所探测到最近敌人距离的Reward |
$$
R_{f} =
\begin{cases}
2 &, &min(D) \leq 0.5 \\
\frac{1}{\sqrt{\frac{min(D)}{2}}} &,& min(D) \gt 0.5
\end{cases}
$$
### GotoTarget
在`GotoTarget`中,主要目标是向指定的`FireBase`移动。根据在`t`时刻和`t-1`时刻的玩家与目标间的距离 $D_t$ 和 $D_{t-1}$ 的差值,设置了每个 Step 可获得的距离奖励 $R_d$ 。当Agent接近目标时将获得正向Reward远离目标时则获得小于0的Penalty。当玩家处于`FireBase`内时,可以获得更高的固定奖励,然而此时`DistanceReward`将不会生效。在此模式中,所有敌人均不被视为攻击目标,但在击中或击败敌人时,可以获得`Common Reward`中的`NonTargetHit`。
| Reward/Penalty | Value/Symbol | Description |
|----------------|--------------|-----------------------------------------|
| DistanceReward | $R_d$ | Agent不在目标内时和目标的距离差Reward |
| InAreaReward | 5 | Agent处于目标内时的持续奖励 |
$$
R_{d} = (D_{t-1} - D_{t}) \cdot 20
$$
### AttackTarget
在 Attack Mode 中主要目的是消灭目标地点的敌人并鼓励Agent对目标地点进行设计。因此与`FreeTarget`类似将视线移动到目标地点同样重要。当Agent面对目标地点并且进行攻击时将会被视为压制行为并获得较低的`SuppressiveReward`。面对目标地点但未进行攻击时,获得持续获得更低的`FacingAreaReward`。处于`FireBase`中的Enemy将会被视为目标当敌人被击败或击中时Agent 将获得`Common Reward`中的`TargetHit Reward`。而`FireBase`外的敌人则不会被视为目标当敌人被击败或击中时Agent 将只能获得`Common Reward`中的`NonTargetHit Reward`。
| Reward/Penalty | Value/Symbol | Description |
|-------------------|--------------|-----------------------------------------|
| SuppressiveReward | 5 | Agent对`FireBase`进行压制攻击时获得奖励 |
| FacingAreaReward | 2 | Agent面朝目标时获得的持续奖励 |
## Side Channel
为了方便对于环境的调试和实现Unity与Python间非实时通信本环境中使用了ML-Agents提供的Side Channel用于向Python侧传递一些额外的信息。这里我参考了huggingface的[Custom Side Channels](https://github.com/huggingface/ml-agents-patch/blob/develop/docs/Custom-SideChannels.md)。在Unity端Side Channel的实现位于[`AimbotSideChannel.cs`](https://koha9.asuscomm.com/yurugit/Koha9/Aimbot-ParallelEnv/src/branch/main/Assets/Script/GameScript/SideChannel/AimbotSideChannel.cs)和[`AimBotSideChannelController.cs`](https://koha9.asuscomm.com/yurugit/Koha9/Aimbot-ParallelEnv/src/branch/main/Assets/Script/GameScript/SideChannel/AimBotSideChannelController.cs)中。每个Log信息将会以`|`来分隔字段其中第一个字段为Log的类型随后字段为自定义Log信息。在本环境中Unity的SideChannel将会发送所有`LogType.Warning`和`LogType.Error`类型的Log到Python侧。其中`LogType.Warning`会传递一回合结束后的的胜败信息和从Unity发送至Python的指令。胜败信息将类似于`Warning|Result|Win`和`Warning|Result|Lose`。而指令则传递了在下一训练结束后保存模型的命令,它的内容为`Warning|Command`。

BIN
ReadmeImages/FreeTargetFacingreward.png (Stored with Git LFS)

Binary file not shown.

BIN
ReadmeImages/LevelProbabilityPanel.jpg (Stored with Git LFS)

Binary file not shown.

BIN
ReadmeImages/RayCastLayout.png (Stored with Git LFS)

Binary file not shown.

View File

@ -9,13 +9,13 @@ EditorUserSettings:
value: 55090405535008090b5608764626074415151c79747c74602b7c1861e4b96c6c
flags: 0
RecentlyUsedSceneGuid-1:
value: 5453565f53020f085f5d0e72157a0844454f4c2b757d7265757a4f64b7b4313c
value: 06570c0704040b0e5a575520137b5e44154f192e292d22367c2c4866b7b3376f
flags: 0
RecentlyUsedSceneGuid-2:
value: 5703025650035e5d090f087148715d4443161e2c7e2c20357b281b31b0e43060
value: 5453565f53020f085f5d0e72157a0844454f4c2b757d7265757a4f64b7b4313c
flags: 0
RecentlyUsedSceneGuid-3:
value: 06570c0704040b0e5a575520137b5e44154f192e292d22367c2c4866b7b3376f
value: 5703025650035e5d090f087148715d4443161e2c7e2c20357b281b31b0e43060
flags: 0
RecentlyUsedScenePath-0:
value: 22424703114646680e0b0227036c6c111b07142f1f2b233e2867083debf42d

View File

@ -1,30 +1,6 @@
%YAML 1.1
%TAG !u! tag:unity3d.com,2011:
--- !u!114 &1
MonoBehaviour:
m_ObjectHideFlags: 52
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 0}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 12004, guid: 0000000000000000e000000000000000, type: 0}
m_Name:
m_EditorClassIdentifier:
m_PixelRect:
serializedVersion: 2
x: -1015
y: -63
width: 963
height: 708
m_ShowMode: 0
m_Title: Game
m_RootView: {fileID: 4}
m_MinSize: {x: 100, y: 121}
m_MaxSize: {x: 4000, y: 4021}
m_Maximized: 0
--- !u!114 &2
MonoBehaviour:
m_ObjectHideFlags: 52
m_CorrespondingSourceObject: {fileID: 0}
@ -38,67 +14,17 @@ MonoBehaviour:
m_EditorClassIdentifier:
m_PixelRect:
serializedVersion: 2
x: 535
y: 182
width: 1175
height: 749
x: -21
y: 128
width: 1855
height: 1037
m_ShowMode: 4
m_Title: Hierarchy
m_RootView: {fileID: 5}
m_Title: Game
m_RootView: {fileID: 2}
m_MinSize: {x: 875, y: 300}
m_MaxSize: {x: 10000, y: 10000}
m_Maximized: 0
--- !u!114 &3
MonoBehaviour:
m_ObjectHideFlags: 52
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 0}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 12006, guid: 0000000000000000e000000000000000, type: 0}
m_Name: GameView
m_EditorClassIdentifier:
m_Children: []
m_Position:
serializedVersion: 2
x: 0
y: 0
width: 963
height: 708
m_MinSize: {x: 100, y: 121}
m_MaxSize: {x: 4000, y: 4021}
m_ActualView: {fileID: 17}
m_Panes:
- {fileID: 17}
m_Selected: 0
m_LastSelected: 0
--- !u!114 &4
MonoBehaviour:
m_ObjectHideFlags: 52
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 0}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 12010, guid: 0000000000000000e000000000000000, type: 0}
m_Name:
m_EditorClassIdentifier:
m_Children:
- {fileID: 3}
m_Position:
serializedVersion: 2
x: 0
y: 0
width: 963
height: 708
m_MinSize: {x: 100, y: 121}
m_MaxSize: {x: 4000, y: 4021}
vertical: 0
controlID: 105
--- !u!114 &5
--- !u!114 &2
MonoBehaviour:
m_ObjectHideFlags: 52
m_CorrespondingSourceObject: {fileID: 0}
@ -111,22 +37,22 @@ MonoBehaviour:
m_Name:
m_EditorClassIdentifier:
m_Children:
- {fileID: 6}
- {fileID: 8}
- {fileID: 7}
- {fileID: 3}
- {fileID: 5}
- {fileID: 4}
m_Position:
serializedVersion: 2
x: 0
y: 0
width: 1175
height: 749
width: 1855
height: 1037
m_MinSize: {x: 875, y: 300}
m_MaxSize: {x: 10000, y: 10000}
m_UseTopView: 1
m_TopViewHeight: 30
m_UseBottomView: 1
m_BottomViewHeight: 20
--- !u!114 &6
--- !u!114 &3
MonoBehaviour:
m_ObjectHideFlags: 52
m_CorrespondingSourceObject: {fileID: 0}
@ -143,12 +69,12 @@ MonoBehaviour:
serializedVersion: 2
x: 0
y: 0
width: 1175
width: 1855
height: 30
m_MinSize: {x: 0, y: 0}
m_MaxSize: {x: 0, y: 0}
m_LastLoadedLayoutName:
--- !u!114 &7
--- !u!114 &4
MonoBehaviour:
m_ObjectHideFlags: 52
m_CorrespondingSourceObject: {fileID: 0}
@ -164,12 +90,12 @@ MonoBehaviour:
m_Position:
serializedVersion: 2
x: 0
y: 729
width: 1175
y: 1017
width: 1855
height: 20
m_MinSize: {x: 0, y: 0}
m_MaxSize: {x: 0, y: 0}
--- !u!114 &8
--- !u!114 &5
MonoBehaviour:
m_ObjectHideFlags: 52
m_CorrespondingSourceObject: {fileID: 0}
@ -182,19 +108,98 @@ MonoBehaviour:
m_Name:
m_EditorClassIdentifier:
m_Children:
- {fileID: 6}
- {fileID: 9}
- {fileID: 12}
- {fileID: 15}
m_Position:
serializedVersion: 2
x: 0
y: 30
width: 1175
height: 699
width: 1855
height: 987
m_MinSize: {x: 300, y: 200}
m_MaxSize: {x: 24288, y: 16192}
vertical: 0
controlID: 52
controlID: 28
--- !u!114 &6
MonoBehaviour:
m_ObjectHideFlags: 52
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 0}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 12010, guid: 0000000000000000e000000000000000, type: 0}
m_Name:
m_EditorClassIdentifier:
m_Children:
- {fileID: 7}
- {fileID: 8}
m_Position:
serializedVersion: 2
x: 0
y: 0
width: 1038
height: 987
m_MinSize: {x: 100, y: 200}
m_MaxSize: {x: 8096, y: 16192}
vertical: 1
controlID: 16
--- !u!114 &7
MonoBehaviour:
m_ObjectHideFlags: 52
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 0}
m_Enabled: 1
m_EditorHideFlags: 1
m_Script: {fileID: 12006, guid: 0000000000000000e000000000000000, type: 0}
m_Name: GameView
m_EditorClassIdentifier:
m_Children: []
m_Position:
serializedVersion: 2
x: 0
y: 0
width: 1038
height: 594
m_MinSize: {x: 101, y: 121}
m_MaxSize: {x: 4001, y: 4021}
m_ActualView: {fileID: 14}
m_Panes:
- {fileID: 14}
- {fileID: 13}
m_Selected: 0
m_LastSelected: 1
--- !u!114 &8
MonoBehaviour:
m_ObjectHideFlags: 52
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 0}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 12006, guid: 0000000000000000e000000000000000, type: 0}
m_Name: ProjectBrowser
m_EditorClassIdentifier:
m_Children: []
m_Position:
serializedVersion: 2
x: 0
y: 594
width: 1038
height: 393
m_MinSize: {x: 231, y: 271}
m_MaxSize: {x: 10001, y: 10021}
m_ActualView: {fileID: 15}
m_Panes:
- {fileID: 15}
- {fileID: 16}
m_Selected: 0
m_LastSelected: 1
--- !u!114 &9
MonoBehaviour:
m_ObjectHideFlags: 52
@ -212,93 +217,15 @@ MonoBehaviour:
- {fileID: 11}
m_Position:
serializedVersion: 2
x: 0
x: 1038
y: 0
width: 609
height: 699
width: 405
height: 987
m_MinSize: {x: 100, y: 200}
m_MaxSize: {x: 8096, y: 16192}
vertical: 1
controlID: 53
controlID: 29
--- !u!114 &10
MonoBehaviour:
m_ObjectHideFlags: 52
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 0}
m_Enabled: 1
m_EditorHideFlags: 1
m_Script: {fileID: 12006, guid: 0000000000000000e000000000000000, type: 0}
m_Name: SceneView
m_EditorClassIdentifier:
m_Children: []
m_Position:
serializedVersion: 2
x: 0
y: 0
width: 609
height: 501
m_MinSize: {x: 201, y: 221}
m_MaxSize: {x: 4001, y: 4021}
m_ActualView: {fileID: 16}
m_Panes:
- {fileID: 16}
m_Selected: 0
m_LastSelected: 0
--- !u!114 &11
MonoBehaviour:
m_ObjectHideFlags: 52
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 0}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 12006, guid: 0000000000000000e000000000000000, type: 0}
m_Name: ProjectBrowser
m_EditorClassIdentifier:
m_Children: []
m_Position:
serializedVersion: 2
x: 0
y: 501
width: 609
height: 198
m_MinSize: {x: 231, y: 271}
m_MaxSize: {x: 10001, y: 10021}
m_ActualView: {fileID: 18}
m_Panes:
- {fileID: 18}
- {fileID: 19}
m_Selected: 0
m_LastSelected: 1
--- !u!114 &12
MonoBehaviour:
m_ObjectHideFlags: 52
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 0}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 12010, guid: 0000000000000000e000000000000000, type: 0}
m_Name:
m_EditorClassIdentifier:
m_Children:
- {fileID: 13}
- {fileID: 14}
m_Position:
serializedVersion: 2
x: 609
y: 0
width: 336
height: 699
m_MinSize: {x: 100, y: 200}
m_MaxSize: {x: 8096, y: 16192}
vertical: 1
controlID: 78
--- !u!114 &13
MonoBehaviour:
m_ObjectHideFlags: 52
m_CorrespondingSourceObject: {fileID: 0}
@ -315,16 +242,16 @@ MonoBehaviour:
serializedVersion: 2
x: 0
y: 0
width: 336
height: 392
width: 405
height: 611
m_MinSize: {x: 202, y: 221}
m_MaxSize: {x: 4002, y: 4021}
m_ActualView: {fileID: 20}
m_ActualView: {fileID: 17}
m_Panes:
- {fileID: 20}
- {fileID: 17}
m_Selected: 0
m_LastSelected: 0
--- !u!114 &14
--- !u!114 &11
MonoBehaviour:
m_ObjectHideFlags: 52
m_CorrespondingSourceObject: {fileID: 0}
@ -340,18 +267,18 @@ MonoBehaviour:
m_Position:
serializedVersion: 2
x: 0
y: 392
width: 336
height: 307
m_MinSize: {x: 102, y: 121}
m_MaxSize: {x: 4002, y: 4021}
m_ActualView: {fileID: 21}
y: 611
width: 405
height: 376
m_MinSize: {x: 100, y: 100}
m_MaxSize: {x: 4000, y: 4000}
m_ActualView: {fileID: 18}
m_Panes:
- {fileID: 21}
- {fileID: 22}
- {fileID: 18}
- {fileID: 19}
m_Selected: 0
m_LastSelected: 1
--- !u!114 &15
--- !u!114 &12
MonoBehaviour:
m_ObjectHideFlags: 52
m_CorrespondingSourceObject: {fileID: 0}
@ -366,18 +293,18 @@ MonoBehaviour:
m_Children: []
m_Position:
serializedVersion: 2
x: 945
x: 1443
y: 0
width: 230
height: 699
width: 412
height: 987
m_MinSize: {x: 276, y: 71}
m_MaxSize: {x: 4001, y: 4021}
m_ActualView: {fileID: 23}
m_ActualView: {fileID: 20}
m_Panes:
- {fileID: 23}
- {fileID: 20}
m_Selected: 0
m_LastSelected: 0
--- !u!114 &16
--- !u!114 &13
MonoBehaviour:
m_ObjectHideFlags: 52
m_CorrespondingSourceObject: {fileID: 0}
@ -397,10 +324,10 @@ MonoBehaviour:
m_Tooltip:
m_Pos:
serializedVersion: 2
x: 535
y: 212
width: 608
height: 480
x: 65
y: 73
width: 1037
height: 573
m_ViewDataDictionary: {fileID: 0}
m_OverlayCanvas:
m_LastAppliedPresetName: Default
@ -421,9 +348,9 @@ MonoBehaviour:
floating: 0
collapsed: 0
displayed: 1
snapOffset: {x: 0, y: 149}
snapOffset: {x: -141, y: 149}
snapOffsetDelta: {x: 0, y: 0}
snapCorner: 0
snapCorner: 1
id: unity-grid-and-snap-toolbar
index: 1
layout: 1
@ -497,7 +424,7 @@ MonoBehaviour:
containerId: overlay-container--right
floating: 0
collapsed: 0
displayed: 0
displayed: 1
snapOffset: {x: 0, y: 0}
snapOffsetDelta: {x: 0, y: 0}
snapCorner: 0
@ -635,9 +562,9 @@ MonoBehaviour:
m_PlayAudio: 0
m_AudioPlay: 0
m_Position:
m_Target: {x: 462, y: 315.5, z: 0}
m_Target: {x: -93.190475, y: 4.007041, z: -23.907015}
speed: 2
m_Value: {x: 461.9999, y: 315.5, z: -0.00004226652}
m_Value: {x: -93.190475, y: 4.007041, z: -23.907015}
m_RenderMode: 0
m_CameraMode:
drawMode: 0
@ -684,13 +611,13 @@ MonoBehaviour:
m_GridAxis: 1
m_gridOpacity: 0.529
m_Rotation:
m_Target: {x: -0.31611243, y: -0.22831854, z: 0.078667216, w: -0.9174724}
m_Target: {x: -0.04341376, y: 0.5871053, z: 0.031550582, w: 0.80778545}
speed: 2
m_Value: {x: -0.31611243, y: -0.22831854, z: 0.07866721, w: -0.9174724}
m_Value: {x: -0.043411803, y: 0.5870788, z: 0.03154916, w: 0.80774903}
m_Size:
m_Target: 10
m_Target: 12.124355
speed: 2
m_Value: 10
m_Value: 12.124355
m_Ortho:
m_Target: 0
speed: 2
@ -708,14 +635,14 @@ MonoBehaviour:
m_FarClip: 10000
m_DynamicClip: 1
m_OcclusionCulling: 0
m_LastSceneViewRotation: {x: -0.08717229, y: 0.89959055, z: -0.21045254, w: -0.3726226}
m_LastSceneViewRotation: {x: -0.23316237, y: -0.24425557, z: 0.060633, w: -0.93931496}
m_LastSceneViewOrtho: 0
m_ReplacementShader: {fileID: 0}
m_ReplacementString:
m_SceneVisActive: 1
m_LastLockedObject: {fileID: 0}
m_ViewIsLockedToObject: 0
--- !u!114 &17
--- !u!114 &14
MonoBehaviour:
m_ObjectHideFlags: 52
m_CorrespondingSourceObject: {fileID: 0}
@ -735,10 +662,10 @@ MonoBehaviour:
m_Tooltip:
m_Pos:
serializedVersion: 2
x: -1015
y: -63
width: 963
height: 687
x: -21
y: 158
width: 1037
height: 573
m_ViewDataDictionary: {fileID: 0}
m_OverlayCanvas:
m_LastAppliedPresetName: Default
@ -746,17 +673,17 @@ MonoBehaviour:
m_SerializedViewNames: []
m_SerializedViewValues: []
m_PlayModeViewName: GameView
m_ShowGizmos: 0
m_ShowGizmos: 1
m_TargetDisplay: 0
m_ClearColor: {r: 0, g: 0, b: 0, a: 0}
m_TargetSize: {x: 963, y: 666}
m_TargetSize: {x: 1037, y: 552}
m_TextureFilterMode: 0
m_TextureHideFlags: 61
m_RenderIMGUI: 1
m_EnterPlayModeBehavior: 0
m_UseMipMap: 0
m_VSyncEnabled: 0
m_Gizmos: 0
m_Gizmos: 1
m_Stats: 0
m_SelectedSizes: 00000000000000000000000000000000000000000000000000000000000000000000000000000000
m_ZoomArea:
@ -764,10 +691,10 @@ MonoBehaviour:
m_VRangeLocked: 0
hZoomLockedByDefault: 0
vZoomLockedByDefault: 0
m_HBaseRangeMin: -481.5
m_HBaseRangeMax: 481.5
m_VBaseRangeMin: -333
m_VBaseRangeMax: 333
m_HBaseRangeMin: -518.5
m_HBaseRangeMax: 518.5
m_VBaseRangeMin: -276
m_VBaseRangeMax: 276
m_HAllowExceedBaseRangeMin: 1
m_HAllowExceedBaseRangeMax: 1
m_VAllowExceedBaseRangeMin: 1
@ -785,29 +712,29 @@ MonoBehaviour:
serializedVersion: 2
x: 0
y: 21
width: 963
height: 666
width: 1037
height: 552
m_Scale: {x: 1, y: 1}
m_Translation: {x: 481.5, y: 333}
m_Translation: {x: 518.5, y: 276}
m_MarginLeft: 0
m_MarginRight: 0
m_MarginTop: 0
m_MarginBottom: 0
m_LastShownAreaInsideMargins:
serializedVersion: 2
x: -481.5
y: -333
width: 963
height: 666
x: -518.5
y: -276
width: 1037
height: 552
m_MinimalGUI: 1
m_defaultScale: 1
m_LastWindowPixelSize: {x: 963, y: 687}
m_LastWindowPixelSize: {x: 1037, y: 573}
m_ClearInEditMode: 1
m_NoCameraWarning: 1
m_LowResolutionForAspectRatios: 01000000000000000000
m_XRRenderMode: 0
m_RenderTexture: {fileID: 0}
--- !u!114 &18
--- !u!114 &15
MonoBehaviour:
m_ObjectHideFlags: 52
m_CorrespondingSourceObject: {fileID: 0}
@ -827,10 +754,10 @@ MonoBehaviour:
m_Tooltip:
m_Pos:
serializedVersion: 2
x: 535
y: 713
width: 608
height: 177
x: 0
y: 613
width: 1037
height: 372
m_ViewDataDictionary: {fileID: 0}
m_OverlayCanvas:
m_LastAppliedPresetName: Default
@ -848,22 +775,22 @@ MonoBehaviour:
m_SkipHidden: 0
m_SearchArea: 1
m_Folders:
- Assets/Scenes
- Assets/Script/GameScript
m_Globs: []
m_OriginalText:
m_ViewMode: 1
m_StartGridSize: 16
m_LastFolders:
- Assets/Script/PlayMode
- Assets/Script/GameScript
m_LastFoldersGridSize: 16
m_LastProjectPath: C:\Users\UCUNI\OneDrive\Unity\ML-Agents\Aimbot-ParallelEnv
m_LockTracker:
m_IsLocked: 0
m_FolderTreeState:
scrollPos: {x: 0, y: 0}
m_SelectedIDs: 05ca9a3b
m_LastClickedID: 1000000005
m_ExpandedIDs: 000000004294000000ca9a3b
scrollPos: {x: 0, y: 352}
m_SelectedIDs: 7c970000
m_LastClickedID: 38780
m_ExpandedIDs: 000000005e97000060970000629700006497000066970000689700006a9700006c9700006e97000070970000729700007497000076970000789700007a9700007c9700007e970000d297000000ca9a3bffffff7f
m_RenameOverlay:
m_UserAcceptedRename: 0
m_Name:
@ -879,7 +806,7 @@ MonoBehaviour:
m_IsRenaming: 0
m_OriginalEventType: 11
m_IsRenamingFilename: 1
m_ClientGUIView: {fileID: 11}
m_ClientGUIView: {fileID: 8}
m_SearchString:
m_CreateAssetUtility:
m_EndAction: {fileID: 0}
@ -891,7 +818,7 @@ MonoBehaviour:
scrollPos: {x: 0, y: 0}
m_SelectedIDs:
m_LastClickedID: 0
m_ExpandedIDs: 0000000042940000
m_ExpandedIDs: 000000005e97000060970000629700006497000066970000689700006a9700006c9700006e97000070970000729700007497000076970000789700007a9700007c9700007e970000
m_RenameOverlay:
m_UserAcceptedRename: 0
m_Name:
@ -922,20 +849,20 @@ MonoBehaviour:
m_ExpandedInstanceIDs: c62300008a5c000000000000
m_RenameOverlay:
m_UserAcceptedRename: 0
m_Name: Train
m_OriginalName: Train
m_Name:
m_OriginalName:
m_EditFieldRect:
serializedVersion: 2
x: 0
y: 0
width: 0
height: 0
m_UserData: 38084
m_UserData: 0
m_IsWaitingForDelay: 0
m_IsRenaming: 0
m_OriginalEventType: 0
m_OriginalEventType: 11
m_IsRenamingFilename: 1
m_ClientGUIView: {fileID: 11}
m_ClientGUIView: {fileID: 8}
m_CreateAssetUtility:
m_EndAction: {fileID: 0}
m_InstanceID: 0
@ -947,7 +874,7 @@ MonoBehaviour:
m_GridSize: 16
m_SkipHiddenPackages: 0
m_DirectoriesAreaWidth: 355
--- !u!114 &19
--- !u!114 &16
MonoBehaviour:
m_ObjectHideFlags: 52
m_CorrespondingSourceObject: {fileID: 0}
@ -967,15 +894,15 @@ MonoBehaviour:
m_Tooltip:
m_Pos:
serializedVersion: 2
x: 64
y: 780
width: 961
height: 259
x: 65
y: 667
width: 1041
height: 372
m_ViewDataDictionary: {fileID: 0}
m_OverlayCanvas:
m_LastAppliedPresetName: Default
m_SaveData: []
--- !u!114 &20
--- !u!114 &17
MonoBehaviour:
m_ObjectHideFlags: 52
m_CorrespondingSourceObject: {fileID: 0}
@ -995,10 +922,10 @@ MonoBehaviour:
m_Tooltip:
m_Pos:
serializedVersion: 2
x: 1144
y: 212
width: 334
height: 371
x: 1017
y: 158
width: 403
height: 590
m_ViewDataDictionary: {fileID: 0}
m_OverlayCanvas:
m_LastAppliedPresetName: Default
@ -1006,9 +933,9 @@ MonoBehaviour:
m_SceneHierarchy:
m_TreeViewState:
scrollPos: {x: 0, y: 0}
m_SelectedIDs: 5ef40000
m_LastClickedID: 62558
m_ExpandedIDs: 9aebffff80ecffff2aeeffff2aefffff60f2ffff22f3ffff72f5ffffbaf5ffffdaf5ffff32fbffff86f50000
m_SelectedIDs: 30740000
m_LastClickedID: 29744
m_ExpandedIDs: 32fbffffea73000064740000187900003e7b0000047c0000
m_RenameOverlay:
m_UserAcceptedRename: 0
m_Name:
@ -1024,7 +951,7 @@ MonoBehaviour:
m_IsRenaming: 0
m_OriginalEventType: 11
m_IsRenamingFilename: 0
m_ClientGUIView: {fileID: 13}
m_ClientGUIView: {fileID: 10}
m_SearchString:
m_ExpandedScenes: []
m_CurrenRootInstanceID: 0
@ -1032,7 +959,7 @@ MonoBehaviour:
m_IsLocked: 0
m_CurrentSortingName: TransformSorting
m_WindowGUID: 4c969a2b90040154d917609493e03593
--- !u!114 &21
--- !u!114 &18
MonoBehaviour:
m_ObjectHideFlags: 52
m_CorrespondingSourceObject: {fileID: 0}
@ -1052,15 +979,15 @@ MonoBehaviour:
m_Tooltip:
m_Pos:
serializedVersion: 2
x: 1144
y: 604
width: 334
height: 286
x: 1017
y: 769
width: 403
height: 355
m_ViewDataDictionary: {fileID: 0}
m_OverlayCanvas:
m_LastAppliedPresetName: Default
m_SaveData: []
--- !u!114 &22
--- !u!114 &19
MonoBehaviour:
m_ObjectHideFlags: 52
m_CorrespondingSourceObject: {fileID: 0}
@ -1072,7 +999,7 @@ MonoBehaviour:
m_Script: {fileID: 11500000, guid: 6c262c1329a02fa49b5cb4c297106f3f, type: 3}
m_Name:
m_EditorClassIdentifier:
m_MinSize: {x: 129, y: 28}
m_MinSize: {x: 136, y: 30}
m_MaxSize: {x: 4000, y: 4000}
m_TitleContent:
m_Text: ProBuilder
@ -1088,7 +1015,7 @@ MonoBehaviour:
m_OverlayCanvas:
m_LastAppliedPresetName: Default
m_SaveData: []
--- !u!114 &23
--- !u!114 &20
MonoBehaviour:
m_ObjectHideFlags: 52
m_CorrespondingSourceObject: {fileID: 0}
@ -1108,10 +1035,10 @@ MonoBehaviour:
m_Tooltip:
m_Pos:
serializedVersion: 2
x: 1480
y: 212
width: 229
height: 678
x: 1422
y: 158
width: 411
height: 966
m_ViewDataDictionary: {fileID: 0}
m_OverlayCanvas:
m_LastAppliedPresetName: Default
@ -1123,7 +1050,7 @@ MonoBehaviour:
m_ControlHash: -371814159
m_PrefName: Preview_InspectorPreview
m_LastInspectedObjectInstanceID: -1
m_LastVerticalScrollValue: 0
m_LastVerticalScrollValue: 280
m_GlobalObjectId:
m_InspectorMode: 0
m_LockTracker: