def closestItem(self, variant=Items.All): ''' Get the closest item on the ground to this agent. Optionally specify additional modifiers for filtering items by an enumerated type. Returns None if no item was found nearby to this agent. ''' # Check for override if self.__shouldPerformActionOverride(self.closestItem): self.__actionOverride.function(*self.__actionOverride.args) return None aPos = self.__position() nearbyEntities = self.nearbyEntities() if variant == Items.All: comparator = Items.All.isMember elif variant == Items.Food: comparator = Items.Food.isMember else: raise Exception("Closest item variant must be an enumerated type") closestDistance = 1000000.0 closestItem = None for entity in nearbyEntities: if comparator(entity.type): ePos = entity.position distance = MathUtils.distanceBetweenPoints(aPos, ePos) if distance < closestDistance: closestDistance = distance closestItem = entity self.__logReports.append(LogUtils.ClosestItemReport(variant, closestItem)) return closestItem
def __calculateTargetYawRate(self, targetPos): ''' Calculate the rate at which to move the agent's POV left/right in order to face an (x,y,z) position. ''' aJSON = self.toJSON() aPos = self.__position() currAngle = aJSON["Yaw"] if aJSON["Yaw"] >= 0 else 360.0 - abs(aJSON["Yaw"]) vec = MathUtils.vectorFromPoints(aPos, targetPos) vec = MathUtils.normalizeVector(vec) # Convert target position to a target angle (360 degrees) if MathUtils.valuesAreEqual(vec.x, 0): if vec.z >= 0: targetAngle = -MathUtils.PI_OVER_TWO else: targetAngle = MathUtils.PI_OVER_TWO else: targetAngle = math.atan(vec.z / vec.x) # Modify target angle based on which quadrant the vector lies in if vec.x <= 0: # quadrant 1 or 2 targetAngle = MathUtils.PI_OVER_TWO + targetAngle else: targetAngle = MathUtils.THREE_PI_OVER_TWO + targetAngle targetAngle = math.degrees(targetAngle) if MathUtils.valuesAreEqual(targetAngle, 360.0): targetAngle = 0 # Get difference between current and target angle if currAngle <= targetAngle: angleDiff = min(targetAngle - currAngle, 360 - targetAngle + currAngle) else: angleDiff = min(currAngle - targetAngle, 360 - currAngle + targetAngle) # Get the turning direction if currAngle > targetAngle and currAngle - targetAngle < 180: turnDirection = -1 elif targetAngle > currAngle and targetAngle - currAngle > 180: turnDirection = -1 else: turnDirection = 1 # Calculate the turning rate rate = MathUtils.affineTransformation(angleDiff, 0.0, 180.0, 0, 1.0) * 4 * turnDirection rate = min(rate, 1) if rate >= 0 else max(rate, -1) return rate
def __isAt(self, targetPos, minTol=0.0, maxTol=STRIKING_DISTANCE): ''' Returns true if the agent is currently at the given (x,y,z) position within tolerance. Tolerance values are as follows: minTol: Agent must be no closer than this value maxTol: Agent must be closer than this value ''' aPos = self.__position() distance = MathUtils.distanceBetweenPointsXZ(aPos, targetPos) if distance >= minTol and distance <= maxTol: return True else: return False
def moveTo(self, entity): ''' Begin moving the agent to the given entity. Returns true if the agent is at the entity, false otherwise. Preconditions: - The agent is looking at the entity ''' # Check for override if self.__shouldPerformActionOverride(self.moveTo): return self.__actionOverride.function(*self.__actionOverride.args) minTol = 0.0 maxTol = STRIKING_DISTANCE # If entity is an agent, represent it as an entity if isinstance(entity, Agent): entity = Entity(entity.id, "agent", entity.__position(), 1) minTol = 2.0 maxTol = GIVING_DISTANCE # Preconditions if not self.__checkPreconditions(self.__isLookingAt(entity.position)): self.stopMoving() return False # Items are a special case since they can be picked up. Once close, lock down on the pickUpItem action. if Items.All.isMember(entity.type): distanceToItem = MathUtils.distanceBetweenPointsXZ(self.__position(), entity.position) if distanceToItem <= PICK_UP_ITEM_LOCKDOWN_DISTANCE: currentInventoryAmt = self.inventory.amountOfItem(entity.type) self.__actionOverride = Agent.ActionOverride(self.__pickUpItem, [entity, currentInventoryAmt]) return self.__pickUpItem(entity, currentInventoryAmt) # Action if self.__moveToPosition(entity.position, minTol, maxTol): self.__stopWalking() self.__logReports.append(LogUtils.MoveToReport(entity)) return True else: return False
def __moveToPosition(self, targetPos, minTol=0.0, maxTol=STRIKING_DISTANCE, hardStop=True): ''' Command the agent to begin walking forwards or backwards in order to reach the given target (x,y,z) position within tolerance. Returns true if the agent is at the target, false otherwise. This function takes several optional arguments: minTol: Agent must be no closer than this value maxTol: Agent must be closer than this value hardStop: Once within tolerance, should the agent come to a complete stop, or slow down ''' aPos = self.__position() distance = MathUtils.distanceBetweenPointsXZ(aPos, targetPos) if distance >= minTol and distance <= maxTol: if hardStop: self.stopMoving() else: self.__startWalking(0.4) return True elif distance > maxTol: self.__startWalking(1) return False else: self.__startWalking(-1) return False
def __isLookingAt(self, targetPos, pitchRate=None, yawRate=None): ''' Returns true if the agent is currently looking at the given (x,y,z) position. Optionally provide the pitch and yaw turning rates if they were already previously calculated. ''' pitchRate = pitchRate if pitchRate != None else self.__calculateTargetPitchRate(targetPos) yawRate = yawRate if yawRate != None else self.__calculateTargetYawRate(targetPos) # Tolerance depends on how close we are to the target aPos = self.__position() distance = MathUtils.distanceBetweenPoints(aPos, targetPos) # If we are close to target and yaw is 1, there is a chance we got stuck and are # circling the target. Step back and return false for this iteration.. # if distance < 2 and yawRate > .9: # self.__startWalking(-1) # time.sleep(0.2) # self.stopMoving() # return False if distance < 2: return abs(pitchRate) <= .4 and abs(yawRate) <= .4 else: return abs(pitchRate) <= .2 and abs(yawRate) <= .2
def __calculateTargetPitchRate(self, targetPos): ''' Calculate the rate at which to move the agent's POV up/down in order to face an (x,y,z) position. ''' aJSON = self.toJSON() aPos = self.__position() currAngle = aJSON["Pitch"] vec = MathUtils.vectorFromPoints(aPos, targetPos) vec = MathUtils.normalizeVector(vec) trimmedVec = Vector(vec.x, 0, vec.z) # Convert target position to a target angle (-90 to 90 degrees) if MathUtils.isZeroVector(trimmedVec): return 0.0 cos = MathUtils.dotProduct(vec, trimmedVec) / (MathUtils.vectorMagnitude(vec) * MathUtils.vectorMagnitude(trimmedVec)) if cos > 1: cos = 1 elif cos < -1: cos = -1 targetAngle = math.acos(cos) if vec.y > 0: targetAngle = -targetAngle targetAngle = math.degrees(targetAngle) # Get difference between current and target angle if currAngle <= targetAngle: angleDiff = targetAngle - currAngle else: angleDiff = currAngle - targetAngle # Get the turning direction if currAngle > targetAngle: turnDirection = -1 else: turnDirection = 1 # Calculate the turning rate rate = MathUtils.affineTransformation(angleDiff, 0.0, 180.0, 0, 1.0) * 4 * turnDirection rate = min(rate, 1) if rate >= 0 else max(rate, -1) return rate