protected override void SetEntityComponents(Entity entity, EntityManager entityManager) { if (MapManager.ActiveMap == null) { var mapManager = FindObjectOfType <MapManager>(); Debug.Assert(mapManager != null, "You must have an Map Manager object in the scene in order to be able to author units from the editor!", this); mapManager.LoadMap(mapManager.mapToLoad); } Layout layout = MapManager.ActiveMap.layout; var hexPos = layout.WorldToFractionalHex((FixVector2)transform.position); entityManager.AddComponentData <Group>(entity, new Group()); entityManager.AddComponentData <HexPosition>(entity, new HexPosition() { HexCoordinates = hexPos }); entityManager.AddComponentData <DirectionAverage>(entity, new DirectionAverage() { Value = FractionalHex.Zero, PreviousDirection1 = FractionalHex.Zero, PreviousDirection2 = FractionalHex.Zero }); //pathfinding entityManager.AddBuffer <PathWaypoint>(entity); entityManager.AddComponentData <PathWaypointIndex>(entity, new PathWaypointIndex() { Value = 0 }); entityManager.AddComponentData <WaypointReachedDistance>(entity, new WaypointReachedDistance() { Value = (Fix64)parentWaypointReachedDistance }); entityManager.AddComponentData <RefreshPathTimer>(entity, new RefreshPathTimer() { TurnsRequired = turnsToRefreshParentPath, TurnsWithoutRefresh = 0 }); //selection entityManager.AddComponentData <Selectable>(entity, new Selectable()); //collider entityManager.AddComponentData <Collider>(entity, new Collider() { Radius = (Fix64)0.5, Layer = ColliderLayer.GROUP }); //target find entityManager.AddComponentData <SightRange>(entity, new SightRange() { Value = (Fix64)sightRange }); entityManager.AddComponentData <ActTargetFilters>(entity, new ActTargetFilters() { ActOnEnemies = actOnEnemyTeam, ActOnTeamates = actOnTeamates, actType = actType }); entityManager.AddBuffer <BEPosibleTarget>(entity); //Target AI entityManager.AddComponentData <GroupBehaviour>(entity, new GroupBehaviour() { Value = behaviour }); //team entityManager.AddComponentData <Team>(entity, new Team() { Number = team }); //movement entityManager.AddComponentData <MovementState>(entity, new MovementState() { HexOcuppied = hexPos.Round(), PreviousStepDestiantionReached = false, DestinationReached = false, DestinationIsReachedDistance = (Fix64)destinationReachedDistance }); entityManager.AddComponentData <Speed>(entity, new Speed() { Value = (Fix64)parentSpeed }); entityManager.AddComponentData <DestinationHex>(entity, new DestinationHex() { FinalDestination = MapUtilities.FindClosestOpenHex(hexPos, MapManager.ActiveMap.map, true) }); // steering entityManager.AddComponentData <SteeringTarget>(entity, new SteeringTarget() { TargetPosition = hexPos, StopAtTarget = false }); entityManager.AddComponentData <DesiredMovement>(entity, new DesiredMovement()); //commands entityManager.AddComponentData <Commandable>(entity, new Commandable() { DeafaultCommand = CommandType.MOVE_COMMAND }); entityManager.AddComponentData <CommandableDeathFlag>(entity, new CommandableDeathFlag()); entityManager.AddComponentData <TriggerPathfinding>(entity, new TriggerPathfinding() { Destination = hexPos.Round() }); //dstManager.AddComponentData<TPAtMouseClick>(entity, new TPAtMouseClick()); //Debug.Log("converting the parent entity"); }
//el steering inicialmente será con aceleración infinita //como inicio lo unico que hará es mover //this implementation is based on the ECS samples one //collecting the data protected override void OnUpdate() { var map = MapManager.ActiveMap; #region On Reinforcement Unit Steering Entities.WithAll <OnReinforcement>().ForEach((ref DesiredMovement desiredMovement, ref HexPosition position, ref Speed speed, ref SteeringTarget target) => { var postionDelta = target.TargetPosition - position.HexCoordinates; var distance = postionDelta.Lenght(); var maxSpeedMovementDistance = speed.Value * MainSimulationLoopSystem.SimulationDeltaTime; if (distance <= Fix64.Zero) { desiredMovement.Value = FractionalHex.Zero; return; } if (target.StopAtTarget) { if (distance <= maxSpeedMovementDistance) { desiredMovement.Value = postionDelta.NormalizedManhathan() * distance; return; } } desiredMovement.Value = postionDelta.NormalizedManhathan() * maxSpeedMovementDistance; }); #endregion #region Unit On Group Steering int OnGroupUnitCount = m_OnGroupUnitQuerry.CalculateEntityCount(); //voy a crear más basura de lo necesario por mas readibilidad. var entityToIndex = new Dictionary <int, int>(OnGroupUnitCount); var groupCohesions = new FractionalHex[OnGroupUnitCount]; var groupAlignements = new FractionalHex[OnGroupUnitCount]; var groupsUnitsHashMap = new Dictionary <int, List <int> >(OnGroupUnitCount); var groupCount = new int[OnGroupUnitCount]; var groupIndices = new int[OnGroupUnitCount]; var unitGroupalSeparations = new FractionalHex[OnGroupUnitCount]; var unitSeparationValues = new FractionalHex[OnGroupUnitCount]; var unitPositions = new FractionalHex[OnGroupUnitCount]; var unitDirections = new FractionalHex[OnGroupUnitCount]; var unitSeparations = new FractionalHex[OnGroupUnitCount]; var unitSeparationCounts = new int[OnGroupUnitCount]; var unitSeparationDistances = new Fix64[OnGroupUnitCount]; var unitSingleSeparationDistances = new Fix64[OnGroupUnitCount]; //made only because the foreach delegate don't have enough slots for all the components needed var unitSpeeds = new Fix64[OnGroupUnitCount]; //InitialFillOnGroupSteeringCollections int entityCollectionIndex = 0; Entities.WithAll <OnGroup>().ForEach((Entity entity, Parent parent, ref Steering steering, ref Speed speed, ref HexPosition position, ref DirectionAverage directionAverage) => { groupCohesions[entityCollectionIndex] = position.HexCoordinates; groupAlignements[entityCollectionIndex] = directionAverage.Value; unitSeparationCounts[entityCollectionIndex] = 0; unitSeparationDistances[entityCollectionIndex] = steering.separationDistance; unitSingleSeparationDistances[entityCollectionIndex] = steering.singleSeparationDistance; unitSpeeds[entityCollectionIndex] = speed.Value; unitPositions[entityCollectionIndex] = position.HexCoordinates; unitDirections[entityCollectionIndex] = directionAverage.Value; //hash map int parentIndex = parent.ParentEntity.Index; List <int> sameGroupIndices; if (groupsUnitsHashMap.TryGetValue(parentIndex, out sameGroupIndices)) { sameGroupIndices.Add(entityCollectionIndex); groupsUnitsHashMap[parentIndex] = sameGroupIndices; } else { sameGroupIndices = new List <int>(); sameGroupIndices.Add(entityCollectionIndex); groupsUnitsHashMap.Add(parentIndex, sameGroupIndices); } entityToIndex.Add(entity.Index, entityCollectionIndex); entityCollectionIndex++; }); CompleteOnGroupSteeringCollections(unitGroupalSeparations, unitSingleSeparationDistances, unitPositions, unitDirections, groupCohesions, groupAlignements, groupsUnitsHashMap, groupCount, groupIndices, unitSeparations, unitSeparationCounts, unitSeparationDistances); Entities.WithAll <OnGroup>().ForEach((Entity entity, Parent parent, ref DesiredMovement desiredMovement, ref Steering steering, ref SteeringTarget target, ref HexPosition position, ref DirectionAverage directionAverage) => { if (!entityToIndex.TryGetValue(entity.Index, out int entityIndex)) { Debug.LogError("There aren't an index asociated with the current entity. Check the assignation of the entity to index dictionary"); } var currentPosition = position.HexCoordinates; var targetPostion = target.TargetPosition; var satisfactionRadius = steering.satisfactionDistance; if (currentPosition.Distance(targetPostion) < satisfactionRadius) { desiredMovement.Value = FractionalHex.Zero; return; } var speed = unitSpeeds[entityIndex]; var separationValue = unitSeparations[entityIndex]; var groupalSeparationValues = unitGroupalSeparations[entityIndex]; var separationCount = unitSeparationCounts[entityIndex]; var groupIndex = groupIndices[entityIndex]; var siblinCount = groupCount[groupIndex]; var cohesionValue = groupCohesions[groupIndex]; var alignementValue = groupAlignements[groupIndex]; var cohesionWeight = steering.cohesionWeight; var alignementWeight = steering.alineationWeight; var separationWeight = steering.separationWeight; var groupalSeparationWeight = steering.groupalSeparationWeight; var flockWeight = steering.flockWeight; var targetWeight = steering.targetWeight; var previousDirectionWeight = steering.previousDirectionWeight; var groupalSeparationDirection = separationCount != 0 ? (currentPosition - (groupalSeparationValues / separationCount)).NormalizedManhathan() : FractionalHex.Zero; var cohesionDirection = ((cohesionValue / siblinCount) - currentPosition).NormalizedManhathan(); var alignementDirection = (alignementValue / siblinCount).NormalizedManhathan(); var flockDirection = ((separationValue * separationWeight) + (cohesionDirection * cohesionWeight) + (alignementDirection * alignementWeight) + groupalSeparationDirection * groupalSeparationWeight) .Normalized(); var targetDirection = (target.TargetPosition - currentPosition).NormalizedManhathan(); var previousDirection = directionAverage.Value; var finalDirection = ((flockDirection * flockWeight) + (targetDirection * targetWeight) + (previousDirection * previousDirectionWeight)) .NormalizedManhathan(); var maxMovement = speed * MainSimulationLoopSystem.SimulationDeltaTime; desiredMovement.Value = finalDirection * maxMovement; if (NotFullSpeedMovementIsNeeded( finalDirection, maxMovement, currentPosition, target.TargetPosition, out FractionalHex closestPosiblePositionToTarget)) { desiredMovement.Value = closestPosiblePositionToTarget - currentPosition; } else { desiredMovement.Value = finalDirection * maxMovement; } }); #endregion #region group steering var countOfChilds = new Dictionary <Entity, int>(); var childPositionSum = new Dictionary <Entity, FractionalHex>(); Entities.WithAll <OnGroup>().ForEach((Parent parent, ref HexPosition hexPosition) => { var parentEntity = parent.ParentEntity; var pos = hexPosition.HexCoordinates; if (countOfChilds.ContainsKey(parentEntity)) { countOfChilds[parentEntity] += 1; childPositionSum[parentEntity] += pos; } else { countOfChilds.Add(parentEntity, 1); childPositionSum.Add(parentEntity, pos); } }); Entities.WithAll <Group>().ForEach((Entity entity, ref DesiredMovement desiredMovement, ref HexPosition position, ref Speed speed, ref SteeringTarget target) => { var postionDelta = target.TargetPosition - position.HexCoordinates; var distance = postionDelta.Lenght(); if (!countOfChilds.ContainsKey(entity)) { Debug.Log("there are a group without any child in the 'OnGroup' state the group will move to the closest open hex. _steering system_"); if (map != null) { var newTarget = (FractionalHex)MapUtilities.FindClosestOpenHex(position.HexCoordinates, map.map, true); postionDelta = newTarget - position.HexCoordinates; distance = postionDelta.Lenght(); var maxSpeedMovementDistance = speed.Value * MainSimulationLoopSystem.SimulationDeltaTime; if (distance <= maxSpeedMovementDistance) { desiredMovement.Value = postionDelta.NormalizedManhathan() * distance; return; } desiredMovement.Value = postionDelta.NormalizedManhathan() * maxSpeedMovementDistance; } else { Debug.Log("WTF why there is no map?"); desiredMovement.Value = FractionalHex.Zero; } } else { var childAveragePos = childPositionSum[entity] / math.max(1, countOfChilds[entity]); var desiredMovementDistance = GetMovementMagnitudeTowardsDesiredConsideringChildPos( position.HexCoordinates, childAveragePos, target.TargetPosition, speed.Value, WAIT_CHILD_WEIGHT, target.StopAtTarget); if (desiredMovementDistance <= (Fix64)Fix64.Precision) { Debug.Log("desired movement distance = 0, check ther function <GetMovementMagnitudeTowardsDesiredConsideringChildPos>"); } desiredMovementDistance *= MainSimulationLoopSystem.SimulationDeltaTime; var maxSpeedMovementDistance = speed.Value * MainSimulationLoopSystem.SimulationDeltaTime; if (distance <= Fix64.Zero) { desiredMovement.Value = FractionalHex.Zero; return; } if (target.StopAtTarget) { if (distance <= desiredMovementDistance) { desiredMovement.Value = postionDelta.NormalizedManhathan() * distance; return; } } desiredMovement.Value = postionDelta.NormalizedManhathan() * desiredMovementDistance; } }); #endregion }
protected override void OnUpdate() { var activeMap = MapManager.ActiveMap; if (activeMap == null) { Debug.LogError("Active map needed for the collision system"); return; } int colliderCount = m_ColliderEntityQuery.CalculateEntityCount(); var positions = new FractionalHex[colliderCount]; var collidersRadious = new Fix64[colliderCount]; var collidersLayers = new ColliderLayer[colliderCount]; var teams = new int[colliderCount]; var areActing = new bool[colliderCount]; //init the collections int collectionIndex = 0; Entities.ForEach((Entity entity, ref HexPosition hexPosition, ref Collider collider, ref Team team) => { var position = hexPosition.HexCoordinates; positions[collectionIndex] = position; collidersRadious[collectionIndex] = collider.Radius; collidersLayers[collectionIndex] = collider.Layer; teams[collectionIndex] = team.Number; if (EntityManager.HasComponent <IsActing>(entity)) { areActing[collectionIndex] = true; } else { areActing[collectionIndex] = false; } collectionIndex++; }); var pointsSorted = SpartialSortUtils.GetPointsSpartiallySorted(positions, 1); //collide Entities.ForEach((ref HexPosition hexPosition, ref Collider collider, ref Team team) => { if (!layerCollisionMatrix.TryGetValue(collider.Layer, out ColliderFlags colliderFlag)) { throw new System.Exception("Assign the collider layer type on the collision matrix!"); } else if (colliderFlag == ColliderFlags.NONE) { return; } FractionalHex position = hexPosition.HexCoordinates; //FractionalHex prevPos = hexPosition.PrevPosition; bool areHexOnCenter = activeMap.map.MovementMapValues.TryGetValue(position.Round(), out bool centerIsWalkable); bool centerOnWalkableHex = areHexOnCenter && centerIsWalkable; if (!centerOnWalkableHex) { FractionalHex closestOpenHex = (FractionalHex)MapUtilities.FindClosestOpenHex(position, activeMap.map, true); FractionalHex collisionResolutionDir = (closestOpenHex - position).NormalizedManhathan(); hexPosition.HexCoordinates = position + (collisionResolutionDir * collider.Radius); } else if (CollisionTestAgainstMap(collider.Radius, position, activeMap, out List <FractionalHex> collidingResponseVectors)) { var colisionIntensity = Fix64.Zero;; var colisionVectorSum = (FractionalHex)Hex.Zero; foreach (var vector in collidingResponseVectors) { if (vector.Lenght() > colisionIntensity) { colisionIntensity = vector.Lenght(); } colisionVectorSum += vector; } var direction = colisionVectorSum.NormalizedManhathan(); hexPosition.HexCoordinates = position + colisionVectorSum;//direction * colisionIntensity; } else //collide with other colliders { int thisIndex; var pointsToCheck = SpartialSortUtils.GetFilteredPoints(position, pointsSorted, out thisIndex); var collidableIndices = GetCollidableCollidersIndices(collider, pointsToCheck, collidersLayers); //SimpleCollisionCheckAndResponse(ref hexPosition, collider.Radious, positions, collidersRadious, teams, thisIndex, collidableIndices); //vamos a intentar una collision de dos pasos. DoubleStepCollision(ref hexPosition, collider.Radius, team, positions, collidersRadious, teams, areActing, thisIndex, collidableIndices); } }); }
protected override void OnUpdate() { ActiveMap activeMap = MapManager.ActiveMap; if (activeMap == null) { return; } Dictionary <Entity, List <Hex> > Paths = new Dictionary <Entity, List <Hex> >(); //clean buffer and restart waypoint Entities.WithAll <PathWaypoint, TriggerPathfinding>().ForEach((Entity entity, ref PathWaypointIndex waypointIndex) => { DynamicBuffer <PathWaypoint> buffer = World.EntityManager.GetBuffer <PathWaypoint>(entity); buffer.Clear(); waypointIndex = new PathWaypointIndex() { Value = 0 }; }); //shortestPath Entities.ForEach((Entity entity, ref TriggerPathfinding pathSolicitude, ref HexPosition hexPosition) => { var path = new List <Hex>(); Hex startHex = hexPosition.HexCoordinates.Round(); Hex destinationHex = pathSolicitude.Destination; if (!activeMap.map.MovementMapValues.TryGetValue(destinationHex, out bool c)) { destinationHex = MapUtilities.FindClosestOpenHex((FractionalHex)destinationHex, activeMap.map, true); } else if (!c) { destinationHex = MapUtilities.FindClosestOpenHex((FractionalHex)destinationHex, activeMap.map, true); } if (!activeMap.map.MovementMapValues.TryGetValue(startHex, out bool b)) { startHex = MapUtilities.FindClosestOpenHex(hexPosition.HexCoordinates, activeMap.map, true); } else if (!b) { startHex = MapUtilities.FindClosestOpenHex(hexPosition.HexCoordinates, activeMap.map, true); } if (startHex == destinationHex) { path.Add(startHex); Paths.Add(entity, path); return; } var startNode = new PathfindingNode(startHex, 0, 0, startHex); var openList = new NativeList <PathfindingNode>(Allocator.Temp); var closedList = new NativeList <PathfindingNode>(Allocator.Temp); openList.Add(startNode); while (openList.Length > 0) { var currentNode = openList[0]; for (int i = 1; i < openList.Length; i++) { var scanNode = openList[i]; if (currentNode.fCost > scanNode.fCost || currentNode.fCost == scanNode.fCost && currentNode.hCost > scanNode.hCost) { currentNode = scanNode; } } openList.RemoveAtSwapBack(openList.IndexOf(currentNode)); closedList.Add(currentNode); //enters here when the path have been found if (currentNode.Equals(destinationHex)) { var reversePath = new List <Hex>(); //esto no incluye el inicial. while (currentNode != startNode) { reversePath.Add(currentNode.hex); currentNode = closedList[closedList.IndexOf(currentNode.parent)]; } //add starting node. reversePath.Add(currentNode.hex); reversePath.Reverse(); path.AddRange(reversePath); //Debug.Log($"Pathfinding called. start hex: {startHex} dest hex: {destinationHex}."); //for (int i = 0; i < path.Count; i++) //{ // Debug.Log($"waypoint {i}: {path[i]}"); //} var simplifiedPath = new List <Hex>(HexFunnelAlgorithm(path, true)); Paths.Add(entity, simplifiedPath); //var path0 = new List<Hex>(path.GetRange(1, path.Count - 1)); //Paths.Add(entity, path0); openList.Dispose(); closedList.Dispose(); return; } for (int i = 0; i < 6; i++) { var neightbor = currentNode.hex.Neightbor(i); if (activeMap.map.MovementMapValues.ContainsKey(neightbor)) { //Este check debe ser actualizado! if (!MapUtilities.IsTraversable(neightbor, currentNode.hex, MapUtilities.MapType.MOVEMENT) || closedList.Contains(neightbor))//!activeMap.map.MovementMapValues[neightbor] { continue; } if (!openList.Contains(neightbor)) { var neightborNode = new PathfindingNode(neightbor, currentNode.gCost + 1, neightbor.Distance(destinationHex), currentNode.hex); openList.Add(neightborNode); } else { int indexOfNeightbor = openList.IndexOf(neightbor); var neightborNode = openList[indexOfNeightbor]; int newMovementCostToNeighbor = currentNode.gCost + 1; if (newMovementCostToNeighbor < neightborNode.gCost) { openList[indexOfNeightbor] = new PathfindingNode(neightbor, newMovementCostToNeighbor, neightbor.Distance(destinationHex), currentNode.hex); } } } } } //we may end up here if the destinaation isn't recheable from the start //a posible solution is to return the path to the closest reachable hex to the destination Debug.LogError($"The pathfinding was a failure for of index:{entity.Index}. The start node is: {startHex} and is open:{activeMap.map.MovementMapValues[startHex]}. the end node is: {destinationHex} and is open:{activeMap.map.MovementMapValues[destinationHex]}"); openList.Dispose(); closedList.Dispose(); }); //clean the buffer and then add the path there Entities.WithAll <PathWaypoint>().ForEach((Entity entity, ref TriggerPathfinding pathSolicitude) => { DynamicBuffer <PathWaypoint> buffer = World.EntityManager.GetBuffer <PathWaypoint>(entity); buffer.Clear(); List <Hex> path; if (Paths.TryGetValue(entity, out path)) { foreach (Hex waypoint in path) { buffer.Add(new PathWaypoint() { Value = waypoint }); } } else { Debug.LogError("the entity doesn't have a path. this is because the pathfinding failed before"); } }); //remove path solicitude Entities.ForEach((Entity entity, ref TriggerPathfinding pathSolicitude) => { PostUpdateCommands.RemoveComponent <TriggerPathfinding>(entity); }); }