public float CalculateSEUtility(DriveType mainDriveType, Agent agent, Mapping mapping) { Entity target = GetEntityTarget(agent, mapping); float amount = outputChangeType.CalculateAmount(agent, target, this, mapping); mapping.previousOutputChangeAmount = amount; float utility = outputChangeType.CalculateSEUtility(agent, target, this, mapping, mainDriveType, amount); // See if this change will also change any of the usesEquation Drives float equationDrivesUtility = 0f; foreach (DriveType driveType in agent.ActiveDrives().Keys) { // If this drive is the main drive for the plan it should not be considered a side-effect // it will be considered in the rate of drive change for the plan if (driveType.syncType == DriveType.SyncType.Equation && driveType != mainDriveType && driveType.includeInSECalculations) { // Only looks at this specfic OutputChange float equationDrivesAmount = driveType.driveTypeEquation.CalculateEquationDriveLevelChange(agent, driveType, mapping, this); // DriveType to figure this change of drive level into the side effect utility // TODO: Just use value for now since the other side effects use value - add in utility later equationDrivesUtility += equationDrivesAmount * (driveType.sideEffectValue < 0.01f ? 1f : driveType.sideEffectValue); } } //Debug.Log(agent.name + ": " + mapping.mappingType.name + ": val: " + value + " amt: " + amount + " equ utl: " + equationDrivesUtility + " tot: " + (value * amount - equationDrivesUtility)); //Debug.Log(value + " * " + amount + " - " + equationDrivesUtility); // Subtract the equationDrivesUtility since a positive value is bad (drive increases) and a negative value is good (drive decreases) return(utility - equationDrivesUtility); }
private void SwitchToAgent(int agentIndex) { agent = agents[agentIndex]; // Change virtual camera to follow newly selected agent //vCamera.Follow = agent.transform; driveSliders = new List <Slider>(); Transform driveTypeParent = transform.Find("DriveType Levels"); foreach (Transform child in driveTypeParent) { Destroy(child.gameObject); } int i = 0; foreach (DriveType driveType in agent.ActiveDrives().Keys) { if (driveType.showInUI) { RectTransform newDriveType = Instantiate(driveTypeSliderPrefab, driveTypeParent); newDriveType.transform.position = new Vector3(newDriveType.transform.position.x, newDriveType.transform.position.y - 30 * i); newDriveType.GetComponentInChildren <TextMeshProUGUI>().text = driveType.name; driveSliders.Add(newDriveType.GetComponentInChildren <Slider>()); ++i; } } }
public override bool Check(InputCondition inputCondition, Agent agent, Mapping mapping, Entity target, bool isRecheck) { Agent targetAgent = target as Agent; if (targetAgent != null) { DriveType driveType = (DriveType)inputCondition.levelType; if (targetAgent.ActiveDrives().ContainsKey(driveType)) { float driveLevel = targetAgent.drives[driveType].GetLevel(); if (driveLevel >= inputCondition.min && driveLevel <= inputCondition.max) { return(true); } } } return(false); }
public virtual bool GetPlans(Agent agent, Plans previousPlans, int previousPlansIndex, bool previouslyInterrupted, out DriveType currentDriveType, out Dictionary <DriveType, float> currentDriveTypesRanked, out Dictionary <DriveType, Plans> allPlans) { currentDriveType = null; Plans currentPlans = null; // Find mapping for highest priority drive currentDriveTypesRanked = RankDrives(agent.ActiveDrives()); allPlans = new Dictionary <DriveType, Plans>(); foreach (DriveType driveType in currentDriveTypesRanked.Keys) { currentPlans = plannerTypes[0].CreatePlansForDriveType(agent, driveType, false); if (currentPlans == null) { return(false); } allPlans.Add(driveType, currentPlans); //Debug.Log(agent.name + ": GetPlans for " + driveType.name + " found " + currentPlans.rootMappings.Count + " plans."); // If there is at least one complete plan we quit early // TODO: Improve this - planner should mark the Plans statuses // TODO: Add a minimum utility threshold so it doesn't quit and go with a bad plan List <Mapping> excludeRootMappings = null; if (previouslyInterrupted && previousPlans != null) { excludeRootMappings = new List <Mapping>() { previousPlans.rootMappings[previousPlansIndex] } } ; if (currentPlans.GetCompletePlans(excludeRootMappings).Count > 0) { //Debug.Log(agent.name + ": GetPlans for " + driveType.name + " found a completed plan."); currentDriveType = driveType; return(true); } } return(false); }
void Update() { if (Input.GetKeyDown("p")) { if (Time.timeScale == 1) { Time.timeScale = 0; } else { Time.timeScale = 1; } } if (!EventSystem.current.IsPointerOverGameObject()) { if (selectedEntityType != null) { Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition); RaycastHit hit; if (Physics.Raycast(ray, out hit, 300, LayerMask.GetMask("Ground", "WorldObject"))) { Vector3 locationOnGrid = hit.point; if (terrainActions != null) { locationOnGrid = terrainActions.FindGridLocation(hit.point, currentBuildHeight); } //Vector3 locationOnGrid = terrainActions.FindGridLocation(hit.point, currentBuildHeight); locationOnGrid.y = hit.point.y; // Place selected entity outline in the world at hit location if (entityGhost != null) { if (!entityGhost.activeSelf) { entityGhost.SetActive(true); } entityGhost.transform.position = locationOnGrid; // TODO: What about moving agents? - Do a OnTriggerEnter for the ghosts which could just clear out previousGhostPosition if (previousGhostPosition != entityGhost.transform.position) { buildableStatus = GetBuildableStatus(selectedEntityType, entityGhost); if (selectedEntityType.rotateToTerrain) { // Rotate and transform ghost so that it is just above the terrain entityGhost.transform.up = hit.normal; } else if (terrainActions != null) { // Calculate distance to move entityGhost in its up direction (normal to terrain) terrainActions.MoveObjectAboveTerrain(entityGhost); } previousGhostPosition = entityGhost.transform.position; //TODO: Makes a utility function for changing all of a nested objects materials Renderer[] renderers = entityGhost.GetComponentsInChildren <Renderer>(); if (renderers.Length == 0) { Debug.LogError("Entity Ghost has no renderer! " + entityGhost.name); } Material material; if (buildableStatus == BuildableStatus.Invalid) { material = invalidPlacementMaterial; } else { material = validPlacementMaterial; } for (int i = 0; i < renderers.Length; i++) { if (renderers[i].material != material) { Material[] materials = new Material[renderers[i].materials.Length]; for (var j = 0; j < renderers[i].materials.Length; j++) { materials[j] = material; } renderers[i].materials = materials; } } } if (Input.GetKeyDown("r")) { entityGhost.transform.Rotate(new Vector3(0, 90, 0)); previousGhostPosition = Vector3.zero; } } if (Input.GetMouseButtonDown(0)) { Quaternion rotation = Quaternion.identity; Vector3 scale = Vector3.one; if (entityGhost != null) { rotation = entityGhost.transform.rotation; scale = entityGhost.transform.localScale; // TODO: Fix this so its not so much of a hack - the .02 prevents the flashing from ghost over placed object // Could make every ghost be .02 larger and then always subtract .02 - also only do scale is FI can be scaled if (scale.x.ToString().IndexOf(".02") != -1) { scale.x = Mathf.Round(scale.x); scale.y = Mathf.Round(scale.y); scale.z = Mathf.Round(scale.z); } } // Create Entity with owner permissions set to selected faction and the selected agent if not in overhead view List <Agent> owners = new List <Agent>(); //if (!overheadCamera.enabled) // owners.Add(agent); GameObject newGameObject; if (entityGhost != null) { newGameObject = selectedEntityType.CreateEntity(prefabVariantIndex, entityGhost.transform.position, rotation, scale, agent); } else { newGameObject = selectedEntityType.CreateEntity(prefabVariantIndex, locationOnGrid, rotation, scale, agent); } if (selectedEntityType is AgentType) { // Add agent to the list of agents newGameObject.name = selectedEntityType.name; Agent a = newGameObject.GetComponent <Agent>(); agents.Add(a); AddAgent(a); } } } else if (entityGhost != null && entityGhost.activeSelf) { entityGhost.SetActive(false); } } else { // No Entity Selected - Allow player to select entities with mouse click // TODO: Add a create box select (hold mouse button down) and multi-entity select if (Input.GetMouseButtonDown(0)) { Debug.Log("Mouse Button Clicked"); Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition); Entity entity = null; if (for2D) { RaycastHit2D[] hits = Physics2D.RaycastAll(Camera.main.ScreenToWorldPoint(Input.mousePosition), Vector2.zero, LayerMask.GetMask("Agent", "WorldObject", "AgentEvent")); if (hits.Length > 0) { float bestDistance = float.PositiveInfinity; foreach (RaycastHit2D hit in hits) { if (hit.distance < bestDistance) { Entity[] hitEntities = hit.collider.gameObject.GetComponentsInParent <Entity>(); foreach (Entity hitEntity in hitEntities) { if (hitEntity.inEntityInventory == null && hitEntity.enabled) { bestDistance = hit.distance; entity = hitEntity; } } } } } } else { RaycastHit[] hits = Physics.RaycastAll(ray, 500, LayerMask.GetMask("Agent", "WorldObject", "AgentEvent"), QueryTriggerInteraction.Collide); if (hits.Length > 0) { float bestDistance = float.PositiveInfinity; foreach (RaycastHit hit in hits) { if (hit.distance < bestDistance) { Entity[] hitEntities = hit.collider.gameObject.GetComponentsInParent <Entity>(); foreach (Entity hitEntity in hitEntities) { if (hitEntity.inEntityInventory == null && hitEntity.enabled) { bestDistance = hit.distance; entity = hitEntity; } } } } } } } } } if (driveSliders == null) { Transform driveTypeParent = transform.Find("DriveType Levels"); driveSliders = new List <Slider>(); int i = 0; foreach (DriveType driveType in agent.ActiveDrives().Keys) { if (driveType.showInUI) { RectTransform newDriveType = Instantiate(driveTypeSliderPrefab, driveTypeParent); newDriveType.transform.position = new Vector3(newDriveType.transform.position.x, newDriveType.transform.position.y - 30 * i); newDriveType.GetComponentInChildren <TextMeshProUGUI>().text = driveType.name; driveSliders.Add(newDriveType.GetComponentInChildren <Slider>()); ++i; } } } driveText.text = ""; int sliderIndex = 0; foreach (var drive in agent.ActiveDrives()) { if (drive.Key.name != "None" && drive.Key.showInUI) { float driveLevel = drive.Value.GetLevel(); driveText.text += drive.Key.name + ": " + Mathf.Round(driveLevel) + "\n"; driveSliders[sliderIndex].value = driveLevel / 100f; sliderIndex++; } } drivesRankedText.text = ""; int driveNumber = 1; if (agent.decider.currentDriveTypesRanked != null) { foreach (KeyValuePair <DriveType, float> drive in agent.decider.currentDriveTypesRanked) { drivesRankedText.text += driveNumber + ": " + drive.Key.name + " (" + System.Math.Round(drive.Value, 3) + ")\n"; ++driveNumber; } } if (agent.decider.CurrentDriveType != null) { drivesRankedText.text += "Current: " + agent.decider.CurrentDriveType.name + "\n"; } else { drivesRankedText.text += "Current: None\n"; } if (showAgentsMappingType) { for (int i = 0; i < markerTexts.Count; i++) { if (!agents[i].gameObject.activeSelf) { if (agents[i] == agent) { HandleCameraSwitching(true); } agents.Remove(agents[i]); GameObject item = markerTexts[i].gameObject; markerTexts.Remove(markerTexts[i]); Destroy(item); continue; } SetMarkerAboveObject(agents[i].gameObject, markerTexts[i]); markerTexts[i].color = Color.white; if (agents[i].decider.CurrentMapping != null) { markerTexts[i].text = agents[i].decider.CurrentMapping.mappingType.name; } else { markerTexts[i].text = "None"; } /* * // TODO: Get this to float up and not cover the mappingType name * HistoryType.OutputChangeLog outputChangeLog = agents[i].historyType.GetLastOutputChangeLog(agents[i]); * if (outputChangeLog != null && outputChangeLog.time > Time.time - 2f) * { * markerTexts[i].text = outputChangeLog.outputChange.ToString(); * if (outputChangeLog.outputChange.floatValue != outputChangeLog.amount) * markerTexts[i].text += "\nActual Amount = " + outputChangeLog.amount; * if (outputChangeLog.succeeded) * markerTexts[i].color = Color.green; * else * markerTexts[i].color = Color.red; * } */ } } HandleCameraSwitching(); // TODO: Only do this every X seconds nameText.text = agent.name + " - " + agent.CurrentAge(); timeText.text = timeManager.PrettyPrintDateTime(); }
// Agent is acting - should the current Mapping be interrupted? // This will not start a new plan - it will just cause the current plan to stop and then plan next time public virtual bool ShouldInterrupt(Agent agent, Mapping currentMapping, DriveType currentDriveType, float currentPlanUtility, float lastReplanTime, out float newReplanTime) { newReplanTime = lastReplanTime; if (currentMapping.mappingType.actionType.interruptType == ActionType.InterruptType.Never) { return(false); } if (currentMapping.mappingType.actionType.replanFrequency > Time.time - lastReplanTime) { return(false); } // Replan and see if there is reason to switch up plans newReplanTime = Time.time; // Find mapping for highest priority drive Dictionary <DriveType, float> currentDriveTypesRanked = RankDrives(agent.ActiveDrives()); Plans currentPlans; foreach (DriveType driveType in currentDriveTypesRanked.Keys) { if ((currentMapping.mappingType.actionType.interruptType == ActionType.InterruptType.Always) || (currentMapping.mappingType.actionType.interruptType == ActionType.InterruptType.OnlyDrivesThatCanInterrupt && driveType.canCauseInterruptions) || (currentMapping.mappingType.actionType.interruptType == ActionType.InterruptType.OnlySpecifiedDrives && currentMapping.mappingType.actionType.interruptingDriveTypes.Contains(driveType))) { Debug.Log(agent.name + ": Checking to see if should interrupt " + currentMapping + " for " + driveType); currentPlans = plannerTypes[0].CreatePlansForDriveType(agent, driveType, true); Mapping bestRootMapping = GetBestPlan(agent, currentPlans, out float bestUtility); // TODO: Should this log the generated Plans? // If it finds one plan that is good enough then interrupt List <Mapping> rootMappings = currentPlans.GetCompletePlans(); if (rootMappings.Count > 0) { foreach (Mapping rootMapping in rootMappings) { // Since this is in the middle of the action the last planning utility calculation for current plan // Should be good enough - but it might make sense to recalulate the utility for the current plan if (bestUtility - currentPlanUtility > currentMapping.mappingType.actionType.minGreaterUtilityToInterrupt) { Debug.Log(agent.name + ": Found better plan - interrupting " + currentMapping); Debug.Log(agent.name + ": New Root Mapping " + rootMapping); Debug.Log(agent.name + ": Utility change: " + currentPlanUtility + " -> " + bestUtility); plannerTypes[0].NotifyOfInterrupt(agent, currentPlans, rootMapping); return(true); } else { Debug.Log(agent.name + ": NOT interrupting " + currentMapping); Debug.Log(agent.name + ": Checked Root Mapping " + rootMapping); Debug.Log(agent.name + ": Utility change: " + currentPlanUtility + " -> " + bestUtility); agent.historyType.RecordDeciderLog(agent, HistoryType.DeciderRunType.PlannedNoInterrupt, currentMapping); } } } } if (driveType == currentDriveType) { // Don't consider DriveTypes ranked lower than the current DriveType break; } } return(false); }