using System; using System.Collections.Generic; using System.Linq; using UnityEngine; public class AgentController : MonoBehaviour { 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; public float vX = 0f; public float vZ = 0f; public Vector3 nowMovement; public float acceleration = 0.9f; // 加速度 public float mouseXSensitivity = 100; public float mouseYSensitivity = 200; public float yRotation = 0.1f;//定义一个浮点类型的量,记录‘围绕’X轴旋转的角度 private List spinRecord = new List(); private bool lockMouse; private float damage; private float fireRate; private bool lockCameraX; private bool lockCameraY; // environment private float lastShootTime = 0.0f; private int enemyKillCount = 0; private Vector3 killEnemyPosition; public bool defaultTPCamera = true; [System.NonSerialized] public bool gunReadyToggle = true; 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 ParameterContainer paramContainer; private SceneBlockContainer blockContainer; private TargetController targetCon; private void Start() { // initialize scripts paramContainer = parameterContainerObj.GetComponent(); blockContainer = sceneBlockContainerObj.GetComponent(); targetCon = targetControllerObj.GetComponent(); raySensors = GetComponent(); playerController = this.transform.GetComponent(); // initialize Environment parameters lockMouse = paramContainer.lockMouse; damage = paramContainer.damage; fireRate = paramContainer.fireRate; lockCameraX = paramContainer.lockCameraX; lockCameraY = paramContainer.lockCameraY; // initialize remainTime // this agent's tag myTag = gameObject.tag; } #region Agent Move Control public void MoveAgent(int vertical, int horizontal) { // Vector3 nowMovement; if (horizontal != 0)//当按下按键(水平方向) { if (vX < moveSpeed && vX > -moveSpeed)//当前速度小于最大速度 { vX += (float)horizontal * acceleration;//增加加速度 } else { //防止在一瞬间切换输入时速度仍保持不变 if ((vX * horizontal) > 0)//输入与当前速度方向同向 { vX = (float)horizontal * moveSpeed; //限制最大速度 } else { vX += (float)horizontal * acceleration;//增加加速度 } } } else { if (Math.Abs(vX) > 0.001) { vX -= (vX / Math.Abs(vX)) * acceleration;//减少加速度 } else { vX = 0; } } if (vertical != 0)//当按下按键(垂直方向) { if (vZ < moveSpeed && vZ > -moveSpeed)//当前速度小于最大速度 { vZ += (float)vertical * acceleration;//增加加速度 } else { if ((vZ * vertical) > 0)//输入与当前速度方向同向 { vZ = (float)vertical * moveSpeed; //限制最大速度 } else { vZ += (float)vertical * acceleration;//增加加速度 } } } else { if (Math.Abs(vZ) > 0.001) { vZ -= (vZ / Math.Abs(vZ)) * acceleration;//减少加速度 } else { vZ = 0; } } nowMovement = (transform.forward * vZ + transform.right * vX); //PlayerController下的.Move为实现物体运动的函数 //Move()括号内放入一个Vector3类型的量,本例中为Player_Move if (nowMovement.magnitude > moveSpeed) { nowMovement = nowMovement.normalized * moveSpeed; } playerController.Move(nowMovement * Time.deltaTime); // update Key Viewer } #endregion Agent Move Control #region Camera Control public void CameraControl(float Mouse_X, float Mouse_Y) { //Mouse_X = Input.GetAxis("Mouse X") * MouseSensitivity * Time.deltaTime; //Debug.Log(Input.GetAxis("Mouse X")); //Mouse_Y = Input.GetAxis("Mouse Y") * MouseSensitivity * Time.deltaTime; if (lockCameraX) { Mouse_X = 0; } if (lockCameraY) { Mouse_Y = 0; } yRotation = yRotation - Mouse_Y; //xRotation值为正时,屏幕下移,当xRotation值为负时,屏幕上移 //当鼠标向上滑动,Mouse_Y值为正,xRotation-Mouse_Y的值为负,xRotation总的值为负,屏幕视角向上滑动 //当鼠标向下滑动,Mouse_Y值为负,xRotation-Mouse_Y的值为正,xRotation总的值为正,屏幕视角向下滑动 //简单来说就是要控制鼠标滑动的方向与屏幕移动的方向要相同 //limit UP DOWN between -90 -> 90 yRotation = Mathf.Clamp(yRotation, -90f, 90f); //相机左右旋转时,是以Y轴为中心旋转的,上下旋转时,是以X轴为中心旋转的 transform.Rotate(Vector3.up * Mouse_X); //Vector3.up相当于Vector3(0,1,0),CameraRotation.Rotate(Vector3.up * Mouse_X)相当于使CameraRotation对象绕y轴旋转Mouse_X个单位 //即相机左右旋转时,是以Y轴为中心旋转的,此时Mouse_X控制着值的大小 //相机在上下旋转移动时,相机方向不会随着移动,类似于低头和抬头,左右移动时,相机方向会随着向左向右移动,类似于向左向右看 //所以在控制相机向左向右旋转时,要保证和父物体一起转动 fpsCam.transform.localRotation = Quaternion.Euler(yRotation, 0, 0); //this.transform指这个CameraRotation的位置,localRotation指的是旋转轴 //transform.localRotation = Quaternion.Eular(x,y,z)控制旋转的时候,按照X-Y-Z轴的旋转顺规 //即以围绕X轴旋转x度,围绕Y轴旋转y度,围绕Z轴旋转z度 //且绕轴旋转的坐标轴是父节点本地坐标系的坐标轴 } #endregion Camera Control #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(ray.origin, ray.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") { // kill enemy GameObject gotHitObj = hit.transform.gameObject;//获取受到Ray撞击的对象 gotHitObj.GetComponent().ReactToHit(damage, gameObject); shootState = 0; return targetCon.HitEnemyReward(gotHitObj.transform.position); } } if (targetCon.targetTypeInt == (int)SceneBlockContainer.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(ray.origin, viewPoint-ray.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; Ray ray = fpsCam.ScreenPointToRay(new Vector3(fpsCam.pixelWidth / 2, fpsCam.pixelHeight / 2, 0)); if (targetCon.targetTypeInt == (int)SceneBlockContainer.Targets.Free) { //free mode RaycastHit hit; if (Physics.Raycast(ray, 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 projectionDis = new List(); foreach (GameObject theEnemy in raySensors.inViewEnemies) { // for each enemy in view Vector3 projection = Vector3.Project(theEnemy.transform.position - transform.position, (ray.direction * 10)); Vector3 verticalToRay = transform.position + projection - theEnemy.transform.position; projectionDis.Add(verticalToRay.magnitude); // Debug.Log("enemy!" + verticalToRay.magnitude); // Debug.DrawRay(transform.position, (ray.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); } } else if (targetCon.targetTypeInt == (int)SceneBlockContainer.Targets.Attack) { // attack mode // Target to Agent distance float targetDis = Vector3.Distance(blockContainer.nowBlock.transform.position, transform.position); // center of screen between target's distance float camCenterToTarget = Vector3.Distance(ray.origin + (ray.direction * targetDis), blockContainer.nowBlock.transform.position); 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(ray.origin, viewPoint-ray.origin, Color.blue); nowReward = paramContainer.facingReward; } else { // while not facing to target nowReward = (lastTargetFacingDistance - camCenterToTarget) * paramContainer.facingTargetReward; } } // update lastTargetFacingDistance lastTargetFacingDistance = camCenterToTarget; } 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; this.killEnemyPosition = killEnemyPosition; } public void UpdateLockMouse() { // lock mouse based on paramContainer lockMouse if (lockMouse) { Cursor.lockState = CursorLockMode.Locked; } } public void UpdateGunState() { // update gun state if ((Time.time - lastShootTime) >= fireRate) { gunReadyToggle = true; } else { gunReadyToggle = false; } } }