/** Dispatches any delayed telegrams with a timestamp that has expired. Dispatched telegrams are removed from the queue. * <p> * This method must be called regularly from inside the main game loop to facilitate the correct and timely dispatch of any * delayed messages. Notice that the message dispatcher internally calls {@link Timepiece#getTime() * GdxAI.getTimepiece().getTime()} to get the current AI time and properly dispatch delayed messages. This means that * <ul> * <li>if you forget to {@link Timepiece#update(float) update the timepiece} the delayed messages won't be dispatched.</li> * <li>ideally the timepiece should be updated before the message dispatcher.</li> * </ul> */ public void update () { float currentTime = GdxAI.getTimepiece().getTime(); // Peek at the queue to see if any telegrams need dispatching. // Remove all telegrams from the front of the queue that have gone // past their time stamp. Telegram telegram; while ((telegram = queue.peek()) != null) { // Exit loop if the telegram is in the future if (telegram.getTimestamp() > currentTime) break; if (debugEnabled) { GdxAI.getLogger().info(LOG_TAG, "Queued telegram ready for dispatch: Sent to " + telegram.receiver + ". Message code is " + telegram.message); } // Send the telegram to the recipient discharge(telegram); // Remove it from the queue queue.poll(); } }
/** Returns the airborne time and sets the {@code outVelocity} vector to the airborne planar velocity required to achieve the * jump. If the jump is not achievable -1 is returned and the {@code outVelocity} vector remains unchanged. * <p> * Be aware that you should avoid using unlimited or very high max velocity, because this might produce a time of flight close * to 0. Actually, the motion equation for T has 2 solutions and Jump always try to use the fastest time. * @param outVelocity the output vector where the airborne planar velocity is calculated * @param jumpDescriptor the jump descriptor * @param maxLinearSpeed the maximum linear speed that can be used to achieve the jump * @return the time of flight or -1 if the jump is not achievable using the given max linear speed. */ public float calculateAirborneTimeAndVelocity (T outVelocity, JumpDescriptor<T> jumpDescriptor, float maxLinearSpeed) { float g = gravityComponentHandler.getComponent(gravity); // Calculate the first jump time, see time of flight at http://hyperphysics.phy-astr.gsu.edu/hbase/traj.html // Notice that the equation has 2 solutions. We'd ideally like to achieve the jump in the fastest time // possible, so we want to use the smaller of the two values. However, this time value might give us // an impossible launch velocity (required speed greater than the max), so we need to check and // use the higher value if necessary. float sqrtTerm = (float)Math.sqrt(2f * g * gravityComponentHandler.getComponent(jumpDescriptor.delta) + maxVerticalVelocity * maxVerticalVelocity); float time = (-maxVerticalVelocity + sqrtTerm) / g; if (DEBUG_ENABLED) GdxAI.getLogger().info("Jump", "1st jump time = " + time); // Check if we can use it if (!checkAirborneTimeAndCalculateVelocity(outVelocity, time, jumpDescriptor, maxLinearSpeed)) { // Otherwise try the other time time = (-maxVerticalVelocity - sqrtTerm) / g; if (DEBUG_ENABLED) GdxAI.getLogger().info("Jump", "2nd jump time = " + time); if (!checkAirborneTimeAndCalculateVelocity(outVelocity, time, jumpDescriptor, maxLinearSpeed)) { return -1f; // Unachievable jump } } return time; // Achievable jump }
private boolean checkAirborneTimeAndCalculateVelocity (T outVelocity, float time, JumpDescriptor<T> jumpDescriptor, float maxLinearSpeed) { // Calculate the planar velocity planarVelocity.set(jumpDescriptor.delta).scl(1f / time); gravityComponentHandler.setComponent(planarVelocity, 0f); // Check the planar linear speed if (planarVelocity.len2() < maxLinearSpeed * maxLinearSpeed) { // We have a valid solution, so store it by merging vertical and non-vertical axes float verticalValue = gravityComponentHandler.getComponent(outVelocity); gravityComponentHandler.setComponent(outVelocity.set(planarVelocity), verticalValue); if (DEBUG_ENABLED) GdxAI.getLogger().info("Jump", "targetLinearVelocity = " + outVelocity + "; targetLinearSpeed = " + outVelocity.len()); return true; } return false; }
public void update(float deltaTime) { // Update AI time GdxAI.getTimepiece().update(deltaTime); // Dispatch delayed messages MessageManager.getInstance().update(); // Update Bullet simulation // On default fixedTimeStep = 1/60, small objects (the stick) will fall through // the ground (the ground has relatively big triangles). dynamicsWorld.stepSimulation(deltaTime, 10, 1f / 240f); for (GameObject object : objectsById.values()) { if (object != null) { object.update(deltaTime); } } }
protected void monitorAnimationTransition(DogCharacter dog, TaskAnimation ta) { if (dog.monitoredTaskAnimation != ta) { if (dog.currentTaskAnimation != null) { dog.monitoredTaskAnimation = ta; dog.switchAnimationTime = GdxAI.getTimepiece().getTime() + 0.2f; return; } } else if (dog.switchAnimationTime < GdxAI.getTimepiece().getTime()) { return; } // Start the new animation since the dog has maintained appropriate speed for long enough dog.currentTaskAnimation = ta; dog.monitoredTaskAnimation = null; dog.switchAnimationTime = -1; startAnimation(dog); }
@Override public void render () { Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT); // Render current steering behavior test if (currentTest != null) { if (!pauseButton.isChecked()) { // Update AI time GdxAI.getTimepiece().update(Gdx.graphics.getDeltaTime()); // Update test currentTest.update(); } // Draw test currentTest.draw(); } stage.act(); stage.draw(); }
@Override public void render () { float delta = Gdx.graphics.getDeltaTime(); elapsedTime += delta; // Update time GdxAI.getTimepiece().update(delta); if (elapsedTime > 0.8f) { // Update Bob and his wife bob.update(elapsedTime); elsa.update(elapsedTime); // Dispatch any delayed messages MessageManager.getInstance().update(); elapsedTime = 0; } }
public void create () { lastUpdateTime = 0; testStack = new Stack(); container.stage.getRoot().addActorAt(0, testStack); testStack.setSize(container.stageWidth, container.stageHeight); testStack.add(testTable = new Table() { @Override public void act (float delta) { float time = GdxAI.getTimepiece().getTime(); if (lastUpdateTime != time) { lastUpdateTime = time; super.act(GdxAI.getTimepiece().getDeltaTime()); } } }); testStack.layout(); }
@Override public void update () { super.update(); // Should the character switch to Jump behavior? if (character.getSteeringBehavior() == seekSB) { if (character.getPosition().x >= jumpDescriptor.takeoffPosition.x - runUpLength && character.getPosition().x <= jumpDescriptor.landingPosition.x) { System.out.println("Switched to Jump behavior. Taking a run up..."); // System.out.println("run up length = " + distFromTakeoffPoint); // character.body.setDamping(0, 0); // System.out.println("friction: " + character.body.getFriction()); // character.body.setFriction(0); System.out.println("owner.linearVelocity = " + character.getLinearVelocity() + "; owner.linearSpeed = " + character.getLinearVelocity().len()); character.setSteeringBehavior(jumpSB); } } // Update the character character.update(GdxAI.getTimepiece().getDeltaTime()); }
@Override public void create () { lastUpdateTime = 0; testStack = new Stack(); container.stage.getRoot().addActorAt(0, testStack); testStack.setSize(container.stageWidth, container.stageHeight); testStack.add(testTable = new Table() { @Override public void act (float delta) { float time = GdxAI.getTimepiece().getTime(); if (lastUpdateTime != time) { lastUpdateTime = time; super.act(GdxAI.getTimepiece().getDeltaTime()); } } }); testStack.layout(); }
/** Scans the queue and passes pending messages to the given callback in any particular order. * <p> * Typically this method is used to save (serialize) pending messages and restore (deserialize and schedule) them back on game * loading. * @param callback The callback used to report pending messages individually. **/ public void scanQueue (PendingMessageCallback callback) { float currentTime = GdxAI.getTimepiece().getTime(); int queueSize = queue.size(); for (int i = 0; i < queueSize; i++) { Telegram telegram = queue.get(i); callback.report(telegram.getTimestamp() - currentTime, telegram.sender, telegram.receiver, telegram.message, telegram.extraInfo, telegram.returnReceiptStatus); } }
/** This method is used by {@link #dispatchMessage(float, Telegraph, Telegraph, int, Object) dispatchMessage} for immediate * telegrams and {@link #update(float) update} for delayed telegrams. It first calls the message handling method of the * receiving agents with the specified telegram then returns the telegram to the pool. * @param telegram the telegram to discharge */ private void discharge (Telegram telegram) { if (telegram.receiver != null) { // Dispatch the telegram to the receiver specified by the telegram itself if (!telegram.receiver.handleMessage(telegram)) { // Telegram could not be handled if (debugEnabled) GdxAI.getLogger().info(LOG_TAG, "Message " + telegram.message + " not handled"); } } else { // Dispatch the telegram to all the registered receivers int handledCount = 0; Array<Telegraph> listeners = msgListeners.get(telegram.message); if (listeners != null) { for (int i = 0; i < listeners.size; i++) { if (listeners.get(i).handleMessage(telegram)) { handledCount++; } } } // Telegram could not be handled if (debugEnabled && handledCount == 0) GdxAI.getLogger().info(LOG_TAG, "Message " + telegram.message + " not handled"); } if (telegram.returnReceiptStatus == Telegram.RETURN_RECEIPT_NEEDED) { // Use this telegram to send the return receipt telegram.receiver = telegram.sender; telegram.sender = this; telegram.returnReceiptStatus = Telegram.RETURN_RECEIPT_SENT; discharge(telegram); } else { // Release the telegram to the pool POOL.free(telegram); } }
@Override public SteeringAcceleration<T> calculateRealSteering (SteeringAcceleration<T> steering) { // Check if we have a trajectory, and create one if not. if (target == null) { target = calculateTarget(); callback.reportAchievability(isJumpAchievable); } // If the trajectory is zero, return no steering acceleration if (!isJumpAchievable) return steering.setZero(); // Check if the owner has reached target position and velocity with acceptable tolerance if (owner.getPosition().epsilonEquals(target.getPosition(), takeoffPositionTolerance)) { if (DEBUG_ENABLED) GdxAI.getLogger().info("Jump", "Good position!!!"); if (owner.getLinearVelocity().epsilonEquals(target.getLinearVelocity(), takeoffVelocityTolerance)) { if (DEBUG_ENABLED) GdxAI.getLogger().info("Jump", "Good Velocity!!!"); isJumpAchievable = false; // Perform the jump, and return no steering (the owner is airborne, no need to steer). callback.takeoff(maxVerticalVelocity, airborneTime); return steering.setZero(); } else { if (DEBUG_ENABLED) GdxAI.getLogger().info("Jump", "Bad Velocity: Speed diff. = " + planarVelocity.set(target.getLinearVelocity()).sub(owner.getLinearVelocity()).len() + ", diff = (" + planarVelocity + ")"); } } // Delegate to MatchVelocity return super.calculateRealSteering(steering); }
@Override protected SteeringAcceleration<T> calculateRealSteering (SteeringAcceleration<T> steering) { // Update the wander orientation float now = GdxAI.getTimepiece().getTime(); if (lastTime > 0) { float delta = now - lastTime; wanderOrientation += MathUtils.randomTriangular(wanderRate * delta); } lastTime = now; // Calculate the combined target orientation float targetOrientation = wanderOrientation + owner.getOrientation(); // Calculate the center of the wander circle wanderCenter.set(owner.getPosition()).mulAdd(owner.angleToVector(steering.linear, owner.getOrientation()), wanderOffset); // Calculate the target location // Notice that we're using steering.linear as temporary vector internalTargetPosition.set(wanderCenter).mulAdd(owner.angleToVector(steering.linear, targetOrientation), wanderRadius); float maxLinearAcceleration = getActualLimiter().getMaxLinearAcceleration(); if (faceEnabled) { // Delegate to face face(steering, internalTargetPosition); // Set the linear acceleration to be at full // acceleration in the direction of the orientation owner.angleToVector(steering.linear, owner.getOrientation()).scl(maxLinearAcceleration); } else { // Seek the internal target position steering.linear.set(internalTargetPosition).sub(owner.getPosition()).nor().scl(maxLinearAcceleration); // No angular acceleration steering.angular = 0; } return steering; }
@Override public boolean finalizeSearch (long timeToRun) { hpfRequest.pathFound = pathFound; if (pathFound) { // Take the first move of this plan and use it for the next run through endNode = resultPath.get(1); } if (DEBUG) GdxAI.getLogger().debug(TAG, "LevelPathFinder finalizeSearch; status: " + status); return true; }
@Override protected void startStatement (String name, boolean isSubtreeReference, boolean isGuard) { if (btParser.debugLevel > BehaviorTreeParser.DEBUG_LOW) GdxAI.getLogger().debug(TAG, (isGuard? " guard" : " task") + " name '" + name + "'"); this.isSubtreeRef = isSubtreeReference; this.statement = isSubtreeReference ? Statement.TreeTask : checkStatement(name); if (isGuard) { if (statement != Statement.TreeTask) throw new GdxRuntimeException(name + ": only tree's tasks can be guarded"); } statement.enter(this, name, isGuard); }
@Override protected void attribute (String name, Object value) { if (btParser.debugLevel > BehaviorTreeParser.DEBUG_LOW) GdxAI.getLogger().debug(TAG, lineNumber + ": attribute '" + name + " : " + value + "'"); boolean validAttribute = statement.attribute(this, name, value); if (!validAttribute) { if (statement == Statement.TreeTask) { throw stackedTaskException(getCurrentTask(), "unknown attribute '" + name + "'"); } else { throw new GdxRuntimeException(statement.name + ": unknown attribute '" + name + "'"); } } }
private void changeTest (int testIndex) { // Dispose the previous test (if any) if (currentTest != null) currentTest.dispose(); // Add the new test currentTest = tests[testIndex]; Gdx.app.log("BehaviorTreeTests", "***********************************************"); Gdx.app.log("BehaviorTreeTests", "Starting test " + currentTest.getName()); Gdx.app.log("BehaviorTreeTests", "***********************************************"); String description = currentTest.getDescription(); if (description != null) { Gdx.app.log("BehaviorTreeTests", description); Gdx.app.log("BehaviorTreeTests", "***********************************************"); testDescriptionLabel.setText(description); } else { testDescriptionLabel.setText("Run the tree, look at the log and see what happens"); } Stack testStack = new Stack() { @Override public void act (float delta) { boolean paused = pauseButton.isChecked(); getChildren().peek().setVisible(paused); if (!paused) { // Update AI time GdxAI.getTimepiece().update(delta); // Call super super.act(delta); } } }; testStack.add(currentTest.createActor(skin)); testStack.add(new Image(skin, "translucent")); splitPane.setSecondWidget(testStack); }
@Override public void update () { elapsedTime += GdxAI.getTimepiece().getDeltaTime(); if (elapsedTime > 1.5f) { MessageManager.getInstance().dispatchMessage(null, MSG_TIME_TO_ACT); elapsedTime = 0; } }
@Override public boolean handleMessage (Telegram msg) { this.msgCounter = (Integer)msg.extraInfo; this.msgTimestamp = msg.getTimestamp(); if (timerEnabledButton.isChecked()) { float lag = GdxAI.getTimepiece().getTime() - msg.getTimestamp(); lag -= (int)lag; // take the decimal part only (in case the lag is > 1) float delay = 1f - lag; sendMessage(delay, msgCounter + 1); } return true; }
@Override public void render () { Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT); // Update time GdxAI.getTimepiece().update(Gdx.graphics.getDeltaTime()); if (currentTest != null) currentTest.render(); stage.act(); stage.draw(); }
@Override public void update () { super.update(); // Update characters float deltaTime = GdxAI.getTimepiece().getDeltaTime(); for (int i = 0; i < characters.size; i++) { characters.get(i).update(deltaTime); } }
@Override public void update () { super.update(); // Update the character character.update(GdxAI.getTimepiece().getDeltaTime()); }
@Override public void update () { float deltaTime = GdxAI.getTimepiece().getDeltaTime(); // Update box2d world world.step(deltaTime, 8, 3); }
@Override public void update () { MessageManager.getInstance().update(); character.update(GdxAI.getTimepiece().getDeltaTime()); super.update(); }
public void update (float delta) { if (!pauseButton.isChecked() && !gameOver) { // Update AI time GdxAI.getTimepiece().update(delta); // Update behavior trees sequencePredator.btree.step(); parallelPredator.btree.step(); } stage.act(delta); }
public void update (float delta) { if (!pauseButton.isChecked() && !gameOver) { // Update AI time GdxAI.getTimepiece().update(delta); // Update behavior trees resumePredator.btree.step(); joinPredator.btree.step(); } stage.act(delta); }
@Override public boolean search (PathFinderRequest<N> request, long timeToRun) { if (DEBUG) GdxAI.getLogger().debug(TAG, "Enter interruptible HPF; request.status = " + request.status); // Make sure the level request and its control are instantiated if (levelRequest == null) { levelRequest = new LevelPathFinderRequest<N>(); levelRequestControl = new PathFinderRequestControl<N>(); } // We have to initialize the search if the status has just changed if (request.statusChanged) { if (DEBUG) GdxAI.getLogger().debug(TAG, "-- statusChanged"); // Check if we have no path to find if (request.startNode == request.endNode) return true; // Prepare the the level request control levelRequestControl.lastTime = TimeUtils.nanoTime(); // Keep track of the current time levelRequestControl.timeToRun = timeToRun; levelRequestControl.timeTolerance = PathFinderQueue.TIME_TOLERANCE; levelRequestControl.server = null; levelRequestControl.pathFinder = levelPathFinder; // Prepare the level request levelRequest.hpf = this; levelRequest.hpfRequest = request; levelRequest.status = PathFinderRequest.SEARCH_NEW; levelRequest.statusChanged = true; levelRequest.heuristic = request.heuristic; levelRequest.resultPath = request.resultPath; levelRequest.startNode = request.startNode; levelRequest.endNode = request.endNode; levelRequest.levelOfNodes = 0; levelRequest.currentLevel = graph.getLevelCount() - 1; } while (levelRequest.currentLevel >= 0) { // if (DEBUG) GdxAI.getLogger().debug(TAG, "currentLevel = "+levelRequest.currentLevel); boolean finished = levelRequestControl.execute(levelRequest); // if (DEBUG) GdxAI.getLogger().debug(TAG, "finished = "+finished); // if (DEBUG) GdxAI.getLogger().debug(TAG, "pathFound = "+levelRequest.pathFound); // if (finished && !levelRequest.pathFound) return true; if (!finished) { return false; } else { levelRequest.executionFrames = 0; // levelRequest.pathFound = false; levelRequest.status = PathFinderRequest.SEARCH_NEW; levelRequest.statusChanged = true; if (!levelRequest.pathFound) return true; } } if (DEBUG) GdxAI.getLogger().debug(TAG, "-- before exit"); // If we're here we have finished return true; }
@Override public boolean initializeSearch (long timeToRun) { // Reset the status // We can do it here because we know this method completes during this frame, // meaning that it is executed once per request this.executionFrames = 0; this.pathFound = false; this.status = SEARCH_NEW; this.statusChanged = false; do { // Find the start node at current level startNode = hpf.graph.convertNodeBetweenLevels(0, hpfRequest.startNode, currentLevel); // Find the end node at current level // Note that if we're examining level 0 and the current end node, the end node and the // start node have the same parent at level 1 then we can use the end node directly. endNode = hpf.graph.convertNodeBetweenLevels(levelOfNodes, endNode, currentLevel); if (currentLevel == 0) { N currentEndNodeParent = hpf.graph.convertNodeBetweenLevels(0, endNode, 1); if (currentEndNodeParent == hpf.graph.convertNodeBetweenLevels(0, hpfRequest.endNode, 1) && currentEndNodeParent == hpf.graph.convertNodeBetweenLevels(0, hpfRequest.startNode, 1)) { endNode = hpfRequest.endNode; } } // Decrease current level and skip it if start and end node are the same // FIXME the break below is wrong if (DEBUG) GdxAI.getLogger().debug(TAG, "LevelPathFinder initializeSearch"); levelOfNodes = currentLevel; currentLevel--; if (startNode != endNode) break; } while (currentLevel >= 0); // Otherwise we can perform the plan hpf.graph.setLevel(levelOfNodes); resultPath.clear(); return true; }