/******************************************************************************* * Copyright 2014 See AUTHORS file. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. ******************************************************************************/ package com.badlogic.gdx.ai.tests.steer.box2d.tests; import com.badlogic.gdx.Gdx; import com.badlogic.gdx.ai.steer.behaviors.PrioritySteering; import com.badlogic.gdx.ai.steer.behaviors.RaycastObstacleAvoidance; import com.badlogic.gdx.ai.steer.behaviors.Wander; import com.badlogic.gdx.ai.steer.limiters.LinearAccelerationLimiter; import com.badlogic.gdx.ai.steer.utils.rays.CentralRayWithWhiskersConfiguration; import com.badlogic.gdx.ai.steer.utils.rays.ParallelSideRayConfiguration; import com.badlogic.gdx.ai.steer.utils.rays.RayConfigurationBase; import com.badlogic.gdx.ai.steer.utils.rays.SingleRayConfiguration; import com.badlogic.gdx.ai.tests.SteeringBehaviorsTest; import com.badlogic.gdx.ai.tests.steer.box2d.Box2dRaycastCollisionDetector; import com.badlogic.gdx.ai.tests.steer.box2d.Box2dSteeringEntity; import com.badlogic.gdx.ai.tests.steer.box2d.Box2dSteeringTest; import com.badlogic.gdx.ai.utils.Ray; import com.badlogic.gdx.ai.utils.RaycastCollisionDetector; import com.badlogic.gdx.graphics.g2d.Batch; import com.badlogic.gdx.graphics.g2d.SpriteBatch; import com.badlogic.gdx.graphics.glutils.ShapeRenderer; import com.badlogic.gdx.graphics.glutils.ShapeRenderer.ShapeType; import com.badlogic.gdx.math.MathUtils; import com.badlogic.gdx.math.Vector2; import com.badlogic.gdx.physics.box2d.Body; import com.badlogic.gdx.physics.box2d.BodyDef; import com.badlogic.gdx.physics.box2d.BodyDef.BodyType; import com.badlogic.gdx.physics.box2d.FixtureDef; import com.badlogic.gdx.physics.box2d.PolygonShape; import com.badlogic.gdx.scenes.scene2d.Actor; import com.badlogic.gdx.scenes.scene2d.InputEvent; import com.badlogic.gdx.scenes.scene2d.ui.CheckBox; import com.badlogic.gdx.scenes.scene2d.ui.Label; import com.badlogic.gdx.scenes.scene2d.ui.SelectBox; import com.badlogic.gdx.scenes.scene2d.ui.Slider; import com.badlogic.gdx.scenes.scene2d.ui.Table; import com.badlogic.gdx.scenes.scene2d.utils.ChangeListener; import com.badlogic.gdx.scenes.scene2d.utils.ClickListener; /** A class to test and experiment with the {@link RaycastObstacleAvoidance} behavior. * * @autor davebaol */ public class Box2dRaycastObstacleAvoidanceTest extends Box2dSteeringTest { Box2dSteeringEntity character; int rayConfigurationIndex; RayConfigurationBase<Vector2>[] rayConfigurations; RaycastObstacleAvoidance<Vector2> raycastObstacleAvoidanceSB; boolean drawDebug; ShapeRenderer shapeRenderer; private Body[] walls; private int[] walls_hw; private int[] walls_hh; private Vector2 tmp = new Vector2(); private Vector2 tmp2 = new Vector2(); private Batch spriteBatch; public Box2dRaycastObstacleAvoidanceTest (SteeringBehaviorsTest container) { super(container, "Raycast Obstacle Avoidance"); } @Override public void create () { super.create(); drawDebug = true; shapeRenderer = new ShapeRenderer(); spriteBatch = new SpriteBatch(); createRandomWalls(8); character = createSteeringEntity(world, container.greenFish); character.setMaxLinearSpeed(2); character.setMaxLinearAcceleration(1); @SuppressWarnings("unchecked") RayConfigurationBase<Vector2>[] localRayConfigurations = new RayConfigurationBase[] { new SingleRayConfiguration<Vector2>(character, Box2dSteeringTest.pixelsToMeters(100)), new ParallelSideRayConfiguration<Vector2>(character, Box2dSteeringTest.pixelsToMeters(100), character.getBoundingRadius()), new CentralRayWithWhiskersConfiguration<Vector2>(character, Box2dSteeringTest.pixelsToMeters(100), Box2dSteeringTest.pixelsToMeters(40), 35 * MathUtils.degreesToRadians)}; rayConfigurations = localRayConfigurations; rayConfigurationIndex = 0; RaycastCollisionDetector<Vector2> raycastCollisionDetector = new Box2dRaycastCollisionDetector(world); raycastObstacleAvoidanceSB = new RaycastObstacleAvoidance<Vector2>(character, rayConfigurations[rayConfigurationIndex], raycastCollisionDetector, Box2dSteeringTest.pixelsToMeters(1000)); Wander<Vector2> wanderSB = new Wander<Vector2>(character) // // Don't use Face internally because independent facing is off .setFaceEnabled(false) // // We don't need a limiter supporting angular components because Face is not used // No need to call setAlignTolerance, setDecelerationRadius and setTimeToTarget for the same reason .setLimiter(new LinearAccelerationLimiter(4)) // .setWanderOffset(3) // .setWanderOrientation(5) // .setWanderRadius(1) // .setWanderRate(MathUtils.PI2 * 4); PrioritySteering<Vector2> prioritySteeringSB = new PrioritySteering<Vector2>(character, 0.0001f) // .add(raycastObstacleAvoidanceSB) // .add(wanderSB); character.setSteeringBehavior(prioritySteeringSB); inputProcessor = null; Table detailTable = new Table(container.skin); detailTable.row(); addMaxLinearAccelerationController(detailTable, character, 0, 30, .5f); detailTable.row(); final Label labelDistFromBoundary = new Label("Distance from Boundary [" + raycastObstacleAvoidanceSB.getDistanceFromBoundary() + "]", container.skin); detailTable.add(labelDistFromBoundary); detailTable.row(); Slider distFromBoundary = new Slider(0, 60, 1f, false, container.skin); distFromBoundary.setValue(raycastObstacleAvoidanceSB.getDistanceFromBoundary()); distFromBoundary.addListener(new ChangeListener() { @Override public void changed (ChangeEvent event, Actor actor) { Slider slider = (Slider)actor; raycastObstacleAvoidanceSB.setDistanceFromBoundary(slider.getValue()); labelDistFromBoundary.setText("Distance from Boundary [" + slider.getValue() + "]"); } }); detailTable.add(distFromBoundary); detailTable.row(); final Label labelRayConfig = new Label("Ray Configuration", container.skin); detailTable.add(labelRayConfig); detailTable.row(); SelectBox<String> rayConfig = new SelectBox<String>(container.skin); rayConfig.setItems(new String[] {"Single Ray", "Parallel Side Rays", "Central Ray with Whiskers"}); rayConfig.setSelectedIndex(0); rayConfig.addListener(new ChangeListener() { @SuppressWarnings("unchecked") @Override public void changed (ChangeEvent event, Actor actor) { SelectBox<String> selectBox = (SelectBox<String>)actor; rayConfigurationIndex = selectBox.getSelectedIndex(); raycastObstacleAvoidanceSB.setRayConfiguration(rayConfigurations[rayConfigurationIndex]); } }); detailTable.add(rayConfig); detailTable.row(); addSeparator(detailTable); detailTable.row(); CheckBox debug = new CheckBox("Draw Rays", container.skin); debug.setChecked(drawDebug); debug.addListener(new ClickListener() { @Override public void clicked (InputEvent event, float x, float y) { CheckBox checkBox = (CheckBox)event.getListenerActor(); drawDebug = checkBox.isChecked(); } }); detailTable.add(debug); detailTable.row(); addSeparator(detailTable); detailTable.row(); addMaxLinearSpeedController(detailTable, character, 0, 15, .5f); detailWindow = createDetailWindow(detailTable); } @Override public void update () { super.update(); // Update the character character.update(Gdx.graphics.getDeltaTime()); } @Override public void draw () { // Draw the walls for (int i = 0; i < walls.length; i++) { renderBox(shapeRenderer, walls[i], walls_hw[i], walls_hh[i]); } if (drawDebug) { Ray<Vector2>[] rays = rayConfigurations[rayConfigurationIndex].getRays(); shapeRenderer.begin(ShapeType.Line); shapeRenderer.setColor(1, 0, 0, 1); transform.idt(); shapeRenderer.setTransformMatrix(transform); for (int i = 0; i < rays.length; i++) { Ray<Vector2> ray = rays[i]; tmp.set(ray.start); tmp.x = Box2dSteeringTest.metersToPixels(tmp.x); tmp.y = Box2dSteeringTest.metersToPixels(tmp.y); tmp2.set(ray.end); tmp2.x = Box2dSteeringTest.metersToPixels(tmp2.x); tmp2.y = Box2dSteeringTest.metersToPixels(tmp2.y); shapeRenderer.line(tmp, tmp2); } shapeRenderer.end(); } // Draw the character spriteBatch.begin(); character.draw(spriteBatch); spriteBatch.end(); } @Override public void dispose () { super.dispose(); shapeRenderer.dispose(); spriteBatch.dispose(); } private void createRandomWalls (int n) { PolygonShape groundPoly = new PolygonShape(); BodyDef groundBodyDef = new BodyDef(); groundBodyDef.type = BodyType.StaticBody; FixtureDef fixtureDef = new FixtureDef(); fixtureDef.shape = groundPoly; fixtureDef.filter.groupIndex = 0; walls = new Body[n]; walls_hw = new int[n]; walls_hh = new int[n]; for (int i = 0; i < n; i++) { groundBodyDef.position.set(Box2dSteeringTest.pixelsToMeters(MathUtils.random(50, (int)container.stageWidth - 50)), Box2dSteeringTest.pixelsToMeters(MathUtils.random(50, (int)container.stageHeight - 50))); walls[i] = world.createBody(groundBodyDef); walls_hw[i] = (int)MathUtils.randomTriangular(20, 150); walls_hh[i] = (int)MathUtils.randomTriangular(30, 80); groundPoly.setAsBox(Box2dSteeringTest.pixelsToMeters(walls_hw[i]), Box2dSteeringTest.pixelsToMeters(walls_hh[i])); walls[i].createFixture(fixtureDef); } groundPoly.dispose(); } }